From 040d8ca8360281af6fa2128aaf629dc7dd6ec8f2 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 23 Jan 2023 01:45:03 +0000 Subject: [PATCH] --- server/biz/GlobalBizSettingsBiz.cs | 12 ++- .../SockBotProcessVendorNotifications.cs | 100 ++++++++++-------- 2 files changed, 62 insertions(+), 50 deletions(-) diff --git a/server/biz/GlobalBizSettingsBiz.cs b/server/biz/GlobalBizSettingsBiz.cs index 885326d..e6fd3d4 100644 --- a/server/biz/GlobalBizSettingsBiz.cs +++ b/server/biz/GlobalBizSettingsBiz.cs @@ -195,7 +195,10 @@ namespace Sockeye.Biz { var CustomerName = jCustomer["name"].Value(); if (multiSite) + { CustomerName += " - " + jSite["name"].Value(); + log.LogInformation($"RFImport MULTISITE CUSTOMER: {CustomerName}"); + } long CurrentCustomerId = 0; //Create customer if we don't have one already @@ -222,9 +225,12 @@ namespace Sockeye.Biz if (jSite["hosted"].Value() == true) c.Tags.Add("hosted"); - var adminEmail = jCustomer["adminEmail"].Value(); - if (!string.IsNullOrWhiteSpace(adminEmail)) - c.Notes += "\nAdmin Email: " + adminEmail; + //In rockfish there were support emails that were people allowed to be contacting us on behalf of the customer + var supportEmail = jCustomer["supportEmail"].Value(); + if (!string.IsNullOrWhiteSpace(supportEmail)) + c.Notes += "\nSupport Emails: " + supportEmail; + //in Rockfish the admin email is the main license related contact and technically the only person responsible to contact us + //usually the same as the purchase email c.EmailAddress = jCustomer["adminEmail"].Value(); CustomerBiz biz = CustomerBiz.GetBiz(ct); var NewObject = await biz.CreateAsync(c); diff --git a/server/generator/SockBotProcessVendorNotifications.cs b/server/generator/SockBotProcessVendorNotifications.cs index 6c90260..c10cfa0 100644 --- a/server/generator/SockBotProcessVendorNotifications.cs +++ b/server/generator/SockBotProcessVendorNotifications.cs @@ -13,11 +13,11 @@ namespace Sockeye.Biz /// - /// Process purchases that are from vendor notification + /// Process vendor notifications /// Turn vendordata into fully filled out purchase /// attempt to match to existing customer or create one if necessary /// - /// A Separate job will make licenses + /// A Separate job will make licenses from the new purchases where feasible /// /// internal static class SockBotProcessVendorNotifications @@ -39,9 +39,6 @@ namespace Sockeye.Biz log.LogDebug("Process vendor notifications starting"); await ProcessVendorNotificationDataIntoPurchases(); - // log.LogDebug("Process licenses from purchases starting"); - // await ProcessPurchasesIntoLicenses(); - lastSweep = DateTime.UtcNow; } @@ -53,58 +50,43 @@ namespace Sockeye.Biz { //get a list of all actionable vendor notifications - var ProcessablePurchaseIdList = await ct.Purchase + var vnList = await ct.VendorNotification .AsNoTracking() - .Where(z => z.Processed == false - && z.ProductId == null) + .Where(z => z.Processed == null) .OrderBy(z => z.Id) - .Select(z => z.Id) .ToListAsync(); try { - foreach (long purchaseId in ProcessablePurchaseIdList) + foreach (var vn in vnList) { var biz = PurchaseBiz.GetBiz(ct); - var p = await biz.GetAsync(purchaseId, false); - if (p == null) + var p = new Purchase(); + + log.LogDebug($"Processing vendor notification {vn.Id}-{vn.Created}"); + + if (string.IsNullOrWhiteSpace(vn.VendorData)) { - //this is a serious issue log and server ops it - var err = $"SockBotProcessPurchases error running job, purchase record id {purchaseId} could not be fetched {biz.GetErrorsAsString}"; - await NotifyEventHelper.AddOpsProblemEvent(err); + var err = $"VendorNotification record {vn.Id}-{vn.Created} has no vendor data"; + await NotifyEventHelper.AddOpsProblemEvent("SockBotProcessVendorNotifications: " + err); log.LogError(err); + continue; } - else - { - log.LogDebug($"Processing purchase {p.Id}-{p.PurchaseDate}"); - - if (string.IsNullOrWhiteSpace(p.VendorData)) - { - var err = $"Purchase record with ID {purchaseId} has no vendor data for sales order {p.SalesOrderNumber}"; - await NotifyEventHelper.AddOpsProblemEvent("SockBotProcessPurchases: " + err); - log.LogError(err); - continue; - } - //Parse json vendordata - //Existing customer or create new one? - //here maybe need to match by vendor provided customer ID?? - //sometimes a user will make a new account for a new purchase but intend it as an addon to v7 - //for v8 it should be straightforward, however if pricing chagnes and new product needs to be purchased they may create a new record - //same dbid for v8 should do the trick + //Parse json vendordata + await ParseVendorNotificationData(vn, ct, log); + //save changes + p.Processed = true; + await biz.PutAsync(p); - //save changes - p.Processed = true; - await biz.PutAsync(p); - } } } catch (Exception ex) { - var err = "SockBotProcessPurchases error running job"; + var err = "SockBotProcessVendorNotifications error running job"; //serious issue requires immediate notification await NotifyEventHelper.AddOpsProblemEvent(err, ex); log.LogError(ex, err); @@ -134,26 +116,50 @@ namespace Sockeye.Biz //CUSTOMER create or locate 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 var customerBiz = CustomerBiz.GetBiz(ct); - Customer c = await ct.Customer.FirstOrDefaultAsync(z => z.Name == jCustomerName) ?? await ct.Customer.FirstOrDefaultAsync(z => z.EmailAddress == jCustomerEmail); + //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) { //New customer c = new Customer(); - c.Name = jCustomerName; - UpdateCustomerFromVendorData(jData, jCustomerEmail, c); + c.EmailAddress = jCustomerEmail; + c.AccountNumber = jCustomerAccountNumber; + UpdateCustomerFromVendorData(jData, c); c = await customerBiz.CreateAsync(c); if (c == null) throw new System.ApplicationException($"Error creating new Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } else { - //existing customer, refresh it - UpdateCustomerFromVendorData(jData, jCustomerEmail, c); + //existing customer + //here there could be several potential issues: + //name differs because it was a separate site in rockfish, it's not cool to change the name + //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) + { + c.EmailAddress = jCustomerEmail;//assume it was empty or has been recently updated + } + + if (c.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{ + // 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}"); + } + } + + //refresh + UpdateCustomerFromVendorData(jData, c); c = await customerBiz.PutAsync(c); if (c == null) throw new System.ApplicationException($"Error updating existing Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); @@ -167,24 +173,23 @@ namespace Sockeye.Biz //ok, turn this into a fully realized Purchase record //Product group + } catch (Exception ex) { - var err = $"ParseVendorNotificationData: Purchase record {vn.Id}-{vn.PurchaseDate} triggered exception, see log"; + 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); } } - private static void UpdateCustomerFromVendorData(JObject jData, string jCustomerEmail, Customer c) + private static void UpdateCustomerFromVendorData(JObject jData, Customer c) { - c.EmailAddress = jCustomerEmail; c.Active = true;//if they just made a purchase they are active even if they weren't before c.DoNotContact = false;//if they just made a purchase they are contactable even if they weren't before - c.AccountNumber = jData["order_notification"]["purchase"]["customer_data"]["shopper_id"].Value();//appears to be mycommerce customer id number hopefully static between orders c.Address = c.PostAddress = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["street1"].Value(); c.City = c.PostCity = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["city"].Value(); c.Region = c.PostRegion = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["address"]["state"].Value(); @@ -201,6 +206,7 @@ namespace Sockeye.Biz c.Notes += "\n"; c.Notes += $"Purchase contact:{firstName} {lastName}, language: {language}\n"; } + }