From 039477a93ac1492628ad6af01007a4fb810d0f4f Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Sat, 9 May 2020 19:59:12 +0000 Subject: [PATCH] --- server/AyaNova/biz/WorkOrderBiz.cs | 852 +++++++++++++++-------------- 1 file changed, 445 insertions(+), 407 deletions(-) diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index c8ca219c..575af416 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -1,9 +1,10 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; -using Microsoft.AspNetCore.JsonPatch; +using System.Collections.Generic; using AyaNova.Util; using AyaNova.Api.ControllerHelpers; using AyaNova.Models; +using System.Linq; namespace AyaNova.Biz { @@ -378,470 +379,507 @@ namespace AyaNova.Biz // internal async Task ItemDeleteAsync(long id) { - WorkOrderItem dbObject = await ct.WorkOrderItem.SingleOrDefaultAsync(m => m.Id == id); + WorkOrderItem dbObject = await ct.WorkOrderItem + .SingleOrDefaultAsync(m => m.Id == id); ItemValidateCanDelete(dbObject); if (HasErrors) return false; - ct.WorkOrderItem.Remove(dbObject); - await ct.SaveChangesAsync(); - //Log event - await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "wo:" + dbObject.WorkOrderId.ToString(), ct); - await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); - await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); - return true; - } + //Traverse and delete tree + //maybe need to get tree list of id's then work with that + //a static function to get all id's from object and below? + //https://docs.microsoft.com/en-us/ef/core/saving/transactions - - //////////////////////////////////////////////////////////////////////////////////////////////// - //VALIDATION - // - private async Task ItemValidateAsync(WorkOrderItem proposedObj, WorkOrderItem currentObj) - { - //run validation and biz rules - bool isNew = currentObj == null; - - //does it have a valid workorder id - if (proposedObj.WorkOrderId == 0) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderId"); - else if (!await WorkOrderExistsAsync(proposedObj.WorkOrderId)) + WorkOrderItem dbObject = await ct.WorkOrderItem + .Include(wi => wi.Labors) + .Include(wi => wi.Parts) + .SingleOrDefaultAsync(m => m.Id == id); + var LaborIds = dbObject.Labors.Select(m => m.Id).ToList(); + var PartIds = dbObject.Parts.Select(m => m.Id).ToList(); + using (var transaction = ct.Database.BeginTransaction()) { - AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderId"); + try + { + //Delete children + foreach(long l in LaborIds){ + if(!await LaborDeleteAsync(id)) return false; + } + foreach(long l in PartIds){ + if(!await LaborDeleteAsync(id)) return false; + } + + + + ct.WorkOrderItem.Remove(dbObject); + await ct.SaveChangesAsync(); + + //Log event + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "wo:" + dbObject.WorkOrderId.ToString(), ct); + await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); + await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); + + //all good do the commit + transaction.Commit(); + } + catch + { + //Just re-throw for now, but in future may want to deal with this more here + throw; + + } + return true; } - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItem.ToString()); - if (FormCustomization != null) + + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + private async Task ItemValidateAsync(WorkOrderItem proposedObj, WorkOrderItem currentObj) { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required + //run validation and biz rules + bool isNew = currentObj == null; - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors + //does it have a valid workorder id + if (proposedObj.WorkOrderId == 0) + AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderId"); + else if (!await WorkOrderExistsAsync(proposedObj.WorkOrderId)) + { + AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderId"); + } - //validate custom fields - CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); - } - } + //Any form customizations to validate? + var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItem.ToString()); + if (FormCustomization != null) + { + //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required + //validate users choices for required non custom fields + RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors - private void ItemValidateCanDelete(WorkOrderItem obj) - { - if (obj == null) - { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return; + //validate custom fields + CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); + } } - //re-check rights here necessary due to traversal delete from Principle object - if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItem)) + + private void ItemValidateCanDelete(WorkOrderItem obj) { - AddError(ApiErrorCode.NOT_AUTHORIZED); - return; + if (obj == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return; + } + + //re-check rights here necessary due to traversal delete from Principle object + if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItem)) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return; + } } - } - private async Task ItemSearchIndexAsync(WorkOrderItem obj, bool isNew) - { - //SEARCH INDEXING - var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItem); - SearchParams.AddText(obj.Notes).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields); - - if (isNew) - await Search.ProcessNewObjectKeywordsAsync(SearchParams); - else - await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); - } - - public async Task ItemGetSearchResultSummary(long id) - { - var obj = await ct.WorkOrderItem.SingleOrDefaultAsync(m => m.Id == id); - var SearchParams = new Search.SearchIndexProcessObjectParameters(); - if (obj != null) + private async Task ItemSearchIndexAsync(WorkOrderItem obj, bool isNew) + { + //SEARCH INDEXING + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItem); SearchParams.AddText(obj.Notes).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields); - return SearchParams; - } - #endregion work order item level - - - - - - - #region WorkOrderItemLabor level - //////////////////////////////////////////////////////////////////////////////////////////////// - //EXISTS - internal async Task LaborExistsAsync(long id) - { - return await ct.WorkOrderItemLabor.AnyAsync(e => e.Id == id); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //CREATE - // - internal async Task LaborCreateAsync(WorkOrderItemLabor newObject) - { - await LaborValidateAsync(newObject, null); - if (HasErrors) - return null; - else - { - newObject.Tags = TagUtil.NormalizeTags(newObject.Tags); - newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); - await ct.WorkOrderItemLabor.AddAsync(newObject); - await ct.SaveChangesAsync(); - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, AyaType.WorkOrderItemLabor, AyaEvent.Created), ct); - await LaborSearchIndexAsync(newObject, true); - await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); - return newObject; - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - // GET - // - internal async Task LaborGetAsync(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.WorkOrderItemLabor - .SingleOrDefaultAsync(m => m.Id == id); - if (logTheGetEvent && ret != null) - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, AyaType.WorkOrderItemLabor, AyaEvent.Retrieved), ct); - return ret; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //UPDATE - // - internal async Task LaborPutAsync(WorkOrderItemLabor dtPutObject) - { - WorkOrderItemLabor dbObject = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == dtPutObject.Id); - if (dbObject == null) - { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return null; - } - - // make a snapshot of the original for validation but update the original to preserve workflow - WorkOrderItemLabor SnapshotOfOriginalDBObj = new WorkOrderItemLabor(); - CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); - CopyObject.Copy(dtPutObject, dbObject, "Id"); - - dbObject.Tags = TagUtil.NormalizeTags(dbObject.Tags); - dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); - - ct.Entry(dbObject).OriginalValues["ConcurrencyToken"] = dtPutObject.ConcurrencyToken; - await LaborValidateAsync(dbObject, SnapshotOfOriginalDBObj); - if (HasErrors) return null; - try - { - await ct.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!await LaborExistsAsync(dtPutObject.Id)) - AddError(ApiErrorCode.NOT_FOUND); + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); else - new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT); - return null; - } - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); - await LaborSearchIndexAsync(dbObject, false); - await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); - return dbObject; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //DELETE - // - internal async Task LaborDeleteAsync(long id) - { - WorkOrderItemLabor dbObject = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == id); - LaborValidateCanDelete(dbObject); - if (HasErrors) - return false; - ct.WorkOrderItemLabor.Remove(dbObject); - await ct.SaveChangesAsync(); - - //Log event - await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct); - await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); - await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //VALIDATION - // - private async Task LaborValidateAsync(WorkOrderItemLabor proposedObj, WorkOrderItemLabor currentObj) - { - //run validation and biz rules - bool isNew = currentObj == null; - - if (proposedObj.WorkOrderItemId == 0) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderItemId"); - else if (!await ItemExistsAsync(proposedObj.WorkOrderItemId)) - { - AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderItemId"); + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); } - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItemLabor.ToString()); - if (FormCustomization != null) + public async Task ItemGetSearchResultSummary(long id) { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required - - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors - - //validate custom fields - CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); + var obj = await ct.WorkOrderItem.SingleOrDefaultAsync(m => m.Id == id); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + if (obj != null) + SearchParams.AddText(obj.Notes).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields); + return SearchParams; } - } + #endregion work order item level - private void LaborValidateCanDelete(WorkOrderItemLabor obj) - { - if (obj == null) + + + + + + #region WorkOrderItemLabor level + //////////////////////////////////////////////////////////////////////////////////////////////// + //EXISTS + internal async Task LaborExistsAsync(long id) { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return; + return await ct.WorkOrderItemLabor.AnyAsync(e => e.Id == id); } - //re-check rights here necessary due to traversal delete from Principle object - if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItemLabor)) + //////////////////////////////////////////////////////////////////////////////////////////////// + //CREATE + // + internal async Task LaborCreateAsync(WorkOrderItemLabor newObject) { - AddError(ApiErrorCode.NOT_AUTHORIZED); - return; - } - } - - - ////////////////////////////////////////////// - //INDEXING - // - private async Task LaborSearchIndexAsync(WorkOrderItemLabor obj, bool isNew) - { - //SEARCH INDEXING - var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItemLabor); - SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); - - if (isNew) - await Search.ProcessNewObjectKeywordsAsync(SearchParams); - else - await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); - } - - public async Task LaborGetSearchResultSummary(long id) - { - var obj = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == id); - var SearchParams = new Search.SearchIndexProcessObjectParameters(); - if (obj != null) - SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); - return SearchParams; - } - #endregion work order item LABOR level - - - - - - - - #region WorkOrderItemPart level - //////////////////////////////////////////////////////////////////////////////////////////////// - //EXISTS - internal async Task PartExistsAsync(long id) - { - return await ct.WorkOrderItemPart.AnyAsync(e => e.Id == id); - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //CREATE - // - internal async Task CreatePartAsync(WorkOrderItemPart newObject) - { - await PartValidateAsync(newObject, null); - if (HasErrors) - return null; - else - { - newObject.Tags = TagUtil.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, AyaType.WorkOrderItemPart, AyaEvent.Created), ct); - await PartSearchIndexAsync(newObject, true); - await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); - return newObject; - } - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - // GET - // - internal async Task GetPartAsync(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.WorkOrderItemPart - .SingleOrDefaultAsync(m => m.Id == id); - if (logTheGetEvent && ret != null) - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, AyaType.WorkOrderItemPart, AyaEvent.Retrieved), ct); - return ret; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //UPDATE - // - internal async Task PartPutAsync(WorkOrderItemPart dtPutObject) - { - WorkOrderItemPart dbObject = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == dtPutObject.Id); - if (dbObject == null) - { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return null; - } - - // make a snapshot of the original for validation but update the original to preserve workflow - WorkOrderItemPart SnapshotOfOriginalDBObj = new WorkOrderItemPart(); - CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); - CopyObject.Copy(dtPutObject, dbObject, "Id"); - - dbObject.Tags = TagUtil.NormalizeTags(dbObject.Tags); - dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); - - ct.Entry(dbObject).OriginalValues["ConcurrencyToken"] = dtPutObject.ConcurrencyToken; - await PartValidateAsync(dbObject, SnapshotOfOriginalDBObj); - if (HasErrors) return null; - try - { - await ct.SaveChangesAsync(); - } - catch (DbUpdateConcurrencyException) - { - if (!await PartExistsAsync(dtPutObject.Id)) - AddError(ApiErrorCode.NOT_FOUND); + await LaborValidateAsync(newObject, null); + if (HasErrors) + return null; else - new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT); - return null; + { + newObject.Tags = TagUtil.NormalizeTags(newObject.Tags); + newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); + await ct.WorkOrderItemLabor.AddAsync(newObject); + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, AyaType.WorkOrderItemLabor, AyaEvent.Created), ct); + await LaborSearchIndexAsync(newObject, true); + await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); + return newObject; + } } - await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); - await PartSearchIndexAsync(dbObject, false); - await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); - return dbObject; - } - //////////////////////////////////////////////////////////////////////////////////////////////// - //DELETE - // - internal async Task PartDeleteAsync(long id) - { - WorkOrderItemPart dbObject = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == id); - PartValidateCanDelete(dbObject); - if (HasErrors) - return false; - ct.WorkOrderItemPart.Remove(dbObject); - await ct.SaveChangesAsync(); - - //Log event - await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct); - await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); - await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////////////// - //VALIDATION - // - private async Task PartValidateAsync(WorkOrderItemPart proposedObj, WorkOrderItemPart currentObj) - { - //run validation and biz rules - bool isNew = currentObj == null; - - if (proposedObj.WorkOrderItemId == 0) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderItemId"); - else if (!await ItemExistsAsync(proposedObj.WorkOrderItemId)) + //////////////////////////////////////////////////////////////////////////////////////////////// + // GET + // + internal async Task LaborGetAsync(long id, bool logTheGetEvent = true) { - AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderItemId"); + //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.WorkOrderItemLabor + .SingleOrDefaultAsync(m => m.Id == id); + if (logTheGetEvent && ret != null) + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, AyaType.WorkOrderItemLabor, AyaEvent.Retrieved), ct); + return ret; } - //Any form customizations to validate? - var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItemPart.ToString()); - if (FormCustomization != null) + //////////////////////////////////////////////////////////////////////////////////////////////// + //UPDATE + // + internal async Task LaborPutAsync(WorkOrderItemLabor dtPutObject) { - //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required + WorkOrderItemLabor dbObject = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == dtPutObject.Id); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return null; + } - //validate users choices for required non custom fields - RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors + // make a snapshot of the original for validation but update the original to preserve workflow + WorkOrderItemLabor SnapshotOfOriginalDBObj = new WorkOrderItemLabor(); + CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); + CopyObject.Copy(dtPutObject, dbObject, "Id"); - //validate custom fields - CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); + dbObject.Tags = TagUtil.NormalizeTags(dbObject.Tags); + dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); + + ct.Entry(dbObject).OriginalValues["ConcurrencyToken"] = dtPutObject.ConcurrencyToken; + await LaborValidateAsync(dbObject, SnapshotOfOriginalDBObj); + if (HasErrors) return null; + try + { + await ct.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await LaborExistsAsync(dtPutObject.Id)) + AddError(ApiErrorCode.NOT_FOUND); + else + new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); + await LaborSearchIndexAsync(dbObject, false); + await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); + return dbObject; } - } - - private void PartValidateCanDelete(WorkOrderItemPart obj) - { - if (obj == null) + //////////////////////////////////////////////////////////////////////////////////////////////// + //DELETE + // + internal async Task LaborDeleteAsync(long id) { - AddError(ApiErrorCode.NOT_FOUND, "id"); - return; + WorkOrderItemLabor dbObject = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == id); + LaborValidateCanDelete(dbObject); + if (HasErrors) + return false; + ct.WorkOrderItemLabor.Remove(dbObject); + await ct.SaveChangesAsync(); + + //Log event + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct); + await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); + await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); + return true; } - //re-check rights here necessary due to traversal delete from Principle object - if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItemPart)) + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + private async Task LaborValidateAsync(WorkOrderItemLabor proposedObj, WorkOrderItemLabor currentObj) { - AddError(ApiErrorCode.NOT_AUTHORIZED); - return; + //run validation and biz rules + bool isNew = currentObj == null; + + if (proposedObj.WorkOrderItemId == 0) + AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderItemId"); + else if (!await ItemExistsAsync(proposedObj.WorkOrderItemId)) + { + AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderItemId"); + } + + //Any form customizations to validate? + var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItemLabor.ToString()); + if (FormCustomization != null) + { + //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required + + //validate users choices for required non custom fields + RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors + + //validate custom fields + CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); + } } - } - private async Task PartSearchIndexAsync(WorkOrderItemPart obj, bool isNew) - { - //SEARCH INDEXING - var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItemPart); - SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); + private void LaborValidateCanDelete(WorkOrderItemLabor obj) + { + if (obj == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return; + } - if (isNew) - await Search.ProcessNewObjectKeywordsAsync(SearchParams); - else - await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); - } + //re-check rights here necessary due to traversal delete from Principle object + if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItemLabor)) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return; + } + } - public async Task PartGetSearchResultSummary(long id) - { - var obj = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == id); - var SearchParams = new Search.SearchIndexProcessObjectParameters(); - if (obj != null) + + ////////////////////////////////////////////// + //INDEXING + // + private async Task LaborSearchIndexAsync(WorkOrderItemLabor obj, bool isNew) + { + //SEARCH INDEXING + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItemLabor); SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); - return SearchParams; - } - #endregion work order item LABOR level - //////////////////////////////////////////////////////////////////////////////////////////////// - //JOB / OPERATIONS - // + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); + else + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); + } + + public async Task LaborGetSearchResultSummary(long id) + { + var obj = await ct.WorkOrderItemLabor.SingleOrDefaultAsync(m => m.Id == id); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + if (obj != null) + SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); + return SearchParams; + } + #endregion work order item LABOR level - //Other job handlers here... - ///////////////////////////////////////////////////////////////////// - - }//eoc -}//eons + + #region WorkOrderItemPart level + //////////////////////////////////////////////////////////////////////////////////////////////// + //EXISTS + internal async Task PartExistsAsync(long id) + { + return await ct.WorkOrderItemPart.AnyAsync(e => e.Id == id); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //CREATE + // + internal async Task CreatePartAsync(WorkOrderItemPart newObject) + { + await PartValidateAsync(newObject, null); + if (HasErrors) + return null; + else + { + newObject.Tags = TagUtil.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, AyaType.WorkOrderItemPart, AyaEvent.Created), ct); + await PartSearchIndexAsync(newObject, true); + await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); + return newObject; + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + // GET + // + internal async Task GetPartAsync(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.WorkOrderItemPart + .SingleOrDefaultAsync(m => m.Id == id); + if (logTheGetEvent && ret != null) + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, AyaType.WorkOrderItemPart, AyaEvent.Retrieved), ct); + return ret; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //UPDATE + // + internal async Task PartPutAsync(WorkOrderItemPart dtPutObject) + { + WorkOrderItemPart dbObject = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == dtPutObject.Id); + if (dbObject == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return null; + } + + // make a snapshot of the original for validation but update the original to preserve workflow + WorkOrderItemPart SnapshotOfOriginalDBObj = new WorkOrderItemPart(); + CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj); + CopyObject.Copy(dtPutObject, dbObject, "Id"); + + dbObject.Tags = TagUtil.NormalizeTags(dbObject.Tags); + dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields); + + ct.Entry(dbObject).OriginalValues["ConcurrencyToken"] = dtPutObject.ConcurrencyToken; + await PartValidateAsync(dbObject, SnapshotOfOriginalDBObj); + if (HasErrors) return null; + try + { + await ct.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await PartExistsAsync(dtPutObject.Id)) + AddError(ApiErrorCode.NOT_FOUND); + else + new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT); + return null; + } + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct); + await PartSearchIndexAsync(dbObject, false); + await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags); + return dbObject; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //DELETE + // + internal async Task PartDeleteAsync(long id) + { + WorkOrderItemPart dbObject = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == id); + PartValidateCanDelete(dbObject); + if (HasErrors) + return false; + ct.WorkOrderItemPart.Remove(dbObject); + await ct.SaveChangesAsync(); + + //Log event + await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, "woitem:" + dbObject.WorkOrderItemId.ToString(), ct); + await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType); + await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags); + return true; + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + private async Task PartValidateAsync(WorkOrderItemPart proposedObj, WorkOrderItemPart currentObj) + { + //run validation and biz rules + bool isNew = currentObj == null; + + if (proposedObj.WorkOrderItemId == 0) + AddError(ApiErrorCode.VALIDATION_REQUIRED, "WorkOrderItemId"); + else if (!await ItemExistsAsync(proposedObj.WorkOrderItemId)) + { + AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "WorkOrderItemId"); + } + + //Any form customizations to validate? + var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrderItemPart.ToString()); + if (FormCustomization != null) + { + //Yeppers, do the validation, there are two, the custom fields and the regular fields that might be set to required + + //validate users choices for required non custom fields + RequiredFieldsValidator.Validate(this, FormCustomization, proposedObj);//note: this is passed only to add errors + + //validate custom fields + CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields); + } + } + + + private void PartValidateCanDelete(WorkOrderItemPart obj) + { + if (obj == null) + { + AddError(ApiErrorCode.NOT_FOUND, "id"); + return; + } + + //re-check rights here necessary due to traversal delete from Principle object + if (!Authorized.HasDeleteRole(CurrentUserRoles, AyaType.WorkOrderItemPart)) + { + AddError(ApiErrorCode.NOT_AUTHORIZED); + return; + } + } + + + private async Task PartSearchIndexAsync(WorkOrderItemPart obj, bool isNew) + { + //SEARCH INDEXING + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, AyaType.WorkOrderItemPart); + SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); + + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); + else + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); + } + + public async Task PartGetSearchResultSummary(long id) + { + var obj = await ct.WorkOrderItemPart.SingleOrDefaultAsync(m => m.Id == id); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + if (obj != null) + SearchParams.AddText(obj.Notes).AddText(obj.Tags).AddCustomFields(obj.CustomFields); + return SearchParams; + } + #endregion work order item LABOR level + + //////////////////////////////////////////////////////////////////////////////////////////////// + //JOB / OPERATIONS + // + + + //Other job handlers here... + + + ///////////////////////////////////////////////////////////////////// + + }//eoc + + + }//eons