diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index f152c880..852a3bd7 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -1194,6 +1194,7 @@ namespace AyaNova.Biz // dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); await ExpenseValidateAsync(putObject, dbObject); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -1206,7 +1207,7 @@ namespace AyaNova.Biz AddError(ApiErrorCode.CONCURRENCY_CONFLICT); return null; } - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, putObject.AyaType, AyaEvent.Modified), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, putObject.Id, putObject.AyaType, AyaEvent.Modified), ct); await ExpenseSearchIndexAsync(putObject, false); //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, putObject.Tags, dbObject.Tags); await ExpenseHandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject); @@ -1429,6 +1430,7 @@ namespace AyaNova.Biz await LaborValidateAsync(putObject, dbObject); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -1469,7 +1471,7 @@ namespace AyaNova.Biz 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(BizType, dbObject.Id, ct); + // await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); if (parentTransaction == null) await transaction.CommitAsync(); await LaborHandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject); @@ -1668,6 +1670,7 @@ namespace AyaNova.Biz await LoanValidateAsync(putObject, dbObject); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -1697,7 +1700,7 @@ namespace AyaNova.Biz transaction = await ct.Database.BeginTransactionAsync(); try { - var dbObject = await LoanGetAsync(id,false); + var dbObject = await LoanGetAsync(id, false); LoanValidateCanDelete(dbObject); if (HasErrors) return false; @@ -1721,7 +1724,7 @@ namespace AyaNova.Biz return true; } - ////////////////////////////////////////////// + ////////////////////////////////////////////// //INDEXING // private async Task LoanSearchIndexAsync(WorkOrderItemLoan obj, bool isNew) @@ -1738,7 +1741,7 @@ namespace AyaNova.Biz public async Task LoanGetSearchResultSummary(long id) { - var obj = await LoanGetAsync(id,false); + var obj = await LoanGetAsync(id, false); var SearchParams = new Search.SearchIndexProcessObjectParameters(); if (obj != null) SearchParams.AddText(obj.Notes); @@ -1750,7 +1753,7 @@ namespace AyaNova.Biz // private async Task LoanValidateAsync(WorkOrderItemLoan proposedObj, WorkOrderItemLoan currentObj) { - //skip validation if seeding + //skip validation if seeding // if (ServerBootConfig.SEEDING) return; //run validation and biz rules @@ -1860,7 +1863,7 @@ namespace AyaNova.Biz { return await ct.WorkOrderItemOutsideService.AnyAsync(z => z.Id == id); } -HERE + //////////////////////////////////////////////////////////////////////////////////////////////// //CREATE // @@ -1875,7 +1878,7 @@ HERE //newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); await ct.WorkOrderItemOutsideService.AddAsync(newObject); await ct.SaveChangesAsync(); - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, AyaType.WorkOrderItemOutsideService, AyaEvent.Created), ct); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, newObject.AyaType, AyaEvent.Created), ct); await OutsideServiceSearchIndexAsync(newObject, true); // await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); await OutsideServiceHandlePotentialNotificationEvent(AyaEvent.Created, newObject); @@ -1888,14 +1891,7 @@ HERE // internal async Task OutsideServiceGetAsync(long id, bool logTheGetEvent = true) { - //Note: there could be rules checking here in future, i.e. can only get own workorder or something - //if so, then need to implement AddError and in route handle Null return with Error check just like PUT route does now - - //https://docs.microsoft.com/en-us/ef/core/querying/related-data - //docs say this will not query twice but will recognize the duplicate woitem bit which is required for multiple grandchild collections - var ret = - await ct.WorkOrderItemOutsideService - .SingleOrDefaultAsync(z => z.Id == id); + var ret = await ct.WorkOrderItemOutsideService.AsNoTracking().SingleOrDefaultAsync(z => z.Id == id); if (logTheGetEvent && ret != null) await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, AyaType.WorkOrderItemOutsideService, AyaEvent.Retrieved), ct); return ret; @@ -1904,70 +1900,113 @@ HERE //////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE // - internal async Task OutsideServicePutAsync(WorkOrderItemOutsideService dtPutObject) + internal async Task OutsideServicePutAsync(WorkOrderItemOutsideService putObject) { - WorkOrderItemOutsideService dbObject = await ct.WorkOrderItemOutsideService.SingleOrDefaultAsync(z => z.Id == dtPutObject.Id); + WorkOrderItemOutsideService dbObject = await OutsideServiceGetAsync(putObject.Id, false); if (dbObject == null) { AddError(ApiErrorCode.NOT_FOUND, "id"); return null; } + if (dbObject.Concurrency != putObject.Concurrency) + { + AddError(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } - WorkOrderItemOutsideService SnapshotOfOriginalDBObj = new WorkOrderItemOutsideService(); - CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); - CopyObject.Copy(dtPutObject, dbObject, "Id"); // dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags); // dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); - ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; - await OutsideServiceValidateAsync(dbObject, SnapshotOfOriginalDBObj); + await OutsideServiceValidateAsync(putObject, dbObject); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { - if (!await OutsideServiceExistsAsync(dtPutObject.Id)) + if (!await OutsideServiceExistsAsync(putObject.Id)) AddError(ApiErrorCode.NOT_FOUND); else AddError(ApiErrorCode.CONCURRENCY_CONFLICT); return null; } - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, AyaType.WorkOrderItemOutsideService, AyaEvent.Modified), ct); - await OutsideServiceSearchIndexAsync(dbObject, false); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, putObject.Id, putObject.AyaType, AyaEvent.Modified), ct); + await OutsideServiceSearchIndexAsync(putObject, false); //await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); - await OutsideServiceHandlePotentialNotificationEvent(AyaEvent.Modified, dbObject, SnapshotOfOriginalDBObj); - return dbObject; + await OutsideServiceHandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject); + return putObject; } //////////////////////////////////////////////////////////////////////////////////////////////// //DELETE // - internal async Task OutsideServiceDeleteAsync(long id) + internal async Task OutsideServiceDeleteAsync(long id, Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction parentTransaction = null) { - WorkOrderItemOutsideService dbObject = await ct.WorkOrderItemOutsideService.SingleOrDefaultAsync(z => z.Id == id); - OutsideServiceValidateCanDelete(dbObject); - if (HasErrors) - return false; - ct.WorkOrderItemOutsideService.Remove(dbObject); - await ct.SaveChangesAsync(); + Microsoft.EntityFrameworkCore.Storage.IDbContextTransaction transaction = null; + if (parentTransaction == null) + transaction = await ct.Database.BeginTransactionAsync(); + try + { + var dbObject = await OutsideServiceGetAsync(id, false); + OutsideServiceValidateCanDelete(dbObject); + if (HasErrors) + return false; + ct.WorkOrderItemOutsideService.Remove(dbObject); + await ct.SaveChangesAsync(); - //Log event - await EventLogProcessor.DeleteObjectLogAsync(UserId, AyaType.WorkOrderItemOutsideService, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct);//FIX why woitem, shouldn't it be outsideservice?? - await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, AyaType.WorkOrderItemOutsideService, ct); - //await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); - //await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct); - await OutsideServiceHandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject); + //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 OutsideServiceHandlePotentialNotificationEvent(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; } + ////////////////////////////////////////////// + //INDEXING + // + private async Task OutsideServiceSearchIndexAsync(WorkOrderItemOutsideService obj, bool isNew) + { + //SEARCH INDEXING + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, obj.AyaType); + SearchParams.AddText(obj.Notes).AddText(obj.RMANumber).AddText(obj.TrackingNumber); + + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); + else + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); + } + + public async Task OutsideServiceGetSearchResultSummary(long id) + { + var obj = await OutsideServiceGetAsync(id,false); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + if (obj != null) + SearchParams.AddText(obj.Notes).AddText(obj.RMANumber).AddText(obj.TrackingNumber); + return SearchParams; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// //VALIDATION // private async Task OutsideServiceValidateAsync(WorkOrderItemOutsideService proposedObj, WorkOrderItemOutsideService currentObj) { + //skip validation if seeding + // if (ServerBootConfig.SEEDING) return; + //run validation and biz rules bool isNew = currentObj == null; @@ -2010,31 +2049,6 @@ HERE } - ////////////////////////////////////////////// - //INDEXING - // - private async Task OutsideServiceSearchIndexAsync(WorkOrderItemOutsideService obj, bool isNew) - { - //SEARCH INDEXING - var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItemOutsideService); - SearchParams.AddText(obj.Notes).AddText(obj.RMANumber).AddText(obj.TrackingNumber); - - if (isNew) - await Search.ProcessNewObjectKeywordsAsync(SearchParams); - else - await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); - } - - public async Task OutsideServiceGetSearchResultSummary(long id) - { - var obj = await ct.WorkOrderItemOutsideService.SingleOrDefaultAsync(z => z.Id == id); - var SearchParams = new Search.SearchIndexProcessObjectParameters(); - if (obj != null) - SearchParams.AddText(obj.Notes).AddText(obj.RMANumber).AddText(obj.TrackingNumber); - return SearchParams; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// // NOTIFICATION PROCESSING // @@ -2155,6 +2169,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await PartValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -2386,6 +2401,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await PartRequestValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -2594,6 +2610,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await ScheduledUserValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -2803,6 +2820,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await TaskValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -3035,6 +3053,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await TravelValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync(); @@ -3266,6 +3285,7 @@ HERE ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency; await UnitValidateAsync(dbObject, SnapshotOfOriginalDBObj); if (HasErrors) return null; + ct.Replace(dbObject, putObject); try { await ct.SaveChangesAsync();