This commit is contained in:
2021-03-25 18:03:01 +00:00
parent c92c5424b0
commit 58d2772593
7 changed files with 235 additions and 216 deletions

View File

@@ -6,6 +6,35 @@ If each part has it's own concurrency then can submit whole and bits that aren't
test this idea
CONCURRENCY decision / research - updating, in bits seperately, entirely at once? [## DECIDED, BITS MAKES MOST SENSE AND DIRTY TRACKING AT CLIENT ##]
Considering we are looking at a system where some users won't see or even get the data for some parts of teh workorder then it's important that they be able to just save their piece of it separately
So this strongly leans towards seperately saved and not entirely at all.
concurrency issue reduction
Things that might require an entire workorder graph be updated at once:
Major header changes?
Customer, Contract because pricing and policies might change?
Anything that affects balances and totals (are there new features happening showing dollar amounts billable right on workorder?)
in v7 not an issue because need to preview report to see totals
in v8 some talk of seeing totals, so need to determine if that's still a thing, if not then this whole class of concurrency issues dissappears
This would be entirely resolved with a "view" showing a type of "invoice" summary kind of thing where you open that tab and it fetches and calcs and displays
however this would be little difference from a report and a little worse because the end user can't edit it to calculate how they would like it to so...
Dirty tracking at every level and object would save having to send entire workorder, just dirty bits
i.e. have non-mapped dirty fields for every woitemrecord
client uses them to track changes and what needs to be saved
upon save client goes through graph looking for dirty records and saves them each but is senstive to the best order to do it and takes into account if save would lock or unlock
Header fields - dirty
a woitem - dirty?
awoitemlabor - dirty?
If something is changed that affects entire graph then that can be checked for at the client and trigger a full save regardless
WorkorderStatus change would always be the first thing saved as it might be required to UNLOCK the workorder
Client needs entire status list cached on hand to know if a status change affects lock status and if it's going from unlocked to locked then it would need to save the entire workorder graph and let the server handle it
or save bottom up before locking with the status / header change
Actually isn't this just handled at the server as a biz rule? (no because if header saved first then it's locked potentially and sub items can't be save subsequently in a graph walking save)
New items are always dirty so a new labor record added would be dirty and saved individually
At the server upon item being saved there would always need to be a quick check of the entire workorder to see if it's LOCKED status or not
if locked then bumps back with error that wo is now locked (of course they could just change teh status in the header and resave I guess)
########### PLANNING BELOW HERE, OUTDATED NOW ########################################
Lot's to go in here but for now this is holding my planning and thoughts until can set in stone:

View File

@@ -39,14 +39,14 @@ namespace AyaNova.Api.Controllers
/*
██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
██║ ██║██╔═══██╗██╔══██╗██║ ██╔╝ ██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
██║ █╗ ██║██║ ██║██████╔╝█████╔╝█████╗██║ ██║██████╔╝██║ ██║█████╗ ██████╔╝
██║███╗██║██║ ██║██╔══██╗██╔═██╗╚════╝██║ ██║██╔══██╗██║ ██║██╔══╝ ██╔══██╗
╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗ ╚██████╔╝██║ ██║██████╔╝███████╗██║ ██║
╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝
*/
/*
██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
██║ ██║██╔═══██╗██╔══██╗██║ ██╔╝ ██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
██║ █╗ ██║██║ ██║██████╔╝█████╔╝█████╗██║ ██║██████╔╝██║ ██║█████╗ ██████╔╝
██║███╗██║██║ ██║██╔══██╗██╔═██╗╚════╝██║ ██║██╔══██╗██║ ██║██╔══╝ ██╔══██╗
╚███╔███╔╝╚██████╔╝██║ ██║██║ ██╗ ╚██████╔╝██║ ██║██████╔╝███████╗██║ ██║
╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝
*/
#region WorkOrder top level routes
/// <summary>
@@ -56,7 +56,7 @@ namespace AyaNova.Api.Controllers
/// <param name="apiVersion">From route path</param>
/// <returns>WorkOrder</returns>
[HttpPost]
public async Task<IActionResult> PostWorkOrder([FromBody] dtWorkOrder newObject, ApiVersion apiVersion)
public async Task<IActionResult> PostWorkOrder([FromBody] WorkOrder newObject, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -126,7 +126,7 @@ namespace AyaNova.Api.Controllers
/// <param name="updatedObject">WorkOrder - top level only, no descendants</param>
/// <returns>New concurrency token</returns>
[HttpPut]
public async Task<IActionResult> PutWorkOrder([FromBody] dtWorkOrder updatedObject)
public async Task<IActionResult> PutWorkOrder([FromBody] WorkOrder updatedObject)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -166,41 +166,19 @@ namespace AyaNova.Api.Controllers
return NoContent();
}
/// <summary>
/// ReStart serial number
///
/// </summary>
/// <param name="newSerial">Next starting value for auto generated serial numbers</param>
/// <param name="apiVersion">From route path</param>
/// <returns>Widget</returns>
[HttpPost("restart-serial/{newSerial}")]
public async Task<IActionResult> ReStartSerial([FromRoute] long newSerial, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext);
if (!Authorized.HasCreateRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
bool result=await biz.RestartSerial(newSerial);
if (!result)
return BadRequest(new ApiErrorResponse(biz.Errors));
else
return NoContent();
}
#endregion WorkOrderTopLevel routes
/*
██╗████████╗███████╗███╗ ███╗███████╗
██║╚══██╔══╝██╔════╝████╗ ████║██╔════╝
██║ ██║ █████╗ ██╔████╔██║███████╗
██║ ██║ ██╔══╝ ██║╚██╔╝██║╚════██║
██║ ██║ ███████╗██║ ╚═╝ ██║███████║
╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝
*/
/*
██╗████████╗███████╗███╗ ███╗███████╗
██║╚══██╔══╝██╔════╝████╗ ████║██╔════╝
██║ ██║ █████╗ ██╔████╔██║███████╗
██║ ██║ ██╔══╝ ██║╚██╔╝██║╚════██║
██║ ██║ ███████╗██║ ╚═╝ ██║███████║
╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝
*/
#region WorkOrderItem
/// <summary>
@@ -210,7 +188,7 @@ namespace AyaNova.Api.Controllers
/// <param name="apiVersion"></param>
/// <returns>WorkOrderItem object</returns>
[HttpPost("items")]
public async Task<IActionResult> PostWorkOrderItem([FromBody] dtWorkOrderItem newObject, ApiVersion apiVersion)
public async Task<IActionResult> PostWorkOrderItem([FromBody] WorkOrderItem newObject, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -257,7 +235,7 @@ namespace AyaNova.Api.Controllers
/// <param name="updatedObject">WorkOrderItem - top level only, no descendants</param>
/// <returns>New concurrency token</returns>
[HttpPut("items/")]
public async Task<IActionResult> PutWorkOrderItem([FromBody] dtWorkOrderItem updatedObject)
public async Task<IActionResult> PutWorkOrderItem([FromBody] WorkOrderItem updatedObject)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -301,14 +279,14 @@ namespace AyaNova.Api.Controllers
#endregion workorderitem
/*
███████╗██╗ ██╗██████╗ ███████╗███╗ ██╗███████╗███████╗███████╗
██╔════╝╚██╗██╔╝██╔══██╗██╔════╝████╗ ██║██╔════╝██╔════╝██╔════╝
█████╗ ╚███╔╝ ██████╔╝█████╗ ██╔██╗ ██║███████╗█████╗ ███████╗
██╔══╝ ██╔██╗ ██╔═══╝ ██╔══╝ ██║╚██╗██║╚════██║██╔══╝ ╚════██║
███████╗██╔╝ ██╗██║ ███████╗██║ ╚████║███████║███████╗███████║
╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚══════╝╚══════╝
*/
/*
███████╗██╗ ██╗██████╗ ███████╗███╗ ██╗███████╗███████╗███████╗
██╔════╝╚██╗██╔╝██╔══██╗██╔════╝████╗ ██║██╔════╝██╔════╝██╔════╝
█████╗ ╚███╔╝ ██████╔╝█████╗ ██╔██╗ ██║███████╗█████╗ ███████╗
██╔══╝ ██╔██╗ ██╔═══╝ ██╔══╝ ██║╚██╗██║╚════██║██╔══╝ ╚════██║
███████╗██╔╝ ██╗██║ ███████╗██║ ╚████║███████║███████╗███████║
╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═══╝╚══════╝╚══════╝╚══════╝
*/
#region WorkOrderItemExpense
@@ -407,15 +385,15 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemExpense
/*
██╗ █████╗ ██████╗ ██████╗ ██████╗
██║ ██╔══██╗██╔══██╗██╔═══██╗██╔══██╗
██║ ███████║██████╔╝██║ ██║██████╔╝
██║ ██╔══██║██╔══██╗██║ ██║██╔══██╗
███████╗██║ ██║██████╔╝╚██████╔╝██║ ██║
╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝
/*
██╗ █████╗ ██████╗ ██████╗ ██████╗
██║ ██╔══██╗██╔══██╗██╔═══██╗██╔══██╗
██║ ███████║██████╔╝██║ ██║██████╔╝
██║ ██╔══██║██╔══██╗██║ ██║██╔══██╗
███████╗██║ ██║██████╔╝╚██████╔╝██║ ██║
╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝
*/
*/
#region WorkOrderItemLabor
@@ -517,14 +495,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemLabor
/*
██╗ ██████╗ █████╗ ███╗ ██╗
██║ ██╔═══██╗██╔══██╗████╗ ██║
██║ ██║ ██║███████║██╔██╗ ██║
██║ ██║ ██║██╔══██║██║╚██╗██║
███████╗╚██████╔╝██║ ██║██║ ╚████║
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
*/
/*
██╗ ██████╗ █████╗ ███╗ ██╗
██║ ██╔═══██╗██╔══██╗████╗ ██║
██║ ██║ ██║███████║██╔██╗ ██║
██║ ██║ ██║██╔══██║██║╚██╗██║
███████╗╚██████╔╝██║ ██║██║ ╚████║
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
*/
#region WorkOrderItemLoan
@@ -623,14 +601,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemLoan
/*
██████╗ █████╗ ██████╗ ████████╗███████╗
██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝
██████╔╝███████║██████╔╝ ██║ ███████╗
██╔═══╝ ██╔══██║██╔══██╗ ██║ ╚════██║
██║ ██║ ██║██║ ██║ ██║ ███████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
*/
/*
██████╗ █████╗ ██████╗ ████████╗███████╗
██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██╔════╝
██████╔╝███████║██████╔╝ ██║ ███████╗
██╔═══╝ ██╔══██║██╔══██╗ ██║ ╚════██║
██║ ██║ ██║██║ ██║ ██║ ███████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝
*/
#region WorkOrderItemPart
@@ -729,14 +707,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemPart
/*
██████╗ █████╗ ██████╗ ████████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗███████╗███████╗████████╗███████╗
██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝ ██╔══██╗██╔════╝██╔═══██╗██║ ██║██╔════╝██╔════╝╚══██╔══╝██╔════╝
██████╔╝███████║██████╔╝ ██║█████╗██████╔╝█████╗ ██║ ██║██║ ██║█████╗ ███████╗ ██║ ███████╗
██╔═══╝ ██╔══██║██╔══██╗ ██║╚════╝██╔══██╗██╔══╝ ██║▄▄ ██║██║ ██║██╔══╝ ╚════██║ ██║ ╚════██║
██║ ██║ ██║██║ ██║ ██║ ██║ ██║███████╗╚██████╔╝╚██████╔╝███████╗███████║ ██║ ███████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚══════╝╚══════╝ ╚═╝ ╚══════╝
*/
/*
██████╗ █████╗ ██████╗ ████████╗ ██████╗ ███████╗ ██████╗ ██╗ ██╗███████╗███████╗████████╗███████╗
██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝ ██╔══██╗██╔════╝██╔═══██╗██║ ██║██╔════╝██╔════╝╚══██╔══╝██╔════╝
██████╔╝███████║██████╔╝ ██║█████╗██████╔╝█████╗ ██║ ██║██║ ██║█████╗ ███████╗ ██║ ███████╗
██╔═══╝ ██╔══██║██╔══██╗ ██║╚════╝██╔══██╗██╔══╝ ██║▄▄ ██║██║ ██║██╔══╝ ╚════██║ ██║ ╚════██║
██║ ██║ ██║██║ ██║ ██║ ██║ ██║███████╗╚██████╔╝╚██████╔╝███████╗███████║ ██║ ███████║
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚══▀▀═╝ ╚═════╝ ╚══════╝╚══════╝ ╚═╝ ╚══════╝
*/
#region WorkOrderItemPartRequest
@@ -835,14 +813,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemPartRequest
/*
███████╗ ██████╗██╗ ██╗███████╗██████╗ ██╗ ██╗██╗ ███████╗██████╗ ██╗ ██╗███████╗███████╗██████╗ ███████╗
██╔════╝██╔════╝██║ ██║██╔════╝██╔══██╗██║ ██║██║ ██╔════╝██╔══██╗ ██║ ██║██╔════╝██╔════╝██╔══██╗██╔════╝
███████╗██║ ███████║█████╗ ██║ ██║██║ ██║██║ █████╗ ██║ ██║█████╗██║ ██║███████╗█████╗ ██████╔╝███████╗
╚════██║██║ ██╔══██║██╔══╝ ██║ ██║██║ ██║██║ ██╔══╝ ██║ ██║╚════╝██║ ██║╚════██║██╔══╝ ██╔══██╗╚════██║
███████║╚██████╗██║ ██║███████╗██████╔╝╚██████╔╝███████╗███████╗██████╔╝ ╚██████╔╝███████║███████╗██║ ██║███████║
╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝
*/
/*
███████╗ ██████╗██╗ ██╗███████╗██████╗ ██╗ ██╗██╗ ███████╗██████╗ ██╗ ██╗███████╗███████╗██████╗ ███████╗
██╔════╝██╔════╝██║ ██║██╔════╝██╔══██╗██║ ██║██║ ██╔════╝██╔══██╗ ██║ ██║██╔════╝██╔════╝██╔══██╗██╔════╝
███████╗██║ ███████║█████╗ ██║ ██║██║ ██║██║ █████╗ ██║ ██║█████╗██║ ██║███████╗█████╗ ██████╔╝███████╗
╚════██║██║ ██╔══██║██╔══╝ ██║ ██║██║ ██║██║ ██╔══╝ ██║ ██║╚════╝██║ ██║╚════██║██╔══╝ ██╔══██╗╚════██║
███████║╚██████╗██║ ██║███████╗██████╔╝╚██████╔╝███████╗███████╗██████╔╝ ╚██████╔╝███████║███████╗██║ ██║███████║
╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝╚══════╝
*/
#region WorkOrderItemScheduledUser
@@ -941,14 +919,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemScheduledUser
/*
████████╗ █████╗ ███████╗██╗ ██╗
╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
██║ ███████║███████╗█████╔╝
██║ ██╔══██║╚════██║██╔═██╗
██║ ██║ ██║███████║██║ ██╗
╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
*/
/*
████████╗ █████╗ ███████╗██╗ ██╗
╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝
██║ ███████║███████╗█████╔╝
██║ ██╔══██║╚════██║██╔═██╗
██║ ██║ ██║███████║██║ ██╗
╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
*/
#region WorkOrderItemtask
@@ -1047,14 +1025,14 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemTask
/*
████████╗██████╗ █████╗ ██╗ ██╗███████╗██╗
╚══██╔══╝██╔══██╗██╔══██╗██║ ██║██╔════╝██║
██║ ██████╔╝███████║██║ ██║█████╗ ██║
██║ ██╔══██╗██╔══██║╚██╗ ██╔╝██╔══╝ ██║
██║ ██║ ██║██║ ██║ ╚████╔╝ ███████╗███████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚══════╝
*/
/*
████████╗██████╗ █████╗ ██╗ ██╗███████╗██╗
╚══██╔══╝██╔══██╗██╔══██╗██║ ██║██╔════╝██║
██║ ██████╔╝███████║██║ ██║█████╗ ██║
██║ ██╔══██╗██╔══██║╚██╗ ██╔╝██╔══╝ ██║
██║ ██║ ██║██║ ██║ ╚████╔╝ ███████╗███████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚══════╝
*/
#region WorkOrderItemTravel
@@ -1152,15 +1130,15 @@ namespace AyaNova.Api.Controllers
#endregion WorkOrderItemTravel
/*
██╗ ██╗███╗ ██╗██╗████████╗
██║ ██║████╗ ██║██║╚══██╔══╝
██║ ██║██╔██╗ ██║██║ ██║
██║ ██║██║╚██╗██║██║ ██║
╚██████╔╝██║ ╚████║██║ ██║
╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
*/
/*
██╗ ██╗███╗ ██╗██╗████████╗
██║ ██║████╗ ██║██║╚══██╔══╝
██║ ██║██╔██╗ ██║██║ ██║
██║ ██║██║╚██╗██║██║ ██║
╚██████╔╝██║ ╚████║██║ ██║
╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
*/
#region WorkOrderItemUnit

View File

@@ -8,15 +8,20 @@ using System.Linq;
using System;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace AyaNova.Biz
{
/*
###############
MUST be able to update any section at any level independantly
Fetch must be able to get entire graph or from any level down or single header only as required
probably need a woheaderfetch route separately
*/
internal class WorkOrderBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject, INotifiableObject
{
//Feature specific roles
internal static AuthorizationRoles RolesAllowedToChangeSerial = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.AccountingFull;
// //Feature specific roles
// internal static AuthorizationRoles RolesAllowedToChangeSerial = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.AccountingFull;
internal WorkOrderBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
@@ -37,16 +42,6 @@ namespace AyaNova.Biz
}
//###################################################################################################################################################
//###################################################################################################################################################
// WARNING! THIS OBJECT IS AN INITIAL TEST VERSION NOT UP TO CURRENT STANDARDS, SEE PARTASSEMBLYBIZ / some of WORKORDERBIZ FOR HOW THIS SHOULD BE CODED
//###################################################################################################################################################
//###################################################################################################################################################
//NOTE: this *is* working but is probably coded wrong for the child collections
//see the partassemblybiz and associated code / schema structure for proper way to do this
//and meld into this design
/*
██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
@@ -69,10 +64,8 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//
internal async Task<WorkOrder> WorkOrderCreateAsync(dtWorkOrder dtNewObject)
internal async Task<WorkOrder> WorkOrderCreateAsync(WorkOrder newObject)
{
WorkOrder newObject = new WorkOrder();
CopyObject.Copy(dtNewObject, newObject);
await WorkOrderValidateAsync(newObject, null);
if (HasErrors)
return null;
@@ -125,7 +118,7 @@ namespace AyaNova.Biz
//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.WorkOrder
await ct.WorkOrder.AsNoTracking()
.Include(w => w.Items)
.ThenInclude(wi => wi.Expenses)
.Include(w => w.Items)
@@ -155,52 +148,43 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
internal async Task<WorkOrder> WorkOrderPutAsync(dtWorkOrder dtPutObject)
internal async Task<WorkOrder> WorkOrderPutAsync(WorkOrder putObject)
{
WorkOrder dbObject = await ct.WorkOrder.SingleOrDefaultAsync(z => z.Id == dtPutObject.Id);
WorkOrder dbObject = await WorkOrderGetAsync(putObject.Id, false);
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
WorkOrder SnapshotOfOriginalDBObj = new WorkOrder();
CopyObject.Copy(dbObject, SnapshotOfOriginalDBObj);
//Replace the db object with the PUT object
CopyObject.Copy(dtPutObject, dbObject, "Id");
//if user has rights then change it, otherwise just ignore it and do the rest
if (dtPutObject.Serial != 0 && SnapshotOfOriginalDBObj.Serial != dtPutObject.Serial && Authorized.HasAnyRole(CurrentUserRoles, RolesAllowedToChangeSerial))
if (dbObject.Concurrency != putObject.Concurrency)
{
dbObject.Serial = dtPutObject.Serial;
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
dbObject.Tags = TagBiz.NormalizeTags(dbObject.Tags);
dbObject.CustomFields = JsonUtil.CompactJson(dbObject.CustomFields);
ct.Entry(dbObject).OriginalValues["Concurrency"] = dtPutObject.Concurrency;
await WorkOrderValidateAsync(dbObject, SnapshotOfOriginalDBObj);
putObject.Tags = TagBiz.NormalizeTags(putObject.Tags);
putObject.CustomFields = JsonUtil.CompactJson(putObject.CustomFields);
await WorkOrderValidateAsync(putObject, dbObject);
if (HasErrors)
return null;
ct.Replace(dbObject, putObject);
try
{
await ct.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await WorkOrderExistsAsync(dtPutObject.Id))
if (!await WorkOrderExistsAsync(putObject.Id))
AddError(ApiErrorCode.NOT_FOUND);
else
AddError(ApiErrorCode.CONCURRENCY_CONFLICT);
return null;
}
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObject.Id, BizType, AyaEvent.Modified), ct);
await WorkOrderSearchIndexAsync(dbObject, false);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, dbObject.Tags, SnapshotOfOriginalDBObj.Tags);
await HandlePotentialNotificationEvent(AyaEvent.Modified, dbObject, SnapshotOfOriginalDBObj);
return dbObject;
await WorkOrderSearchIndexAsync(putObject, false);
await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, putObject.Tags, dbObject.Tags);
await HandlePotentialNotificationEvent(AyaEvent.Modified, putObject, dbObject);
return putObject;
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -237,15 +221,6 @@ namespace AyaNova.Biz
await Search.ProcessDeletedObjectKeywordsAsync(dbObject.Id, BizType, ct);
await TagBiz.ProcessDeleteTagsInRepositoryAsync(ct, dbObject.Tags);
await FileUtil.DeleteAttachmentsForObjectAsync(BizType, dbObject.Id, ct);
#if (DEBUG)
if (dbObject.Wiki == "INTEGRATION_DELETE_TEST_FAIL_BEFORE_COMMIT")
{
// await transaction.RollbackAsync();
return false;//thus not committing transaction
}
#endif
await transaction.CommitAsync();
await HandlePotentialNotificationEvent(AyaEvent.Deleted, dbObject);
}
@@ -623,7 +598,7 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//
internal async Task<WorkOrderItem> ItemCreateAsync(dtWorkOrderItem dtNewObject)
internal async Task<WorkOrderItem> ItemCreateAsync(WorkOrderItem dtNewObject)
{
WorkOrderItem newObject = new WorkOrderItem();
CopyObject.Copy(dtNewObject, newObject);
@@ -676,7 +651,7 @@ namespace AyaNova.Biz
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
internal async Task<WorkOrderItem> ItemPutAsync(dtWorkOrderItem dtPutObject)
internal async Task<WorkOrderItem> ItemPutAsync(WorkOrderItem dtPutObject)
{
WorkOrderItem dbObject = await ct.WorkOrderItem.SingleOrDefaultAsync(z => z.Id == dtPutObject.Id);
@@ -2647,19 +2622,19 @@ namespace AyaNova.Biz
case AyaType.WorkOrder:
if (o is WorkOrder)
{
dtWorkOrder dto = new dtWorkOrder();
WorkOrder dto = new WorkOrder();
CopyObject.Copy(o, dto);
return await WorkOrderPutAsync((dtWorkOrder)dto);
return await WorkOrderPutAsync((WorkOrder)dto);
}
return await WorkOrderPutAsync((dtWorkOrder)o) as ICoreBizObjectModel;
return await WorkOrderPutAsync((WorkOrder)o) as ICoreBizObjectModel;
case AyaType.WorkOrderItem:
if (o is WorkOrderItem)
{
dtWorkOrderItem dto = new dtWorkOrderItem();
WorkOrderItem dto = new WorkOrderItem();
CopyObject.Copy(o, dto);
return await ItemPutAsync((dtWorkOrderItem)dto);
return await ItemPutAsync((WorkOrderItem)dto);
}
return await ItemPutAsync((dtWorkOrderItem)o);
return await ItemPutAsync((WorkOrderItem)o);
case AyaType.WorkOrderItemExpense:
return await ExpensePutAsync((WorkOrderItemExpense)o);
case AyaType.WorkOrderItemLabor:

View File

@@ -7,44 +7,20 @@ using AyaNova.Biz;
namespace AyaNova.Models
{
//Data transfer no child collections
public class dtWorkOrder : ICoreBizObjectModel
{
public dtWorkOrder()
{
Tags = new List<string>();
}
public long Id { get; set; }
public uint Concurrency { get; set; }
/*
[Required]
public long Serial { get; set; }
public bool Active { get; set; }
public string Notes { get; set; }
public string Wiki { get; set; }
public string CustomFields { get; set; }
public List<string> Tags { get; set; }
[NotMapped, JsonIgnore]
public AyaType AyaType { get => AyaType.WorkOrder; }
}//eoc
/*
todo: Consider adding latitude / longitude to wo, quote, pm objects
can copy over from the unit or customer or set themselves
and can always hide
means wo could be scheduled for ad-hoc locations and serviced that way, i.e. a truck parked on the side of the highway etc
*/
todo: Consider adding latitude / longitude to wo, quote, pm objects
can copy over from the unit or customer or set themselves
and can always hide
means wo could be scheduled for ad-hoc locations and serviced that way, i.e. a truck parked on the side of the highway etc
*/
public class WorkOrder : ICoreBizObjectModel
{
public WorkOrder()
{
Tags = new List<string>();
//dependents
Items = new List<WorkOrderItem>();
}
public long Id { get; set; }
@@ -56,24 +32,19 @@ todo: Consider adding latitude / longitude to wo, quote, pm objects
public string Notes { get; set; }
public string Wiki { get; set; }
public string CustomFields { get; set; }
public List<string> Tags { get; set; }
public List<string> Tags { get; set; } = new List<string>();
// [Required]
// public long CustomerId {get;set;}
//dependents
public List<WorkOrderItem> Items { get; set; }
public List<WorkOrderItem> Items { get; set; } = new List<WorkOrderItem>();
public List<WorkOrderState> States { get; set; } = new List<WorkOrderState>();
// [NotMapped, JsonIgnore]
// public string Name
// {
// //Used by notification processor
// get
// {
// return this.Serial.ToString();
// }
// }
[NotMapped, JsonIgnore]
public AyaType AyaType { get => AyaType.WorkOrder; }
@@ -126,4 +97,44 @@ CREATE TABLE [dbo].[AWORKORDER](
[ACLOSEBYDATE] [datetime] NULL,
[ASIGNATURE] [ntext] NULL,
[ASIGNED] [datetime] NULL,
*/
*/
//STuff that was here before and may potentially be useful in future
// [NotMapped, JsonIgnore]
// public string Name
// {
// //Used by notification processor
// get
// {
// return this.Serial.ToString();
// }
// }
// //Data transfer no child collections
// public class dtWorkOrder : ICoreBizObjectModel
// {
// public dtWorkOrder()
// {
// Tags = new List<string>();
// }
// public long Id { get; set; }
// public uint Concurrency { get; set; }
// [Required]
// public long Serial { get; set; }
// public bool Active { get; set; }
// public string Notes { get; set; }
// public string Wiki { get; set; }
// public string CustomFields { get; set; }
// public List<string> Tags { get; set; }
// [NotMapped, JsonIgnore]
// public AyaType AyaType { get => AyaType.WorkOrder; }
// }//eoc

View File

@@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using System;
namespace AyaNova.Models
{
public class WorkOrderState
{
public long Id { get; set; }
public uint Concurrency { get; set; }
[Required]
public long WorkOrderId { get; set; }
[Required]
public long WorkOrderStatusId { get; set; }
public DateTime Created { get; set; } = DateTime.UtcNow;
[Required]
public long UserId { get; set; }
}//eoc
}//eons

View File

@@ -836,6 +836,10 @@ $BODY$ LANGUAGE PLPGSQL STABLE");
await ExecQueryAsync("CREATE TABLE aworkorder (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, serial BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, active BOOL NOT NULL, "
+ "notes TEXT, wiki TEXT, customfields TEXT, tags VARCHAR(255) ARRAY )");
await ExecQueryAsync("CREATE TABLE aworkorderstate (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, workorderid BIGINT NOT NULL REFERENCES aworkorder (id), "
+ "workorderstatusid BIGINT NOT NULL REFERENCES aworkorderstatus (id), created TIMESTAMP NOT NULL, userid BIGINT NOT NULL REFERENCES auser (id)"
+")");
await ExecQueryAsync("CREATE TABLE aworkorderitem (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, workorderid BIGINT NOT NULL REFERENCES aworkorder (id), "
+ "notes TEXT, wiki TEXT, customfields TEXT, tags VARCHAR(255) ARRAY )");

View File

@@ -387,6 +387,7 @@ namespace AyaNova.Util
await EraseTableAsync("aworkorderitemunit", conn);
//---
await EraseTableAsync("aworkorderitem", conn);
await EraseTableAsync("aworkorderstate", conn);
await EraseTableAsync("aworkorder", conn);
await EraseTableAsync("aworkordertemplateitem", conn);
await EraseTableAsync("aworkordertemplate", conn);