From e9cf952c1fc520c5b30fce5146c282204881a025 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Thu, 3 Jun 2021 19:53:43 +0000 Subject: [PATCH] --- server/AyaNova/biz/WorkOrderBiz.cs | 352 ++++++++++++++++------------- 1 file changed, 198 insertions(+), 154 deletions(-) diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index b43e61c0..a221f156 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -61,44 +61,64 @@ namespace AyaNova.Biz // internal async Task WorkOrderCreateAsync(WorkOrder newObject, bool populateViz = true) { - await WorkOrderValidateAsync(newObject, null); - if (HasErrors) - return null; - else + using (var transaction = await ct.Database.BeginTransactionAsync()) { - await WorkOrderBizActionsAsync(AyaEvent.Created, newObject, null, null); - newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); - newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); - await ct.WorkOrder.AddAsync(newObject); - await ct.SaveChangesAsync(); - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); - 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 + await WorkOrderValidateAsync(newObject, null); + if (HasErrors) + return null; + else { - await GetCurrentContractFromContractIdAsync(newObject.ContractId); - - - if (mContractInEffect != null && mContractInEffect.ResponseTime != TimeSpan.Zero) - newObject.CompleteByDate = DateTime.UtcNow.Add(mContractInEffect.ResponseTime); - - //GRANDCHILD BIZ ACTIONS - foreach (WorkOrderItem wi in newObject.Items) - { - foreach (WorkOrderItemPart wip in wi.Parts) - await PartBizActionsAsync(AyaEvent.Created, wip, null, null); - foreach (WorkOrderItemLoan wil in wi.Loans) - await LoanBizActionsAsync(AyaEvent.Created, wil, null, null); - } + await WorkOrderBizActionsAsync(AyaEvent.Created, newObject, null, null); + newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); + newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); + await ct.WorkOrder.AddAsync(newObject); await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); + 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 + { + await GetCurrentContractFromContractIdAsync(newObject.ContractId); + + + if (mContractInEffect != null && mContractInEffect.ResponseTime != TimeSpan.Zero) + newObject.CompleteByDate = DateTime.UtcNow.Add(mContractInEffect.ResponseTime); + + //GRANDCHILD BIZ ACTIONS + foreach (WorkOrderItem wi in newObject.Items) + { + foreach (WorkOrderItemPart wip in wi.Parts) + await PartBizActionsAsync(AyaEvent.Created, wip, null, null); + foreach (WorkOrderItemLoan wil in wi.Loans) + await LoanBizActionsAsync(AyaEvent.Created, wil, null, null); + } + await ct.SaveChangesAsync(); + + //INVENTORY ADJUSTMENTS + foreach (WorkOrderItem wi in newObject.Items) + { + foreach (WorkOrderItemPart wip in wi.Parts) + { + await PartInventoryAdjustmentAsync(AyaEvent.Created, wip, null, transaction); + if (HasErrors) + { + await transaction.RollbackAsync(); + return null; + } + } + + } + } + await transaction.CommitAsync(); + if (populateViz) + await WorkOrderPopulateVizFields(newObject, true); + + await WorkOrderHandlePotentialNotificationEvent(AyaEvent.Created, newObject); + return newObject; } - if (populateViz) - await WorkOrderPopulateVizFields(newObject, true); - await WorkOrderHandlePotentialNotificationEvent(AyaEvent.Created, newObject); - return newObject; } } @@ -3088,21 +3108,31 @@ namespace AyaNova.Biz // internal async Task CreatePartAsync(WorkOrderItemPart newObject) { - await PartValidateAsync(newObject, null); - if (HasErrors) - return null; - else + using (var transaction = await ct.Database.BeginTransactionAsync()) { - await PartBizActionsAsync(AyaEvent.Created, newObject, null, null); - //newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); - //newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); - await ct.WorkOrderItemPart.AddAsync(newObject); - await ct.SaveChangesAsync(); - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, newObject.AyaType, AyaEvent.Created), ct); - await PartSearchIndexAsync(newObject, true); - //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); - await PartPopulateVizFields(newObject); - return newObject; + await PartValidateAsync(newObject, null); + if (HasErrors) + return null; + else + { + await PartBizActionsAsync(AyaEvent.Created, newObject, null, null); + //newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); + //newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); + await ct.WorkOrderItemPart.AddAsync(newObject); + await ct.SaveChangesAsync(); + await PartInventoryAdjustmentAsync(AyaEvent.Created, newObject, null, transaction); + if (HasErrors) + { + await transaction.RollbackAsync(); + return null; + } + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, newObject.AyaType, AyaEvent.Created), ct); + await PartSearchIndexAsync(newObject, true); + await transaction.CommitAsync(); + //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); + await PartPopulateVizFields(newObject); + return newObject; + } } } @@ -3122,43 +3152,53 @@ namespace AyaNova.Biz // internal async Task PartPutAsync(WorkOrderItemPart putObject) { - WorkOrderItemPart dbObject = await PartGetAsync(putObject.Id, false); - if (dbObject == null) + using (var transaction = await ct.Database.BeginTransactionAsync()) { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return null; - } - if (dbObject.Concurrency != putObject.Concurrency) - { - AddError(ApiErrorCode.CONCURRENCY_CONFLICT); - return null; - } - - //dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags); - //dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); - - await PartValidateAsync(putObject, dbObject); - if (HasErrors) return null; - await PartBizActionsAsync(AyaEvent.Modified, putObject, dbObject, null); - ct.Replace(dbObject, putObject); - try - { - await ct.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!await PartExistsAsync(putObject.Id)) - AddError(ApiErrorCode.NOT_FOUND); - else + WorkOrderItemPart dbObject = await PartGetAsync(putObject.Id, false); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return null; + } + if (dbObject.Concurrency != putObject.Concurrency) + { AddError(ApiErrorCode.CONCURRENCY_CONFLICT); - return null; + return null; + } + + //dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags); + //dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); + + await PartValidateAsync(putObject, dbObject); + if (HasErrors) return null; + await PartBizActionsAsync(AyaEvent.Modified, putObject, dbObject, null); + ct.Replace(dbObject, putObject); + try + { + await ct.SaveChangesAsync(); + await PartInventoryAdjustmentAsync(AyaEvent.Modified, putObject, dbObject, transaction); + if (HasErrors) + { + await transaction.RollbackAsync(); + return null; + } + } + catch (DbUpdateConcurrencyException) + { + if (!await PartExistsAsync(putObject.Id)) + AddError(ApiErrorCode.NOT_FOUND); + else + AddError(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, putObject.Id, putObject.AyaType, AyaEvent.Modified), ct); + await PartSearchIndexAsync(putObject, false); + //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); + await PartHandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject); + await transaction.CommitAsync(); + await PartPopulateVizFields(putObject); + return putObject; } - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, putObject.Id, putObject.AyaType, AyaEvent.Modified), ct); - await PartSearchIndexAsync(putObject, false); - //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); - await PartHandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject); - await PartPopulateVizFields(putObject); - return putObject; } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -3166,29 +3206,38 @@ namespace AyaNova.Biz // internal async Task PartDeleteAsync(long id, IDbContextTransaction parentTransaction = null) { - var transaction = parentTransaction ?? await ct.Database.BeginTransactionAsync(); - try + // var transaction = parentTransaction ?? await ct.Database.BeginTransactionAsync(); + using (var transaction = parentTransaction ?? await ct.Database.BeginTransactionAsync()) { - var dbObject = await PartGetAsync(id, false); - PartValidateCanDelete(dbObject); - if (HasErrors) - return false; - await PartBizActionsAsync(AyaEvent.Modified, null, dbObject, transaction); - ct.WorkOrderItemPart.Remove(dbObject); - await ct.SaveChangesAsync(); - //Log event - await EventLogProcessor.DeleteObjectLogAsync(UserId, dbObject.AyaType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct);//Fix?? - await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, dbObject.AyaType, ct); - //await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); - //await FileUtil.DeleteAttachmentsForObjectAsync(dbObject.AyaType, dbObject.Id, ct); - if (parentTransaction == null) - await transaction.CommitAsync(); - await PartHandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject); - } - catch - { - //Just re-throw for now, let exception handler deal, but in future may want to deal with this more here - throw; + try + { + var dbObject = await PartGetAsync(id, false); + PartValidateCanDelete(dbObject); + if (HasErrors) + return false; + await PartBizActionsAsync(AyaEvent.Deleted, null, dbObject, transaction); + ct.WorkOrderItemPart.Remove(dbObject); + await ct.SaveChangesAsync(); + await PartInventoryAdjustmentAsync(AyaEvent.Deleted, null, dbObject, transaction); + if (HasErrors) + { + await transaction.RollbackAsync(); + return false; + } + //Log event + await EventLogProcessor.DeleteObjectLogAsync(UserId, dbObject.AyaType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct);//Fix?? + await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, dbObject.AyaType, ct); + //await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); + //await FileUtil.DeleteAttachmentsForObjectAsync(dbObject.AyaType, dbObject.Id, ct); + if (parentTransaction == null) + await transaction.CommitAsync(); + await PartHandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject); + } + catch + { + //Just re-throw for now, let exception handler deal, but in future may want to deal with this more here + throw; + } } return true; } @@ -3328,7 +3377,51 @@ namespace AyaNova.Biz // // private async Task PartBizActionsAsync(AyaEvent ayaEvent, WorkOrderItemPart newObj, WorkOrderItemPart oldObj, IDbContextTransaction transaction) - { + { + + + //SNAPSHOT PRICING IF NECESSARY + if (ayaEvent != AyaEvent.Created && ayaEvent != AyaEvent.Modified) + return; + + //SNAPSHOT PRICING + bool SnapshotPricing = true; + + //if modifed, see what has changed and should be re-applied + if (ayaEvent == AyaEvent.Modified) + { + //If it wasn't a complete part change there is no need to set pricing + if (newObj.PartId == oldObj.PartId) + { + SnapshotPricing = false; + } + } + + + //Pricing + if (SnapshotPricing) + { + //default in case nothing to apply + newObj.Cost = 0; + newObj.ListPrice = 0; + + + var s = await ct.Part.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.PartId); + if (s != null) + { + newObj.Cost = s.Cost; + newObj.ListPrice = s.Retail; + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //BIZ ACTIONS + // + // + private async Task PartInventoryAdjustmentAsync(AyaEvent ayaEvent, WorkOrderItemPart newObj, WorkOrderItemPart oldObj, IDbContextTransaction transaction) + { + if (AyaNova.Util.ServerGlobalBizSettings.UseInventory) { @@ -3453,58 +3546,9 @@ namespace AyaNova.Biz } - //SNAPSHOT PRICING IF NECESSARY - if (ayaEvent != AyaEvent.Created && ayaEvent != AyaEvent.Modified) - return; - //SNAPSHOT PRICING - bool SnapshotPricing = true; - - //if modifed, see what has changed and should be re-applied - if (ayaEvent == AyaEvent.Modified) - { - //If it wasn't a complete part change there is no need to set pricing - if (newObj.PartId == oldObj.PartId) - { - SnapshotPricing = false; - } - } - - - //Pricing - if (SnapshotPricing) - { - //default in case nothing to apply - newObj.Cost = 0; - newObj.ListPrice = 0; - - - var s = await ct.Part.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.PartId); - if (s != null) - { - newObj.Cost = s.Cost; - newObj.ListPrice = s.Retail; - } - } } - // //////////////////////////////////////////////////////////////////////////////////////////////// - // // SET PER UNIT LIST PRICE - // // - // //(called by woitempart save and also by header save on change of contract) - // private static void PartSetListPrice(WorkOrderItemPart o, Contract c) - // { - // if (c == null || c.ServiceRatesOverridePct == 0) - // { - // o.Price = o.ListPrice;//default with no contract - // return; - // } - // if (c.ServiceRatesOverrideType == ContractOverrideType.CostMarkup) - // o.Price = o.Cost + (o.Cost * c.ServiceRatesOverridePct); - // else if (c.ServiceRatesOverrideType == ContractOverrideType.PriceDiscount) - // o.Price = o.ListPrice - (o.ListPrice * c.ServiceRatesOverridePct); - // } - //////////////////////////////////////////////////////////////////////////////////////////////// //VALIDATION