STUB remaining v7 corebizobjects
This commit is contained in:
305
server/AyaNova/biz/ContractBiz.cs
Normal file
305
server/AyaNova/biz/ContractBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 ContractBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal ContractBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Contract;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ContractBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new ContractBiz(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 ContractBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Contract.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Contract> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Contract.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<Contract> CreateAsync(Contract inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Contract
|
||||||
|
Contract outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Contract.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Contract> DuplicateAsync(Contract dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Contract outObj = new Contract();
|
||||||
|
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.Contract.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Contract.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(Contract dbObj, Contract inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Contract SnapshotOfOriginalDBObj = new Contract();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Contract dbObj, JsonPatchDocument<Contract> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Contract>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Contract SnapshotOfOriginalDBObj = new Contract();
|
||||||
|
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(Contract obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Contract.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Contract dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Contract.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Contract proposedObj, Contract 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.Contract.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.Contract.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(Contract inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/HeadOfficeBiz.cs
Normal file
305
server/AyaNova/biz/HeadOfficeBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 HeadOfficeBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal HeadOfficeBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.HeadOffice;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static HeadOfficeBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new HeadOfficeBiz(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 HeadOfficeBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.HeadOffice.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<HeadOffice> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.HeadOffice.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<HeadOffice> CreateAsync(HeadOffice inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with HeadOffice
|
||||||
|
HeadOffice outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.HeadOffice.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<HeadOffice> DuplicateAsync(HeadOffice dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
HeadOffice outObj = new HeadOffice();
|
||||||
|
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.HeadOffice.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.HeadOffice.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(HeadOffice dbObj, HeadOffice inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
HeadOffice SnapshotOfOriginalDBObj = new HeadOffice();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(HeadOffice dbObj, JsonPatchDocument<HeadOffice> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<HeadOffice>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
HeadOffice SnapshotOfOriginalDBObj = new HeadOffice();
|
||||||
|
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(HeadOffice obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.HeadOffice.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(HeadOffice dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.HeadOffice.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(HeadOffice proposedObj, HeadOffice 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.HeadOffice.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.HeadOffice.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(HeadOffice inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/LoanUnitBiz.cs
Normal file
305
server/AyaNova/biz/LoanUnitBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 LoanUnitBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal LoanUnitBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.LoanUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static LoanUnitBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new LoanUnitBiz(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 LoanUnitBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.LoanUnit.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<LoanUnit> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.LoanUnit.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<LoanUnit> CreateAsync(LoanUnit inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with LoanUnit
|
||||||
|
LoanUnit outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.LoanUnit.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<LoanUnit> DuplicateAsync(LoanUnit dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
LoanUnit outObj = new LoanUnit();
|
||||||
|
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.LoanUnit.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.LoanUnit.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(LoanUnit dbObj, LoanUnit inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
LoanUnit SnapshotOfOriginalDBObj = new LoanUnit();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(LoanUnit dbObj, JsonPatchDocument<LoanUnit> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<LoanUnit>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
LoanUnit SnapshotOfOriginalDBObj = new LoanUnit();
|
||||||
|
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(LoanUnit obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.LoanUnit.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(LoanUnit dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.LoanUnit.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(LoanUnit proposedObj, LoanUnit 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.LoanUnit.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.LoanUnit.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(LoanUnit inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PMBiz.cs
Normal file
305
server/AyaNova/biz/PMBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PMBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PMBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.PM;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PMBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PMBiz(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 PMBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.PM.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<PM> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.PM.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<PM> CreateAsync(PM inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with PM
|
||||||
|
PM outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.PM.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<PM> DuplicateAsync(PM dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
PM outObj = new PM();
|
||||||
|
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.PM.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.PM.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(PM dbObj, PM inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PM SnapshotOfOriginalDBObj = new PM();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(PM dbObj, JsonPatchDocument<PM> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<PM>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PM SnapshotOfOriginalDBObj = new PM();
|
||||||
|
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(PM obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.PM.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(PM dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.PM.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(PM proposedObj, PM 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.PM.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.PM.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(PM inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PMItemBiz.cs
Normal file
305
server/AyaNova/biz/PMItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PMItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PMItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.PMItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PMItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PMItemBiz(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 PMItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.PMItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<PMItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.PMItem.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<PMItem> CreateAsync(PMItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with PMItem
|
||||||
|
PMItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.PMItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<PMItem> DuplicateAsync(PMItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
PMItem outObj = new PMItem();
|
||||||
|
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.PMItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.PMItem.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(PMItem dbObj, PMItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMItem SnapshotOfOriginalDBObj = new PMItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(PMItem dbObj, JsonPatchDocument<PMItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<PMItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMItem SnapshotOfOriginalDBObj = new PMItem();
|
||||||
|
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(PMItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.PMItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(PMItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.PMItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(PMItem proposedObj, PMItem 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.PMItem.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.PMItem.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(PMItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PMTemplateBiz.cs
Normal file
305
server/AyaNova/biz/PMTemplateBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PMTemplateBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PMTemplateBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.PMTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PMTemplateBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PMTemplateBiz(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 PMTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.PMTemplate.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<PMTemplate> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.PMTemplate.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<PMTemplate> CreateAsync(PMTemplate inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with PMTemplate
|
||||||
|
PMTemplate outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.PMTemplate.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<PMTemplate> DuplicateAsync(PMTemplate dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
PMTemplate outObj = new PMTemplate();
|
||||||
|
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.PMTemplate.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.PMTemplate.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(PMTemplate dbObj, PMTemplate inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMTemplate SnapshotOfOriginalDBObj = new PMTemplate();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(PMTemplate dbObj, JsonPatchDocument<PMTemplate> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<PMTemplate>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMTemplate SnapshotOfOriginalDBObj = new PMTemplate();
|
||||||
|
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(PMTemplate obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.PMTemplate.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(PMTemplate dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.PMTemplate.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(PMTemplate proposedObj, PMTemplate 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.PMTemplate.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.PMTemplate.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(PMTemplate inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PMTemplateItemBiz.cs
Normal file
305
server/AyaNova/biz/PMTemplateItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PMTemplateItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PMTemplateItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.PMTemplateItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PMTemplateItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PMTemplateItemBiz(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 PMTemplateItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.PMTemplateItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<PMTemplateItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.PMTemplateItem.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<PMTemplateItem> CreateAsync(PMTemplateItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with PMTemplateItem
|
||||||
|
PMTemplateItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.PMTemplateItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<PMTemplateItem> DuplicateAsync(PMTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
PMTemplateItem outObj = new PMTemplateItem();
|
||||||
|
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.PMTemplateItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.PMTemplateItem.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(PMTemplateItem dbObj, PMTemplateItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMTemplateItem SnapshotOfOriginalDBObj = new PMTemplateItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(PMTemplateItem dbObj, JsonPatchDocument<PMTemplateItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<PMTemplateItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PMTemplateItem SnapshotOfOriginalDBObj = new PMTemplateItem();
|
||||||
|
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(PMTemplateItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.PMTemplateItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(PMTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.PMTemplateItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(PMTemplateItem proposedObj, PMTemplateItem 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.PMTemplateItem.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.PMTemplateItem.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(PMTemplateItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PartBiz.cs
Normal file
305
server/AyaNova/biz/PartBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PartBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PartBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Part;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PartBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PartBiz(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 PartBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Part.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Part> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Part.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<Part> CreateAsync(Part inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Part
|
||||||
|
Part outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Part.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Part> DuplicateAsync(Part dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Part outObj = new Part();
|
||||||
|
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.Part.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Part.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(Part dbObj, Part inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Part SnapshotOfOriginalDBObj = new Part();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Part dbObj, JsonPatchDocument<Part> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Part>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Part SnapshotOfOriginalDBObj = new Part();
|
||||||
|
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(Part obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Part.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Part dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Part.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Part proposedObj, Part 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.Part.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.Part.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(Part inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/ProjectBiz.cs
Normal file
305
server/AyaNova/biz/ProjectBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 ProjectBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal ProjectBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Project;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ProjectBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new ProjectBiz(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 ProjectBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Project.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Project> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Project.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<Project> CreateAsync(Project inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Project
|
||||||
|
Project outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Project.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Project> DuplicateAsync(Project dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Project outObj = new Project();
|
||||||
|
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.Project.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Project.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(Project dbObj, Project inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Project SnapshotOfOriginalDBObj = new Project();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Project dbObj, JsonPatchDocument<Project> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Project>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Project SnapshotOfOriginalDBObj = new Project();
|
||||||
|
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(Project obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Project.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Project dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Project.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Project proposedObj, Project 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.Project.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.Project.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(Project inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/PurchaseOrderBiz.cs
Normal file
305
server/AyaNova/biz/PurchaseOrderBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 PurchaseOrderBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal PurchaseOrderBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.PurchaseOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static PurchaseOrderBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new PurchaseOrderBiz(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 PurchaseOrderBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.PurchaseOrder.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<PurchaseOrder> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.PurchaseOrder.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<PurchaseOrder> CreateAsync(PurchaseOrder inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with PurchaseOrder
|
||||||
|
PurchaseOrder outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.PurchaseOrder.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<PurchaseOrder> DuplicateAsync(PurchaseOrder dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
PurchaseOrder outObj = new PurchaseOrder();
|
||||||
|
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.PurchaseOrder.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.PurchaseOrder.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(PurchaseOrder dbObj, PurchaseOrder inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PurchaseOrder SnapshotOfOriginalDBObj = new PurchaseOrder();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(PurchaseOrder dbObj, JsonPatchDocument<PurchaseOrder> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<PurchaseOrder>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
PurchaseOrder SnapshotOfOriginalDBObj = new PurchaseOrder();
|
||||||
|
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(PurchaseOrder obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.PurchaseOrder.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(PurchaseOrder dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.PurchaseOrder.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(PurchaseOrder proposedObj, PurchaseOrder 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.PurchaseOrder.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.PurchaseOrder.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(PurchaseOrder inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/QuoteBiz.cs
Normal file
305
server/AyaNova/biz/QuoteBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 QuoteBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal QuoteBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Quote;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static QuoteBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new QuoteBiz(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 QuoteBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Quote.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Quote> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Quote.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<Quote> CreateAsync(Quote inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Quote
|
||||||
|
Quote outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Quote.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Quote> DuplicateAsync(Quote dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Quote outObj = new Quote();
|
||||||
|
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.Quote.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Quote.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(Quote dbObj, Quote inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Quote SnapshotOfOriginalDBObj = new Quote();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Quote dbObj, JsonPatchDocument<Quote> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Quote>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Quote SnapshotOfOriginalDBObj = new Quote();
|
||||||
|
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(Quote obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Quote.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Quote dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Quote.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Quote proposedObj, Quote 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.Quote.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.Quote.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(Quote inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/QuoteItemBiz.cs
Normal file
305
server/AyaNova/biz/QuoteItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 QuoteItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal QuoteItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.QuoteItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static QuoteItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new QuoteItemBiz(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 QuoteItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.QuoteItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<QuoteItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.QuoteItem.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<QuoteItem> CreateAsync(QuoteItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with QuoteItem
|
||||||
|
QuoteItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.QuoteItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<QuoteItem> DuplicateAsync(QuoteItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
QuoteItem outObj = new QuoteItem();
|
||||||
|
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.QuoteItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.QuoteItem.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(QuoteItem dbObj, QuoteItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteItem SnapshotOfOriginalDBObj = new QuoteItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(QuoteItem dbObj, JsonPatchDocument<QuoteItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<QuoteItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteItem SnapshotOfOriginalDBObj = new QuoteItem();
|
||||||
|
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(QuoteItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.QuoteItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(QuoteItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.QuoteItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(QuoteItem proposedObj, QuoteItem 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.QuoteItem.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.QuoteItem.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(QuoteItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/QuoteTemplateBiz.cs
Normal file
305
server/AyaNova/biz/QuoteTemplateBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 QuoteTemplateBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal QuoteTemplateBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.QuoteTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static QuoteTemplateBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new QuoteTemplateBiz(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 QuoteTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.QuoteTemplate.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<QuoteTemplate> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.QuoteTemplate.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<QuoteTemplate> CreateAsync(QuoteTemplate inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with QuoteTemplate
|
||||||
|
QuoteTemplate outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.QuoteTemplate.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<QuoteTemplate> DuplicateAsync(QuoteTemplate dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
QuoteTemplate outObj = new QuoteTemplate();
|
||||||
|
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.QuoteTemplate.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.QuoteTemplate.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(QuoteTemplate dbObj, QuoteTemplate inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteTemplate SnapshotOfOriginalDBObj = new QuoteTemplate();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(QuoteTemplate dbObj, JsonPatchDocument<QuoteTemplate> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<QuoteTemplate>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteTemplate SnapshotOfOriginalDBObj = new QuoteTemplate();
|
||||||
|
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(QuoteTemplate obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.QuoteTemplate.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(QuoteTemplate dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.QuoteTemplate.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(QuoteTemplate proposedObj, QuoteTemplate 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.QuoteTemplate.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.QuoteTemplate.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(QuoteTemplate inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/QuoteTemplateItemBiz.cs
Normal file
305
server/AyaNova/biz/QuoteTemplateItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 QuoteTemplateItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal QuoteTemplateItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.QuoteTemplateItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static QuoteTemplateItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new QuoteTemplateItemBiz(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 QuoteTemplateItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.QuoteTemplateItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<QuoteTemplateItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.QuoteTemplateItem.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<QuoteTemplateItem> CreateAsync(QuoteTemplateItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with QuoteTemplateItem
|
||||||
|
QuoteTemplateItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.QuoteTemplateItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<QuoteTemplateItem> DuplicateAsync(QuoteTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
QuoteTemplateItem outObj = new QuoteTemplateItem();
|
||||||
|
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.QuoteTemplateItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.QuoteTemplateItem.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(QuoteTemplateItem dbObj, QuoteTemplateItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteTemplateItem SnapshotOfOriginalDBObj = new QuoteTemplateItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(QuoteTemplateItem dbObj, JsonPatchDocument<QuoteTemplateItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<QuoteTemplateItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
QuoteTemplateItem SnapshotOfOriginalDBObj = new QuoteTemplateItem();
|
||||||
|
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(QuoteTemplateItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.QuoteTemplateItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(QuoteTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.QuoteTemplateItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(QuoteTemplateItem proposedObj, QuoteTemplateItem 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.QuoteTemplateItem.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.QuoteTemplateItem.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(QuoteTemplateItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/UnitBiz.cs
Normal file
305
server/AyaNova/biz/UnitBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 UnitBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal UnitBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static UnitBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new UnitBiz(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 UnitBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Unit.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Unit> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Unit.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<Unit> CreateAsync(Unit inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Unit
|
||||||
|
Unit outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Unit.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Unit> DuplicateAsync(Unit dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Unit outObj = new Unit();
|
||||||
|
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.Unit.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Unit.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(Unit dbObj, Unit inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Unit SnapshotOfOriginalDBObj = new Unit();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Unit dbObj, JsonPatchDocument<Unit> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Unit>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Unit SnapshotOfOriginalDBObj = new Unit();
|
||||||
|
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(Unit obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Unit.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Unit dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Unit.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Unit proposedObj, Unit 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.Unit.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.Unit.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(Unit inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/UnitModelBiz.cs
Normal file
305
server/AyaNova/biz/UnitModelBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 UnitModelBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal UnitModelBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.UnitModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static UnitModelBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new UnitModelBiz(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 UnitModelBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.UnitModel.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<UnitModel> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.UnitModel.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<UnitModel> CreateAsync(UnitModel inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with UnitModel
|
||||||
|
UnitModel outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.UnitModel.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<UnitModel> DuplicateAsync(UnitModel dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
UnitModel outObj = new UnitModel();
|
||||||
|
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.UnitModel.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.UnitModel.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(UnitModel dbObj, UnitModel inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
UnitModel SnapshotOfOriginalDBObj = new UnitModel();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(UnitModel dbObj, JsonPatchDocument<UnitModel> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<UnitModel>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
UnitModel SnapshotOfOriginalDBObj = new UnitModel();
|
||||||
|
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(UnitModel obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.UnitModel.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(UnitModel dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.UnitModel.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(UnitModel proposedObj, UnitModel 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.UnitModel.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.UnitModel.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(UnitModel inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/VendorBiz.cs
Normal file
305
server/AyaNova/biz/VendorBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 VendorBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal VendorBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.Vendor;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static VendorBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new VendorBiz(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 VendorBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.Vendor.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<Vendor> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.Vendor.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<Vendor> CreateAsync(Vendor inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with Vendor
|
||||||
|
Vendor outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.Vendor.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<Vendor> DuplicateAsync(Vendor dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
Vendor outObj = new Vendor();
|
||||||
|
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.Vendor.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.Vendor.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(Vendor dbObj, Vendor inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Vendor SnapshotOfOriginalDBObj = new Vendor();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(Vendor dbObj, JsonPatchDocument<Vendor> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<Vendor>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
Vendor SnapshotOfOriginalDBObj = new Vendor();
|
||||||
|
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(Vendor obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Vendor.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(Vendor dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.Vendor.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(Vendor proposedObj, Vendor 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.Vendor.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.Vendor.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(Vendor inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/WorkOrderBiz.cs
Normal file
305
server/AyaNova/biz/WorkOrderBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
//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);
|
||||||
|
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(WorkOrder inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with WorkOrder
|
||||||
|
WorkOrder outObj = inObj;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<WorkOrder> DuplicateAsync(WorkOrder dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
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 inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//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(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(WorkOrder dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//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.Name, 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
|
||||||
|
|
||||||
305
server/AyaNova/biz/WorkOrderItemBiz.cs
Normal file
305
server/AyaNova/biz/WorkOrderItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 WorkOrderItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal WorkOrderItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.WorkOrderItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static WorkOrderItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new WorkOrderItemBiz(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 WorkOrderItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.WorkOrderItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<WorkOrderItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.WorkOrderItem.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<WorkOrderItem> CreateAsync(WorkOrderItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with WorkOrderItem
|
||||||
|
WorkOrderItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.WorkOrderItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<WorkOrderItem> DuplicateAsync(WorkOrderItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
WorkOrderItem outObj = new WorkOrderItem();
|
||||||
|
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.WorkOrderItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.WorkOrderItem.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(WorkOrderItem dbObj, WorkOrderItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderItem SnapshotOfOriginalDBObj = new WorkOrderItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(WorkOrderItem dbObj, JsonPatchDocument<WorkOrderItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<WorkOrderItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderItem SnapshotOfOriginalDBObj = new WorkOrderItem();
|
||||||
|
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(WorkOrderItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.WorkOrderItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(WorkOrderItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.WorkOrderItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(WorkOrderItem proposedObj, WorkOrderItem 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.WorkOrderItem.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.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);
|
||||||
|
|
||||||
|
//validate custom fields
|
||||||
|
CustomFieldsValidator.Validate(this, FormCustomization, proposedObj.CustomFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Can delete?
|
||||||
|
// private void ValidateCanDelete(WorkOrderItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/WorkOrderTemplateItemBiz.cs
Normal file
305
server/AyaNova/biz/WorkOrderTemplateItemBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 WorkOrderTemplateItemBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal WorkOrderTemplateItemBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.WorkOrderTemplateItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static WorkOrderTemplateItemBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new WorkOrderTemplateItemBiz(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 WorkOrderTemplateItemBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.WorkOrderTemplateItem.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<WorkOrderTemplateItem> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.WorkOrderTemplateItem.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<WorkOrderTemplateItem> CreateAsync(WorkOrderTemplateItem inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with WorkOrderTemplateItem
|
||||||
|
WorkOrderTemplateItem outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.WorkOrderTemplateItem.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<WorkOrderTemplateItem> DuplicateAsync(WorkOrderTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
WorkOrderTemplateItem outObj = new WorkOrderTemplateItem();
|
||||||
|
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.WorkOrderTemplateItem.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.WorkOrderTemplateItem.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(WorkOrderTemplateItem dbObj, WorkOrderTemplateItem inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderTemplateItem SnapshotOfOriginalDBObj = new WorkOrderTemplateItem();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(WorkOrderTemplateItem dbObj, JsonPatchDocument<WorkOrderTemplateItem> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<WorkOrderTemplateItem>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderTemplateItem SnapshotOfOriginalDBObj = new WorkOrderTemplateItem();
|
||||||
|
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(WorkOrderTemplateItem obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.WorkOrderTemplateItem.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(WorkOrderTemplateItem dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.WorkOrderTemplateItem.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(WorkOrderTemplateItem proposedObj, WorkOrderTemplateItem 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.WorkOrderTemplateItem.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.WorkOrderTemplateItem.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(WorkOrderTemplateItem inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
305
server/AyaNova/biz/WorkorderTemplateBiz.cs
Normal file
305
server/AyaNova/biz/WorkorderTemplateBiz.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
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 WorkOrderTemplateBiz : BizObject, ISearchAbleObject
|
||||||
|
{
|
||||||
|
|
||||||
|
internal WorkOrderTemplateBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles)
|
||||||
|
{
|
||||||
|
ct = dbcontext;
|
||||||
|
UserId = currentUserId;
|
||||||
|
UserTranslationId = userTranslationId;
|
||||||
|
CurrentUserRoles = UserRoles;
|
||||||
|
BizType = AyaType.WorkOrderTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static WorkOrderTemplateBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (httpContext != null)
|
||||||
|
return new WorkOrderTemplateBiz(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 WorkOrderTemplateBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//EXISTS
|
||||||
|
internal async Task<bool> ExistsAsync(long id)
|
||||||
|
{
|
||||||
|
return await ct.WorkOrderTemplate.AnyAsync(e => e.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// GET
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
internal async Task<WorkOrderTemplate> GetAsync(long fetchId, bool logTheGetEvent = true)
|
||||||
|
{
|
||||||
|
//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.WorkOrderTemplate.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<WorkOrderTemplate> CreateAsync(WorkOrderTemplate inObj)
|
||||||
|
{
|
||||||
|
await ValidateAsync(inObj, null);
|
||||||
|
if (HasErrors)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//do stuff with WorkOrderTemplate
|
||||||
|
WorkOrderTemplate outObj = inObj;
|
||||||
|
|
||||||
|
outObj.Tags = TagUtil.NormalizeTags(outObj.Tags);
|
||||||
|
outObj.CustomFields = JsonUtil.CompactJson(outObj.CustomFields);
|
||||||
|
//Save to db
|
||||||
|
await ct.WorkOrderTemplate.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DUPLICATE
|
||||||
|
//
|
||||||
|
|
||||||
|
internal async Task<WorkOrderTemplate> DuplicateAsync(WorkOrderTemplate dbObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
WorkOrderTemplate outObj = new WorkOrderTemplate();
|
||||||
|
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.WorkOrderTemplate.AnyAsync(m => m.Name == newUniqueName);
|
||||||
|
} while (NotUnique);
|
||||||
|
|
||||||
|
outObj.Name = newUniqueName;
|
||||||
|
|
||||||
|
|
||||||
|
outObj.Id = 0;
|
||||||
|
outObj.ConcurrencyToken = 0;
|
||||||
|
|
||||||
|
await ct.WorkOrderTemplate.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(WorkOrderTemplate dbObj, WorkOrderTemplate inObj)
|
||||||
|
{
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderTemplate SnapshotOfOriginalDBObj = new WorkOrderTemplate();
|
||||||
|
CopyObject.Copy(dbObj, SnapshotOfOriginalDBObj);
|
||||||
|
|
||||||
|
//Replace the db object with the PUT object
|
||||||
|
CopyObject.Copy(inObj, dbObj, "Id");
|
||||||
|
|
||||||
|
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"] = inObj.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//patch
|
||||||
|
internal async Task<bool> PatchAsync(WorkOrderTemplate dbObj, JsonPatchDocument<WorkOrderTemplate> objectPatch, uint concurrencyToken)
|
||||||
|
{
|
||||||
|
//Validate Patch is allowed
|
||||||
|
if (!ValidateJsonPatch<WorkOrderTemplate>.Validate(this, objectPatch)) return false;
|
||||||
|
|
||||||
|
//make a snapshot of the original for validation but update the original to preserve workflow
|
||||||
|
WorkOrderTemplate SnapshotOfOriginalDBObj = new WorkOrderTemplate();
|
||||||
|
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(WorkOrderTemplate obj, bool isNew)
|
||||||
|
{
|
||||||
|
//SEARCH INDEXING
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType);
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).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.WorkOrderTemplate.SingleOrDefaultAsync(m => m.Id == id);
|
||||||
|
var SearchParams = new Search.SearchIndexProcessObjectParameters();
|
||||||
|
if (obj != null)
|
||||||
|
SearchParams.AddText(obj.Notes).AddText(obj.Name).AddText(obj.Wiki).AddText(obj.Tags).AddCustomFields(obj.CustomFields);
|
||||||
|
return SearchParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//DELETE
|
||||||
|
//
|
||||||
|
internal async Task<bool> DeleteAsync(WorkOrderTemplate dbObj)
|
||||||
|
{
|
||||||
|
//Determine if the object can be deleted, do the deletion tentatively
|
||||||
|
//Probably also in here deal with tags and associated search text etc
|
||||||
|
|
||||||
|
//NOT REQUIRED NOW BUT IF IN FUTURE ValidateCanDelete(dbObj);
|
||||||
|
if (HasErrors)
|
||||||
|
return false;
|
||||||
|
ct.WorkOrderTemplate.Remove(dbObj);
|
||||||
|
await ct.SaveChangesAsync();
|
||||||
|
|
||||||
|
//Log event
|
||||||
|
await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObj.Id, dbObj.Name, ct);
|
||||||
|
await Search.ProcessDeletedObjectKeywordsAsync(dbObj.Id, BizType);
|
||||||
|
await TagUtil.ProcessDeleteTagsInRepositoryAsync(ct, dbObj.Tags);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//VALIDATION
|
||||||
|
//
|
||||||
|
|
||||||
|
//Can save or update?
|
||||||
|
private async Task ValidateAsync(WorkOrderTemplate proposedObj, WorkOrderTemplate 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.WorkOrderTemplate.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.WorkOrderTemplate.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(WorkOrderTemplate inObj)
|
||||||
|
// {
|
||||||
|
// //whatever needs to be check to delete this object
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//JOB / OPERATIONS
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
//Other job handlers here...
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}//eoc
|
||||||
|
|
||||||
|
|
||||||
|
}//eons
|
||||||
|
|
||||||
Reference in New Issue
Block a user