From e78624b45078012b9b20757d83b273bb9743d53f Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 10 Feb 2022 21:23:45 +0000 Subject: [PATCH] case 4108 --- .../NotifySubscriptionController.cs | 4 +- server/AyaNova/biz/QuoteBiz.cs | 108 +++++++++++++++++- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/server/AyaNova/Controllers/NotifySubscriptionController.cs b/server/AyaNova/Controllers/NotifySubscriptionController.cs index a2e74ba4..b1f4580d 100644 --- a/server/AyaNova/Controllers/NotifySubscriptionController.cs +++ b/server/AyaNova/Controllers/NotifySubscriptionController.cs @@ -118,7 +118,7 @@ namespace AyaNova.Api.Controllers NotifySubscriptionBiz biz = NotifySubscriptionBiz.GetBiz(ct, HttpContext); if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType)) return StatusCode(403, new ApiNotAuthorizedResponse()); - var o = await biz.PutAsync(updatedObject); + var o = await biz.PutAsync(updatedObject); if (o == null) { if (biz.Errors.Exists(z => z.Code == ApiErrorCode.CONCURRENCY_CONFLICT)) @@ -190,7 +190,7 @@ namespace AyaNova.Api.Controllers return await ct.WorkOrderStatus.AsNoTracking().Where(x => x.Id == s.IdValue).Select(x => x.Name).FirstOrDefaultAsync(); case NotifyEventType.QuoteStatusAge: case NotifyEventType.QuoteStatusChange: - throw new System.NotImplementedException("TODO: quote status events in notifysubscriptioncontroller::getStatusName"); + return await ct.QuoteStatus.AsNoTracking().Where(x => x.Id == s.IdValue).Select(x => x.Name).FirstOrDefaultAsync(); } return string.Empty; } diff --git a/server/AyaNova/biz/QuoteBiz.cs b/server/AyaNova/biz/QuoteBiz.cs index 4c922ba5..76936bf3 100644 --- a/server/AyaNova/biz/QuoteBiz.cs +++ b/server/AyaNova/biz/QuoteBiz.cs @@ -1112,7 +1112,7 @@ namespace AyaNova.Biz qoute.LastStatusId = newObject.QuoteStatusId; await ct.SaveChangesAsync(); await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, AyaType.QuoteStatus, AyaEvent.Created), ct); - //await StateHandlePotentialNotificationEvent(AyaEvent.Created, newObject); + await StateHandlePotentialNotificationEvent(AyaEvent.Created, newObject); return newObject; } } @@ -1209,7 +1209,109 @@ namespace AyaNova.Biz } } + //////////////////////////////////////////////////////////////////////////////////////////////// + // NOTIFICATION PROCESSING + // + public async Task StateHandlePotentialNotificationEvent(AyaEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null) + { + ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger(); + if (ServerBootConfig.SEEDING || ServerBootConfig.MIGRATING) return; + log.LogDebug($"HandlePotentialNotificationEvent processing: [AyaType:{proposedObj.AyaType}, AyaEvent:{ayaEvent}]"); + + bool isNew = currentObj == null; + QuoteState oProposed = (QuoteState)proposedObj; + + var QuoteInfo = await ct.Quote.AsNoTracking().Where(x => x.Id == oProposed.QuoteId).Select(x => new { x.Serial, x.Tags, x.CustomerId }).FirstOrDefaultAsync(); + QuoteStatus qos = await ct.QuoteStatus.AsNoTracking().FirstOrDefaultAsync(x => x.Id == oProposed.QuoteStatusId); + //for notification purposes because has no name / tags field itself + oProposed.Name = QuoteInfo.Serial.ToString(); + oProposed.Tags = QuoteInfo.Tags; + + //STANDARD EVENTS FOR ALL OBJECTS + //NONE: state notifications are specific and not the same as for general objects so don't process standard events + + //SPECIFIC EVENTS FOR THIS OBJECT + //QuoteStatusChange = 9,//* Quote object, any *change* of status including from no status (new) to a specific conditional status ID value + // QuoteStatusAge = 29,//* Quote object Created / Updated, conditional on exact status selected IdValue, Tags conditional, advance notice can be set + + //NOTE: ID, state notifications are for the Quote, not the state itself unlike other objects, so use the quote type and ID here for all notifications + + + //## DELETED EVENTS + //A state cannot be deleted so nothing to handle that is required + //a quote 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 in a blanket way, however specific events below may remove them as appropriate + + + //## CREATED (this is the only possible notification CREATION ayaEvent type for a quote state as they are create only) + if (ayaEvent == AyaEvent.Created) + { + //# STATUS CHANGE (create new status) + { + //Conditions: must match specific status id value and also tags below + //delivery is immediate so no need to remove old ones of this kind + var subs = await ct.NotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.QuoteStatusChange && z.IdValue == oProposed.QuoteStatusId).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.ObjectHasAllSubscriptionTags(QuoteInfo.Tags, sub.Tags)) + { + NotifyEvent n = new NotifyEvent() + { + EventType = NotifyEventType.QuoteStatusChange, + UserId = sub.UserId, + AyaType = AyaType.WorkOrder, + ObjectId = oProposed.QuoteId, + NotifySubscriptionId = sub.Id, + Name = $"{QuoteInfo.Serial.ToString()} - {qos.Name}" + }; + await ct.NotifyEvent.AddAsync(n); + log.LogDebug($"Adding NotifyEvent: [{n.ToString()}]"); + await ct.SaveChangesAsync(); + } + } + }//quote status change event + + //# STATUS AGE + { + //QuoteStatusAge = 29,//* Quote STATUS unchanged for set time (stuck in state), conditional on: Duration (how long stuck), exact status selected IdValue, Tags. Advance notice can NOT be set + //Always clear any old ones for this object as they are all irrelevant the moment the state has changed: + await NotifyEventHelper.ClearPriorEventsForObject(ct, proposedObj.AyaType, proposedObj.Id, NotifyEventType.QuoteStatusAge); + var subs = await ct.NotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.QuoteStatusAge && z.IdValue == oProposed.QuoteStatusId).ToListAsync(); + foreach (var sub in subs) + { + //not for inactive users + if (!await UserBiz.UserIsActive(sub.UserId)) continue; + + //WorkOrder Tag match? (Not State, state has no tags, will be true if no sub tags so always safe to call this) + if (NotifyEventHelper.ObjectHasAllSubscriptionTags(QuoteInfo.Tags, sub.Tags)) + { + NotifyEvent n = new NotifyEvent() + { + EventType = NotifyEventType.QuoteStatusAge, + UserId = sub.UserId, + AyaType = AyaType.WorkOrder, + ObjectId = oProposed.QuoteId, + NotifySubscriptionId = sub.Id, + Name = $"{QuoteInfo.Serial.ToString()} - {qos.Name}" + }; + await ct.NotifyEvent.AddAsync(n); + log.LogDebug($"Adding NotifyEvent: [{n.ToString()}]"); + await ct.SaveChangesAsync(); + } + } + }//quote status age event + + + + + } + + }//end of process notifications #endregion work order STATE level @@ -2200,7 +2302,7 @@ namespace AyaNova.Biz //POTENTIAL CONTRACT ADJUSTMENTS //First check if there is a matching tagged service rate contract discount, that takes precedence - if (c.ContractServiceRateOverrideItems.Count > 0 && Rate!=null) + if (c.ContractServiceRateOverrideItems.Count > 0 && Rate != null) { //Iterate all contract tagged items in order of ones with the most tags first foreach (var csr in c.ContractServiceRateOverrideItems.OrderByDescending(z => z.Tags.Count)) @@ -4364,7 +4466,7 @@ namespace AyaNova.Biz //POTENTIAL CONTRACT ADJUSTMENTS //First check if there is a matching tagged Travel rate contract discount, that takes precedence - if (c.ContractTravelRateOverrideItems.Count > 0 && Rate!=null) + if (c.ContractTravelRateOverrideItems.Count > 0 && Rate != null) { //Iterate all contract tagged items in order of ones with the most tags first foreach (var csr in c.ContractTravelRateOverrideItems.OrderByDescending(z => z.Tags.Count))