This commit is contained in:
@@ -8,11 +8,12 @@ using System.Linq;
|
||||
using System;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AyaNova.Biz
|
||||
{
|
||||
|
||||
internal class WorkOrderBiz : BizObject, IJobObject, ISearchAbleObject, INotifiableObject
|
||||
internal class WorkOrderBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject, IImportAbleObject, INotifiableObject
|
||||
{
|
||||
//Feature specific roles
|
||||
internal static AuthorizationRoles RolesAllowedToChangeSerial = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.AccountingFull;
|
||||
@@ -260,48 +261,6 @@ namespace AyaNova.Biz
|
||||
|
||||
|
||||
|
||||
// ////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// //GENERATE
|
||||
// //
|
||||
// internal async Task<WorkOrder> GenerateWorkOrderAsync(long customerId)//MIGRATE_OUTSTANDING will need more overloads as required and fleshing out later
|
||||
// {
|
||||
// WorkOrder newObject = new WorkOrder();
|
||||
// newObject.
|
||||
// await WorkOrderValidateAsync(newObject, null);
|
||||
// if (HasErrors)
|
||||
// return null;
|
||||
// else
|
||||
// {
|
||||
// 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);
|
||||
// await HandlePotentialNotificationEvent(AyaEvent.Created, newObject);
|
||||
// return newObject;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//RESTART SERIAL
|
||||
//
|
||||
internal async Task<bool> RestartSerial(long newSerial)
|
||||
{
|
||||
|
||||
using (var command = ct.Database.GetDbConnection().CreateCommand())
|
||||
{
|
||||
command.CommandText = $"alter table aworkorder alter column serial restart with {newSerial}";
|
||||
await ct.Database.OpenConnectionAsync();
|
||||
await command.ExecuteNonQueryAsync();
|
||||
await ct.Database.CloseConnectionAsync();
|
||||
}
|
||||
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 0, BizType, AyaEvent.ResetSerial, newSerial.ToString()), ct);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -383,6 +342,23 @@ namespace AyaNova.Biz
|
||||
//run validation and biz rules
|
||||
bool isNew = currentObj == null;
|
||||
|
||||
/*
|
||||
|
||||
todo: workorder status list first, it's a table of created items, keep properties from v7 but add the following properties:
|
||||
SelectRoles - who can select the status (still shows if they can't select but that's the current status, like active does)
|
||||
This is best handled at the client. It prefetches all the status out of the normal picklist process, more like how other things are separately handled now without a picklist
|
||||
client then knows if a status is available or not and can process to only present available ones
|
||||
#### Server can use a biz rule to ensure that it can't be circumvented
|
||||
UI defaults to any role
|
||||
DeselectRoles - who can unset this status (important for process control)
|
||||
UI defaults to any role
|
||||
CompletedStatus bool - this is a final status indicating all work on the workorder is completed, affects notification etc
|
||||
UI defaults to false but when set to true auto sets lockworkorder to true (but user can just unset lockworkorder)
|
||||
LockWorkorder - this status is considered read only and the workorder is locked
|
||||
Just a read only thing, can just change status to "unlock" it
|
||||
to support states where no one should work on a wo for whatever reason but it's not necessarily completed
|
||||
e.g. "Hold for inspection", "On hold" generally etc
|
||||
*/
|
||||
// //Name required
|
||||
// if (string.IsNullOrWhiteSpace(proposedObj.Name))
|
||||
// AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name");
|
||||
@@ -420,6 +396,210 @@ namespace AyaNova.Biz
|
||||
{
|
||||
//whatever needs to be check to delete this object
|
||||
}
|
||||
|
||||
|
||||
//############### NOTIFICATION TODO
|
||||
/*
|
||||
|
||||
todo: workorder notifications remove #30 and #32 as redundant
|
||||
WorkorderStatusChange = 4,//* Workorder object, any *change* of status including from no status (new) to a specific conditional status ID value
|
||||
|
||||
WorkorderStatusAge = 24,//* Workorder object Created / Updated, conditional on exact status selected IdValue, Tags conditional, advance notice can be set
|
||||
|
||||
//THESE TWO ARE REDUNDANT:
|
||||
|
||||
this is actually workorderstatuschange because can just pick any status under workorderstatuschange to be notified about
|
||||
WorkorderFinished = 30, //*Service work order is set to any status that is flagged as a "Finished" type of status. Customer & User
|
||||
|
||||
//This one could be accomplished with WorkorderStatusAge, just pick a finished status and set a time frame and wala!
|
||||
WorkorderFinishedFollowUp = 32, //* Service workorder closed status follow up again after this many TIMESPAN
|
||||
|
||||
todo: CHANGE WorkorderFinishStatusOverdue = 15,//* Workorder object not set to a "Finished" flagged workorder status type in selected time span from creation of workorder
|
||||
Change this to a new type that is based on so many days *without* being set to a particular status
|
||||
but first check if tied to contract response time stuff, how that's handled
|
||||
that's closeby date in v7 but isn't that deprecated now without a "close"?
|
||||
maybe I do need the Finished status bool thing above
|
||||
|
||||
*/
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//REPORTING
|
||||
//
|
||||
public async Task<JArray> GetReportData(long[] idList)
|
||||
{
|
||||
JArray ReportData = new JArray();
|
||||
while (idList.Any())
|
||||
{
|
||||
var batch = idList.Take(IReportAbleObject.REPORT_DATA_BATCH_SIZE);
|
||||
idList = idList.Skip(IReportAbleObject.REPORT_DATA_BATCH_SIZE).ToArray();
|
||||
//query for this batch, comes back in db natural order unfortunately
|
||||
var batchResults = await ct.WorkOrder.AsNoTracking().Where(z => batch.Contains(z.Id)).ToArrayAsync();
|
||||
//order the results back into original
|
||||
var orderedList = from id in batch join z in batchResults on id equals z.Id select z;
|
||||
foreach (WorkOrder w in orderedList)
|
||||
{
|
||||
await PopulateVizFields(w);
|
||||
var jo = JObject.FromObject(w);
|
||||
if (!JsonUtil.JTokenIsNullOrEmpty(jo["CustomFields"]))
|
||||
jo["CustomFields"] = JObject.Parse((string)jo["CustomFields"]);
|
||||
ReportData.Add(jo);
|
||||
}
|
||||
}
|
||||
return ReportData;
|
||||
}
|
||||
|
||||
|
||||
//populate viz fields from provided object
|
||||
private async Task PopulateVizFields(WorkOrder o)
|
||||
{
|
||||
// if (o.WorkOrderOverseerId != null)
|
||||
// o.WorkOrderOverseerViz = await ct.User.AsNoTracking().Where(x => x.Id == o.WorkOrderOverseerId).Select(x => x.Name).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// IMPORT EXPORT
|
||||
//
|
||||
|
||||
public async Task<JArray> GetExportData(long[] idList)
|
||||
{
|
||||
//for now just re-use the report data code
|
||||
//this may turn out to be the pattern for most biz object types but keeping it seperate allows for custom usage from time to time
|
||||
return await GetReportData(idList);
|
||||
}
|
||||
|
||||
public async Task<List<string>> ImportData(JArray ja)
|
||||
{
|
||||
List<string> ImportResult = new List<string>();
|
||||
string ImportTag = $"imported-{FileUtil.GetSafeDateFileName()}";
|
||||
|
||||
var jsset = JsonSerializer.CreateDefault(new JsonSerializerSettings { ContractResolver = new AyaNova.Util.JsonUtil.ShouldSerializeContractResolver(new string[] { "Concurrency", "Id", "CustomFields" }) });
|
||||
foreach (JObject j in ja)
|
||||
{
|
||||
var w = j.ToObject<WorkOrder>(jsset);
|
||||
if (j["CustomFields"] != null)
|
||||
w.CustomFields = j["CustomFields"].ToString();
|
||||
w.Tags.Add(ImportTag);//so user can find them all and revert later if necessary
|
||||
var res = await WorkOrderCreateAsync(w);
|
||||
if (res == null)
|
||||
{
|
||||
ImportResult.Add($"* {w.Serial} - {this.GetErrorsAsString()}");
|
||||
this.ClearErrors();
|
||||
}
|
||||
else
|
||||
{
|
||||
ImportResult.Add($"{w.Serial} - ok");
|
||||
}
|
||||
}
|
||||
return ImportResult;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//JOB / OPERATIONS
|
||||
//
|
||||
public async Task HandleJobAsync(OpsJob job)
|
||||
{
|
||||
switch (job.JobType)
|
||||
{
|
||||
case JobType.BatchCoreObjectOperation:
|
||||
await ProcessBatchJobAsync(job);
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"WorkOrder.HandleJob-> Invalid job type{job.JobType.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ProcessBatchJobAsync(OpsJob job)
|
||||
{
|
||||
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Running);
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:StartJob {job.SubType}");
|
||||
List<long> idList = new List<long>();
|
||||
long FailedObjectCount = 0;
|
||||
JObject jobData = JObject.Parse(job.JobInfo);
|
||||
if (jobData.ContainsKey("idList"))
|
||||
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
|
||||
else
|
||||
idList = await ct.Widget.Select(z => z.Id).ToListAsync();
|
||||
bool SaveIt = false;
|
||||
foreach (long id in idList)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveIt = false;
|
||||
ClearErrors();
|
||||
ICoreBizObjectModel o = null;
|
||||
//save a fetch if it's a delete
|
||||
if (job.SubType != JobSubType.Delete)
|
||||
o = await GetWorkOrderGraphItem(job.AType, id);
|
||||
switch (job.SubType)
|
||||
{
|
||||
case JobSubType.TagAddAny:
|
||||
case JobSubType.TagAdd:
|
||||
case JobSubType.TagRemoveAny:
|
||||
case JobSubType.TagRemove:
|
||||
case JobSubType.TagReplaceAny:
|
||||
case JobSubType.TagReplace:
|
||||
SaveIt = TagBiz.ProcessBatchTagOperation(o.Tags, (string)jobData["tag"], jobData.ContainsKey("toTag") ? (string)jobData["toTag"] : null, job.SubType);
|
||||
break;
|
||||
case JobSubType.Delete:
|
||||
if (!await DeleteWorkOrderGraphItem(job.AType, id))
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
|
||||
FailedObjectCount++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"ProcessBatchJobAsync -> Invalid job Subtype{job.SubType}");
|
||||
}
|
||||
if (SaveIt)
|
||||
{
|
||||
o = await PutWorkOrderGraphItem(job.AType, o);
|
||||
if (o == null)
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
|
||||
FailedObjectCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors id({id})");
|
||||
await JobsBiz.LogJobAsync(job.GId, ExceptionUtil.ExtractAllExceptionMessages(ex));
|
||||
}
|
||||
}
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:BatchJob {job.SubType} {idList.Count}{(FailedObjectCount > 0 ? " - LT:Failed " + FailedObjectCount : "")}");
|
||||
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Completed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTIFICATION PROCESSING
|
||||
//
|
||||
public async Task HandlePotentialNotificationEvent(AyaEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null)
|
||||
{
|
||||
ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger<WorkOrderBiz>();
|
||||
if (ServerBootConfig.SEEDING) return;
|
||||
log.LogDebug($"HandlePotentialNotificationEvent processing: [AyaType:{this.BizType}, AyaEvent:{ayaEvent}]");
|
||||
|
||||
bool isNew = currentObj == null;
|
||||
|
||||
|
||||
//STANDARD EVENTS FOR ALL OBJECTS
|
||||
await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct);
|
||||
|
||||
//SPECIFIC EVENTS FOR THIS OBJECT
|
||||
|
||||
//todo: contract response time notification
|
||||
|
||||
}//end of process notifications
|
||||
|
||||
|
||||
#endregion workorder level
|
||||
|
||||
|
||||
@@ -2538,110 +2718,6 @@ namespace AyaNova.Biz
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//JOB / OPERATIONS
|
||||
//
|
||||
public async Task HandleJobAsync(OpsJob job)
|
||||
{
|
||||
switch (job.JobType)
|
||||
{
|
||||
case JobType.BatchCoreObjectOperation:
|
||||
await ProcessBatchJobAsync(job);
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"WorkOrder.HandleJob-> Invalid job type{job.JobType.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task ProcessBatchJobAsync(OpsJob job)
|
||||
{
|
||||
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Running);
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:StartJob {job.SubType}");
|
||||
List<long> idList = new List<long>();
|
||||
long FailedObjectCount = 0;
|
||||
JObject jobData = JObject.Parse(job.JobInfo);
|
||||
if (jobData.ContainsKey("idList"))
|
||||
idList = ((JArray)jobData["idList"]).ToObject<List<long>>();
|
||||
else
|
||||
idList = await ct.Widget.Select(z => z.Id).ToListAsync();
|
||||
bool SaveIt = false;
|
||||
foreach (long id in idList)
|
||||
{
|
||||
try
|
||||
{
|
||||
SaveIt = false;
|
||||
ClearErrors();
|
||||
ICoreBizObjectModel o = null;
|
||||
//save a fetch if it's a delete
|
||||
if (job.SubType != JobSubType.Delete)
|
||||
o = await GetWorkOrderGraphItem(job.AType, id);
|
||||
switch (job.SubType)
|
||||
{
|
||||
case JobSubType.TagAddAny:
|
||||
case JobSubType.TagAdd:
|
||||
case JobSubType.TagRemoveAny:
|
||||
case JobSubType.TagRemove:
|
||||
case JobSubType.TagReplaceAny:
|
||||
case JobSubType.TagReplace:
|
||||
SaveIt = TagBiz.ProcessBatchTagOperation(o.Tags, (string)jobData["tag"], jobData.ContainsKey("toTag") ? (string)jobData["toTag"] : null, job.SubType);
|
||||
break;
|
||||
case JobSubType.Delete:
|
||||
if (!await DeleteWorkOrderGraphItem(job.AType, id))
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
|
||||
FailedObjectCount++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentOutOfRangeException($"ProcessBatchJobAsync -> Invalid job Subtype{job.SubType}");
|
||||
}
|
||||
if (SaveIt)
|
||||
{
|
||||
o = await PutWorkOrderGraphItem(job.AType, o);
|
||||
if (o == null)
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors {GetErrorsAsString()} id {id}");
|
||||
FailedObjectCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:Errors id({id})");
|
||||
await JobsBiz.LogJobAsync(job.GId, ExceptionUtil.ExtractAllExceptionMessages(ex));
|
||||
}
|
||||
}
|
||||
await JobsBiz.LogJobAsync(job.GId, $"LT:BatchJob {job.SubType} {idList.Count}{(FailedObjectCount > 0 ? " - LT:Failed " + FailedObjectCount : "")}");
|
||||
await JobsBiz.UpdateJobStatusAsync(job.GId, JobStatus.Completed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// NOTIFICATION PROCESSING
|
||||
//
|
||||
public async Task HandlePotentialNotificationEvent(AyaEvent ayaEvent, ICoreBizObjectModel proposedObj, ICoreBizObjectModel currentObj = null)
|
||||
{
|
||||
ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger<WorkOrderBiz>();
|
||||
if (ServerBootConfig.SEEDING) return;
|
||||
log.LogDebug($"HandlePotentialNotificationEvent processing: [AyaType:{this.BizType}, AyaEvent:{ayaEvent}]");
|
||||
|
||||
bool isNew = currentObj == null;
|
||||
|
||||
|
||||
//STANDARD EVENTS FOR ALL OBJECTS
|
||||
await NotifyEventHelper.ProcessStandardObjectEvents(ayaEvent, proposedObj, ct);
|
||||
|
||||
//SPECIFIC EVENTS FOR THIS OBJECT
|
||||
|
||||
//todo: contract response time notification
|
||||
|
||||
}//end of process notifications
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Reference in New Issue
Block a user