Files
sockeye/server/generator/SockBotProcessPurchasesIntoLicenses.cs
2023-02-06 22:20:13 +00:00

389 lines
24 KiB
C#

using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using Sockeye.Models;
using Sockeye.Util;
namespace Sockeye.Biz
{
/// <summary>
/// Process purchases into licenses
///
/// </summary>
internal static class SockBotProcessPurchasesIntoLicenses
{
private static ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger("SockBotProcessPurchasesIntoLicenses");
private static DateTime lastSweep = DateTime.MinValue;
private static TimeSpan PROCESS_V7_AGE = new TimeSpan(0, 5, 0);//Don't process a v7 order until at least 5 minutes old as it doesn't always come in at once
#if (DEBUG)
private static TimeSpan PROCESS_EVERY_INTERVAL = new TimeSpan(0, 0, 30);//every 30 seconds during development
#else
private static TimeSpan PROCESS_EVERY_INTERVAL = new TimeSpan(0, 5, 10);//every 5 minutes
#endif
////////////////////////////////////////////////////////////////////////////////////////////////
// DoSweep
//
public static async Task DoWorkAsync()
{
//This will get triggered roughly every minute, but we don't want to check that frequently
if (DateTime.UtcNow - lastSweep < PROCESS_EVERY_INTERVAL)
return;
if (ServerBootConfig.MIGRATING) return;//don't do this during migration (migration is one time only so can remove after up and running)
log.LogDebug("Process purchases into licenses starting");
await ProcessPurchasesIntoLicenses();
lastSweep = DateTime.UtcNow;
}
private static async Task ProcessPurchasesIntoLicenses()
{
using (AyContext ct = Sockeye.Util.ServiceProviderProvider.DBContext)
{
//get a list of all actionable purchases grouped by customer id
var purchaseList = (await ct.Purchase.AsNoTracking()
.Where(z => z.Processed == false
&& z.LicenseId == null
&& z.CustomerId != null
&& (z.PGroup == ProductGroup.AyaNova7 || z.PGroup == ProductGroup.RavenPerpetual || z.PGroup == ProductGroup.RavenSubscription))
.OrderByDescending(z => z.PurchaseDate)
.ToListAsync()).GroupBy(z => (long)z.CustomerId);
try
{
foreach (var purchaseGroup in purchaseList)
{
//Iterate the group and qualify it
bool isRavenPerpetual = false;
bool isRavenSubscription = false;
bool isV7 = false;
bool isLessThanV7Age = false;
var purchaseGroupCustomer = await ct.Customer.AsNoTracking().FirstAsync(z => z.Id == purchaseGroup.First().CustomerId);
foreach (var purchase in purchaseGroup)
{
if (purchase.PGroup == ProductGroup.AyaNova7)
isV7 = true;
if (purchase.PGroup == ProductGroup.RavenPerpetual)
isRavenPerpetual = true;
if (purchase.PGroup == ProductGroup.RavenSubscription)
isRavenSubscription = true;
if (DateTime.UtcNow - purchase.PurchaseDate < PROCESS_V7_AGE)
isLessThanV7Age = true; ;
}
//sanity checks should never be mixed
if ((isRavenPerpetual && isV7))
{
var err = $"SockBotProcessPurchasesIntoLicenses both raven perpetual and v7 in same customer's purchase group First record order number: {purchaseGroup.First().SalesOrderNumber}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}
if ((isRavenSubscription && isV7))
{
var err = $"SockBotProcessPurchasesIntoLicenses both raven subscription and v7 in same customer's purchase group First record order number: {purchaseGroup.First().SalesOrderNumber}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}
if ((isRavenPerpetual && isRavenSubscription))
{
var err = $"SockBotProcessPurchasesIntoLicenses both raven perpetual and raven subscription in same customer's purchase group First record order number: {purchaseGroup.First().SalesOrderNumber}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}
//if v7 skip for this iteration to ensure multiple add-on's have all arrived from mycommerce
if (isV7 && isLessThanV7Age)
continue;
License newLicense = new License();
//Get last license if any, set up some basic stuff common to all license types
var firstPurchase = purchaseGroup.First();
var lastLicense = await ct.License.AsNoTracking().OrderByDescending(z => z.Id).FirstOrDefaultAsync(z => z.CustomerId == firstPurchase.CustomerId && z.PGroup == firstPurchase.PGroup);
newLicense.CustomerId = firstPurchase.CustomerId;
newLicense.Active = false;
newLicense.RegTo = firstPurchase.RegTo;
//if v7 license then lookup last license for same pgroup for same customer, if none then consider it a new license fresh
//if there is one and it's not entirely expired then duplicate and fixup from purchases in this group
if (isV7)
{
newLicense.FetchEmail = purchaseGroupCustomer.EmailAddress;
//is there a prior license?
if (lastLicense != null)
{
//copy all the values to the new license
newLicense.ExportToXLS = lastLicense.ExportToXLS;
newLicense.ExportToXLSExpires = lastLicense.ExportToXLSExpires;
newLicense.ImportExportCSVDuplicate = lastLicense.ImportExportCSVDuplicate;
newLicense.ImportExportCSVDuplicateExpires = lastLicense.ImportExportCSVDuplicateExpires;
newLicense.MaintenanceExpire = lastLicense.MaintenanceExpire;
newLicense.MBI = lastLicense.MBI;
newLicense.MBIExpires = lastLicense.MBIExpires;
newLicense.OLI = lastLicense.OLI;
newLicense.OLIExpires = lastLicense.OLIExpires;
newLicense.OutlookSchedule = lastLicense.OutlookSchedule;
newLicense.OutlookScheduleExpires = lastLicense.OutlookScheduleExpires;
newLicense.PTI = lastLicense.PTI;
newLicense.PTIExpires = lastLicense.PTIExpires;
newLicense.QBI = lastLicense.QBI;
newLicense.QBIExpires = lastLicense.QBIExpires;
newLicense.QBOI = lastLicense.QBOI;
newLicense.QBOIExpires = lastLicense.QBOIExpires;
newLicense.QuickNotification = lastLicense.QuickNotification;
newLicense.QuickNotificationExpires = lastLicense.QuickNotificationExpires;
newLicense.Renewal = true;
newLicense.RI = lastLicense.RI;
newLicense.RIExpires = lastLicense.RIExpires;
newLicense.Tags = lastLicense.Tags;
newLicense.Users = lastLicense.Users;
newLicense.WBI = lastLicense.WBI;
newLicense.WBIExpires = lastLicense.WBIExpires;
newLicense.Wiki = lastLicense.Wiki;
}
//iterate the purchases and update / set the license
foreach (var purchase in purchaseGroup)
{
var product = await ct.Product.AsNoTracking().FirstOrDefaultAsync(z => z.Id == purchase.ProductId);//should *always* exist
if (product == null)
{
var err = $"SockBotProcessPurchasesIntoLicenses purchase: {purchase.Id} has un-matchable product id: {purchase.ProductId}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}
/*
id name productCode price renewPrice
1 Key administration 300093112 3500 3500
2 Custom work 300151145 0 0
3 AyaNova RI 1 year subscription license 300740314 19900 6965
4 Single AyaNova schedulable resource 1 year subscription license 300740315 15900 5565
5 Single AyaNova Lite 1 year subscription license 300740316 6900 2415
6 Up to 5 AyaNova schedulable resource 1 year subscription license 300740317 69500 24325
7 Up to 10 AyaNova schedulable resource 1 year subscription license 300740318 119000 41650
8 Up to 20 AyaNova schedulable resource 1 year subscription license 300740319 198000 69300
9 AyaNova WBI (web browser interface) 1 year subscription license 300740321 9900 3465
10 AyaNova MBI (minimal browser interface) 1 year subscription license 300740322 9900 3465
11 AyaNova QBI(QuickBooks interface) 1 year subscription license 300740323 9900 3465
12 AyaNova PTI(US Peachtree/Sage 50 interface) 1 year subscription license 300740324 9900 3465
13 AyaNova OLI(Outlook interface) 1 year subscription license 300740325 9900 3465
14 Plug-in Outlook Schedule Export 1 year subscription license 300740326 1900 665
15 Plug-in Export to XLS 1 year subscription license 300740327 1900 665
16 Plug-in Quick Notification 1 year subscription license 300740328 1900 665
17 Plug-in importexport.csv duplicate 1 year subscription license 300740329 1900 665
18 Up to 999 AyaNova schedulable resource 1 year subscription license 300741264 15000 5250
19 Up to 15 AyaNova schedulable resource 1 year subscription license 300807973 165000 57750
20 AyaNova QBOI(QuickBooks Online interface) 1 year subscription license 300784766 12900 4515
21 Single AyaNova service techncian perpetual license 301028314 135 100
22 Single AyaNova service techncian 1 year maintenance plan - new 301028317 135 100
23 Single AyaNova service techncian 1 year maintenance plan - active 301028315 100 100
*/
var dtOneYear = DateTime.UtcNow.AddYears(1);
switch (product.VendorCode)
{
case "300740314":
newLicense.RI = true;
newLicense.RIExpires = dtOneYear;
break;
case "300740321":
newLicense.WBI = true;
newLicense.WBIExpires = dtOneYear;
break;
case "300740322":
newLicense.MBI = true;
newLicense.MBIExpires = dtOneYear;
break;
case "300740323":
newLicense.QBI = true;
newLicense.QBIExpires = dtOneYear;
break;
case "300740324":
newLicense.PTI = true;
newLicense.PTIExpires = dtOneYear;
break;
case "300740325":
newLicense.OLI = true;
newLicense.OLIExpires = dtOneYear;
break;
case "300740326":
newLicense.OutlookSchedule = true;
newLicense.OutlookScheduleExpires = dtOneYear;
break;
case "300740327":
newLicense.ExportToXLS = true;
newLicense.ExportToXLSExpires = dtOneYear;
break;
case "300740328":
newLicense.QuickNotification = true;
newLicense.QuickNotificationExpires = dtOneYear;
break;
case "300740329":
newLicense.ImportExportCSVDuplicate = true;
newLicense.ImportExportCSVDuplicateExpires = dtOneYear;
break;
case "300784766":
newLicense.QBOI = true;
newLicense.QBOIExpires = dtOneYear;
break;
//USERS
case "300740315":
newLicense.Users = 1;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "300740317":
newLicense.Users = 5;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "300740318":
newLicense.Users = 10;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "300740319":
newLicense.Users = 20;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "300741264":
newLicense.Users = 999;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "300807973":
newLicense.Users = 15;
newLicense.MaintenanceExpire = dtOneYear;
break;
default:
var err = $"SockBotProcessPurchasesIntoLicenses purchase: {purchase.Id} has product not part of v7 group expected: {product.Name}-{product.VendorCode}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}//switch product code
}//each purchase in purchasegroup
//license done ready to save not active so still requires manual intervention but should be substantially ready to send out by here fingers crossed!
}//if v7 block
else
{
//it's a RAVEN license
//iterate the purchases and update / set the license
foreach (var purchase in purchaseGroup)
{
var product = await ct.Product.AsNoTracking().FirstOrDefaultAsync(z => z.Id == purchase.ProductId);//should *always* exist
if (product == null)
{
var err = $"SockBotProcessPurchasesIntoLicenses purchase: {purchase.Id} has un-matchable product id: {purchase.ProductId}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}
/*
301028317 AyaNova perpetual single user license includes one year maintenance plan perpetual Product 12 months HD1 Aug 15, 2022, 9:07 PM
301028467 AyaNova subscription one user monthly subscriptionmonthly Product monthly HD1 Aug 18, 2022, 1:21 AM
301028468 AyaNova subscription one user yearly subscriptionyearly Product 12 months HD1 Aug 18, 2022, 1:25 AM
301033167 AyaNova subscription additional 250 customer users monthly 250customerusersmonthly Product monthly HD1 Oct 6, 2022, 12:56 AM
301033168 AyaNova subscription additional 250 customer users yearly 250customerusersyearly Product 12 months HD1 Oct 6, 2022, 12:59 AM
*/
//RAVEN licenses have one week padding to be on the safe side
var dtOneYear = DateTime.UtcNow.AddYears(1).AddDays(7);
var dtOneMonth = DateTime.UtcNow.AddMonths(1).AddDays(7);
switch (product.VendorCode)
{
case "301028317"://perpetual
newLicense.Users = purchase.Quantity;
newLicense.MaintenanceExpire = dtOneYear;
break;
case "301028467"://subscription monthly
newLicense.Users = purchase.Quantity;
newLicense.MaintenanceExpire = dtOneMonth;
//FUTURE: for now subscriptions all 20gb if ever add ability to go higher max data with product code etc then remove this
newLicense.MaxDataGB = 20;
break;
case "301028468"://subscription yearly
newLicense.Users = purchase.Quantity;
newLicense.MaintenanceExpire = dtOneYear;
//FUTURE: for now subscriptions all 20gb if ever add ability to go higher max data with product code etc then remove this
newLicense.MaxDataGB = 20;
break;
case "301033167"://Customer users monthly price
case "301033168"://Customer users yearly price
newLicense.CustomerUsers = purchase.Quantity;
break;
default:
var err = $"SockBotProcessPurchasesIntoLicenses purchase: {purchase.Id} has product not part of RAVEN products expected: {product.Name}-{product.VendorCode}";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err);
log.LogError(err);
continue;
}//switch product code
}//each purchase in purchasegroup
}
//we have a new savable license at this point
if (newLicense != null)
{
LicenseBiz lbiz = LicenseBiz.GetBiz(ct);
newLicense = await lbiz.CreateAsync(newLicense);
if (newLicense == null)
{
//did not save, throw an error
throw new System.ApplicationException($"Error creating license from purchases: {lbiz.GetErrorsAsString()}");
}
}
}
}
catch (Exception ex)
{
var err = "SockBotProcessPurchasesIntoLicenses exepected exception error running job";
//serious issue requires immediate notification
await NotifyEventHelper.AddOpsProblemEvent(err, ex);
log.LogError(ex, err);
}
}
}
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons