This commit is contained in:
2021-06-14 18:50:35 +00:00
parent c48b29ecf4
commit 25822c9c65
2 changed files with 155 additions and 80 deletions

View File

@@ -94,7 +94,7 @@ There are no settings adjustable for in app General notifications, however Users
| NotifyHealthCheck | Automatic daily "ping" notification to confirm notification and Generator system is active at server |
| BackupStatus | Result of last Backup operation at server |
| CustomerServiceImminent | Scheduled service date / time is about to be reached. Intended for Customer type User |
| WorkorderTotalExceedsThreshold | The balance of a Work order has exceeded a threshold (aka the "Andy") |
| WorkorderTotalExceedsThreshold | The balance of a Work order has exceeded a threshold (the "Andy") |
| WorkorderStatusAge | A Workorder has been sitting at the selected status for longer than the selected time frame |
| UnitWarrantyExpiry | A Unit's warranty expiration date is reached |
| UnitMeterReadingMultipleExceeded | A meter readingn *multiple* exceeds selected threshold (e.g. every 10,000 etc) |

View File

@@ -574,62 +574,71 @@ namespace AyaNova.Biz
//NOTE: REMOVED WHEN REMOVED STATIC PRICING
// ////////////////////////////////////////////////////////////////////////////////////////////////
// //CONTRACT CHANGE HANDLER
// //
// //
// private async Task<WorkOrder> ProcessChangeOfContractAsync(long woId)
// {
// //contract has changed, update entire graph pricing and potentially response time stuff as well here now
// //iterate graph calling *SetListPrice on each item
// var wo = await ct.WorkOrder.AsSplitQuery()
// .Include(s => s.States)
// .Include(w => w.Items.OrderBy(item => item.Sequence))
// .ThenInclude(wi => wi.Expenses)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Labors)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Loans)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Parts)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.PartRequests)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.ScheduledUsers)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Tasks.OrderBy(t => t.Sequence))
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Travels)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.Units)
// .Include(w => w.Items)
// .ThenInclude(wi => wi.OutsideServices)
// .SingleOrDefaultAsync(z => z.Id == woId);
////////////////////////////////////////////////////////////////////////////////////////////////
// "The Andy" notification helper
//
// (for now this is only for the notification exceeds total so only need one grand total of
// line totals, if in future need more can return a Record object instead with split out
// taxes, net etc etc)
//
private async Task<decimal> WorkorderTotalAsync(long workOrderId, AyContext ct)
{
// //If Contract has response time then set CompleteByDate
// if (mContractInEffect != null && mContractInEffect.ResponseTime != TimeSpan.Zero)
// {
// wo.CompleteByDate = DateTime.UtcNow.Add(mContractInEffect.ResponseTime);
// }
var wo = await ct.WorkOrder.AsNoTracking().AsSplitQuery()
.Include(w => w.Items.OrderBy(item => item.Sequence))
.ThenInclude(wi => wi.Expenses)
.Include(w => w.Items)
.ThenInclude(wi => wi.Labors)
.Include(w => w.Items)
.ThenInclude(wi => wi.Loans)
.Include(w => w.Items)
.ThenInclude(wi => wi.Parts)
.Include(w => w.Items)
.ThenInclude(wi => wi.Travels)
.Include(w => w.Items)
.ThenInclude(wi => wi.OutsideServices)
.SingleOrDefaultAsync(z => z.Id == workOrderId);
if (wo == null) return 0m;
// // //update pricing
// // foreach (WorkOrderItem wi in wo.Items)
// // {
// // // foreach (WorkOrderItemLabor o in wi.Labors)
// // // await LaborSetPrice(o, mContractInEffect);
// // foreach (WorkOrderItemTravel o in wi.Travels)
// // TravelSetListPrice(o, mContractInEffect);
// // foreach (WorkOrderItemPart o in wi.Parts)
// // PartSetListPrice(o, mContractInEffect);
// // }
decimal GrandTotal = 0m;
//update pricing
foreach (WorkOrderItem wi in wo.Items)
{
foreach (WorkOrderItemExpense o in wi.Expenses)
await ExpensePopulateVizFields(o, true);
foreach (WorkOrderItemLabor o in wi.Labors)
await LaborPopulateVizFields(o, true);
foreach (WorkOrderItemLoan o in wi.Loans)
await LoanPopulateVizFields(o, null, true);
foreach (WorkOrderItemPart o in wi.Parts)
await PartPopulateVizFields(o, true);
foreach (WorkOrderItemTravel o in wi.Travels)
await TravelPopulateVizFields(o, true);
foreach (WorkOrderItemOutsideService o in wi.OutsideServices)
await OutsideServicePopulateVizFields(o, true);
}
// await ct.SaveChangesAsync();
// return wo;
foreach (WorkOrderItem wi in wo.Items)
{
foreach (WorkOrderItemExpense o in wi.Expenses)
GrandTotal += o.LineTotalViz;
foreach (WorkOrderItemLabor o in wi.Labors)
GrandTotal += o.LineTotalViz;
foreach (WorkOrderItemLoan o in wi.Loans)
GrandTotal += o.LineTotalViz;
foreach (WorkOrderItemPart o in wi.Parts)
GrandTotal += o.LineTotalViz;
foreach (WorkOrderItemTravel o in wi.Travels)
GrandTotal += o.LineTotalViz;
foreach (WorkOrderItemOutsideService o in wi.OutsideServices)
GrandTotal += o.LineTotalViz;
}
// }
return GrandTotal;
}
@@ -1146,8 +1155,7 @@ namespace AyaNova.Biz
}//CustomerServiceImminent
#endregion
#endregion
}//end of process notifications
@@ -1417,6 +1425,53 @@ namespace AyaNova.Biz
}
}//workorder complete by overdue change event
//# WorkorderTotalExceedsThreshold / "The Andy"
{
if (wos.Completed)
{
//see if any subscribers to the workorder total exceeds notification
//that are active then proceed to fetch billed woitem children and total workorder and send notification if necessary
bool haveTotal = false;
decimal total = 0m;
//look for potential subscribers
var subs = await ct.NotifySubscription.AsNoTracking().Where(z => z.EventType == NotifyEventType.WorkorderTotalExceedsThreshold).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)
//check early to avoid cost of fetching and calculating total if unnecessary
if (!NotifyEventHelper.ObjectHasAllSubscriptionTags(WorkorderInfo.Tags, sub.Tags)) continue;
//get the total because we have at least one subscriber and matching tags
if (haveTotal == false)
{
long WorkOrderId = 0;
if (ayaType == AyaType.WorkOrder)
WorkOrderId = id;
else
WorkOrderId = await GetWorkOrderIdFromRelativeAsync(ayaType, id, ct);
//clear out any existing ones as they may have *just* been set from a save and we don't want a workorder save of a bunch of items to trigger 10,000 notifications
//Always clear any old ones for this object as they are all irrelevant the moment changed:
await NotifyEventHelper.ClearPriorEventsForObject(ct, AyaType.WorkOrder, WorkOrderId, NotifyEventType.WorkorderTotalExceedsThreshold);
//total workorder
}
}
}
}//The Andy notification
}
}//end of process notifications
@@ -1973,11 +2028,13 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task ExpensePopulateVizFields(WorkOrderItemExpense o)
private async Task ExpensePopulateVizFields(WorkOrderItemExpense o, bool calculateTotalsOnly = false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
if (calculateTotalsOnly == false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
}
TaxCode Tax = null;
if (o.ChargeTaxCodeId != null)
Tax = await ct.TaxCode.AsNoTracking().FirstOrDefaultAsync(z => z.Id == o.ChargeTaxCodeId);
@@ -2314,10 +2371,13 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task LaborPopulateVizFields(WorkOrderItemLabor o)
private async Task LaborPopulateVizFields(WorkOrderItemLabor o, bool calculateTotalsOnly = false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
if (calculateTotalsOnly == false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
}
ServiceRate Rate = null;
if (o.ServiceRateId != null)
{
@@ -2799,14 +2859,17 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task LoanPopulateVizFields(WorkOrderItemLoan o, List<NameIdItem> loanUnitRateEnumList = null)
private async Task LoanPopulateVizFields(WorkOrderItemLoan o, List<NameIdItem> loanUnitRateEnumList = null, bool calculateTotalsOnly = false)
{
if (loanUnitRateEnumList == null)
loanUnitRateEnumList = await AyaNova.Api.Controllers.EnumListController.GetEnumList(
StringUtil.TrimTypeName(typeof(LoanUnitRateUnit).ToString()),
UserTranslationId,
CurrentUserRoles);
o.UnitOfMeasureViz = loanUnitRateEnumList.Where(x => x.Id == (long)o.Rate).Select(x => x.Name).First();
if (calculateTotalsOnly == false)
{
if (loanUnitRateEnumList == null)
loanUnitRateEnumList = await AyaNova.Api.Controllers.EnumListController.GetEnumList(
StringUtil.TrimTypeName(typeof(LoanUnitRateUnit).ToString()),
UserTranslationId,
CurrentUserRoles);
o.UnitOfMeasureViz = loanUnitRateEnumList.Where(x => x.Id == (long)o.Rate).Select(x => x.Name).First();
}
LoanUnit loanUnit = await ct.LoanUnit.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.LoanUnitId);
o.LoanUnitViz = loanUnit.Name;
@@ -3182,14 +3245,17 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task OutsideServicePopulateVizFields(WorkOrderItemOutsideService o)
private async Task OutsideServicePopulateVizFields(WorkOrderItemOutsideService o, bool calculateTotalsOnly = false)
{
if (o.UnitId != 0)
o.UnitViz = await ct.Unit.AsNoTracking().Where(x => x.Id == o.UnitId).Select(x => x.Serial).FirstOrDefaultAsync();
if (o.VendorSentToId != null)
o.VendorSentToViz = await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorSentToId).Select(x => x.Name).FirstOrDefaultAsync();
if (o.VendorSentViaId != null)
o.VendorSentViaViz = await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorSentViaId).Select(x => x.Name).FirstOrDefaultAsync();
if (calculateTotalsOnly == false)
{
if (o.UnitId != 0)
o.UnitViz = await ct.Unit.AsNoTracking().Where(x => x.Id == o.UnitId).Select(x => x.Serial).FirstOrDefaultAsync();
if (o.VendorSentToId != null)
o.VendorSentToViz = await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorSentToId).Select(x => x.Name).FirstOrDefaultAsync();
if (o.VendorSentViaId != null)
o.VendorSentViaViz = await ct.Vendor.AsNoTracking().Where(x => x.Id == o.VendorSentViaId).Select(x => x.Name).FirstOrDefaultAsync();
}
TaxCode Tax = null;
if (o.TaxCodeId != null)
@@ -3662,13 +3728,19 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task PartPopulateVizFields(WorkOrderItemPart o)
private async Task PartPopulateVizFields(WorkOrderItemPart o, bool calculateTotalsOnly = false)
{
if (o.PartWarehouseId != 0)
o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync();
if (calculateTotalsOnly == false)
{
if (o.PartWarehouseId != 0)
o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync();
}
Part part = null;
if (o.PartId != 0)
part = await ct.Part.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.PartId);
else
return;//this should never happen but this is insurance in case it does
o.PartViz = part.PartNumber;
o.UpcViz = part.UPC;
@@ -5095,10 +5167,13 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//VIZ POPULATE
//
private async Task TravelPopulateVizFields(WorkOrderItemTravel o)
private async Task TravelPopulateVizFields(WorkOrderItemTravel o, bool calculateTotalsOnly = false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
if (calculateTotalsOnly == false)
{
if (o.UserId != null)
o.UserViz = await ct.User.AsNoTracking().Where(x => x.Id == o.UserId).Select(x => x.Name).FirstOrDefaultAsync();
}
TravelRate Rate = null;
if (o.TravelRateId != null)
{