diff --git a/server/biz/GlobalBizSettingsBiz.cs b/server/biz/GlobalBizSettingsBiz.cs index d0abdcb..55467cc 100644 --- a/server/biz/GlobalBizSettingsBiz.cs +++ b/server/biz/GlobalBizSettingsBiz.cs @@ -352,7 +352,8 @@ namespace Sockeye.Biz #if (DEBUG) //Test dev stuff #warning DEV TEST ORDER PROCESSING REMOVE THIS WHEN DONE - await SockBotProcessVendorNotifications.ParseVendorNotificationData(vn, ct, log); + if (!string.IsNullOrWhiteSpace(vn.VendorData)) + await SockBotProcessVendorNotifications.ParseVendorNotificationData(vn, ct, log); #endif @@ -793,7 +794,7 @@ namespace Sockeye.Biz } - + ///////////////////////////////////////////////////////////////////// }//eoc diff --git a/server/generator/SockBotProcessVendorNotifications.cs b/server/generator/SockBotProcessVendorNotifications.cs index 929d3cc..88cb29a 100644 --- a/server/generator/SockBotProcessVendorNotifications.cs +++ b/server/generator/SockBotProcessVendorNotifications.cs @@ -51,7 +51,6 @@ namespace Sockeye.Biz //get a list of all actionable vendor notifications var vnList = await ct.VendorNotification - .AsNoTracking() .Where(z => z.Processed == null) .OrderBy(z => z.Id) .ToListAsync(); @@ -60,8 +59,7 @@ namespace Sockeye.Biz { foreach (var vn in vnList) { - var biz = PurchaseBiz.GetBiz(ct); - var p = new Purchase(); + log.LogDebug($"Processing vendor notification {vn.Id}-{vn.Created}"); @@ -73,15 +71,9 @@ namespace Sockeye.Biz continue; } - - //Parse json vendordata await ParseVendorNotificationData(vn, ct, log); - //save changes - p.Processed = true; - await biz.PutAsync(p); - } } catch (Exception ex) @@ -97,14 +89,17 @@ namespace Sockeye.Biz - internal static async Task ParseVendorNotificationData(VendorNotification vn, AyContext ct, ILogger log) + internal static async Task ParseVendorNotificationData(VendorNotification vn, AyContext ct, ILogger log) { - if (string.IsNullOrWhiteSpace(vn.VendorData)) return; - try { var jData = JObject.Parse(vn.VendorData); + //It's a test purchase, no need to process it any further...or is there?? + if (jData["order_notification"]["purchase"]["is_test"].Value() == true) + { + vn.Processed = DateTime.UtcNow; + } //fundamentally validate the object is a purchase notification if (jData["order_notification"]["purchase"]["purchase_id"] == null) @@ -113,7 +108,11 @@ namespace Sockeye.Biz throw new System.FormatException($"Vendor data unexpected format:{vn.VendorData}"); } - //CUSTOMER create or locate + + + /////////////////////////////////////////////////////////////////////////////////////////// + #region CUSTOMER MAKE OR LOCATED + /////////////////////////////////////////////////////////////////////////////////////////// var jCustomerName = jData["order_notification"]["purchase"]["customer_data"]["reg_name"].Value() ?? throw new System.FormatException($"Vendor data empty reg_name:{vn.VendorData}"); var jCustomerEmail = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"].Value() ?? throw new System.FormatException($"Vendor data empty email:{vn.VendorData}"); var jCustomerAccountNumber = jData["order_notification"]["purchase"]["customer_data"]["shopper_id"].Value();//appears to be mycommerce customer id number hopefully static between orders @@ -122,17 +121,17 @@ namespace Sockeye.Biz //attempt to match to existing customer //account number is most ideal match, name second but could be multiple in sockeye from rockfish sites so name will start the same, finally email if nothing else - Customer c = await ct.Customer.FirstOrDefaultAsync(z => z.AccountNumber == jCustomerAccountNumber) ?? await ct.Customer.FirstOrDefaultAsync(z => z.Name.StartsWith(jCustomerName)) ?? await ct.Customer.FirstOrDefaultAsync(z => z.EmailAddress == jCustomerEmail); - if (c == null) + Customer customer = await ct.Customer.FirstOrDefaultAsync(z => z.AccountNumber == jCustomerAccountNumber) ?? await ct.Customer.FirstOrDefaultAsync(z => z.Name.StartsWith(jCustomerName)) ?? await ct.Customer.FirstOrDefaultAsync(z => z.EmailAddress == jCustomerEmail); + if (customer == null) { //New customer - c = new Customer(); - c.Name = jCustomerName; - c.EmailAddress = jCustomerEmail; - c.AccountNumber = jCustomerAccountNumber; - UpdateCustomerFromVendorData(jData, c); - c = await customerBiz.CreateAsync(c); - if (c == null) + customer = new Customer(); + customer.Name = jCustomerName; + customer.EmailAddress = jCustomerEmail; + customer.AccountNumber = jCustomerAccountNumber; + UpdateCustomerFromVendorData(jData, customer); + customer = await customerBiz.CreateAsync(customer); + if (customer == null) throw new System.ApplicationException($"Error creating new Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } else @@ -143,47 +142,90 @@ namespace Sockeye.Biz //email differs, this can happen and is ok //account number differs if was empty then it's ok. If it wasn't empty and it differs this is a potential problem: // - if (c.EmailAddress != jCustomerEmail) + if (customer.EmailAddress != jCustomerEmail) { - c.EmailAddress = jCustomerEmail;//assume it was empty or has been recently updated + customer.EmailAddress = jCustomerEmail;//assume it was empty or has been recently updated } - - if (c.AccountNumber != jCustomerAccountNumber) + + if (customer.AccountNumber != jCustomerAccountNumber) { - if (string.IsNullOrWhiteSpace(c.AccountNumber)) - c.AccountNumber=jCustomerAccountNumber;//this is ok, there wasn't an account number before so we're just adding it - else{ + if (string.IsNullOrWhiteSpace(customer.AccountNumber)) + customer.AccountNumber = jCustomerAccountNumber;//this is ok, there wasn't an account number before so we're just adding it + else + { // this is problematic, it matched but for some reason the account numbers differ, there's no safe way to process this so alert and bail - throw new System.ApplicationException($"Not processed due to error updating existing Customer: {c.Name} Account numbers differ, expected: {c.AccountNumber} vendor data:{vn.VendorData}"); - } + throw new System.ApplicationException($"Not processed due to error updating existing Customer: {customer.Name} Account numbers differ, expected: {customer.AccountNumber} vendor data:{vn.VendorData}"); + } } - + //refresh - UpdateCustomerFromVendorData(jData, c); - c = await customerBiz.PutAsync(c); - if (c == null) + UpdateCustomerFromVendorData(jData, customer); + customer = await customerBiz.PutAsync(customer); + if (customer == null) throw new System.ApplicationException($"Error updating existing Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } + /////////////////////////////////////////////////////////////////////////////////////////// + #endregion customer make or locate + + /////////////////////////////////////////////////////////////////////////////////////////// + #region MAKE PURCHASE RECORD + /////////////////////////////////////////////////////////////////////////////////////////// var salesOrderNumber = jData["order_notification"]["purchase"]["purchase_id"].Value(); if (await ct.Purchase.AnyAsync(z => z.SalesOrderNumber == salesOrderNumber)) throw new System.ApplicationException($"Sales order already exists: {salesOrderNumber} will not be processed"); - //ok, turn this into a fully realized Purchase record + //iterate purchase items array + var jaPurchaseList = (JArray)jData["order_notification"]["purchase"]["purchase_item"]; - //Product group - p.PGroup = ProductBiz.ProductGroupFromProductCode(p.OurCode); + foreach (JObject jPurchase in jaPurchaseList) + { + Purchase p = new Purchase(); + p.CustomerId = customer.Id; + p.VendorId = vn.VendorId; + p.Notes = vn.VendorData;//redundantly keep it + p.SalesOrderNumber = salesOrderNumber; + p.Currency = jPurchase["currency"].Value(); + var pId = jPurchase["product_id"].Value(); + var product = await ct.Product.AsNoTracking().FirstOrDefaultAsync(z => z.VendorCode == pId) ?? throw new System.ArgumentOutOfRangeException($"Vendor product code:{pId} was not found in Sockeye Products, record not processed"); + p.ProductId = product.Id; + p.PGroup = product.PGroup; + p. + if (p.ProductId == 0) + switch (p.PGroup) + { + case ProductGroup.Misc: + //not a licensed product, probably misc custom or some fee or etc + p.Processed = true;//flag no further processing is required + break; + case ProductGroup.AyaNova7: + break; + case ProductGroup.RavenPerpetual: + break; + case ProductGroup.RavenSubscription: + + break; + default://not set or unrecognized + throw new System.NotSupportedException($"Product group {p.PGroup} not recognized or supported for processing into purchase"); + + } + + } + + /////////////////////////////////////////////////////////////////////////////////////////// + #endregion make purchase record } catch (Exception ex) { var err = $"ParseVendorNotificationData: Purchase record {vn.Id}-{vn.Created} triggered exception, see log"; await NotifyEventHelper.AddOpsProblemEvent(err);//notify, this is serious log.LogError(ex, err); + return false; } }