This commit is contained in:
2021-06-07 18:17:06 +00:00
parent 59fd41d8d0
commit 7a73e6c78e
11 changed files with 109 additions and 30 deletions

View File

@@ -24,7 +24,7 @@ LICENSE BOOTSTRAPPING
- Also this is the new fetch code
- RAVEN Is unlicensed it should check for a new key on a shorter frequency loop rather than once a day?
or, providing a button to trigger that should be enough really
or when server starts it starts the GENERATE loop and checks for a key right away, so they can just reboot teh server if they want an immediate license check?
or when server starts it starts the GENERATE loop and checks for a key right away, so they can just reboot the server if they want an immediate license check?
- RAVEN DB is empty (of biz data) it's license locked and a User MUST DONE ONE OF THE FOLLOWING:

View File

@@ -24,7 +24,7 @@ BACKEND TODO / SYSTEM FOR RAVEN
IMPLEMENTATION PATH / PLAN - code as minimal a route as possible to get to working notifications end to end with minimal subset
first is test notification which is done as an implementation of the GeneralNotification Event type and is delivered to the current use running the test
This will go through all potential problems or maybe trigger a troubleshoot route at teh server that checks all and reports back with any issues found
This will go through all potential problems or maybe trigger a troubleshoot route at the server that checks all and reports back with any issues found
Also needs to be localized (do the test route thing, that just makes the most sense. The test route should accept the type of notification i.e. inapp or smtp, act accordingly if it's a customer user vs regular if there is any difference)
Should report to user exactly any issues found at any level preventing it from happening
Should submit as a job and await running in place like other ones so that its actually working end to end with the generator

View File

@@ -119,7 +119,7 @@ todo: MEMORY / PERFORMANCE / CRASHING
todo: memory usage and timeout is directly related to the amount of space taken up physically on the page
it's NOT related to the helpers as just putting static text on the page causes teh same issue
it's NOT related to the helpers as just putting static text on the page causes the same issue
it's memory taken to render to pdf probably and a byte is a byte even if it's blank white page
=-=-=-=-=-

View File

@@ -7,7 +7,7 @@ If each part has it's own concurrency then can submit whole and bits that aren't
CONCURRENCY decision / research - updating, in bits seperately, entirely at once? [## DECIDED, BITS MAKES MOST SENSE AND DIRTY TRACKING AT CLIENT ##]
Considering we are looking at a system where some users won't see or even get the data for some parts of teh workorder then it's important that they be able to just save their piece of it separately
Considering we are looking at a system where some users won't see or even get the data for some parts of the workorder then it's important that they be able to just save their piece of it separately
So this strongly leans towards seperately saved and not entirely at all.
concurrency issue reduction
Things that might require an entire workorder graph be updated at once:
@@ -38,7 +38,7 @@ CONCURRENCY decision / research - updating, in bits seperately, entirely at once
Actually isn't this just handled at the server as a biz rule? (no because if header saved first then it's locked potentially and sub items can't be save subsequently in a graph walking save)
New items are always dirty so a new labor record added would be dirty and saved individually
At the server upon item being saved there would always need to be a quick check of the entire workorder to see if it's LOCKED status or not
if locked then bumps back with error that wo is now locked (of course they could just change teh status in the header and resave I guess)
if locked then bumps back with error that wo is now locked (of course they could just change the status in the header and resave I guess)
UI stuff
@@ -47,7 +47,7 @@ UI stuff
so a single workorderitem workorder would have all fields exposed and if any grandchildren those are entered in a single form too until the user has more than one
in v7 it's three clicks to go to a grandchild items specific field from the top header, woitem click -> left nav button row selection of area click -> grid row column click
in v8 it would be one more click then if it was a multi record grandchild but if it was a single record all teh way down then all would be exposed and visible at hand could go directly to the field in question
in v8 it would be one more click then if it was a multi record grandchild but if it was a single record all the way down then all would be exposed and visible at hand could go directly to the field in question
so in this concept every level exposes an edit form below a grid of one or more items, but grid doesn't show if there is one item so it's very clean.
if there is no record at that collection then just a click to add button as unobtrusive but clearly marked as possible (if user has selected to hide that section then doesn't even show and some users don't see at all)
if multiple at that level then a grid to select the active row to edit below it showing as much data as possible in each row to help user see at a glance while editing a sibling item

View File

@@ -67,6 +67,9 @@ namespace AyaNova.Api.Controllers
WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (newObject.Items.Count > 0)
return BadRequest(new ApiErrorResponse(ApiErrorCode.INVALID_OPERATION, "generalerror", "Work order POST route accepts header only; POST Work order descendants separately"));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
WorkOrder o = await biz.WorkOrderCreateAsync(newObject);
@@ -127,7 +130,7 @@ namespace AyaNova.Api.Controllers
/// Update WorkOrder
///
/// </summary>
/// <param name="updatedObject">WorkOrder - top level only, no descendants</param>
/// <param name="updatedObject">WorkOrder - top level only, no Items or other descendants</param>
/// <returns>Updated work order header</returns>
[HttpPut]
public async Task<IActionResult> PutWorkOrder([FromBody] WorkOrder updatedObject)

View File

@@ -10,10 +10,11 @@ namespace AyaNova.Biz
{
//see core-notifications.txt spec doc for a bit more info on each type (they are named a little bit differently)
//#### NOTE: once event is NOTED IN COMMENT (not necessarily coded yet as some can't be yet) in NotifyEventProcessor I'll mark it with a * in the comment so I know if I miss any
//#### NOTE: once event is fully coded I'll mark it with CODED at the start of the comment line
ObjectDeleted = 1,//* Deletion of any object of conditional specific AyaType and optionally conditional tags
ObjectCreated = 2,//* creation of any object of conditional specific AyaType and optionally conditional tags
ObjectModified = 3,//* Modification / update of any kind of any object of conditional specific AyaType and optionally conditional tags
WorkorderStatusChange = 4,//* Workorder object, any *change* of status including from no status (new) to a specific conditional status ID value
WorkorderStatusChange = 4,//* Workorder object, any NEW status set. Conditions: specific status ID value, Workorder TAGS
ContractExpiring = 5,//* Contract object, aged notification with optional advance notice for expiration date of contract. Customer version and User version deliveries possible.
CSRAccepted = 6,//*CustomerServiceRequest object, saved with ACCEPTED status, delivered to Customer only
CSRRejected = 7,//*CustomerServiceRequest object, saved with REJECTED status, delivered to Customer only

View File

@@ -77,9 +77,9 @@ namespace AyaNova.Biz
await WorkOrderSearchIndexAsync(newObject, true);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null);
//Was this a full workorder posted all at once?
//(seeder or api user, not something AyaNova front end would do)
if (newObject.Items.Count > 0)//our front end will post the header alone on new so this indicates a fully populated wo was saved
//# NOTE: only internal code can post an entire workorder graph, no external user can as controller will reject right up front
//however, internally seeder will post entire workorders
if (newObject.Items.Count > 0)
{
await GetCurrentContractFromContractIdAsync(newObject.ContractId);
@@ -111,6 +111,8 @@ namespace AyaNova.Biz
}
}
//NOTE: not running individual notification here for children, seeder won't require it and that's all that posts an entire wo currently
}
await transaction.CommitAsync();
if (populateViz)
@@ -985,6 +987,14 @@ namespace AyaNova.Biz
//SPECIFIC EVENTS FOR THIS OBJECT
if (ayaEvent == AyaEvent.Deleted)
{
//remove any potential time delayed STATUS notifications for OVERDUE NOT CLOSED STATUS
//NOTE: if the workorder is deleted the above process standard etc will automatically
//remove *ALL* events created for this workorder so no need to remove overdue or any wo related time delayed status here
}
//todo: contract response time notification
}//end of process notifications
@@ -1029,6 +1039,7 @@ namespace AyaNova.Biz
await ct.WorkOrderState.AddAsync(newObject);
await ct.SaveChangesAsync();
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, AyaType.WorkOrderStatus, AyaEvent.Created), ct);
await StateHandlePotentialNotificationEvent(AyaEvent.Created, newObject);
return newObject;
}
}
@@ -1074,9 +1085,12 @@ namespace AyaNova.Biz
foreach (var wostate in stateList)
{
await StateHandlePotentialNotificationEvent(AyaEvent.Deleted, wostate);
ct.WorkOrderState.Remove(wostate);
await ct.SaveChangesAsync();
//no need to call this because it's only going to run this method if the workorder is deleted and
//via process standard notifciation events for workorder deletion will remove any state delayed notifications anyway so
//nothing to call or do here related to notification
// await StateHandlePotentialNotificationEvent(AyaEvent.Deleted, wostate);
}
}
catch
@@ -1139,24 +1153,89 @@ namespace AyaNova.Biz
//STANDARD EVENTS FOR ALL OBJECTS
await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct);
//NONE: state notifications are specific and not the same as for general objects so don't process standard events
//SPECIFIC EVENTS FOR THIS OBJECT
//WorkorderStatusChange = 4,//* Workorder object, any *change* of status including from no status (new) to a specific conditional status ID value
//WorkorderFinishStatusOverdue = 15,//* Workorder object not set to a "Finished" flagged workorder status type in selected time span from creation of workorderWorkorderSetToFinishedStatus
//WorkorderStatusAge = 24,//* Workorder object Created / Updated, conditional on exact status selected IdValue, Tags conditional, advance notice can be set
WorkOrderState o = (WorkOrderState)proposedObj;
WorkOrder wo = await ct.WorkOrder.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.WorkOrderId);
//## DELETED EVENTS
//any event added below needs to be removed, so
//just blanket remove any event for this object of eventtype that would be added below here
//do it regardless any time there's an update and then
//let this code below handle the refreshing addition that could have changes
// await NotifyEventHelper.ClearPriorEventsForObject(ct, proposedObj.AyaType, o.Id, NotifyEventType.ContractExpiring);
//A state cannot be deleted so nothing to handle that is required
//a workorder CAN be deleted and it will automatically remove all events for it so also no need to remove time delayed status events either if wo is deleted.
//so in essence there is nothing to be done regarding deleted events with states
//NO, the above is not correct, time delayed ones should NOT be removed for example finished until appropriate or if wo deleted
//so there should be a deleted event here handled with delete of all notifications sitting and the blanket remove below should be more surgical
//and only apply to stuff that it is approriate to remove
//#NOTE: state can be deleted so this block will handle that
await NotifyEventHelper.ClearPriorEventsForObject(ct, proposedObj.AyaType, o.Id, NotifyEventType.WorkorderStatusChange);
await NotifyEventHelper.ClearPriorEventsForObject(ct, proposedObj.AyaType, o.Id, NotifyEventType.WorkorderFinishStatusOverdue);
await NotifyEventHelper.ClearPriorEventsForObject(ct, proposedObj.AyaType, o.Id, NotifyEventType.WorkorderStatusAge);
//## CREATED / MODIFIED EVENTS
if (ayaEvent == AyaEvent.Created || ayaEvent == AyaEvent.Modified)
//## CREATED (this is the only possible notification CREATION ayaEvent type for a workorder state as they are create only)
if (ayaEvent == AyaEvent.Created)
{
//todo: fix etc, tons of shit here incoming
//# STATUS CHANGE (create new status)
{
var subs = await ct.NotifySubscription.Where(z => z.EventType == NotifyEventType.WorkorderStatusChange).ToListAsync();
foreach (var sub in subs)
{
//not for inactive users
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
//Tag match? (will be true if no sub tags so always safe to call this)
if (NotifyEventHelper.TagsMatch(wo.Tags, sub.Tags))
{
NotifyEvent n = new NotifyEvent()
{
EventType = NotifyEventType.WorkorderStatusChange,
UserId = sub.UserId,
AyaType = o.AyaType,
ObjectId = o.Id,
NotifySubscriptionId = sub.Id,
Name = wo.Serial.ToString()
};
await ct.NotifyEvent.AddAsync(n);
log.LogDebug($"Adding NotifyEvent: [{n.ToString()}]");
await ct.SaveChangesAsync();
}
}
}//workorder status change event
//# WorkorderFinishStatusOverdue
{//* Workorder object not set to a "Finished" flagged workorder status type in selected time span from creation of workorderWorkorderSetToFinishedStatus
var subs = await ct.NotifySubscription.Where(z => z.EventType == NotifyEventType.WorkorderStatusChange).ToListAsync();
foreach (var sub in subs)
{
//not for inactive users
if (!await UserBiz.UserIsActive(sub.UserId)) continue;
//Tag match? (will be true if no sub tags so always safe to call this)
if (NotifyEventHelper.TagsMatch(wo.Tags, sub.Tags))
{
NotifyEvent n = new NotifyEvent()
{
EventType = NotifyEventType.WorkorderStatusChange,
UserId = sub.UserId,
AyaType = o.AyaType,
ObjectId = o.Id,
NotifySubscriptionId = sub.Id,
Name = wo.Serial.ToString()
};
await ct.NotifyEvent.AddAsync(n);
log.LogDebug($"Adding NotifyEvent: [{n.ToString()}]");
await ct.SaveChangesAsync();
}
}
}//workorder finish change event
}

