diff --git a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs index c8b6921..b3aaa58 100644 --- a/EgwCoreLib.Lux.Data/Controllers/LuxController.cs +++ b/EgwCoreLib.Lux.Data/Controllers/LuxController.cs @@ -944,15 +944,18 @@ namespace EgwCoreLib.Lux.Data.Controllers // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; + double totPrice = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = newBomList.Count; // validazione e completamento BOM - validateBom(itemGroupList, bomGenList, ref newBomList, ref totCost, ref numGroupOk, ref numItemOk); + validateBom(itemGroupList, bomGenList, ref newBomList, ref totCost, ref totPrice, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(newBomList); currRec.ItemBOM = itemBom; - currRec.BomCost = totCost; + // salvo arrotondato alla 3° decimale + currRec.BomCost = Math.Round(totCost,3); + currRec.BomPrice = Math.Round(totPrice,3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; dbCtx.Entry(currRec).State = EntityState.Modified; @@ -1011,11 +1014,12 @@ namespace EgwCoreLib.Lux.Data.Controllers { // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; + double totPrice = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM - validateBom(itemGroupList, bomGenList, ref bomList, ref totCost, ref numGroupOk, ref numItemOk); + validateBom(itemGroupList, bomGenList, ref bomList, ref totCost, ref totPrice, ref numGroupOk, ref numItemOk); #if false // ciclo x ogni elemento della BOM, cercando x gruppo e ExtItemCode foreach (var item in bomList) @@ -1064,7 +1068,9 @@ namespace EgwCoreLib.Lux.Data.Controllers // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; - currRec.BomCost = totCost; + // salvo arrotondato alla 3° decimale + currRec.BomCost = Math.Round(totCost, 3); + currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; dbCtx.Entry(currRec).State = EntityState.Modified; @@ -1116,15 +1122,18 @@ namespace EgwCoreLib.Lux.Data.Controllers // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; + double totPrice = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM - validateBom(itemGroupList, bomGenList, ref bomList, ref totCost, ref numGroupOk, ref numItemOk); + validateBom(itemGroupList, bomGenList, ref bomList, ref totCost, ref totPrice, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; - currRec.BomCost = totCost; + // salvo arrotondato alla 3° decimale + currRec.BomCost = Math.Round(totCost, 3); + currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; dbCtx.Entry(currRec).State = EntityState.Modified; @@ -1157,17 +1166,21 @@ namespace EgwCoreLib.Lux.Data.Controllers /// Esegue completamento e la validazione dei dati BOM da lista articoli + gruppi, /// validando i dati stessi /// - /// - /// - /// - /// - /// - /// - private static void validateBom(List itemGroupList, List bomGenList, ref List bomList, ref double totCost, ref int numGroupOk, ref int numItemOk) + /// Elenco ItemGroup da considerare + /// Item di tipo BOM/BomAlt + /// Lista BOM ricevuta da validare + /// Costo netto componenti BOM calcolato + /// Prezzo complessivo calcolato (con aggiunta marginalità) + /// Controllo coerenza calcoli sui gruppi items + /// Controllo coerenza calcoli su num items + private static void validateBom(List itemGroupList, List bomGenList, ref List bomList, ref double totCost, ref double totPrice, ref int numGroupOk, ref int numItemOk) { + double margin = 0; // ciclo x ogni elemento della BOM, cercando x gruppo e ExtItemCode foreach (var item in bomList) { + // init del margine + margin = 0; // verifico item group esistente... if (itemGroupList.Where(x => x.CodGroup == item.ClassCode).Count() > 0) { @@ -1182,6 +1195,8 @@ namespace EgwCoreLib.Lux.Data.Controllers // conto l'item numItemOk++; item.PriceEff = item.Price; + // dovrei recuperare margine da BOM... da rivedere, x ora cablato 20%... + margin = 0.2; } else { @@ -1219,6 +1234,7 @@ namespace EgwCoreLib.Lux.Data.Controllers item.ItemCode = recCost.ExtItemCode; //item.DescriptionCode = recCost.Name; } + margin = recCost.Margin; } else { @@ -1228,6 +1244,8 @@ namespace EgwCoreLib.Lux.Data.Controllers } // ...e aggiorno totale totCost += item.TotalCost; + // e prezzo totale compreso margine + totPrice += item.TotalCost * (1 + margin); } } diff --git a/EgwCoreLib.Lux.Data/DbModel/Sales/OfferModel.cs b/EgwCoreLib.Lux.Data/DbModel/Sales/OfferModel.cs index 39ccd0c..cb72b19 100644 --- a/EgwCoreLib.Lux.Data/DbModel/Sales/OfferModel.cs +++ b/EgwCoreLib.Lux.Data/DbModel/Sales/OfferModel.cs @@ -83,6 +83,11 @@ namespace EgwCoreLib.Lux.Data.DbModel.Sales /// public OfferStates OffertState { get; set; } = OfferStates.Open; + /// + /// Sconto applicato (deve essere < del MAX) + /// + public double Discount { get; set; } = 0; + /// /// Numero Item compresi /// @@ -93,14 +98,33 @@ namespace EgwCoreLib.Lux.Data.DbModel.Sales } /// - /// Imposto totale offerta + /// Costo totale offerta (rock bottom) /// [NotMapped] public double TotalCost { - get => OfferRowNav?.Sum(x => x.BomCost) ?? 0; + get => OfferRowNav?.Sum(x => x.TotalCost) ?? 0; } + /// + /// Prezzo totale offerta (compreso di amrginalità) + /// + [NotMapped] + public double TotalPrice + { + get => OfferRowNav?.Sum(x => x.TotalPrice) ?? 0; + } + + /// + /// Sconto massimo applicabile + /// + [NotMapped] + public double MaxDiscount + { + get => (TotalCost > 0 && TotalPrice > TotalCost) ? (TotalPrice - TotalCost) / TotalPrice : 0; + } + + #if false /// /// ID Ordine (nullo se non c'è ordine) diff --git a/EgwCoreLib.Lux.Data/ModelBuilderExtensions.cs b/EgwCoreLib.Lux.Data/ModelBuilderExtensions.cs index cc45052..75befcf 100644 --- a/EgwCoreLib.Lux.Data/ModelBuilderExtensions.cs +++ b/EgwCoreLib.Lux.Data/ModelBuilderExtensions.cs @@ -39,7 +39,7 @@ namespace EgwCoreLib.Lux.Data ); modelBuilder.Entity().HasData( - new GenValueModel { GenValID=1, Ordinal=1, ClassCod = "WoodMat", ValString = "Pine" }, + new GenValueModel { GenValID = 1, Ordinal = 1, ClassCod = "WoodMat", ValString = "Pine" }, new GenValueModel { GenValID = 2, Ordinal = 2, ClassCod = "WoodMat", ValString = "Maple" }, new GenValueModel { GenValID = 3, Ordinal = 1, ClassCod = "WoodCol", ValString = "Legno" }, new GenValueModel { GenValID = 4, Ordinal = 2, ClassCod = "WoodCol", ValString = "Bianco" }, @@ -157,14 +157,14 @@ namespace EgwCoreLib.Lux.Data // init cost drivers modelBuilder.Entity().HasData( // Risorsa principale di calcolo produttività/costi - new CostDriverModel() { CostDriverID = 1, Name = "WorkHour", Unit = "h", Descript = "Ore lavorate per step/fase"}, + new CostDriverModel() { CostDriverID = 1, Name = "WorkHour", Unit = "h", Descript = "Ore lavorate per step/fase" }, new CostDriverModel() { CostDriverID = 2, Name = "Meter", Unit = "m", Descript = "Metri prodotti per step/fase" }, new CostDriverModel() { CostDriverID = 3, Name = "Unit", Unit = "#", Descript = "Numero unità prodotte (lavorate) per step/fase" } ); // inizializzazione risorse modelBuilder.Entity().HasData( - new ResourceModel { ResourceID = 1, Name = "Sezionatrice", FixedCost = 12000, VariableCost=6000 , OverHeadCost= 5000, CostDriverID = 1 , CostDriverBudget= 220*4, LaborCost=30, OverHeadPerc=0.15M, EBTPerc=0.15M }, + new ResourceModel { ResourceID = 1, Name = "Sezionatrice", FixedCost = 12000, VariableCost = 6000, OverHeadCost = 5000, CostDriverID = 1, CostDriverBudget = 220 * 4, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M }, new ResourceModel { ResourceID = 2, Name = "Linea SAOMAD WoodPecker Just 3500", FixedCost = 100000, VariableCost = 30000, OverHeadCost = 15000, CostDriverID = 1, CostDriverBudget = 220 * 8, LaborCost = 40, OverHeadPerc = 0.15M, EBTPerc = 0.15M }, new ResourceModel { ResourceID = 3, Name = "Linea Pantografo", FixedCost = 24000, VariableCost = 6000, OverHeadCost = 5000, CostDriverID = 1, CostDriverBudget = 220 * 8, LaborCost = 35, OverHeadPerc = 0.15M, EBTPerc = 0.15M }, new ResourceModel { ResourceID = 4, Name = "Stazione Verniciatura", FixedCost = 24000, VariableCost = 6000, OverHeadCost = 3000, CostDriverID = 1, CostDriverBudget = 220 * 4, LaborCost = 30, OverHeadPerc = 0.15M, EBTPerc = 0.15M }, @@ -234,9 +234,9 @@ namespace EgwCoreLib.Lux.Data // inizializzazione dei valori di default x OfferRow modelBuilder.Entity().HasData( - new OfferRowModel { OfferRowID = 1, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000001", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 950, SellingItemID = 1, Qty = 3, RowNum = 1, SerStruct = "{}", Note = "Finestra anta singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true }, - new OfferRowModel { OfferRowID = 2, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000002", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 160, SellingItemID = 2, Qty = 3, RowNum = 2, SerStruct = "{}", Note = "Persiana per Finestra anta singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true }, - new OfferRowModel { OfferRowID = 3, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000003", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 200, SellingItemID = 3, Qty = 3, RowNum = 3, SerStruct = "{}", Note = "Installazione serramento", ItemSteps = "{}", BomOk = true, ItemOk = true } + new OfferRowModel { OfferRowID = 1, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000001", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 900, BomPrice = 950, SellingItemID = 1, Qty = 3, RowNum = 1, SerStruct = "{}", Note = "Finestra anta singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true }, + new OfferRowModel { OfferRowID = 2, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000002", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 160, BomPrice = 200, SellingItemID = 2, Qty = 3, RowNum = 2, SerStruct = "{}", Note = "Persiana per Finestra anta singola 2025", ItemSteps = "{}", BomOk = true, ItemOk = true }, + new OfferRowModel { OfferRowID = 3, OfferID = 1, OfferRowUID = $"OFF{DateTime.Today:yy}0000000003", Inserted = DateTime.Now, Modified = DateTime.Now, BomCost = 200, BomPrice = 250, SellingItemID = 3, Qty = 3, RowNum = 3, SerStruct = "{}", Note = "Installazione serramento", ItemSteps = "{}", BomOk = true, ItemOk = true } ); diff --git a/Lux.API/Lux.API.csproj b/Lux.API/Lux.API.csproj index c066d19..e69de29 100644 --- a/Lux.API/Lux.API.csproj +++ b/Lux.API/Lux.API.csproj @@ -1,88 +0,0 @@ - - - - net8.0 - enable - enable - 0.9.2509.2311 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - diff --git a/Lux.UI/Components/Compo/OfferRowMan.razor b/Lux.UI/Components/Compo/OfferRowMan.razor index 87ad448..a7e498f 100644 --- a/Lux.UI/Components/Compo/OfferRowMan.razor +++ b/Lux.UI/Components/Compo/OfferRowMan.razor @@ -45,8 +45,9 @@ else Codice Descrizione Qty - Importo - Tot + Importo + Totale + Marg. @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { Mat. @@ -76,18 +77,29 @@ else @item.OfferRowUID @item.Note @item.Qty - - @if (!(item.BomOk && item.ItemOk)) - { - - } - @($"{item.BomCost:C2}") + + + @if (!(item.BomOk && item.ItemOk)) + { + + } + @($"{item.UnitPrice:C2}") + + (@item.UnitCost.ToString("C2")) + + + + @($"{item.TotalPrice:C2}") + + (@item.TotalCost.ToString("C2")) + + + @item.MaxDiscount.ToString("P2") - @($"{item.TotalCost:C2}") @if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { - @if(!string.IsNullOrEmpty(item.ItemBOM)) + @if (!string.IsNullOrEmpty(item.ItemBOM)) { DoSwapMat(item)"> } diff --git a/Lux.UI/Components/Pages/Offers.razor b/Lux.UI/Components/Pages/Offers.razor index 2bf9bc6..e9453da 100644 --- a/Lux.UI/Components/Pages/Offers.razor +++ b/Lux.UI/Components/Pages/Offers.razor @@ -130,6 +130,7 @@ else } # articoli Importo + Marg. @@ -165,7 +166,11 @@ else @item.NumItems - @item.TotalCost.ToString("C2") + @item.TotalPrice.ToString("C2") + (@item.TotalCost.ToString("C2")) + + + @item.MaxDiscount.ToString("P2") }