diff --git a/server/AyaNova/biz/PartBiz.cs b/server/AyaNova/biz/PartBiz.cs index db79907c..9ef8db17 100644 --- a/server/AyaNova/biz/PartBiz.cs +++ b/server/AyaNova/biz/PartBiz.cs @@ -237,6 +237,52 @@ namespace AyaNova.Biz } + //////////////////////////////////////////////////////////////////////////////////////////////// + //APPEND SERIALS - called by PO ops to add serials to a part + // + internal static async Task AppendSerialsAsync(long partId, string serialText, AyContext ct, long UserId) + { + if (string.IsNullOrWhiteSpace(serialText)) return; + var serials = System.Text.RegularExpressions.Regex.Split(serialText, "[\\s,]+", System.Text.RegularExpressions.RegexOptions.IgnoreCase, + TimeSpan.FromMilliseconds(2000)).Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().OrderBy(x => x).ToArray(); + int nAdded = 0; + var ExistingSerials = await ct.PartSerial.Where(z => z.PartId == partId).OrderBy(z => z.Serial).ToListAsync(); + //Add any new ones + foreach (string s in serials) + { + if (!ExistingSerials.Any(z => z.Serial == s)) + { + ct.PartSerial.Add(new PartSerial() { Serial = s, PartId = partId }); + nAdded++; + } + } + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, partId, AyaType.Part, AyaEvent.Modified, $"LT:PartSerialNumbersAvailable change (+{nAdded})"), ct); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// + //REMOVE SERIALS - called by PO ops to remove serials from a part + // + internal static async Task RemoveSerialsAsync(long partId, string serialText, AyContext ct, long UserId) + { + if (string.IsNullOrWhiteSpace(serialText)) return; + var serials = System.Text.RegularExpressions.Regex.Split(serialText, "[\\s,]+", System.Text.RegularExpressions.RegexOptions.IgnoreCase, + TimeSpan.FromMilliseconds(2000)).Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().OrderBy(x => x).ToArray(); + + int nRemoved = 0; + var ExistingSerials = await ct.PartSerial.Where(z => z.PartId == partId).OrderBy(z => z.Serial).ToListAsync(); + foreach (PartSerial ps in ExistingSerials) + { + if (serials.Contains(ps.Serial)) + { + ct.PartSerial.Remove(ps); + nRemoved++; + } + } + await ct.SaveChangesAsync(); + await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, partId, AyaType.Part, AyaEvent.Modified, $"LT:PartSerialNumbersAvailable change (-{nRemoved})"), ct); + } + //////////////////////////////////////////////////////////////////////////////////////////////// //UPDATE STOCK LEVELS @@ -412,7 +458,7 @@ namespace AyaNova.Biz AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "generalerror", await Translate("PartInventoryList"));//translates to "Part inventory" if (await ct.PartStockLevel.AnyAsync(m => m.PartId == inObj.Id)) AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "generalerror", await Translate("PartStockingLevels")); - if (await ct.PurchaseOrderItem.AnyAsync(m => m.PartId == inObj.Id)) + if (await ct.PurchaseOrderItem.AnyAsync(m => m.PartId == inObj.Id)) AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "generalerror", await Translate("PurchaseOrder")); } diff --git a/server/AyaNova/biz/PurchaseOrderBiz.cs b/server/AyaNova/biz/PurchaseOrderBiz.cs index 8bd7c66b..8e136956 100644 --- a/server/AyaNova/biz/PurchaseOrderBiz.cs +++ b/server/AyaNova/biz/PurchaseOrderBiz.cs @@ -449,6 +449,8 @@ namespace AyaNova.Biz return; } + await PartBiz.AppendSerialsAsync(poItem.PartId, poItem.Serials, ct, UserId); + //MIGRATE_OUTSTANDING - update workorderitempart here if applicable } @@ -461,6 +463,7 @@ namespace AyaNova.Biz //MODIFIED if (ayaEvent == AyaEvent.Modified) { + //any old po items deleted? foreach (var oldItem in oldObj.Items) { @@ -486,15 +489,24 @@ namespace AyaNova.Biz AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({i.Description}):{pib.GetErrorsAsString()}"); return; } + //might have serials so remove those as well + await PartBiz.RemoveSerialsAsync(oldItem.PartId, oldItem.Serials, ct, UserId); } } } + //ITERATE NEW ITEMS LOOK FOR CHANGES foreach (var newItem in newObj.Items) { - //get the matching currentPoItem + //get the matching oldItem var oldItem = oldObj.Items.FirstOrDefault(z => z.Id == newItem.Id); + //CHANGED THE VENDOR? (this doesn't preclude other changes as well below) + if (oldObj.VendorId != newObj.VendorId) + { + SetPoItemDefaultPartValues(newItem, PoParts, newObj.VendorId); + } + //NEW ITEM ADDED if (oldItem == null) { @@ -514,28 +526,13 @@ namespace AyaNova.Biz return; } - if (!string.IsNullOrWhiteSpace(newItem.Serials)) - { - //javascript version at UI for partserials entry - //let splitted = this.newSerial.split(/[\s,]+/).filter(Boolean); //filter Boolean is equivalent to array.filter(item => Boolean(item)) and it's to filter out nulls concisely from badly formatted strings - // splitted = [...splitted, ...this.obj]; - // let uniqueItems = [...new Set(splitted)]; - // uniqueItems.sort(); - - //c# version to replicate above - var serials = System.Text.RegularExpressions.Regex.Split(newItem.Serials, "[\\s,]+", System.Text.RegularExpressions.RegexOptions.IgnoreCase, - TimeSpan.FromMilliseconds(2000)).Select(x => x.Trim()).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().OrderBy(x => x).ToArray(); - - - - - } + await PartBiz.AppendSerialsAsync(newItem.PartId, newItem.Serials, ct, UserId); } SetPoItemDefaultPartValues(newItem, PoParts, newObj.VendorId); //MIGRATE_OUTSTANDING - update workorderitempart here if applicable - continue;//on to next item + continue;//on to next item no possible other changes here } //CHANGED PART OR WAREHOUSE ID @@ -557,6 +554,8 @@ namespace AyaNova.Biz AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({i.Description}):{pib.GetErrorsAsString()}"); return; } + + await PartBiz.RemoveSerialsAsync(oldItem.PartId, oldItem.Serials, ct, UserId); } if (newItem.QuantityReceived > 0) @@ -574,13 +573,15 @@ namespace AyaNova.Biz AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({i.Description}):{pib.GetErrorsAsString()}"); return; } + + await PartBiz.AppendSerialsAsync(newItem.PartId, newItem.Serials, ct, UserId); } //Update part values into poitem if the part or vendor has changed if (oldItem.PartId != newItem.PartId || oldObj.VendorId != newObj.VendorId) SetPoItemDefaultPartValues(newItem, PoParts, newObj.VendorId); - continue;//on to next item + continue;//Note: this accounts also for any change received quantities so no need to check that below } @@ -616,16 +617,18 @@ namespace AyaNova.Biz if (newItem.QuantityReceived > 0 && newItem.ReceivedCost == 0 && newItem.PurchaseOrderCost != 0) newItem.ReceivedCost = newItem.PurchaseOrderCost; + //update serials if they have changed + if (oldItem.Serials != newItem.Serials) + { + await PartBiz.RemoveSerialsAsync(oldItem.PartId, oldItem.Serials, ct, UserId); + await PartBiz.AppendSerialsAsync(newItem.PartId, newItem.Serials, ct, UserId); + } + continue;//on to next } + //### BEFORE ADDING MORE CHECKS HERE MAKE SURE LOGIC WORKS WITH ABOVE ### - //CHANGED ONLY THE VENDOR - if (oldObj.VendorId != newObj.VendorId) - { - SetPoItemDefaultPartValues(newItem, PoParts, newObj.VendorId); - continue;//on to next - } } }//modified block }