Fleshing otu workorder

This commit is contained in:
2020-05-03 22:55:45 +00:00
parent b9da7a45b6
commit ff54daac8f
7 changed files with 78 additions and 117 deletions

View File

@@ -115,59 +115,19 @@ namespace AyaNova.Api.Controllers
/// <summary>
/// Patch (update) WorkOrder
/// </summary>
/// <param name="id"></param>
/// <param name="concurrencyToken"></param>
/// <param name="objectPatch"></param>
/// <returns></returns>
[HttpPatch("{id}/{concurrencyToken}")]
public async Task<IActionResult> PatchWorkOrder([FromRoute] long id, [FromRoute] uint concurrencyToken, [FromBody]JsonPatchDocument<WorkOrder> objectPatch)
{
//https://dotnetcoretutorials.com/2017/11/29/json-patch-asp-net-core/
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
//Instantiate the business object handler
WorkOrderBiz biz = WorkOrderBiz.GetBiz(ct, HttpContext);
var o = await biz.GetAsync(id, false);
if (o == null)
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
try
{
//patch and validate
if (!await biz.PatchAsync(o, objectPatch, concurrencyToken))
return BadRequest(new ApiErrorResponse(biz.Errors));
}
catch (DbUpdateConcurrencyException)
{
if (!await biz.ExistsAsync(id))
return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
else
return StatusCode(409, new ApiErrorResponse(ApiErrorCode.CONCURRENCY_CONFLICT));
}
return Ok(ApiOkResponse.Response(new { ConcurrencyToken = o.ConcurrencyToken }, true));
}
/// <summary>
/// Post WorkOrder
/// </summary>
/// <param name="inObj"></param>
/// <param name="workorderTemplateId"></param>
/// <param name="customerId"></param>
/// <param name="serial">force a workorder number, leave null to autogenerate the next one in sequence (mostly used for import)</param>
/// <param name="apiVersion">Automatically filled from route path, no need to specify in body</param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostWorkOrder([FromBody] WorkOrder inObj, ApiVersion apiVersion)
/// <returns>A created workorder ready to fill out</returns>
[HttpPost("Create")]
public async Task<IActionResult> PostWorkOrder([FromQuery] long? workorderTemplateId, long? customerId, uint? serial, ApiVersion apiVersion)
{
if (!serverState.IsOpen)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
@@ -183,7 +143,7 @@ namespace AyaNova.Api.Controllers
return BadRequest(new ApiErrorResponse(ModelState));
//Create and validate
WorkOrder o = await biz.CreateAsync(inObj);
WorkOrder o = await biz.CreateAsync(workorderTemplateId,customerId, serial);
if (o == null)
return BadRequest(new ApiErrorResponse(biz.Errors));
else

View File

@@ -60,28 +60,34 @@ namespace AyaNova.Biz
//CREATE
//Called from route and also seeder
internal async Task<WorkOrder> CreateAsync(WorkOrder inObj)
internal async Task<WorkOrder> CreateAsync(long? workorderTemplateId, long? customerId, uint? serial)
{
await ValidateAsync(inObj, null);
if (HasErrors)
return null;
else
{
//do stuff with WorkOrder
WorkOrder outObj = inObj;
//Create and save to db a new workorder and return it
//By default it should generate a unique id as the "name" field
//but need to support export to v8 plugin so need to be able to accept name already
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
//Save to db
await ct.WorkOrder.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(outObj, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
// await ValidateAsync(inObj, null);
// if (HasErrors)
// return null;
// else
// {
//do stuff with WorkOrder
WorkOrder outObj = new WorkOrder();
outObj.Serial = serial ?? ServerBootConfig.WORKORDER_SERIAL.GetNext();
return outObj;
}
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
//Save to db
await ct.WorkOrder.AddAsync(outObj);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, outObj.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(outObj, true);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, outObj.Tags, null);
return outObj;
//}
}
@@ -93,7 +99,7 @@ namespace AyaNova.Biz
internal async Task<WorkOrder> DuplicateAsync(WorkOrder dbObj)
{
throw new System.NotImplementedException("STUB: WORKORDER DUPLICATE");
WorkOrder outObj = new WorkOrder();
CopyObject.Copy(dbObj, outObj, "Wiki");
// outObj.Name = Util.StringUtil.NameUniquify(outObj.Name, 255);
@@ -131,7 +137,7 @@ namespace AyaNova.Biz
//put
internal async Task<bool> PutAsync(WorkOrder dbObj, WorkOrder inObj)
{
throw new System.NotImplementedException("STUB: WORKORDER PUT");
//make a snapshot of the original for validation but update the original to preserve workflow
WorkOrder SnapshotOfOriginalDBObj = new WorkOrder();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
@@ -159,35 +165,6 @@ namespace AyaNova.Biz
return true;
}
//patch
internal async Task<bool> PatchAsync(WorkOrder dbObj, JsonPatchDocument<WorkOrder> objectPatch, uint concurrencyToken)
{
//Validate Patch is allowed
if (!ValidateJsonPatch<WorkOrder>.Validate(this, objectPatch)) return false;
//make a snapshot of the original for validation but update the original to preserve workflow
WorkOrder SnapshotOfOriginalDBObj = new WorkOrder();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
//Do the patching
objectPatch.ApplyTo(dbObj);
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = concurrencyToken;
await ValidateAsync(dbObj, SnapshotOfOriginalDBObj);
if (HasErrors)
return false;
//Log event and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, dbObj.Id, BizType, AyaEvent.Modified), ct);
await SearchIndexAsync(dbObj, false);
await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, dbObj.Tags, SnapshotOfOriginalDBObj.Tags);
return true;
}
private async Task SearchIndexAsync(WorkOrder obj, bool isNew)
@@ -216,6 +193,7 @@ namespace AyaNova.Biz
//
internal async Task<bool> DeleteAsync(WorkOrder dbObj)
{
throw new System.NotImplementedException("STUB: WORKORDER DELETE");
//Determine if the object can be deleted, do the deletion tentatively
//Probably also in here deal with tags and associated search text etc

View File

@@ -124,10 +124,10 @@ namespace AyaNova.Models
.HasForeignKey<Widget>(b => b.UserId)
.OnDelete(DeleteBehavior.NoAction);
//Workorder
modelBuilder.Entity<WorkOrderItem>()
.HasOne(p => p.WorkOrder)
.WithMany(b => b.WorkorderItems);
// //Workorder
// modelBuilder.Entity<WorkOrderItem>()
// .HasOne(p => p.WorkOrder)
// .WithMany(b => b.WorkorderItems);
//-----------

View File

@@ -1,21 +1,20 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace AyaNova.Models
{
//NOTE: Any non required field (nullable in DB) sb nullable here, i.e. decimal? not decimal,
//otherwise the server will call it an invalid record if the field isn't sent from client
public partial class WorkOrder
{
public long Id { get; set; }
public uint ConcurrencyToken { get; set; }
[Required]
public string Name { get; set; }
public uint Serial { get; set; }
public bool Active { get; set; }
public string Notes { get; set; }
public string Wiki {get;set;}
@@ -30,6 +29,14 @@ namespace AyaNova.Models
Tags = new List<string>();
}
//Not persisted business properties
//NOTE: this could be a common class applied to everything for common biz rule stuff
//i.e. specific rights in situations based on rules, like candelete, canedit etc
[NotMapped]
public bool NonDataBaseExampleColumn { get; set; }//example of how to add a property that is not persisted but is used by both ends dynamically, should come up with a naming scheme so can see them at a glance
}//eoc
}//eons

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using AyaNova.Biz;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
namespace AyaNova.Models
@@ -23,6 +22,7 @@ namespace AyaNova.Models
public List<string> Tags { get; set; }
//Principle
[Required]//this required annotation should cause a cascade delete to happen automatically when wo is deleted, need to test that
public int WorkorderId { get; set; }//fk
public WorkOrder WorkOrder { get; set; }
@@ -33,6 +33,12 @@ namespace AyaNova.Models
Tags = new List<string>();
}
//Not persisted business properties
//NOTE: this could be a common class applied to everything for common biz rule stuff
//i.e. specific rights in situations based on rules, like candelete, canedit etc
[NotMapped]
public bool NonDataBaseExampleColumn { get; set; }//example of how to add a property that is not persisted but is used by both ends dynamically, should come up with a naming scheme so can see them at a glance
}//eoc
}//eons

