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 { /// /// Process purchases into licenses /// /// 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; //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) { //get one representative product in the group, first is fine var firstPurchase = purchaseGroup.First(); var lastLicense = await ct.License.AsNoTracking().OrderByDescending(z => z.Id).FirstOrDefaultAsync(z => z.CustomerId == firstPurchase.CustomerId && z.PGroup == firstPurchase.PGroup); var newLicense = new License(); newLicense.CustomerId = firstPurchase.CustomerId; newLicense.Active = false; newLicense.RegTo = firstPurchase.RegTo; 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; } foreach (var purchase in purchaseGroup) { } } //IF raven license and have what is likely a recognizable dbid that ends in an = symbol then just make the new license and cancel the last one if that's necessary (NARRATOR: it wasn't, raven only ever fetches the newest) // log.LogDebug($"Processing purchase id:{purchaseGroup[0].Id},purchasedate:{purchaseGroup.PurchaseDate}, custid:{purchaseGroup.CustomerId}"); } } catch (Exception ex) { var err = "SockBotProcessPurchasesIntoLicenses error running job"; //serious issue requires immediate notification await NotifyEventHelper.AddOpsProblemEvent(err, ex); log.LogError(ex, err); } } } ///////////////////////////////////////////////////////////////////// }//eoc }//eons