From a45b35802c7736ed9e44ab659389188d0b57d3a9 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Wed, 20 Jan 2021 20:11:37 +0000 Subject: [PATCH] --- server/AyaNova/biz/AyaType.cs | 3 +- server/AyaNova/biz/PartInventoryBiz.cs | 183 +++++++++++++++++++++++++ server/AyaNova/models/AyContext.cs | 1 + server/AyaNova/util/AySchema.cs | 9 +- 4 files changed, 188 insertions(+), 8 deletions(-) create mode 100644 server/AyaNova/biz/PartInventoryBiz.cs diff --git a/server/AyaNova/biz/AyaType.cs b/server/AyaNova/biz/AyaType.cs index 953a3d8c..9a7ff527 100644 --- a/server/AyaNova/biz/AyaType.cs +++ b/server/AyaNova/biz/AyaType.cs @@ -132,7 +132,8 @@ namespace AyaNova.Biz [CoreBizObject] PartAssembly = 65, [CoreBizObject] - PartWarehouse = 66 + PartWarehouse = 66, + PartInventory = 67 diff --git a/server/AyaNova/biz/PartInventoryBiz.cs b/server/AyaNova/biz/PartInventoryBiz.cs new file mode 100644 index 00000000..0d2167c0 --- /dev/null +++ b/server/AyaNova/biz/PartInventoryBiz.cs @@ -0,0 +1,183 @@ +using System; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using AyaNova.Util; +using AyaNova.Api.ControllerHelpers; +using Microsoft.Extensions.Logging; +using AyaNova.Models; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace AyaNova.Biz +{ + internal class PartInventoryBiz : BizObject, ISearchAbleObject, IReportAbleObject, IExportAbleObject + { + internal PartInventoryBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles UserRoles) + { + ct = dbcontext; + UserId = currentUserId; + UserTranslationId = userTranslationId; + CurrentUserRoles = UserRoles; + BizType = AyaType.PartInventory; + } + + internal static PartInventoryBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) + { + if (httpContext != null) + return new PartInventoryBiz(ct, UserIdFromContext.Id(httpContext.Items), UserTranslationIdFromContext.Id(httpContext.Items), UserRolesFromContext.Roles(httpContext.Items)); + else + return new PartInventoryBiz(ct, 1, ServerBootConfig.AYANOVA_DEFAULT_TRANSLATION_ID, AuthorizationRoles.BizAdminFull); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //EXISTS + internal async Task ExistsAsync(long id) + { + return await ct.PartInventory.AnyAsync(z => z.Id == id); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //CREATE + // + internal async Task CreateAsync(PartInventory newObject) + { + await ValidateAsync(newObject, null); + if (HasErrors) + return null; + else + { + + await ct.PartInventory.AddAsync(newObject); + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); + await SearchIndexAsync(newObject, true); + + return newObject; + } + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //GET + // + internal async Task GetAsync(long id, bool logTheGetEvent = true) + { + var ret = await ct.PartInventory.AsNoTracking().SingleOrDefaultAsync(m => m.Id == id); + if (logTheGetEvent && ret != null) + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, id, BizType, AyaEvent.Retrieved), ct); + return ret; + } + + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //SEARCH + // + private async Task SearchIndexAsync(PartInventory obj, bool isNew) + { + var SearchParams = new Search.SearchIndexProcessObjectParameters(UserTranslationId, obj.Id, BizType); + DigestSearchText(obj, SearchParams); + if (isNew) + await Search.ProcessNewObjectKeywordsAsync(SearchParams); + else + await Search.ProcessUpdatedObjectKeywordsAsync(SearchParams); + } + + public async Task GetSearchResultSummary(long id) + { + var obj = await GetAsync(id, false); + var SearchParams = new Search.SearchIndexProcessObjectParameters(); + DigestSearchText(obj, SearchParams); + return SearchParams; + } + + public void DigestSearchText(PartInventory obj, Search.SearchIndexProcessObjectParameters searchParams) + { + if (obj != null) + searchParams.AddText(obj.Description); + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //VALIDATION + // + + private async Task ValidateAsync(PartInventory proposedObj, PartInventory currentObj) + { + + bool isNew = currentObj == null; + + //Name required + if (string.IsNullOrWhiteSpace(proposedObj.Name)) + AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name"); + + + + //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.PartInventory.AnyAsync(m => m.Name == proposedObj.Name && m.Id != proposedObj.Id)) + { + AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "Name"); + } + } + + + } + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + //REPORTING + // + public async Task GetReportData(long[] idList) + { + JArray ReportData = new JArray(); + while (idList.Any()) + { + var batch = idList.Take(IReportAbleObject.REPORT_DATA_BATCH_SIZE); + idList = idList.Skip(IReportAbleObject.REPORT_DATA_BATCH_SIZE).ToArray(); + //query for this batch, comes back in db natural order unfortunately + var batchResults = await ct.PartInventory.AsNoTracking().Where(z => batch.Contains(z.Id)).ToArrayAsync(); + //order the results back into original + var orderedList = from id in batch join z in batchResults on id equals z.Id select z; + foreach (PartInventory w in orderedList) + { + var jo = JObject.FromObject(w); + if (!JsonUtil.JTokenIsNullOrEmpty(jo["CustomFields"])) + jo["CustomFields"] = JObject.Parse((string)jo["CustomFields"]); + ReportData.Add(jo); + } + } + return ReportData; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // IMPORT EXPORT + // + public async Task GetExportData(long[] idList) + { + //for now just re-use the report data code + //this may turn out to be the pattern for most biz object types but keeping it seperate allows for custom usage from time to time + return await GetReportData(idList); + } + + + + + + + ///////////////////////////////////////////////////////////////////// + + }//eoc + + +}//eons + diff --git a/server/AyaNova/models/AyContext.cs b/server/AyaNova/models/AyContext.cs index 0ac6bd50..3ef081b1 100644 --- a/server/AyaNova/models/AyContext.cs +++ b/server/AyaNova/models/AyContext.cs @@ -40,6 +40,7 @@ namespace AyaNova.Models public virtual DbSet Notification { get; set; } public virtual DbSet NotifyDeliveryLog { get; set; } public virtual DbSet Part { get; set; } + public virtual DbSet PartInventory { get; set; } public virtual DbSet PartWarehouse { get; set; } public virtual DbSet PartSerial { get; set; } public virtual DbSet PartAssembly { get; set; } diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index ae1448ff..024de686 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -22,8 +22,8 @@ namespace AyaNova.Util //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! private const int DESIRED_SCHEMA_LEVEL = 15; - internal const long EXPECTED_COLUMN_COUNT = 676; - internal const long EXPECTED_INDEX_COUNT = 119; + internal const long EXPECTED_COLUMN_COUNT = 687; + internal const long EXPECTED_INDEX_COUNT = 122; //!!!!WARNING: BE SURE TO UPDATE THE DbUtil::EmptyBizDataFromDatabaseForSeedingOrImporting WHEN NEW TABLES ADDED!!!! @@ -687,10 +687,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); // await ExecQueryAsync("CREATE INDEX idx_apartassemblyitem_partid ON apartassemblyitem(partid)"); // await ExecQueryAsync("CREATE INDEX idx_apartassemblyitem_partassemblyid ON apartassemblyitem(partassemblyid)"); - - //PART INVENTORY - await ExecQueryAsync("CREATE TABLE apartinventory (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, description text null, " + "entrydate timestamp not null, lastentrydate timestamp null, partid bigint not null references apart, partwarehouseid bigint not null references apartwarehouse, " + "sourcetype integer not null, sourceid bigint not null, " + @@ -707,8 +704,6 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); //see what if any index tuning will help with a huge db of realistic data doing most common ops //await ExecQueryAsync("CREATE INDEX idx_PartInventory_SourceId_SourceType ON apartinventory (sourceid, sourcetype);"); - - //PROJECT await ExecQueryAsync("CREATE TABLE aproject (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name text not null unique, active bool not null, " + "notes text, wiki text, customfields text, tags varchar(255) ARRAY, " +