Files
raven/server/AyaNova/biz/WorkOrderBiz.cs
2020-05-05 22:16:26 +00:00

313 lines
12 KiB
C#

using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.JsonPatch;
using AyaNova.Util;
using AyaNova.Api.ControllerHelpers;
using AyaNova.Models;
namespace AyaNova.Biz
{
internal class WorkOrderBiz : BizObject, ISearchAbleObject
{
//Feature specific roles
internal static AuthorizationRoles RolesAllowedToChangeSerial = AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.AccountingFull;
internal WorkOrderBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
{
ct = dbcontext;
UserId = currentUserId;
UserTranslationId = userTranslationId;
CurrentUserRoles = UserRoles;
BizType = AyaType.WorkOrder;
}
internal static WorkOrderBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
{
if (httpContext != null)
return new WorkOrderBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items));
else//when called internally for internal ops there will be no context so need to set default values for that
return new WorkOrderBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
}
////////////////////////////////////////////////////////////////////////////////////////////////
//EXISTS
internal async Task<bool> ExistsAsync(long id)
{
return await ct.WorkOrder.AnyAsync(e => e.Id == id);
}
////////////////////////////////////////////////////////////////////////////////////////////////
/// GET
///
///
internal async Task<WorkOrder> GetAsync(long fetchId, bool logTheGetEvent = true)
{
/*
https://docs.microsoft.com/en-us/ef/core/querying/related-data
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();
}
WARNING: Since version 3.0.0, each Include will cause an additional JOIN to be added to SQL queries produced by relational providers, whereas previous versions generated additional SQL queries. This can significantly change the performance of your queries, for better or worse. In particular, LINQ queries with an exceedingly high number of Include operators may need to be broken down into multiple separate LINQ queries in order to avoid the cartesian explosion problem.
*/
//This is simple so nothing more here, but often will be copying to a different output object or some other ops
//var ret = await ct.WorkOrder.SingleOrDefaultAsync(m => m.Id == fetchId);
var ret = await ct.WorkOrder.Include(w=>w.WorkorderItems).SingleOrDefaultAsync(m => m.Id == fetchId);
if (logTheGetEvent && ret != null)
{
//Log
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, fetchId, BizType, AyaEvent.Retrieved), ct);
}
return ret;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//CREATE
//Called from route and also seeder
internal async Task<WorkOrder> CreateAsync(long? workorderTemplateId, long? customerId, uint? serial)
{
//Create and save to db a new workorder and return it
//NOTE: Serial can be specified or edited after the fact in a limited way by full role specfic only!! (service manager, bizadminfull, accounting maybe)
if (serial != null && !Authorized.HasAnyRole(CurrentUserRoles, RolesAllowedToChangeSerial))
{
AddError(ApiErrorCode.NOT_AUTHORIZED, "Serial");
return null;
}
// await ValidateAsync(inObj, null);
// if (HasErrors)
// return null;
// else
// {
//do stuff with WorkOrder
WorkOrder o = new WorkOrder();
o.Serial = serial ?? ServerBootConfig.WORKORDER_SERIAL.GetNext();
//TODO: template
//TODO: CUSTOMER ID
//Save to db
await ct.WorkOrder.AddAsync(o);
await ct.SaveChangesAsync();
//Handle child and associated items:
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, o.Id, BizType, AyaEvent.Created), ct);
await SearchIndexAsync(o, true);
// await TagUtil.ProcessUpdateTagsInRepositoryAsync(ct, o.Tags, null);
return o;
//}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DUPLICATE
//
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);
// //generate unique name
// string newUniqueName = string.Empty;
// bool NotUnique = true;
// long l = 1;
// do
// {
// newUniqueName = Util.StringUtil.UniqueNameBuilder(dbObj.Name, l++, 255);
// NotUnique = await ct.WorkOrder.AnyAsync(m => m.Name == newUniqueName);
// } while (NotUnique);
// outObj.Name = newUniqueName;
// outObj.Id = 0;
// outObj.ConcurrencyToken = 0;
// 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;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//UPDATE
//
//put
internal async Task<bool> PutAsync(WorkOrder dbObj, WorkOrder putObj)
{
// make a snapshot of the original for validation but update the original to preserve workflow
WorkOrder SnapshotOfOriginalDBObj = new WorkOrder();
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
//Replace the db object with the PUT object
CopyObject.Copy(putObj, dbObj, "Id,Serial");
//if user has rights then change it, otherwise just ignore it and do the rest
if (SnapshotOfOriginalDBObj.Serial != putObj.Serial && Authorized.HasAnyRole(CurrentUserRoles, RolesAllowedToChangeSerial))
{
dbObj.Serial = putObj.Serial;
}
dbObj.Tags = TagUtil.NormalizeTags(dbObj.Tags);
dbObj.CustomFields = JsonUtil.CompactJson(dbObj.CustomFields);
//Set "original" value of concurrency token to input token
//this will allow EF to check it out
ct.Entry(dbObj).OriginalValues["ConcurrencyToken"] = putObj.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)
{
//SEARCH INDEXING
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
SearchParams.AddText(obj.Notes).AddText(obj.Serial).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
if (isNew)
await Search.ProcessNewObjectKeywordsAsync(SearchParams);
else
await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams);
}
public async Task<Search.SearchIndexProcessObjectParameters> GetSearchResultSummary(long id)
{
var obj = await ct.WorkOrder.SingleOrDefaultAsync(m => m.Id == id);
var SearchParams = new Search.SearchIndexProcessObjectParameters();
if (obj != null)
SearchParams.AddText(obj.Notes).AddText(obj.Serial).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
return SearchParams;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//DELETE
//
internal async Task<bool> DeleteAsync(WorkOrder dbObj)
{
// NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
if (HasErrors)
return false;
ct.WorkOrder.Remove(dbObj);
await ct.SaveChangesAsync();
//Log event
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Serial.ToString(), ct);
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////
//VALIDATION
//
//Can save or update?
private async Task ValidateAsync(WorkOrder proposedObj, WorkOrder currentObj)
{
//run validation and biz rules
bool isNew = currentObj == null;
// //Name required
// if (string.IsNullOrWhiteSpace(proposedObj.Name))
// AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name");
// //Name must be less than 255 characters
// if (proposedObj.Name.Length > 255)
// AddError(ApiErrorCode.VALIDATION_LENGTH_EXCEEDED, "Name", "255 max");
// //If name is otherwise OK, check that name is unique
// if (!PropertyHasErrors("Name"))
// {
// //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false
// if (await ct.WorkOrder.AnyAsync(m => m.Name == proposedObj.Name && m.Id != proposedObj.Id))
// {
// AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name");
// }
// }
//Any form customizations to validate?
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(x => x.FormKey == AyaType.WorkOrder.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);
//validate custom fields
CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields);
}
}
//Can delete?
// private void ValidateCanDelete(WorkOrder inObj)
// {
// //whatever needs to be check to delete this object
// }
////////////////////////////////////////////////////////////////////////////////////////////////
//JOB / OPERATIONS
//
//Other job handlers here...
/////////////////////////////////////////////////////////////////////
}//eoc
}//eons