From ec6ec2701e2b66864083cc9f2fe144dbcfac74cf Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 23 Jan 2023 00:59:38 +0000 Subject: [PATCH] --- server/Controllers/OrderController.cs | 33 +- server/DataList/PurchaseDataList.cs | 12 +- server/DataList/VendorNotificationDataList.cs | 39 ++ server/biz/BizObjectExistsInDatabase.cs | 2 + server/biz/BizObjectFactory.cs | 2 + server/biz/BizRoles.cs | 53 ++- server/biz/FormFieldReference.cs | 3 +- server/biz/GlobalBizSettingsBiz.cs | 27 +- server/biz/JobsBiz.cs | 4 +- server/biz/PurchaseBiz.cs | 7 +- server/biz/SockType.cs | 4 +- server/biz/VendorNotificationBiz.cs | 416 ++++++++++++++++++ ...s => SockBotProcessVendorNotifications.cs} | 295 ++++++++++++- server/models/AyContext.cs | 1 + server/models/Purchase.cs | 2 +- server/models/VendorNotification.cs | 64 +++ server/util/AySchema.cs | 54 ++- 17 files changed, 924 insertions(+), 94 deletions(-) create mode 100644 server/DataList/VendorNotificationDataList.cs create mode 100644 server/biz/VendorNotificationBiz.cs rename server/generator/{SockBotProcessPurchases.cs => SockBotProcessVendorNotifications.cs} (72%) create mode 100644 server/models/VendorNotification.cs diff --git a/server/Controllers/OrderController.cs b/server/Controllers/OrderController.cs index f12c919..1ba2828 100644 --- a/server/Controllers/OrderController.cs +++ b/server/Controllers/OrderController.cs @@ -50,33 +50,18 @@ namespace Sockeye.Api.Controllers return BadRequest(ModelState); } try - { - //do stuff with the notification - (string username, string password) = Sockeye.Util.AutoOrderProcessingUtil.GetUsernameAndPasswordFromAuthorizeHeader(Authorization); - // Now use username and password with whatever authentication process you want + { + (string username, string password) = Sockeye.Util.AutoOrderProcessingUtil.GetUsernameAndPasswordFromAuthorizeHeader(Authorization); if (username == "Y24PYYDQSA1L12905N5MKU" && password == "MA8GMQK2PC3FDNT1RTR68R") - { - //put the jobject notification into the db as json string freeform notification information - //to be processed by other code later. i.e. just capture it as is cleanly and don't bother trying to do anything fancy here this should be tight and focused and side effect free - - - //although...on second thought it could just make a purchase record with some sort of state of unprocessed so a job could swing by and process it or we could - //do that manually so then there would be no need for extra UI and stuff, and it wont' conflict with the need to process immediately and lightly here - //i.e. two stage: one is to make the record barebones for purchase then a job comes along and turns it into a real purchase - //also add error handling here iwth proper notification see trial request code so we get notified on error - //and don't return OK / 200 unless the order is stored successfully in db as shareit will retry until it's successful several times, see the link below for deets - //Need to be able to make a manual purchase record with almost nothing more than the vendor notifciation text so can recover if auto system fails and for testing - + { //save it to the database - var Purchase = new Purchase(); - Purchase.PurchaseDate = DateTime.UtcNow;//this is kind of fucky, what if the order takes more than a day to arrive? I guess fixup from the vendordata later - Purchase.VendorId = 1;//MyCommerce/shareit is vendor 1, for now that's all that's needed - Purchase.VendorData = j.ToString(); - await ct.Purchase.AddAsync(Purchase); + var vn = new VendorNotification(); + vn.Created = DateTime.UtcNow; + vn.VendorId = 1;//MyCommerce/shareit is vendor 1, for now that's all that's needed + vn.VendorData = j.ToString(); + await ct.VendorNotification.AddAsync(vn); await ct.SaveChangesAsync(); } - - } catch (Exception ex) { @@ -84,7 +69,7 @@ namespace Sockeye.Api.Controllers await NotifyEventHelper.AddOpsProblemEvent("OrderController/shareit - Exception processing vendor notification, see log"); return StatusCode(503);//ask it to retry again later } - return Ok("ok");//shareit robot looks for an OK response to know if it should resend or not https://account.mycommerce.com/home/wiki/7479805 + return Ok("ok");//shareit notifier looks for an OK response to know if it should resend or not https://account.mycommerce.com/home/wiki/7479805 } //------------ diff --git a/server/DataList/PurchaseDataList.cs b/server/DataList/PurchaseDataList.cs index 575e906..800b5f7 100644 --- a/server/DataList/PurchaseDataList.cs +++ b/server/DataList/PurchaseDataList.cs @@ -14,7 +14,7 @@ namespace Sockeye.DataList var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; - DefaultColumns = new List() { "PurchaseDate", "PurchaseSalesOrderNumber", "Customer", "Product", "PurchaseProcessed" }; + DefaultColumns = new List() { "PurchaseDate", "PurchaseSalesOrderNumber", "Customer", "Product", "PurchaseProcessed", "PurchaseVendorNotificationId" }; DefaultSortBy = new Dictionary() { { "PurchaseDate", "-" } }; FieldDefinitions = new List(); @@ -50,6 +50,16 @@ namespace Sockeye.DataList SqlValueColumnName = "avendor.name" }); + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PurchaseVendorNotificationId", + FieldKey = "PurchaseVendorNotificationId", + SockType = (int)SockType.VendorNotification, + UiFieldDataType = (int)UiFieldDataType.Integer, + SqlIdColumnName = "apurchase.vendornotificationid", + SqlValueColumnName = "apurchase.vendornotificationid" + }); + FieldDefinitions.Add(new DataListFieldDefinition { TKey = "Product", diff --git a/server/DataList/VendorNotificationDataList.cs b/server/DataList/VendorNotificationDataList.cs new file mode 100644 index 0000000..2cc9510 --- /dev/null +++ b/server/DataList/VendorNotificationDataList.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Sockeye.Biz; + +namespace Sockeye.DataList +{ + internal class VendorNotificationDataList : DataListProcessingBase + { + public VendorNotificationDataList(long translationId) + { + DefaultListAType = SockType.VendorNotification; + SQLFrom = @"FROM avendornotification "; + + var RoleSet = BizRoles.GetRoleSet(DefaultListAType); + AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; + DefaultColumns = new List() { "VendorNotificationCreated", "VendorNotificationProcessed" }; + DefaultSortBy = new Dictionary() { { "VendorNotificationCreated", "-" } }; + FieldDefinitions = new List(); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "VendorNotificationCreated", + FieldKey = "VendorNotificationSalesOrderNumber", + SockType = (int)SockType.VendorNotification, + UiFieldDataType = (int)UiFieldDataType.DateTime, + SqlIdColumnName = "avendornotification.id", + SqlValueColumnName = "avendornotification.created", + IsRowId = true + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "VendorNotificationProcessed", + FieldKey = "VendorNotificationProcessed", + UiFieldDataType = (int)UiFieldDataType.DateTime, + SqlValueColumnName = "avendornotification.processed" + }); + } + }//eoc +}//eons \ No newline at end of file diff --git a/server/biz/BizObjectExistsInDatabase.cs b/server/biz/BizObjectExistsInDatabase.cs index 5b96a72..e05a593 100644 --- a/server/biz/BizObjectExistsInDatabase.cs +++ b/server/biz/BizObjectExistsInDatabase.cs @@ -59,6 +59,8 @@ namespace Sockeye.Biz return await ct.SubscriptionServer.AnyAsync(z => z.Id == id); case SockType.Purchase: return await ct.Purchase.AnyAsync(z => z.Id == id); + case SockType.VendorNotification: + return await ct.VendorNotification.AnyAsync(z => z.Id == id); case SockType.Product: return await ct.Product.AnyAsync(z => z.Id == id); case SockType.GZCase: diff --git a/server/biz/BizObjectFactory.cs b/server/biz/BizObjectFactory.cs index 4cd4579..881fd6c 100644 --- a/server/biz/BizObjectFactory.cs +++ b/server/biz/BizObjectFactory.cs @@ -61,6 +61,8 @@ namespace Sockeye.Biz return new SubscriptionServerBiz(ct, userId, translationId, roles); case SockType.Purchase: return new PurchaseBiz(ct, userId, translationId, roles); + case SockType.VendorNotification: + return new VendorNotificationBiz(ct, userId, translationId, roles); case SockType.Product: return new ProductBiz(ct, userId, translationId, roles); case SockType.GZCase: diff --git a/server/biz/BizRoles.cs b/server/biz/BizRoles.cs index 3dfa925..23abea7 100644 --- a/server/biz/BizRoles.cs +++ b/server/biz/BizRoles.cs @@ -512,7 +512,7 @@ namespace Sockeye.Biz }); - //////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////// //LICENSE // roles.Add(SockType.Vendor, new BizRoleSet() @@ -598,6 +598,23 @@ namespace Sockeye.Biz Select = AuthorizationRoles.All }); + //////////////////////////////////////////////////////////// + //VENDOR NOTIFICATION + // + roles.Add(SockType.VendorNotification, new BizRoleSet() + { + Change = AuthorizationRoles.BizAdmin + | AuthorizationRoles.Service + | AuthorizationRoles.Sales + | AuthorizationRoles.Accounting, + ReadFullRecord = AuthorizationRoles.BizAdminRestricted + | AuthorizationRoles.ServiceRestricted + | AuthorizationRoles.Tech + | AuthorizationRoles.SalesRestricted + , + Select = AuthorizationRoles.All + }); + //////////////////////////////////////////////////////////// //PRODUCT // @@ -648,23 +665,23 @@ namespace Sockeye.Biz //GENERATE CLIENT COMPATIBLE JSON FROM ROLES OUTPUT TO DEBUG LOG //And seperately, set the JSON variable so can copy from debug variable "value" property for lastRoles here to compare - - - - /* - string json = Newtonsoft.Json.JsonConvert.SerializeObject(roles, Newtonsoft.Json.Formatting.None); - System.Diagnostics.Debugger.Log(1, "JSONFRAGMENTFORCLIENT", "BizRoles.cs -> biz-role-rights.js Client roles JSON fragment:\n\n"); - System.Diagnostics.Debugger.Log(1, "JSONFRAGMENTFORCLIENT", json + "\n\n"); - var lastRoles = "{\"Customer\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"CustomerNote\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"CustomerNotifySubscription\":{\"Change\":10,\"ReadFullRecord\":65797,\"Select\":131071},\"HeadOffice\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Global\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"GlobalOps\":{\"Change\":16384,\"ReadFullRecord\":8192,\"Select\":0},\"User\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"UserOptions\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"ServerState\":{\"Change\":16384,\"ReadFullRecord\":131071,\"Select\":0},\"LogFile\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"Backup\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"FileAttachment\":{\"Change\":2,\"ReadFullRecord\":3,\"Select\":0},\"ServerJob\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"OpsNotificationSettings\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"ServerMetrics\":{\"Change\":16384,\"ReadFullRecord\":24576,\"Select\":0},\"Translation\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"DataListSavedFilter\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"FormUserOptions\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"FormCustom\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"PickListTemplate\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"BizMetrics\":{\"Change\":2,\"ReadFullRecord\":98369,\"Select\":0},\"Notification\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"NotifySubscription\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"Report\":{\"Change\":3,\"ReadFullRecord\":131071,\"Select\":131071},\"Memo\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Reminder\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Review\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Integration\":{\"Change\":49514,\"ReadFullRecord\":49514,\"Select\":49514},\"License\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"TrialLicenseRequest\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"SubscriptionServer\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Purchase\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Product\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"GZCase\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071}}"; - Dictionary lastRolesDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject>(lastRoles); - if (lastRolesDeserialized.Count != roles.Count) - { - - { - ((ILogger)Sockeye.Util.ApplicationLogging.CreateLogger("BizRoles.cs")).LogWarning("BizRoles::Constructor - roles were modified from last snapshot for client!!!"); - } - } -*/ + + + + /* + string json = Newtonsoft.Json.JsonConvert.SerializeObject(roles, Newtonsoft.Json.Formatting.None); + System.Diagnostics.Debugger.Log(1, "JSONFRAGMENTFORCLIENT", "BizRoles.cs -> biz-role-rights.js Client roles JSON fragment:\n\n"); + System.Diagnostics.Debugger.Log(1, "JSONFRAGMENTFORCLIENT", json + "\n\n"); + var lastRoles = "{\"Customer\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"CustomerNote\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"CustomerNotifySubscription\":{\"Change\":10,\"ReadFullRecord\":65797,\"Select\":131071},\"HeadOffice\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Global\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"GlobalOps\":{\"Change\":16384,\"ReadFullRecord\":8192,\"Select\":0},\"User\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"UserOptions\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":0},\"ServerState\":{\"Change\":16384,\"ReadFullRecord\":131071,\"Select\":0},\"LogFile\":{\"Change\":0,\"ReadFullRecord\":24576,\"Select\":0},\"Backup\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"FileAttachment\":{\"Change\":2,\"ReadFullRecord\":3,\"Select\":0},\"ServerJob\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"OpsNotificationSettings\":{\"Change\":16384,\"ReadFullRecord\":8195,\"Select\":0},\"ServerMetrics\":{\"Change\":16384,\"ReadFullRecord\":24576,\"Select\":0},\"Translation\":{\"Change\":2,\"ReadFullRecord\":1,\"Select\":131071},\"DataListSavedFilter\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"FormUserOptions\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"FormCustom\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"PickListTemplate\":{\"Change\":2,\"ReadFullRecord\":131071,\"Select\":0},\"BizMetrics\":{\"Change\":2,\"ReadFullRecord\":98369,\"Select\":0},\"Notification\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"NotifySubscription\":{\"Change\":131071,\"ReadFullRecord\":131071,\"Select\":0},\"Report\":{\"Change\":3,\"ReadFullRecord\":131071,\"Select\":131071},\"Memo\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Reminder\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Review\":{\"Change\":124927,\"ReadFullRecord\":124927,\"Select\":124927},\"Integration\":{\"Change\":49514,\"ReadFullRecord\":49514,\"Select\":49514},\"License\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"TrialLicenseRequest\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"SubscriptionServer\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Purchase\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"Product\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071},\"GZCase\":{\"Change\":32842,\"ReadFullRecord\":65797,\"Select\":131071}}"; + Dictionary lastRolesDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject>(lastRoles); + if (lastRolesDeserialized.Count != roles.Count) + { + + { + ((ILogger)Sockeye.Util.ApplicationLogging.CreateLogger("BizRoles.cs")).LogWarning("BizRoles::Constructor - roles were modified from last snapshot for client!!!"); + } + } + */ #endif diff --git a/server/biz/FormFieldReference.cs b/server/biz/FormFieldReference.cs index 6a93c30..93b318d 100644 --- a/server/biz/FormFieldReference.cs +++ b/server/biz/FormFieldReference.cs @@ -379,7 +379,7 @@ namespace Sockeye.Biz l.Add(new FormField { TKey = "PurchaseCancelDate", FieldKey = "PurchaseCancelDate" }); l.Add(new FormField { TKey = "PurchaseCouponCode", FieldKey = "PurchaseCouponCode" }); l.Add(new FormField { TKey = "PurchaseNotes", FieldKey = "PurchaseNotes" }); - l.Add(new FormField { TKey = "PurchaseVendorData", FieldKey = "PurchaseVendorData" }); + l.Add(new FormField { TKey = "PurchaseVendorNotificationId", FieldKey = "PurchaseVendorNotificationId" }); l.Add(new FormField { TKey = "PurchaseProcessed", FieldKey = "PurchaseProcessed" }); l.Add(new FormField { TKey = "Tags", FieldKey = "Tags" }); @@ -391,6 +391,7 @@ namespace Sockeye.Biz } #endregion + #region SubscriptionServer { List l = new List(); diff --git a/server/biz/GlobalBizSettingsBiz.cs b/server/biz/GlobalBizSettingsBiz.cs index 8f0c0cb..885326d 100644 --- a/server/biz/GlobalBizSettingsBiz.cs +++ b/server/biz/GlobalBizSettingsBiz.cs @@ -336,28 +336,19 @@ namespace Sockeye.Biz }, */ - var jData = JObject.Parse(jVendorNotificationItem["data"].Value()); - var salesOrderNumber = jData["order_notification"]["purchase"]["purchase_id"].Value(); - var p = await ct.Purchase.FirstOrDefaultAsync(z => z.SalesOrderNumber == salesOrderNumber); - if (p == null) - { - //this is only test orders no real orders hit here - // log.LogError($"RFImport Vendor notifications can't match sales order number {salesOrderNumber}"); - // log.LogError(jData["order_notification"].ToString()); - } - else - { - if (!string.IsNullOrWhiteSpace(p.VendorData)) - p.VendorData += ",\n"; - p.VendorData += jVendorNotificationItem["data"].Value(); - await ct.SaveChangesAsync(); + var vn = new VendorNotification(); + vn.Created = DateUtil.EpochToDateNullIsMin(jVendorNotificationItem["dtCreated"].Value()); + vn.VendorId = 1; + vn.VendorData = jVendorNotificationItem["data"].Value(); + vn.Processed = vn.Created;//indicate it's been processed + await ct.SaveChangesAsync(); #if (DEBUG) - //Test dev stuff + //Test dev stuff #warning DEV TEST ORDER PROCESSING REMOVE THIS WHEN DONE - await SockBotProcessPurchases.ParseVendorNotificationData(p, ct); + await SockBotProcessVendorNotifications.ParseVendorNotificationData(vn, ct, log); #endif - } + }// all vendor notifications loop } diff --git a/server/biz/JobsBiz.cs b/server/biz/JobsBiz.cs index aed19d4..cbc5c95 100644 --- a/server/biz/JobsBiz.cs +++ b/server/biz/JobsBiz.cs @@ -263,8 +263,8 @@ namespace Sockeye.Biz await SockBotSubscriptionServerHealthChecks.DoWorkAsync(); if (!KeepOnWorking()) return; - //SOCKBOT - PROCESS PURCHASES - await SockBotProcessPurchases.DoWorkAsync(); + //SOCKBOT - PROCESS VENDOR NOTIFICATIONS + await SockBotProcessVendorNotifications.DoWorkAsync(); if (!KeepOnWorking()) return; //JOB SWEEPER diff --git a/server/biz/PurchaseBiz.cs b/server/biz/PurchaseBiz.cs index 07e57a0..46eac9f 100644 --- a/server/biz/PurchaseBiz.cs +++ b/server/biz/PurchaseBiz.cs @@ -193,8 +193,7 @@ namespace Sockeye.Biz .AddText(obj.CouponCode) .AddText(obj.Wiki) .AddText(obj.Tags) - .AddText(obj.Notes) - .AddText(obj.VendorData); + .AddText(obj.Notes); } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -270,7 +269,7 @@ namespace Sockeye.Biz { vc.Add(await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorId).Select(x => x.Name).FirstOrDefaultAsync(), "vendor", o.VendorId); } - o.VendorViz = vc.Get("vendor", o.CustomerId); + o.VendorViz = vc.Get("vendor", o.VendorId); if (!vc.Has("product", o.ProductId)) @@ -441,7 +440,7 @@ namespace Sockeye.Biz { //# NEW PURCHASE VENDOR NOTIFICATION - if (ayaEvent == SockEvent.Created && (!string.IsNullOrWhiteSpace(o.VendorData))) + if (ayaEvent == SockEvent.Created) { //Get product name and get customer name to notify var productName = await ct.Product.AsNoTracking().Where(x => x.Id == o.ProductId).Select(x => x.Name).FirstOrDefaultAsync(); diff --git a/server/biz/SockType.cs b/server/biz/SockType.cs index 61c6c94..f47d5b0 100644 --- a/server/biz/SockType.cs +++ b/server/biz/SockType.cs @@ -75,7 +75,9 @@ namespace Sockeye.Biz [CoreBizObject, ReportableBizObject] Product = 97, [CoreBizObject, ReportableBizObject] - GZCase = 98 + GZCase = 98, + [CoreBizObject, ReportableBizObject] + VendorNotification = 99 diff --git a/server/biz/VendorNotificationBiz.cs b/server/biz/VendorNotificationBiz.cs new file mode 100644 index 0000000..bc018e8 --- /dev/null +++ b/server/biz/VendorNotificationBiz.cs @@ -0,0 +1,416 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System.Linq; +using Sockeye.Util; +using Sockeye.Api.ControllerHelpers; +using Sockeye.Models; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Sockeye.Biz +{ + internal class VendorNotificationBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject, INotifiableObject + { + internal VendorNotificationBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles) + { + ct = dbcontext; + UserId = currentUserId; + UserTranslationId = userTranslationId; + CurrentUserRoles = UserRoles; + BizType = SockType.VendorNotification; + } + + internal static VendorNotificationBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) + { + if (httpContext != null) + return new VendorNotificationBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); + else + return new VendorNotificationBiz(ct, 1, ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdmin); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //EXISTS + internal async Task ExistsAsync(long id) + { + return await ct.VendorNotification.AnyAsync(z => z.Id == id); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //CREATE + // + internal async Task CreateAsync(VendorNotification newObject) + { + await ValidateAsync(newObject, null); + if (HasErrors) + return null; + else + { + await ct.VendorNotification.AddAsync(newObject); + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, SockEvent.Created), ct); + await SearchIndexAsync(newObject, true); + await HandlePotentialNotificationEvent(SockEvent.Created, newObject); + return newObject; + } + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //GET + // + internal async Task GetAsync(long id, bool logTheGetEvent = true) + { + var ret = await ct.VendorNotification.AsNoTracking().SingleOrDefaultAsync(z => z.Id == id); + if (logTheGetEvent && ret != null) + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, SockEvent.Retrieved), ct); + return ret; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //UPDATE + // + internal async Task PutAsync(VendorNotification putObject) + { + var dbObject = await GetAsync(putObject.Id, false); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return null; + } + if (dbObject.Concurrency != putObject.Concurrency) + { + AddError(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } + + await ValidateAsync(putObject, dbObject); + if (HasErrors) return null; + ct.Replace(dbObject, putObject); + try + { + await ct.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await ExistsAsync(putObject.Id)) + AddError(ApiErrorCode.NOT_FOUND); + else + AddError(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, putObject.Id, BizType, SockEvent.Modified), ct); + await SearchIndexAsync(putObject, false); + await HandlePotentialNotificationEvent(SockEvent.Modified, putObject, dbObject); + return putObject; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //DELETE + // + internal async Task DeleteAsync(long id) + { + using (var transaction = await ct.Database.BeginTransactionAsync()) + { + + VendorNotification dbObject = await GetAsync(id, false); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND); + return false; + } + await ValidateCanDeleteAsync(dbObject); + if (HasErrors) + return false; + + + + + { + var IDList = await ct.Review.AsNoTracking().Where(x => x.SockType == SockType.VendorNotification && x.ObjectId == id).Select(x => x.Id).ToListAsync(); + if (IDList.Count() > 0) + { + ReviewBiz b = new ReviewBiz(ct, UserId, UserTranslationId, CurrentUserRoles); + foreach (long ItemId in IDList) + if (!await b.DeleteAsync(ItemId, transaction)) + { + AddError(ApiErrorCode.CHILD_OBJECT_ERROR, null, $"Review [{ItemId}]: {b.GetErrorsAsString()}"); + return false; + } + } + } + + ct.VendorNotification.Remove(dbObject); + await ct.SaveChangesAsync(); + + //Log event + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct); + await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct); + await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); + await transaction.CommitAsync(); + await HandlePotentialNotificationEvent(SockEvent.Deleted, dbObject); + + return true; + } + } + + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //SEARCH + // + private async Task SearchIndexAsync(VendorNotification obj, bool isNew) + { + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType); + DigestSearchText(obj, SearchParams); + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); + else + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); + } + + public async Task GetSearchResultSummary(long id, SockType specificType) + { + var obj = await GetAsync(id, false); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + DigestSearchText(obj, SearchParams); + return SearchParams; + } + + public void DigestSearchText(VendorNotification obj, Search.SearchIndexProcessObjectParameters searchParams) + { + if (obj != null) + searchParams.AddText(obj.VendorData); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + + private async Task ValidateAsync(VendorNotification proposedObj, VendorNotification currentObj) + { + await Task.CompletedTask; + + } + + + private async Task ValidateCanDeleteAsync(VendorNotification inObj) + { + + await Task.CompletedTask; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //REPORTING + // + public async Task GetReportData(DataListSelectedRequest dataListSelectedRequest, Guid jobId) + { + var idList = dataListSelectedRequest.SelectedRowIds; + JArray ReportData = new JArray(); + while (idList.Any()) + { + var batch = idList.Take(IReportAbleObject.REPORT_DATA_BATCH_SIZE); + idList = idList.Skip(IReportAbleObject.REPORT_DATA_BATCH_SIZE).ToArray(); + + //query for this batch, comes back in db natural order unfortunately + var batchResults = await ct.VendorNotification.AsNoTracking().Where(z => batch.Contains(z.Id)).ToArrayAsync(); + + //order the results back into original + //What is happening here: + //for performance the query is batching a bunch at once by fetching a block of items from the sql server + //however it's returning in db order which is often not the order the id list is in + //so it needs to be sorted back into the same order as the ide list + //This would not be necessary if just fetching each one at a time individually (like in workorder get report data) + + var orderedList = from id in batch join z in batchResults on id equals z.Id select z; + batchResults = null; + + foreach (VendorNotification w in orderedList) + { + if (!ReportRenderManager.KeepGoing(jobId)) return null; + await PopulateVizFields(w); + var jo = JObject.FromObject(w); + + ReportData.Add(jo); + } + orderedList = null; + } + vc.Clear(); + return ReportData; + } + private VizCache vc = new VizCache(); + + + //populate viz fields from provided object + private async Task PopulateVizFields(VendorNotification o) + { + + if (!vc.Has("vendor", o.VendorId)) + { + vc.Add(await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorId).Select(x => x.Name).FirstOrDefaultAsync(), "vendor", o.VendorId); + } + o.VendorViz = vc.Get("vendor", o.VendorId); + + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // IMPORT EXPORT + // + + public async Task GetExportData(DataListSelectedRequest dataListSelectedRequest, Guid jobId) + { + return await GetReportData(dataListSelectedRequest, jobId); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //JOB / OPERATIONS + // + public async Task HandleJobAsync(OpsJob job) + { + //Hand off the particular job to the corresponding processing code + //NOTE: If this code throws an exception the caller (JobsBiz::ProcessJobsAsync) will automatically set the job to failed and log the exeption so + //basically any error condition during job processing should throw up an exception if it can't be handled + switch (job.JobType) + { + case JobType.BatchCoreObjectOperation: + await ProcessBatchJobAsync(job); + break; + default: + throw new System.ArgumentOutOfRangeException($"VendorNotificationBiz.HandleJob-> Invalid job type{job.JobType.ToString()}"); + } + } + + + + private async Task ProcessBatchJobAsync(OpsJob job) + { + await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Running); + await JobsBiz.LogJobAsync(job.GId, $"LT:StartJob {job.SubType}"); + List idList = new List(); + long FailedObjectCount = 0; + JObject jobData = JObject.Parse(job.JobInfo); + if (jobData.ContainsKey("idList")) + idList = ((JArray)jobData["idList"]).ToObject>(); + else + idList = await ct.VendorNotification.AsNoTracking().Select(z => z.Id).ToListAsync(); + bool SaveIt = false; + + //--------------------------------- + //case 4192 + TimeSpan ProgressAndCancelCheckSpan = new TimeSpan(0, 0, ServerBootConfig.JOB_PROGRESS_UPDATE_AND_CANCEL_CHECK_SECONDS); + DateTime LastProgressCheck = DateTime.UtcNow.Subtract(new TimeSpan(1, 1, 1, 1, 1)); + var TotalRecords = idList.LongCount(); + long CurrentRecord = -1; + //--------------------------------- + + foreach (long id in idList) + { + try + { + //-------------------------------- + //case 4192 + //Update progress / cancel requested? + CurrentRecord++; + if (DateUtil.IsAfterDuration(LastProgressCheck, ProgressAndCancelCheckSpan)) + { + await JobsBiz.UpdateJobProgressAsync(job.GId, $"{CurrentRecord}/{TotalRecords}"); + if (await JobsBiz.GetJobStatusAsync(job.GId) == JobStatus.CancelRequested) + break; + LastProgressCheck = DateTime.UtcNow; + } + //--------------------------------- + + SaveIt = false; + ClearErrors(); + VendorNotification o = null; + //save a fetch if it's a delete + if (job.SubType != JobSubType.Delete) + o = await GetAsync(id, false); + switch (job.SubType) + { + // case JobSubType.TagAddAny: + // case JobSubType.TagAdd: + // case JobSubType.TagRemoveAny: + // case JobSubType.TagRemove: + // case JobSubType.TagReplaceAny: + // case JobSubType.TagReplace: + // SaveIt = TagBiz.ProcessBatchTagOperation(o.Tags, (string)jobData["tag"], jobData.ContainsKey("toTag") ? (string)jobData["toTag"] : null, job.SubType); + // break; + case JobSubType.Delete: + if (!await DeleteAsync(id)) + { + await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}"); + FailedObjectCount++; + } + break; + default: + throw new System.ArgumentOutOfRangeException($"ProcessBatchJobAsync -> Invalid job Subtype{job.SubType}"); + } + if (SaveIt) + { + o = await PutAsync(o); + if (o == null) + { + await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}"); + FailedObjectCount++; + } + } + + //delay so we're not tying up all the resources in a tight loop + await Task.Delay(Sockeye.Util.ServerBootConfig.JOB_OBJECT_HANDLE_BATCH_JOB_LOOP_DELAY); + } + catch (Exception ex) + { + await JobsBiz.LogJobAsync(job.GId, $"LT:Errors id({id})"); + await JobsBiz.LogJobAsync(job.GId, ExceptionUtil.ExtractAllExceptionMessages(ex)); + } + } + + //--------------------------------- + //case 4192 + await JobsBiz.UpdateJobProgressAsync(job.GId, $"{++CurrentRecord}/{TotalRecords}"); + //--------------------------------- + + await JobsBiz.LogJobAsync(job.GId, $"LT:BatchJob {job.SubType} {idList.Count}{(FailedObjectCount > 0 ? " - LT:Failed " + FailedObjectCount : "")}"); + await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Completed); + } + + + + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // NOTIFICATION PROCESSING + // + public async Task HandlePotentialNotificationEvent(SockEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null) + { + if (ServerBootConfig.MIGRATING) return; + ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger(); + + log.LogDebug($"HandlePotentialNotificationEvent processing: [SockType:{this.BizType}, AyaEvent:{ayaEvent}]"); + + bool isNew = currentObj == null; + + proposedObj.Name = $"VENDOR NOTIFICATION created:{((VendorNotification)proposedObj).Created} id:{proposedObj.Id.ToString()}"; + //STANDARD EVENTS FOR ALL OBJECTS + await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct); + + + }//end of process notifications + + + ///////////////////////////////////////////////////////////////////// + + }//eoc + + +}//eons + diff --git a/server/generator/SockBotProcessPurchases.cs b/server/generator/SockBotProcessVendorNotifications.cs similarity index 72% rename from server/generator/SockBotProcessPurchases.cs rename to server/generator/SockBotProcessVendorNotifications.cs index 07c6caf..6c90260 100644 --- a/server/generator/SockBotProcessPurchases.cs +++ b/server/generator/SockBotProcessVendorNotifications.cs @@ -20,9 +20,9 @@ namespace Sockeye.Biz /// A Separate job will make licenses /// /// - internal static class SockBotProcessPurchases + internal static class SockBotProcessVendorNotifications { - private static ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger("SockBotProcessPurchases"); + private static ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger("SockBotProcessVendorNotifications"); private static DateTime lastSweep = DateTime.MinValue; private static TimeSpan PROCESS_EVERY_INTERVAL = new TimeSpan(0, 5, 10);//every 5 minutes roughly meaning 15 minutes down is highest fail state //////////////////////////////////////////////////////////////////////////////////////////////// @@ -36,8 +36,8 @@ namespace Sockeye.Biz 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 starting"); - await ProcessVendorDataIntoPurchases(); + log.LogDebug("Process vendor notifications starting"); + await ProcessVendorNotificationDataIntoPurchases(); // log.LogDebug("Process licenses from purchases starting"); // await ProcessPurchasesIntoLicenses(); @@ -45,14 +45,14 @@ namespace Sockeye.Biz lastSweep = DateTime.UtcNow; } - private static async Task ProcessVendorDataIntoPurchases() + private static async Task ProcessVendorNotificationDataIntoPurchases() { return; using (AyContext ct = Sockeye.Util.ServiceProviderProvider.DBContext) { - //get a list of all actionable purchases + //get a list of all actionable vendor notifications var ProcessablePurchaseIdList = await ct.Purchase .AsNoTracking() .Where(z => z.Processed == false @@ -115,25 +115,25 @@ namespace Sockeye.Biz - internal static async Task ParseVendorNotificationData(Purchase p, AyContext ct, ILogger log) + internal static async Task ParseVendorNotificationData(VendorNotification vn, AyContext ct, ILogger log) { - if (string.IsNullOrWhiteSpace(p.VendorData)) return; + if (string.IsNullOrWhiteSpace(vn.VendorData)) return; try { - var jData = JObject.Parse(p.VendorData); + var jData = JObject.Parse(vn.VendorData); //fundamentally validate the object is a purchase notification if (jData["order_notification"]["purchase"]["purchase_id"] == null) { //this is not the expected format data, stop processing and alert: - throw new System.FormatException($"Vendor data unexpected format:{p.VendorData}"); + throw new System.FormatException($"Vendor data unexpected format:{vn.VendorData}"); } //CUSTOMER create or locate - var jCustomerName = jData["order_notification"]["purchase"]["customer_data"]["reg_name"].Value() ?? throw new System.FormatException($"Vendor data empty reg_name:{p.VendorData}"); - var jCustomerEmail = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"].Value() ?? throw new System.FormatException($"Vendor data empty email:{p.VendorData}"); + 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 customerBiz = CustomerBiz.GetBiz(ct); @@ -148,7 +148,7 @@ namespace Sockeye.Biz UpdateCustomerFromVendorData(jData, jCustomerEmail, c); c = await customerBiz.CreateAsync(c); if (c == null) - throw new System.ApplicationException($"Error creating new Customer: {customerBiz.GetErrorsAsString()} vendor data :{p.VendorData}"); + throw new System.ApplicationException($"Error creating new Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } else { @@ -156,7 +156,7 @@ namespace Sockeye.Biz UpdateCustomerFromVendorData(jData, jCustomerEmail, c); c = await customerBiz.PutAsync(c); if (c == null) - throw new System.ApplicationException($"Error updating existing Customer: {customerBiz.GetErrorsAsString()} vendor data :{p.VendorData}"); + throw new System.ApplicationException($"Error updating existing Customer: {customerBiz.GetErrorsAsString()} vendor data :{vn.VendorData}"); } var salesOrderNumber = jData["order_notification"]["purchase"]["purchase_id"].Value(); @@ -166,11 +166,14 @@ namespace Sockeye.Biz //ok, turn this into a fully realized Purchase record + //Product group + + } catch (Exception ex) { - var err = $"ParseVendorNotificationData: Purchase record {p.Id}-{p.PurchaseDate} triggered exception, see log"; + var err = $"ParseVendorNotificationData: Purchase record {vn.Id}-{vn.PurchaseDate} triggered exception, see log"; await NotifyEventHelper.AddOpsProblemEvent(err);//notify, this is serious log.LogError(ex, err); } @@ -204,6 +207,268 @@ namespace Sockeye.Biz #region SAMPLE VENDOR PURCHASE NOTIFICATIONS /* + +///////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////// V7 MULTIPLE PRODUCTS IN ONE ORDER///////////////////////////// + +{ + "creation_date": "2023-01-22T23:21:49Z", + "id": 357678128, + "order_notification": { + "purchase": { + "customer_data": { + "billing_contact": { + "address": { + "city": "Courtenay", + "country": "Canada", + "country_id": "CA", + "postal_code": "V9J9T6", + "state": "British Columbia", + "state_id": "BC", + "street1": "05-3610 Christie Parkway" + }, + "company": "GZTestCo", + "email": "gzmailadmin@gmail.com", + "first_name": "Test", + "last_name": "Testerson" + }, + "customer_payment_data": { + "currency": "USD", + "payment_method": "Other" + }, + "delivery_contact": { + "address": { + "city": "Courtenay", + "country": "Canada", + "country_id": "CA", + "postal_code": "V9J9T6", + "state": "British Columbia", + "state_id": "BC", + "street1": "05-3610 Christie Parkway" + }, + "company": "GZTestCo", + "email": "gzmailadmin@gmail.com", + "first_name": "Test", + "last_name": "Testerson" + }, + "language": "English", + "language_iso": "en", + "reg_name": "GZTestCo", + "shopper_id": "65860321", + "subscribe_newsletter": false, + "user_id": "gzmailadmin@gmail.com-38" + }, + "is_test": true, + "payment_complete_date": "2023-01-22T23:21:48Z", + "payment_status": "testpaymentarrived", + "payment_status_id": "TCA", + "purchase_date": "2023-01-22T23:21:47Z", + "purchase_id": 843671213, + "purchase_item": [ + { + "additional_information": [ + { + "additional_id": "ADDITIONAL1", + "additional_value": "YES" + }, + { + "additional_id": "ADDITIONAL2", + "additional_value": "YES" + }, + { + "additional_id": "ADDITIONAL3", + "additional_value": "YES" + } + ], + "currency": "USD", + "delivery_type": "Electronically", + "discount": 0.0, + "extended_download_price": 0.0, + "manual_order_price": 0.0, + "notification_no": 0, + "product_id": 300740317, + "product_name": "Up to 5 AyaNova schedulable resource 1 year subscription license", + "product_single_price": 695.0, + "purchase_item_key": [], + "quantity": 1, + "running_no": 1, + "shipping_price": 0.0, + "shipping_vat_pct": 0.0, + "subscription": { + "expiration_date": "2024-01-22T23:21:48Z", + "id": "843671213-1", + "interval": "Yearly without end", + "renewal_discount_count": "", + "renewal_discount_start": "", + "renewal_type": "auto", + "retention_discount_count": "", + "retention_discount_percent": "", + "start_date": "2023-01-23T00:00:00", + "status": "ToProcess", + "status_id": "TOP" + }, + "vat_pct": 12.0 + }, + { + "additional_information": [ + { + "additional_id": "ADDITIONAL1", + "additional_value": "YES" + }, + { + "additional_id": "ADDITIONAL2", + "additional_value": "YES" + }, + { + "additional_id": "ADDITIONAL3", + "additional_value": "YES" + } + ], + "currency": "USD", + "delivery_type": "Electronically", + "discount": 0.0, + "extended_download_price": 0.0, + "manual_order_price": 0.0, + "notification_no": 0, + "product_id": 300740314, + "product_name": "optional add-on AyaNova RI (responsive interface) 1 year subscription license", + "product_single_price": 199.0, + "purchase_item_key": [], + "quantity": 1, + "running_no": 2, + "shipping_price": 0.0, + "shipping_vat_pct": 0.0, + "subscription": { + "expiration_date": "2024-01-22T23:21:48Z", + "id": "843671213-2", + "interval": "Yearly without end", + "renewal_discount_count": "", + "renewal_discount_start": "", + "renewal_type": "auto", + "retention_discount_count": "", + "retention_discount_percent": "", + "start_date": "2023-01-23T00:00:00", + "status": "ToProcess", + "status_id": "TOP" + }, + "vat_pct": 12.0 + }, + { + "additional_information": [ + { + "additional_id": "ADDITIONAL1", + "additional_value": "YES" + } + ], + "currency": "USD", + "delivery_type": "Electronically", + "discount": 0.0, + "extended_download_price": 0.0, + "manual_order_price": 0.0, + "notification_no": 0, + "product_id": 300740321, + "product_name": "optional add-on AyaNova WBI (web browser interface) 1 year subscription license", + "product_single_price": 99.0, + "purchase_item_key": [], + "quantity": 1, + "running_no": 3, + "shipping_price": 0.0, + "shipping_vat_pct": 0.0, + "subscription": { + "expiration_date": "2024-01-22T23:21:48Z", + "id": "843671213-3", + "interval": "Yearly without end", + "renewal_discount_count": "", + "renewal_discount_start": "", + "renewal_type": "auto", + "retention_discount_count": "", + "retention_discount_percent": "", + "start_date": "2023-01-23T00:00:00", + "status": "ToProcess", + "status_id": "TOP" + }, + "vat_pct": 12.0 + }, + { + "additional_information": [ + { + "additional_id": "ADDITIONAL1", + "additional_value": "YES" + } + ], + "currency": "USD", + "delivery_type": "Electronically", + "discount": 0.0, + "extended_download_price": 0.0, + "manual_order_price": 0.0, + "notification_no": 0, + "product_id": 300740322, + "product_name": "optional add-on AyaNova MBI (minimal browser interface) 1 year subscription license", + "product_single_price": 99.0, + "purchase_item_key": [], + "quantity": 1, + "running_no": 4, + "shipping_price": 0.0, + "shipping_vat_pct": 0.0, + "subscription": { + "expiration_date": "2024-01-22T23:21:48Z", + "id": "843671213-4", + "interval": "Yearly without end", + "renewal_discount_count": "", + "renewal_discount_start": "", + "renewal_type": "auto", + "retention_discount_count": "", + "retention_discount_percent": "", + "start_date": "2023-01-23T00:00:00", + "status": "ToProcess", + "status_id": "TOP" + }, + "vat_pct": 12.0 + }, + { + "additional_information": [ + { + "additional_id": "ADDITIONAL1", + "additional_value": "YES" + } + ], + "currency": "USD", + "delivery_type": "Electronically", + "discount": 0.0, + "extended_download_price": 0.0, + "manual_order_price": 0.0, + "notification_no": 0, + "product_id": 300740323, + "product_name": "optional add-on AyaNova QBI(QuickBooks interface) 1 year subscription license", + "product_single_price": 99.0, + "purchase_item_key": [], + "quantity": 1, + "running_no": 5, + "shipping_price": 0.0, + "shipping_vat_pct": 0.0, + "subscription": { + "expiration_date": "2024-01-22T23:21:48Z", + "id": "843671213-5", + "interval": "Yearly without end", + "renewal_discount_count": "", + "renewal_discount_start": "", + "renewal_type": "auto", + "retention_discount_count": "", + "retention_discount_percent": "", + "start_date": "2023-01-23T00:00:00", + "status": "ToProcess", + "status_id": "TOP" + }, + "vat_pct": 12.0 + } + ], + "purchase_origin": "online" + } + } +} + + + ///////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////v7 export to xls add on///////////////////////////// { diff --git a/server/models/AyContext.cs b/server/models/AyContext.cs index eb6415f..fb88e6e 100644 --- a/server/models/AyContext.cs +++ b/server/models/AyContext.cs @@ -60,6 +60,7 @@ namespace Sockeye.Models public virtual DbSet Vendor { get; set; } public virtual DbSet Product { get; set; } public virtual DbSet Purchase { get; set; } + public virtual DbSet VendorNotification { get; set; } public virtual DbSet TrialLicenseRequest { get; set; } public virtual DbSet GZCase { get; set; } public virtual DbSet SubscriptionServer { get; set; } diff --git a/server/models/Purchase.cs b/server/models/Purchase.cs index d7b90cf..43704ba 100644 --- a/server/models/Purchase.cs +++ b/server/models/Purchase.cs @@ -54,7 +54,7 @@ namespace Sockeye.Models public string Notes { get; set; } public bool RenewNoticeSent { get; set; } = false; public int Quantity { get; set; } = 1; - public string VendorData { get; set; } + public long? VendorNotificationId { get; set; } public bool Processed { get; set; } = false;//indicates it was fully processed and need not be processed by purchase or license generating jobs (all imported data set to processed) public string Wiki { get; set; } public List Tags { get; set; } diff --git a/server/models/VendorNotification.cs b/server/models/VendorNotification.cs new file mode 100644 index 0000000..000e873 --- /dev/null +++ b/server/models/VendorNotification.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Newtonsoft.Json; +using Sockeye.Biz; + +namespace Sockeye.Models +{ + public class VendorNotification : ICoreBizObjectModel + { + public long Id { get; set; } + public uint Concurrency { get; set; } + [Required] + public DateTime Created { get; set; } = DateTime.UtcNow; + [Required] + public long VendorId { get; set; } + [NotMapped] + public string VendorViz { get; set; } + public string VendorData { get; set; } + public DateTime? Processed { get; set; } = null;//indicates it was fully processed and need not be processed by purchase generating job (all imported data set to processed) + + //workaround for notification + [NotMapped, JsonIgnore] + public string Name { get; set; } + + public VendorNotification() + { + + } + + [NotMapped, JsonIgnore] + public SockType SType { get => SockType.VendorNotification; } + public List Tags { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + }//eoc +}//eons + +/* + +## How to make test sales and product links in ShareIt + +fake sale of v8 to get license emails to update rockfish to auto read in the deets + FAKE CREDIT CARD + I have generated a test credit card number for your account. Please use the following as the credit card number making sure to use the word test and the 12 digit number with no spaces. + You can use any valid information you would like for the other fields. + test134661442658 + Expiration Date: any date in the future + CVS code: 123 + Example typical database ID: + tZy9aSHjacFf9a9v3EtzUqM1amq/eMvJa2nXswLle74= + Links to purchase + $50 customization charge https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B300525428%5D=1 + Use this for custom report work minimum $50 for small changes + + subscription month to month https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B301028467%5D=1 + subscription yearly https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B301028468%5D=1 + Perpetual single license: https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B301028314%5D=1 + *** TESTING THIS ONE FIRST Perpetual single one year maintenance ACTIVE DISCOUNT PRICE: https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B301028315%5D=1 + Perpetual single one year maintenance NEW FULL PRICE: https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B301028317%5D=1 + v7 single for comparison 300740315 https://order.mycommerce.com/cart/new?vendorid=14466&PRODUCT%5B300740315%5D=1 + Address to use for testing + 05-3610 Christie Parkway Courtenay B.C. Canada V9J 9T6 + +*/ diff --git a/server/util/AySchema.cs b/server/util/AySchema.cs index 598e70d..796583a 100644 --- a/server/util/AySchema.cs +++ b/server/util/AySchema.cs @@ -897,16 +897,20 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); + "vendorid BIGINT NOT NULL REFERENCES avendor(id), pgroup INTEGER NOT NULL DEFAULT 0, licenseinterval INTERVAL, maintinterval INTERVAL, vendorcode TEXT NOT NULL UNIQUE, ourcode TEXT NOT NULL UNIQUE, " + "wiki TEXT, tags VARCHAR(255) ARRAY )"); + await ExecQueryAsync("CREATE TABLE avendornotification (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, created TIMESTAMPTZ NOT NULL, vendorid BIGINT NOT NULL REFERENCES avendor(id), " + + "vendordata TEXT, processed TIMESTAMPTZ)"); await ExecQueryAsync("CREATE TABLE apurchase (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, customerid BIGINT REFERENCES acustomer(id) ON DELETE CASCADE, " + "vendorid BIGINT NOT NULL REFERENCES avendor(id), productid BIGINT REFERENCES aproduct(id), pgroup INTEGER NOT NULL DEFAULT 4, salesordernumber TEXT NOT NULL, " + "productnet DECIMAL(38,18) NOT NULL default 0, discount DECIMAL(38,18) NOT NULL default 0, vendorfee DECIMAL(38,18) NOT NULL default 0, revenue DECIMAL(38,18) NOT NULL default 0, " + "currency TEXT NOT NULL DEFAULT 'USD', purchasedate TIMESTAMPTZ NOT NULL, expiredate TIMESTAMPTZ, canceldate TIMESTAMPTZ, couponcode text, notes text, " + "renewnoticesent BOOL NOT NULL DEFAULT false, quantity INTEGER NOT NULL DEFAULT 1, " - + "vendordata TEXT, processed BOOL NOT NULL DEFAULT false, " + + "vendornotificationid BIGINT REFERENCES avendornotification(id), processed BOOL NOT NULL DEFAULT false, " + "wiki TEXT, tags VARCHAR(255) ARRAY )"); + + await ExecQueryAsync("CREATE TABLE agzcase (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, caseid BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, " + "created TIMESTAMPTZ NOT NULL, closed TIMESTAMPTZ, name TEXT NOT NULL, notes TEXT, " + "wiki TEXT, tags VARCHAR(255) ARRAY, customerid BIGINT REFERENCES acustomer(id) )"); @@ -1053,7 +1057,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerDatacenter', 'Data center' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTimeZone', 'Time zone' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastUpdated', 'Last updated' FROM atranslation t where t.baselanguage = 'en'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthStatus', 'Health status' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthCheck', 'Last health check' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTrialContact', 'Trial contact' FROM atranslation t where t.baselanguage = 'en'"); @@ -1079,7 +1083,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerDatacenter', 'Data center' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTimeZone', 'Time zone' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastUpdated', 'Last updated' FROM atranslation t where t.baselanguage = 'es'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthStatus', 'Health status' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthCheck', 'Last health check' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTrialContact', 'Trial contact' FROM atranslation t where t.baselanguage = 'es'"); @@ -1105,7 +1109,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerDatacenter', 'Data center' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTimeZone', 'Time zone' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastUpdated', 'Last updated' FROM atranslation t where t.baselanguage = 'fr'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthStatus', 'Health status' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthCheck', 'Last health check' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTrialContact', 'Trial contact' FROM atranslation t where t.baselanguage = 'fr'"); @@ -1131,7 +1135,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerDatacenter', 'Data center' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTimeZone', 'Time zone' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastUpdated', 'Last updated' FROM atranslation t where t.baselanguage = 'de'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerSubExpire', 'Subscription expires' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthStatus', 'Health status' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerLastHealthCheck', 'Last health check' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'SubServerTrialContact', 'Trial contact' FROM atranslation t where t.baselanguage = 'de'"); @@ -1206,7 +1210,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseNotes', 'Notes' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseRenewNoticeSent', 'Renew notice sent' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseQuantity', 'Quantity' FROM atranslation t where t.baselanguage = 'en'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorNotificationId', 'Vendor data' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseCurrency', 'Currency' FROM atranslation t where t.baselanguage = 'en'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProductNet', 'Product net' FROM atranslation t where t.baselanguage = 'en'"); @@ -1225,7 +1229,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseNotes', 'Notes' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseRenewNoticeSent', 'Renew notice sent' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseQuantity', 'Quantity' FROM atranslation t where t.baselanguage = 'es'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorNotificationId', 'Vendor data' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseCurrency', 'Currency' FROM atranslation t where t.baselanguage = 'es'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProductNet', 'Product net' FROM atranslation t where t.baselanguage = 'es'"); @@ -1244,7 +1248,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseNotes', 'Notes' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseRenewNoticeSent', 'Renew notice sent' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseQuantity', 'Quantity' FROM atranslation t where t.baselanguage = 'fr'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorNotificationId', 'Vendor data' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseCurrency', 'Currency' FROM atranslation t where t.baselanguage = 'fr'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProductNet', 'Product net' FROM atranslation t where t.baselanguage = 'fr'"); @@ -1264,7 +1268,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseNotes', 'Notes' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseRenewNoticeSent', 'Renew notice sent' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseQuantity', 'Quantity' FROM atranslation t where t.baselanguage = 'de'"); - await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseVendorNotificationId', 'Vendor data' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseCurrency', 'Currency' FROM atranslation t where t.baselanguage = 'de'"); await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'PurchaseProductNet', 'Product net' FROM atranslation t where t.baselanguage = 'de'"); @@ -1274,6 +1278,38 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); #endregion purchase + #region VENDOR NOTIFICATION + + //english translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotification', 'Vendor notification' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationList', 'Vendor notifications' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationCreated', 'Created' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'en'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'en'"); + + //spanish translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotification', 'Vendor notification' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationList', 'Vendor notifications' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationCreated', 'Created' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'es'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'es'"); + + //french translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotification', 'Vendor notification' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationList', 'Vendor notifications' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationCreated', 'Created' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'fr'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'fr'"); + + //german translations + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotification', 'Vendor notification' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationList', 'Vendor notifications' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationCreated', 'Created' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationVendorData', 'Vendor data' FROM atranslation t where t.baselanguage = 'de'"); + await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'VendorNotificationProcessed', 'Processed' FROM atranslation t where t.baselanguage = 'de'"); + + #endregion vendor notification + #region GZCASE //english translations await ExecQueryAsync("INSERT INTO atranslationitem(translationid,key,display) SELECT t.id, 'GZCase', 'Case' FROM atranslation t where t.baselanguage = 'en'");