View File

@@ -383,13 +383,13 @@ namespace AyaNova.Util
await ExecQueryAsync("CREATE INDEX avendor_tags ON avendor using GIN(tags)");
//WORKORDER
await ExecQueryAsync("CREATE TABLE aworkorder (id BIGSERIAL PRIMARY KEY, name varchar(255) not null unique, active bool, " +
await ExecQueryAsync("CREATE TABLE aworkorder (id BIGSERIAL PRIMARY KEY, serial bigint not null, active bool, " +
"notes text NULL, wiki text null, customfields text NULL, tags varchar(255) ARRAY NULL)");
await ExecQueryAsync("CREATE UNIQUE INDEX aworkorder_name_id_idx ON aworkorder (id, name);");
await ExecQueryAsync("CREATE INDEX aworkorder_number_id_idx ON aworkorder (id, serial);");
await ExecQueryAsync("CREATE INDEX aworkorder_tags ON aworkorder using GIN(tags)");
//WORKORDERITEM
await ExecQueryAsync("CREATE TABLE aworkorderitem (id BIGSERIAL PRIMARY KEY, name varchar(255) not null unique, active bool, " +
await ExecQueryAsync("CREATE TABLE aworkorderitem (id BIGSERIAL PRIMARY KEY, workorderid bigint not null REFERENCES aworkorder (id), name varchar(255) not null unique, active bool, " +
"notes text NULL, wiki text null, customfields text NULL, tags varchar(255) ARRAY NULL)");
await ExecQueryAsync("CREATE UNIQUE INDEX aworkorderitem_name_id_idx ON aworkorderitem (id, name);");
await ExecQueryAsync("CREATE INDEX aworkorderitem_tags ON aworkorderitem using GIN(tags)");

View File

@@ -15,9 +15,9 @@ namespace AyaNova.Util
//AUTOID's
//Get the highest id number issued previously and start the auto-id at that level
//Get the most recent id number issued previously and start the auto-id at that level
internal static AutoId WIDGET_SERIAL { get; set; }
internal static AutoId WORKORDER_SERIAL { get; set; }
//Diagnostic static values used during development, may not be related to config at all, this is just a convenient class to put them in
#if (DEBUG)
@@ -226,12 +226,22 @@ namespace AyaNova.Util
//query for most recently used serial number
//(note, can't query for highest serial as it can and likely will reset or be changed manually from time to time
// so the algorithm is to keep the most recent sequence going on startup of the server)
var MostRecentWidget = ct.Widget.AsNoTracking().OrderByDescending(x => x.Id).FirstOrDefault();
uint MaxWidgetId = 0;
if (MostRecentWidget != null)
MaxWidgetId = MostRecentWidget.Serial;
WIDGET_SERIAL = new AutoId(MaxWidgetId);
var serializedObject = ct.Widget.AsNoTracking().OrderByDescending(x => x.Id).FirstOrDefault();
WIDGET_SERIAL = new AutoId(serializedObject == null ? 0 : serializedObject.Serial);
// var MostRecentWidget = ct.Widget.AsNoTracking().OrderByDescending(x => x.Id).FirstOrDefault();
// uint MaxWidgetId = 0;
// if (MostRecentWidget != null)
// MaxWidgetId = MostRecentWidget.Serial;
// WIDGET_SERIAL = new AutoId(MaxWidgetId);
}
//Workorder
if (WORKORDER_SERIAL == null)
{
var serializedObject = ct.WorkOrder.AsNoTracking().OrderByDescending(x => x.Id).FirstOrDefault();
WORKORDER_SERIAL = new AutoId(serializedObject == null ? 0 : serializedObject.Serial);
}
//OTHER SERIALS HERE IN FUTURE...