View File

@@ -23,16 +23,12 @@ namespace AyaNova.Biz
private static DateTime lastRun = DateTime.MinValue;
// private static TimeSpan DELETE_AFTER_AGE = new TimeSpan(90, 0, 0, 0);
// private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 2, 0);//once every 2 minutes minimum
private static DateTime lastNotifyHealthCheckSentLocal = DateTime.MinValue;
private static TimeSpan TS_24_HOURS = new TimeSpan(24, 0, 0);//used to ensure daily ops happen no more than that
#if (DEBUG)
private static TimeSpan DELETE_AFTER_AGE = new TimeSpan(0, 12, 0, 0);
#if (DEBUG)
private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 0, 20);//no more frequently than once every 20 seconds
#else
private static TimeSpan DELETE_AFTER_AGE = new TimeSpan(90, 0, 0, 0);
#else
private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 2, 0);//no more frequently than once every 2 minutes
#endif

View File

@@ -37,7 +37,7 @@ namespace AyaNova.Generator
/*
todo: improve this
it should timeout: https://stackoverflow.com/questions/23476576/cancellationtoken-timeout-vs-task-delay-and-timeout
it should never stop running no matter what unless teh server shuts down
it should never stop running no matter what unless the server shuts down
*/
protected override async Task ExecuteAsync(CancellationToken stoppingToken)

View File

@@ -107,7 +107,7 @@ CREATE TABLE [dbo].[APARTBYWAREHOUSEINVENTORY](
[APARTID] [uniqueidentifier] NOT NULL,
[APARTWAREHOUSEID] [uniqueidentifier] NOT NULL,
[AQUANTITYONHAND] [decimal](19, 5) NOT NULL,
[AQUANTITYONORDER] [decimal](19, 5) NOT NULL,//calculated on teh fly from active PO's
[AQUANTITYONORDER] [decimal](19, 5) NOT NULL,//calculated on the fly from active PO's
[AMINSTOCKLEVEL] [decimal](19, 5) NOT NULL,//New table / feature PartRestock (partid/warhouseid/stocklevel)
[AQTYONORDERCOMMITTED] [decimal](19, 5) NOT NULL, //Calculated on the fly from po and partrequests
*/

View File

@@ -66,7 +66,7 @@ namespace AyaNova.Core
//Current license key, can be empty
private static AyaNovaLicenseKey _ActiveLicense = new AyaNovaLicenseKey();
//The license dbid, separate from teh server dbid
//The license dbid, separate from the server dbid
private static string LicenseDbId { get; set; }
#region license classes