This commit is contained in:
2023-01-23 00:59:38 +00:00
parent dec9ca1fff
commit ec6ec2701e
17 changed files with 924 additions and 94 deletions

View File

@@ -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
}
//------------

View File

@@ -14,7 +14,7 @@ namespace Sockeye.DataList
var RoleSet = BizRoles.GetRoleSet(DefaultListAType);
AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change;
DefaultColumns = new List<string>() { "PurchaseDate", "PurchaseSalesOrderNumber", "Customer", "Product", "PurchaseProcessed" };
DefaultColumns = new List<string>() { "PurchaseDate", "PurchaseSalesOrderNumber", "Customer", "Product", "PurchaseProcessed", "PurchaseVendorNotificationId" };
DefaultSortBy = new Dictionary<string, string>() { { "PurchaseDate", "-" } };
FieldDefinitions = new List<DataListFieldDefinition>();
@@ -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",

View File

@@ -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<string>() { "VendorNotificationCreated", "VendorNotificationProcessed" };
DefaultSortBy = new Dictionary<string, string>() { { "VendorNotificationCreated", "-" } };
FieldDefinitions = new List<DataListFieldDefinition>();
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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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<SockType, BizRoleSet> lastRolesDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<SockType, BizRoleSet>>(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<SockType, BizRoleSet> lastRolesDeserialized = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<SockType, BizRoleSet>>(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

View File

@@ -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<FormField> l = new List<FormField>();

View File

@@ -336,28 +336,19 @@ namespace Sockeye.Biz
},
*/
var jData = JObject.Parse(jVendorNotificationItem["data"].Value<string>());
var salesOrderNumber = jData["order_notification"]["purchase"]["purchase_id"].Value<string>();
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<string>();
await ct.SaveChangesAsync();
var vn = new VendorNotification();
vn.Created = DateUtil.EpochToDateNullIsMin(jVendorNotificationItem["dtCreated"].Value<long>());
vn.VendorId = 1;
vn.VendorData = jVendorNotificationItem["data"].Value<string>();
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
}

View File

@@ -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

View File

@@ -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();

View File

@@ -75,7 +75,9 @@ namespace Sockeye.Biz
[CoreBizObject, ReportableBizObject]
Product = 97,
[CoreBizObject, ReportableBizObject]
GZCase = 98
GZCase = 98,
[CoreBizObject, ReportableBizObject]
VendorNotification = 99

View File

@@ -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<bool> ExistsAsync(long id)
{
return await ct.VendorNotification.AnyAsync(z => z.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//
internal async Task<VendorNotification> 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<VendorNotification> 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<VendorNotification> 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<bool> 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<Search.SearchIndexProcessObjectParameters> 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<JArray> 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<JArray> 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<long> idList = new List<long>();
long FailedObjectCount = 0;
JObject jobData = JObject.Parse(job.JobInfo);
if (jobData.ContainsKey("idList"))
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
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<VendorNotificationBiz>();
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

View File

@@ -20,9 +20,9 @@ namespace Sockeye.Biz
/// A Separate job will make licenses
///
/// </summary>
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<string>() ?? throw new System.FormatException($"Vendor data empty reg_name:{p.VendorData}");
var jCustomerEmail = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"].Value<string>() ?? throw new System.FormatException($"Vendor data empty email:{p.VendorData}");
var jCustomerName = jData["order_notification"]["purchase"]["customer_data"]["reg_name"].Value<string>() ?? throw new System.FormatException($"Vendor data empty reg_name:{vn.VendorData}");
var jCustomerEmail = jData["order_notification"]["purchase"]["customer_data"]["delivery_contact"]["email"].Value<string>() ?? 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<string>();
@@ -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/////////////////////////////
{

View File

@@ -60,6 +60,7 @@ namespace Sockeye.Models
public virtual DbSet<Vendor> Vendor { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Purchase> Purchase { get; set; }
public virtual DbSet<VendorNotification> VendorNotification { get; set; }
public virtual DbSet<TrialLicenseRequest> TrialLicenseRequest { get; set; }
public virtual DbSet<GZCase> GZCase { get; set; }
public virtual DbSet<SubscriptionServer> SubscriptionServer { get; set; }

View File

@@ -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<string> Tags { get; set; }

View File

@@ -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<string> 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
*/

View File

@@ -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'");