From 3f761e64ca4836cd79fed551c0c7152dd1f15b63 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Fri, 21 May 2021 22:10:51 +0000 Subject: [PATCH] --- server/AyaNova/biz/WorkOrderBiz.cs | 244 ++++++++++++------ server/AyaNova/models/WorkOrderItemExpense.cs | 4 +- server/AyaNova/models/WorkOrderItemPart.cs | 10 +- 3 files changed, 178 insertions(+), 80 deletions(-) diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index 6ce3613e..7228c954 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -2916,7 +2916,7 @@ namespace AyaNova.Biz return null; else { - await PartBizActionsAsync(AyaEvent.Created, newObject, null, null); + //await PartBizActionsAsync(AyaEvent.Created, newObject, null, null); //newObject.Tags = TagBiz.NormalizeTags(newObject.Tags); //newObject.CustomFields = JsonUtil.CompactJson(newObject.CustomFields); await ct.WorkOrderItemPart.AddAsync(newObject); @@ -2962,7 +2962,7 @@ namespace AyaNova.Biz await PartValidateAsync(putObject, dbObject); if (HasErrors) return null; - await PartBizActionsAsync(AyaEvent.Modified, putObject, dbObject, null); + // await PartBizActionsAsync(AyaEvent.Modified, putObject, dbObject, null); ct.Replace(dbObject, putObject); try { @@ -3044,97 +3044,189 @@ namespace AyaNova.Biz // private async Task PartPopulateVizFields(WorkOrderItemPart o) { - await Task.CompletedTask; - // if (o.WorkOrderOverseerId != null) - // o.WorkOrderOverseerViz = await ct.User.AsNoTracking().Where(x => x.Id == o.WorkOrderOverseerId).Select(x => x.Name).FirstOrDefaultAsync(); - } + if (o.PartWarehouseId != 0) + o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync(); + Part part = null; + if (o.PartId != 0) + part = await ct.Part.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.PartId); + TaxCode Tax = null; + if (o.TaxPartSaleId != null) + Tax = await ct.TaxCode.AsNoTracking().FirstOrDefaultAsync(z => z.Id == o.TaxPartSaleId); + if (Tax != null) + o.TaxPartSaleViz = Tax.Name; - //////////////////////////////////////////////////////////////////////////////////////////////// - //BIZ ACTIONS - // - // - private async Task PartBizActionsAsync(AyaEvent ayaEvent, WorkOrderItemPart newObj, WorkOrderItemPart oldObj, IDbContextTransaction transaction) - { - //automatic actions on record change, called AFTER validation - - //currently no processing required except for created or modified at this time - if (ayaEvent != AyaEvent.Created && ayaEvent != AyaEvent.Modified) - return; - - //SET TAXES AND PRICING - - //by default apply all automatic actions with further restrictions possible below - bool ApplyTax = true; - bool ApplyPricingUpdate = true; - - //if modifed, see what has changed and should be re-applied - if (ayaEvent == AyaEvent.Modified) + o.PriceViz = 0; + if (part != null) { - //If it wasn't a complete part change there is no need to set pricing - if (newObj.PartId == oldObj.PartId) - { - ApplyPricingUpdate = false; - } - //If taxes haven't change then no need to update taxes - if (newObj.TaxPartSaleId == oldObj.TaxPartSaleId) - ApplyTax = false; + o.CostViz = part.Cost; + o.ListPriceViz = part.Retail; + o.UnitOfMeasureViz = part.UnitOfMeasure; + o.PriceViz = part.Retail;//default price used if not manual or contract override } - //Tax code - if (ApplyTax) + //manual price overrides anything + if (o.PriceOverride != null) + o.PriceViz = (decimal)o.PriceOverride; + else { - //Default in case nothing to apply - newObj.TaxAPct = 0; - newObj.TaxBPct = 0; - newObj.TaxOnTax = false; - - if (newObj.TaxPartSaleId != null) + //not manual so could potentially have a contract adjustment + var c = await GetCurrentWorkOrderContractFromRelatedAsync(AyaType.WorkOrderItem, o.WorkOrderItemId); + if (c != null) { - var t = await ct.TaxCode.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.TaxPartSaleId); - if (t != null) + decimal pct = 0; + ContractOverrideType cot = ContractOverrideType.PriceDiscount; + + bool TaggedAdjustmentInEffect = false; + + //POTENTIAL CONTRACT ADJUSTMENTS + //First check if there is a matching tagged contract discount, that takes precedence + if (c.ContractPartOverrideItems.Count > 0) { - newObj.TaxAPct = t.TaxAPct; - newObj.TaxBPct = t.TaxBPct; - newObj.TaxOnTax = t.TaxOnTax; + //Iterate all contract tagged items in order of ones with the most tags first + foreach (var cp in c.ContractPartOverrideItems.OrderByDescending(z => z.Tags.Count)) + if (cp.Tags.All(z => part.Tags.Any(x => x == z))) + { + if (cp.OverridePct != 0) + { + pct = cp.OverridePct / 100; + cot = cp.OverrideType; + TaggedAdjustmentInEffect = true; + } + } + } + + //Generic discount? + if (!TaggedAdjustmentInEffect && c.ServiceRatesOverridePct != 0) + { + pct = c.ServiceRatesOverridePct / 100; + cot = c.ServiceRatesOverrideType; + } + + //apply if discount found + if (pct != 0) + { + if (cot == ContractOverrideType.CostMarkup) + o.PriceViz = o.CostViz + (o.CostViz * pct); + else if (cot == ContractOverrideType.PriceDiscount) + o.PriceViz = o.ListPriceViz - (o.ListPriceViz * pct); } } } - //Pricing - if (ApplyPricingUpdate) - { - //default in case nothing to apply - newObj.Cost = 0; - newObj.ListPrice = 0; - newObj.Price = 0; + //Calculate totals and taxes + //NET + o.NetViz = o.PriceViz * o.Quantity; - var s = await ct.Part.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.PartId); - if (s != null) + //TAX + o.TaxAViz = 0; + o.TaxBViz = 0; + if (Tax != null) + { + if (Tax.TaxAPct != 0) { - newObj.Cost = s.Cost; - newObj.ListPrice = s.Retail; - var Contract = await GetCurrentWorkOrderContractFromRelatedAsync(AyaType.WorkOrderItem, newObj.WorkOrderItemId); - PartSetListPrice(newObj, Contract); + o.TaxAViz = o.NetViz * (Tax.TaxAPct / 100); + } + if (Tax.TaxBPct != 0) + { + if (Tax.TaxOnTax) + { + o.TaxBViz = (o.NetViz + o.TaxAViz) * (Tax.TaxBPct / 100); + } + else + { + o.TaxBViz = o.NetViz * (Tax.TaxBPct / 100); + } } } + o.LineTotalViz = o.NetViz + o.TaxAViz + o.TaxBViz; } - //////////////////////////////////////////////////////////////////////////////////////////////// - // SET PER UNIT LIST PRICE - // - //(called by woitempart save and also by header save on change of contract) - private static void PartSetListPrice(WorkOrderItemPart o, Contract c) - { - if (c == null || c.ServiceRatesOverridePct == 0) - { - o.Price = o.ListPrice;//default with no contract - return; - } - if (c.ServiceRatesOverrideType == ContractOverrideType.CostMarkup) - o.Price = o.Cost + (o.Cost * c.ServiceRatesOverridePct); - else if (c.ServiceRatesOverrideType == ContractOverrideType.PriceDiscount) - o.Price = o.ListPrice - (o.ListPrice * c.ServiceRatesOverridePct); - } + // //////////////////////////////////////////////////////////////////////////////////////////////// + // //BIZ ACTIONS + // // + // // + // private async Task PartBizActionsAsync(AyaEvent ayaEvent, WorkOrderItemPart newObj, WorkOrderItemPart oldObj, IDbContextTransaction transaction) + // { + // //automatic actions on record change, called AFTER validation + + // //currently no processing required except for created or modified at this time + // if (ayaEvent != AyaEvent.Created && ayaEvent != AyaEvent.Modified) + // return; + + // //SET TAXES AND PRICING + + // //by default apply all automatic actions with further restrictions possible below + // bool ApplyTax = true; + // bool ApplyPricingUpdate = true; + + // //if modifed, see what has changed and should be re-applied + // if (ayaEvent == AyaEvent.Modified) + // { + // //If it wasn't a complete part change there is no need to set pricing + // if (newObj.PartId == oldObj.PartId) + // { + // ApplyPricingUpdate = false; + // } + // //If taxes haven't change then no need to update taxes + // if (newObj.TaxPartSaleId == oldObj.TaxPartSaleId) + // ApplyTax = false; + // } + + // //Tax code + // if (ApplyTax) + // { + // //Default in case nothing to apply + // newObj.TaxAPct = 0; + // newObj.TaxBPct = 0; + // newObj.TaxOnTax = false; + + // if (newObj.TaxPartSaleId != null) + // { + // var t = await ct.TaxCode.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.TaxPartSaleId); + // if (t != null) + // { + // newObj.TaxAPct = t.TaxAPct; + // newObj.TaxBPct = t.TaxBPct; + // newObj.TaxOnTax = t.TaxOnTax; + // } + // } + // } + + // //Pricing + // if (ApplyPricingUpdate) + // { + // //default in case nothing to apply + // newObj.Cost = 0; + // newObj.ListPrice = 0; + // newObj.Price = 0; + + // var s = await ct.Part.AsNoTracking().FirstOrDefaultAsync(z => z.Id == newObj.PartId); + // if (s != null) + // { + // newObj.Cost = s.Cost; + // newObj.ListPrice = s.Retail; + // var Contract = await GetCurrentWorkOrderContractFromRelatedAsync(AyaType.WorkOrderItem, newObj.WorkOrderItemId); + // PartSetListPrice(newObj, Contract); + // } + // } + // } + + // //////////////////////////////////////////////////////////////////////////////////////////////// + // // SET PER UNIT LIST PRICE + // // + // //(called by woitempart save and also by header save on change of contract) + // private static void PartSetListPrice(WorkOrderItemPart o, Contract c) + // { + // if (c == null || c.ServiceRatesOverridePct == 0) + // { + // o.Price = o.ListPrice;//default with no contract + // return; + // } + // if (c.ServiceRatesOverrideType == ContractOverrideType.CostMarkup) + // o.Price = o.Cost + (o.Cost * c.ServiceRatesOverridePct); + // else if (c.ServiceRatesOverrideType == ContractOverrideType.PriceDiscount) + // o.Price = o.ListPrice - (o.ListPrice * c.ServiceRatesOverridePct); + // } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/server/AyaNova/models/WorkOrderItemExpense.cs b/server/AyaNova/models/WorkOrderItemExpense.cs index 1dee590a..9ab2e11a 100644 --- a/server/AyaNova/models/WorkOrderItemExpense.cs +++ b/server/AyaNova/models/WorkOrderItemExpense.cs @@ -20,9 +20,9 @@ namespace AyaNova.Models [Required] public decimal TaxPaid { get; set; } public long? ChargeTaxCodeId { get; set; } - [NotMapped] + [NotMapped] public string ChargeTaxCodeViz { get; set; } - + public bool ReimburseUser { get; set; } = false; public long? UserId { get; set; } [NotMapped] diff --git a/server/AyaNova/models/WorkOrderItemPart.cs b/server/AyaNova/models/WorkOrderItemPart.cs index 53ef40c4..dffae59b 100644 --- a/server/AyaNova/models/WorkOrderItemPart.cs +++ b/server/AyaNova/models/WorkOrderItemPart.cs @@ -15,11 +15,17 @@ namespace AyaNova.Models public string Serials { get; set; } [Required] public long PartId { get; set; } + [NotMapped] + public string PartViz { get; set; } [Required] public long PartWarehouseId { get; set; } + [NotMapped] + public string PartWarehouseViz { get; set; } [Required] - public decimal Quantity { get; set; } + public decimal Quantity { get; set; } public long? TaxPartSaleId { get; set; } + [NotMapped] + public string TaxPartSaleViz { get; set; } // //PRICE FIELDS // [Required] @@ -43,7 +49,7 @@ namespace AyaNova.Models // [NotMapped] // public decimal LineTotalViz { get; set; } - //Standard pricing fields (mostly to support printed reports though some show in UI) + //Standard pricing fields (mostly to support printed reports though some show in UI) //some not to be sent with record depending on role (i.e. cost and charge in some cases) public decimal? PriceOverride { get; set; }//user entered manually overridden price, if null then ignored in calcs otherwise this *is* the price even if zero [NotMapped]