using Egw.Window.Data; using EgwCoreLib.Razor; using Newtonsoft.Json; using WebWindowComplex; using WebWindowComplex.DTO; using static EgwCoreLib.Lux.Core.Enums; using static WebWindowComplex.LayoutConst; namespace Lux.UI.Components.Compo.Offer { public partial class OfferRowMan : IDisposable { #region Public Properties [Parameter] public OfferModel CurrRecord { get; set; } = default!; [Parameter] public DisplayMode DisplayMode { get; set; } = DisplayMode.Standard; [Parameter] public EventCallback EC_Action { get; set; } [Parameter] public EventCallback EC_Updated { get; set; } #endregion Public Properties #region Public Methods /// /// Dispose sottoscrizione canale /// public void Dispose() { DLService.PipeUpdate.EA_NewMessage -= PipeUpdate_EA_NewMessage; DLService.PipePng.EA_NewMessage -= PipePng_EA_NewMessage; DLService.PipeSvg.EA_NewMessage -= PipeSvg_EA_NewMessage; DLService.PipeHwOpt.EA_NewMessage -= PipeHwOpt_EA_NewMessage; DLService.PipeProfElement.EA_NewMessage -= PipeProfElement_EA_NewMessage; DLService.PipeProfList.EA_NewMessage -= PipeProfList_EA_NewMessage; DLService.PipeShape.EA_NewMessage -= PipeShape_EA_NewMessage; } #endregion Public Methods #region Protected Methods /// /// Verifica after render x stato interattivo pagina /// /// protected override void OnAfterRender(bool firstRender) { if (firstRender) { // JS interop or data fetches go here isInteractive = true; } } /// /// init obj /// protected override async Task OnInitializedAsync() { ConfInit(); prevJwd = ""; await ReloadBaseList(); DLService.PipeUpdate.EA_NewMessage += PipeUpdate_EA_NewMessage; DLService.PipePng.EA_NewMessage += PipePng_EA_NewMessage; DLService.PipeSvg.EA_NewMessage += PipeSvg_EA_NewMessage; DLService.PipeHwOpt.EA_NewMessage += PipeHwOpt_EA_NewMessage; DLService.PipeProfElement.EA_NewMessage += PipeProfElement_EA_NewMessage; DLService.PipeProfList.EA_NewMessage += PipeProfList_EA_NewMessage; DLService.PipeShape.EA_NewMessage += PipeShape_EA_NewMessage; } protected override async Task OnParametersSetAsync() { await ORService.FixImgTypeAsync(OfferID); await ReloadDataAsync(); UpdateTable(); } #endregion Protected Methods #region Private Fields private static Logger Log = LogManager.GetCurrentClassLogger(); /// /// Boolean selezione prodotto da aggiungere (template) /// private bool addFromTemplate = false; private List AllColors = new(); private List AllConfEnvir = new(); private List AllConfGlass = new(); private List AllConfHardware = new(); private List AllConfWood = new(); private List AllRecords = new List(); private string apiUrl = ""; private List AvailColorMaterialList = new List(); private List AvailFamilyHardwareList = new List(); private List AvailGlassList = new List(); private List AvailHardwareList = new(); /// /// Lista profili da DB /// private List AvailProfileList = new(); /// /// Lista profili da Redis (old way) /// private List AvailProfileListOld = new List(); private Dictionary> AvailThresholdDict = new Dictionary>(); private List AvailWoodList = new List(); /// /// Base path x network share files /// private string basePath = "unsafe_uploads"; private string calcTag = "calc"; private EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS cEnvir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW; /// /// Channel update HwOptions /// private string chHwOpt = ""; /// /// Channel update PNG /// private string chPng = ""; /// /// Channel update Profile List /// private string chProfElem = ""; /// /// Channel update Profile List /// private string chProfList = ""; /// /// Channel update Shape /// private string chShape = ""; /// /// Channel update SVG /// private string chSvg = ""; private List currAreaProfiles = new(); private List? CurrBomList = null; /// /// Predisposizione valori live SVG/JWD /// private LivePayload CurrData = new LivePayload(); /// /// Modalit� editint attiva /// private EditMode CurrEditMode = EditMode.None; private Dictionary currGroupShape = new(); private Dictionary currHwOption = new(); private int currPage = 1; /// /// Elenco delle richieste pensding da DB x RigaOfferta corrente /// private Dictionary CurrPendReq = new(); private string currPng = ""; private List currProfList = new(); private string currSvg = ""; /// /// Record in Edit corrente x modifica file/serializzato /// private OfferRowModel? EditRecord = null; /// /// Abilita edit massivo record ITEM /// private bool enableMassEdit = false; private string genericBasePath = ""; private string imgBasePath = ""; private bool isForceParamMode = false; /// /// Semaforo x definire se sia gia in modalita ionterattiva o di prerendering /// private bool isInteractive = false; private bool isLoading = false; private List ListAllCatalog = new(); private List ListCataloghi = new(); private List ListRecords = new(); private List ListTemplateAll = new(); private int numRecord = 10; /// /// Versione originale (pre edit) /// private string origJwd = ""; private List PreparedFile = new(); /// /// Versione precedente JWD x test e confronto /// private string prevJwd = ""; /// /// Dizionario richieste /// private Dictionary reqDict = new Dictionary(); /// /// Configurazione elenchi anagrafiche /// private BaseListPayload SetupList = new BaseListPayload(); private int totalCount = 0; private BootstrapModal Modal = new(); private string mTitle = ""; private string mMessage = ""; private BootstrapModal.ModalMode mMode = BootstrapModal.ModalMode.ND; private Dictionary modalOpt = new Dictionary(); #endregion Private Fields #region Private Properties private Dictionary> AvailThreshold { get; set; } = new Dictionary>() { {"Profilo78", new List() { new Threshold(3, "Bottom")}}, {"ProfiloSaomad", new List(){ new Threshold(3, "Bottom")}} #if false {"Profilo78", new List() { new Threshold(3, "Bottom"), new Threshold(1, "Threshold")}}, {"ProfiloSaomad", new List(){ new Threshold(3, "Bottom"), new Threshold(2, "BottomWaterdrip"), new Threshold(1, "Threshold")}} #endif }; [Inject] private IConfigDataService CDService { get; set; } = default!; [Inject] private IConfGlassService CGService { get; set; } = null!; [Inject] private IConfiguration Config { get; set; } = default!; [Inject] private IConfProfileService CPService { get; set; } = null!; [Inject] private ICalcRuidService CRService { get; set; } = default!; [Inject] private ICalcRequestService CService { get; set; } = default!; [Inject] private IWebHostEnvironment CurrEnv { get; set; } = default!; [Inject] private IConfWoodService CWService { get; set; } = null!; [Inject] private IDataLayerServices DLService { get; set; } = default!; [Inject] private IEnvirParamService EPService { get; set; } = null!; [Inject] private IFileService FService { get; set; } = default!; /// /// Costo totale calcolato x offerta /// private double GrandTotCost { get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.TotalCost) : 0; } /// /// Margine medio calcolato x offerta /// private double GrandTotMargin { get { double answ = 0; if (AllRecords != null && AllRecords.Count > 0) { double totPrice = AllRecords.Sum(x => x.TotalPrice); double totCost = AllRecords.Sum(x => x.TotalCost); if (totPrice > 0) { answ = (totPrice - totCost) / totPrice; } } return answ; } } /// /// Importo totale calcolato x offerta /// private double GrandTotNumItems { get { double answ = 0; if (AllRecords != null && AllRecords.Count > 0) { answ = AllRecords.Sum(x => x.ProdItemQtyTot); } return answ; } } /// /// Importo totale calcolato x offerta /// private double GrandTotPrice { get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.TotalPrice) : 0; } /// /// Num totale obj calcolato x offerta /// private double GrandTotQty { get => AllRecords != null && AllRecords.Count > 0 ? AllRecords.Sum(x => x.Qty) : 0; } [Inject] private IGenValService GVService { get; set; } = default!; [Inject] private IWebHostEnvironment HostEnv { get; set; } = default!; [Inject] private IImageCacheService ICService { get; set; } = default!; [Inject] private IItemService ItemService { get; set; } = null!; [Inject] private IJSRuntime JSRuntime { get; set; } = default!; private string noteCss => EditRecord == null ? "btn-success" : "btn-primary bg-gradient"; /// /// ID Offerta corrente /// private int OfferID { get => CurrRecord.OfferID; } [Inject] private IOfferRowService ORService { get; set; } = default!; [Inject] private IOfferService OService { get; set; } = default!; [Inject] private ITemplateRowService TRService { get; set; } = default!; [Inject] private ITemplateService TService { get; set; } = default!; #endregion Private Properties #region Private Methods /// /// Effettua vera richiesta della BOM /// /// /// private async Task callBomUpdate(OfferRowModel currRec) { // salvo richiesta BOM su record currRec.AwaitBom = true; currRec.AwaitPrice = true; await ORService.UpdateAwaitStateAsync(currRec.OfferRowID, true, true); // preparo la domanda serializzata Dictionary DictExec = new Dictionary(); // verifico parametri da conf envir... var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir); string serKey = envRec != null ? envRec.SerStrucKey : "SerializedData"; // cablata la BOM DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}"); // UID cablato x ora... DictExec.Add("UID", currRec.OfferRowUID); // aggiungo file secondo ambiente... switch (currRec.Envir) { case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW: DictExec.Add(serKey, currRec.SerStruct); break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET: // rileggo da file... string folderPath = FolderPath(currRec.OfferID); string rawData = FService.LoadFileContent(folderPath, currRec.FileResource); DictExec.Add(serKey, rawData); DictExec.Add("FileName", currRec.FileName); break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL: default: break; } CalcRequestDTO req = new CalcRequestDTO() { EnvType = currRec.Envir, DictExec = DictExec }; await InvokeAsync(StateHasChanged); // chiamo la chiamata POST alla API, che manda la richiesta via REDIS await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{currRec.OfferRowUID}", req); } /// /// Effettua vera richiesta update IMG /// /// /// private async Task callImgUpdate(OfferRowModel currRec) { // preparo la domanda serializzata Dictionary DictExec = new Dictionary(); // verifico parametri da conf envir... var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir); string serKey = envRec != null ? envRec.SerStrucKey : "SerializedData"; // cablata la BOM DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}"); // UID cablato x ora... DictExec.Add("UID", currRec.OfferRowUID); // aggiungo file secondo ambiente... switch (currRec.Envir) { case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW: DictExec.Add(serKey, currRec.SerStruct); break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET: // rileggo da file... string folderPath = FolderPath(currRec.OfferID); string rawData = FService.LoadFileContent(folderPath, currRec.FileResource); DictExec.Add(serKey, rawData); DictExec.Add("FileName", currRec.FileName); break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL: default: break; } CalcRequestDTO req = new CalcRequestDTO() { EnvType = currRec.Envir, DictExec = DictExec }; await InvokeAsync(StateHasChanged); // chiamo la chiamata POST alla API, che manda la richiesta via REDIS await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{currRec.OfferRowUID}", req); } /// /// Effettua vera richiesta della BOM /// /// private async Task callRefreshProfList() { Dictionary DictExec = new Dictionary(); var cMode = Egw.Window.Data.Enums.QuestionModes.CONFIG; var cSubMode = Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST; // compongo righiesta string reqUid = "Default"; DictExec.Add("Mode", $"{(int)cMode}"); DictExec.Add("UID", reqUid); // creo registrazione richiesta... var ruid = await CRService.AddRequestAsync($"{cEnvir}", $"{cMode}-{cSubMode}", reqUid); // aggiungo RUID effettivo DictExec.Add("RUID", ruid); DictExec.Add("SubMode", $"{(int)cSubMode}"); CalcRequestDTO req = new CalcRequestDTO() { EnvType = cEnvir, DictExec = DictExec }; // chiamo la chiamata POST alla API, che manda la richiesta via REDIS await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req); } /// /// Chiude edit andando eventualmente a salvare /// /// /// private async Task CloseEdit(bool doSave) { // Proseguo solo se sono in interattivo (NO prerender pagina) if (isInteractive) { // ...se ho editing if (EditRecord != null) { bool updateBom = false; // SE richiesto salvataggio... if (doSave) { // salvo su DB! await ORService.UpdateSerStructDirtyAsync(EditRecord.OfferRowID, prevJwd, CurrPendReq); // salvo nel record corrente! EditRecord.SerStruct = prevJwd; updateBom = true; } else // altrimenti ricalcolo valore salvato { prevJwd = EditRecord.SerStruct; CurrData.CurrJwd = EditRecord.SerStruct; } if (updateBom) { await callBomUpdate(EditRecord); } // aggiorno nel dizionari if (reqDict.ContainsKey("SerializedData")) { reqDict["SerializedData"] = prevJwd; } if (reqDict != null && reqDict.Count > 0) { // chiamo richiesta update CalcRequestDTO calcRequestDTO = new CalcRequestDTO(); calcRequestDTO.EnvType = EditRecord.Envir; calcRequestDTO.DictExec = reqDict; // chiamo la chiamata POST alla API, che manda la richiesta via REDIS await ICService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO); } EditRecord = null; CurrEditMode = EditMode.None; } } } /// /// Chiude edit con preprocess x caso JWD /// /// /// private Task CloseEditJwd(DataSave infoSave) { prevJwd = infoSave.currJwd; return CloseEdit(infoSave.ForceSave); } private void ClosePopup() { CurrEditMode = EditMode.None; EditRecord = null; addFromTemplate = false; } private void ConfInit() { basePath = Config.GetValue("ServerConf:FileSharePath") ?? "unsafe_uploads"; apiUrl = Config.GetValue("ServerConf:Prog.ApiUrl") ?? ""; imgBasePath = Config.GetValue("ServerConf:ImageBaseUrl") ?? ""; genericBasePath = Config.GetValue("ServerConf:GenericBaseUrl") ?? ""; calcTag = Config.GetValue("ServerConf:CalcTag") ?? "calc"; chHwOpt = Config.GetValue("ServerConf:ChannelHwOpt") ?? ""; chPng = Config.GetValue("ServerConf:ChannelPng") ?? ""; chProfElem = Config.GetValue("ServerConf:ChannelProfElem") ?? ""; chProfList = Config.GetValue("ServerConf:ChannelProfList") ?? ""; chShape = Config.GetValue("ServerConf:ChannelShape") ?? ""; chSvg = Config.GetValue("ServerConf:ChannelSvg") ?? ""; } private string cssReqPend(bool isDirty) { return isDirty ? "border border-warning rounded border-3" : ""; } /// /// Esecuzione azione richiesta /// /// Azione richiesta private void DoAction(LayoutConst.DataAction actReq) { switch (actReq) { case LayoutConst.DataAction.None: break; case LayoutConst.DataAction.ResetDictShape: CurrData.DictShape = new Dictionary(); break; case LayoutConst.DataAction.ResetHwOpt: CurrData.DictOptionsXml = new Dictionary(); break; case DataAction.ResetDimElem: CurrData.ProfElementList = new List(); break; default: break; } } /// /// Aggiunge una nuova riga vuota come nota sotto il record selezionato oppure in coda... /// /// private async Task DoAddNote() { int numRow = AllRecords.Count + 1; if (EditRecord != null) { numRow = EditRecord.RowNum + 1; } OfferRowModel newNote = new OfferRowModel() { Note = $"--- Nuova nota | {DateTime.Now:ddd yyyy.MM.dd HH:mm:ss}---", OfferID = CurrRecord.OfferID, Envir = CurrRecord.Envir, RowNum = numRow, OfferRowUID = "", SellingItemID = null, Qty = 0, BomCost = 0, BomPrice = 0, StepCost = 0, StepPrice = 0 }; await ORService.UpsertAsync(newNote); await ReloadDataAsync(); UpdateTable(); } /// /// Aggiunge una nuova riga ordine in coda... /// /// private async Task DoAddOrderRow(TemplateRowModel selTemplate) { int numRow = AllRecords.Count + 1; if (EditRecord != null) { numRow = EditRecord.RowNum + 1; } // SOLO SE è un oggetto calcolabile (JWD, BTL...) richiedo update img e BOM bool needRecalc = (selTemplate.SellingItemNav != null && selTemplate.SellingItemNav.HasBOM); // creo riga! OfferRowModel newSOR = new OfferRowModel() { AwaitBom = needRecalc, AwaitPrice = needRecalc, OfferID = CurrRecord.OfferID, Envir = CurrRecord.Envir, Inserted = DateTime.Now, RowNum = numRow, OfferRowUID = "", Qty = 1, SellingItemID = selTemplate.SellingItemID, SerStruct = selTemplate.SerStruct, BomCost = 0, BomPrice = 0, StepCost = 0, StepPrice = 0 }; // SE FOSSE NON calcolabile aggiungo costi std if (!needRecalc && selTemplate.SellingItemNav != null) { // recupero costi da ancestor newSOR.BomCost = selTemplate.SellingItemNav?.Cost ?? 0; newSOR.BomPrice = newSOR.BomCost * (1 + selTemplate.SellingItemNav?.Margin) ?? 1; newSOR.BomOk = true; newSOR.ItemOk = true; newSOR.ProdItemQty = 1; } addFromTemplate = false; var dbRec = await ORService.UpsertAsync(newSOR); // chiamo ricalcolo BOM e IMG... if (dbRec != null) { // se ho parametri validi di preselezione li applico... if (!string.IsNullOrEmpty(CurrRecord.DictPresel) && CurrRecord.DictParameter.Count > 0) { // in primis provo conversione diretta var updAnsw = SerialMan.MassUpdate((string)dbRec.SerStruct, CurrRecord.DictParameter); // salvo jwd + versione aggiornata parametri await ORService.UpdateSerStructDirtyAsync(dbRec.OfferRowID, updAnsw.serializeStruct, updAnsw.DictParameter); // devo rileggere dal DB dbRec = await ORService.GetByIdAsync(dbRec.OfferRowID); } // SOLO SE è un oggetto calcolabile (JWD, BTL...) richiedo update img e BOM if (needRecalc && dbRec != null) { await callBomUpdate(dbRec); await callImgUpdate(dbRec); // imposto ad editing record secondo tipo if (dbRec.Envir == EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW) { DoEditJwd(dbRec); } else { DoEditFile(dbRec); } } } await ReloadDataAsync(); UpdateTable(); } /// /// Annullamento modifica /// /// /// private async Task DoCancel() { isLoading = true; CurrEditMode = EditMode.None; EditRecord = null; await Task.Delay(20); await OService.FlushCacheOffersAsync(); await Task.Delay(20); await ReloadDataAsync(); UpdateTable(); isLoading = false; } /// /// Clona riga richiesta /// /// private async Task DoClone(OfferRowModel rec2clone) { mTitle = "Attenzione"; mMessage = "Confermi di voler duplicare la riga corrente?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; // calcolo indice riga... int numRow = totalCount + 1; bool needRecalc = (rec2clone.SellingItemNav != null && rec2clone.SellingItemNav.HasBOM); OfferRowModel newRec = new OfferRowModel() { AwaitBom = needRecalc, AwaitPrice = needRecalc, Envir = rec2clone.Envir, FileName = rec2clone.FileName, FileResource = rec2clone.FileResource, FileSize = rec2clone.FileSize, Inserted = DateTime.Now, ItemBOM = rec2clone.ItemBOM, ItemSteps = rec2clone.ItemSteps, Modified = DateTime.Now, Note = rec2clone.Note, OfferID = OfferID, OfferRowUID = "", Qty = rec2clone.Qty, RowNum = numRow, SellingItemID = rec2clone.SellingItemID, SerStruct = rec2clone.SerStruct, StepCost = rec2clone.StepCost, StepPrice = rec2clone.StepPrice, BomCost = needRecalc ? 0 : rec2clone.BomCost, BomPrice = needRecalc ? 0 : rec2clone.BomPrice, BomOk = needRecalc ? false : true, ItemOk = needRecalc ? false : true, ProdItemQty = needRecalc ? 0 : rec2clone.ProdItemQty }; // salvo sul DB await ORService.UpsertAsync(newRec); // chiamo update record che non hanno UID x questo ordine await ORService.FixImgTypeAsync(OfferID); var list2fix = await ORService.FixUidAsync(OfferID); if (list2fix != null && list2fix.Count > 0) { // rileggo i miei record... await ReloadDataAsync(); var listCalc = SorListCalc(); foreach (var item in listCalc) { // se è con BOM calcolabile... if (item.SellingItemNav != null && item.SellingItemNav.HasBOM) { // se UID e' tra quelli da ricalcolare ed e' calcolabile... if (list2fix.Contains(item.OfferRowUID)) { // chiedo BOM e immagine await callBomUpdate(item); } } } } await ReloadDataAsync(); UpdateTable(); } /// /// Eliminazione riga offerta /// /// /// private async Task DoDelete(OfferRowModel rec2del) { mTitle = "Attenzione"; mMessage = "Confermi di voler eliminare la riga corrente?\n" + $" Codice: {rec2del.OfferRowUID} | {rec2del.Note}\n" + $"Importo tot: {rec2del.TotalPrice}"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; await ORService.DeleteAsync(rec2del); // elimino cache img await ICService.DeleteSvgAsync(rec2del.OfferRowUID, rec2del.Envir); await ReloadDataAsync(); UpdateTable(); } /// /// Va in edit della riga richiesta /// /// private void DoEdit(OfferRowModel curRec) { // imposto edit record EditRecord = curRec; /// modalita edit: gestione valori campi record CurrEditMode = EditMode.RecData; isLoading = false; } /// /// Edit del file: /// - abilitazione fileUpload /// - anteprima grande (live) /// /// private void DoEditFile(OfferRowModel curRec) { EditRecord = curRec; /// modalit�edit: gestione JWD CurrEditMode = EditMode.File; } /// /// Apre editor finestre del record richiesto /// /// private void DoEditJwd(OfferRowModel curRec) { /// modalit�edit: gestione JWD CurrEditMode = EditMode.SerStruc; EditRecord = curRec; // recupero dictReqPending correnti CurrPendReq = curRec.DictPendingPresel; // preparazione dati da record corrente PrepareWindowData(EditRecord.SerStruct); // reset prev prevJwd = ""; } private async Task DoForceParam(Dictionary writeSet) { mTitle = "Attenzione"; mMessage = $"Confermi di voler forzare {writeSet.Count} parametro/i selezionati per l'offerta?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; // recupero obj dizionario x i parametri compresi... ParamDict CurrSel = new ParamDict(CurrRecord.DictPresel); // li incrocerò con i parametri da forzare ricevuti... // metto a waiting tutte le righe con bom... var listCalc = SorListCalc(); foreach (var item in listCalc) { await ORService.UpdateAwaitStateAsync(item.OfferRowID, true, true); Dictionary dict2write = new(); // inserisco solo se valorizzati e selezionati if (writeSet.ContainsKey("Color") && writeSet["Color"] && !string.IsNullOrEmpty(CurrSel.GetVal("Color"))) { dict2write.Add("Color", CurrSel.GetVal("Color")); } if (writeSet.ContainsKey("Glass") && writeSet["Glass"] && !string.IsNullOrEmpty(CurrSel.GetVal("Glass"))) { dict2write.Add("Glass", CurrSel.GetVal("Glass")); } if (writeSet.ContainsKey("Profile") && writeSet["Profile"] && !string.IsNullOrEmpty(CurrSel.GetVal("Profile"))) { dict2write.Add("Profile", CurrSel.GetVal("Profile")); } if (writeSet.ContainsKey("Wood") && writeSet["Wood"] && !string.IsNullOrEmpty(CurrSel.GetVal("Wood"))) { dict2write.Add("Wood", CurrSel.GetVal("Wood")); } var updAnsw = SerialMan.MassUpdate((string)item.SerStruct, dict2write); await ORService.UpdateSerStructDirtyAsync(item.OfferRowID, updAnsw.serializeStruct, updAnsw.DictParameter ?? new Dictionary()); } await InvokeAsync(StateHasChanged); // verifica preliminare UID await ORService.FixUidAsync(OfferID); await ORService.FixImgTypeAsync(OfferID); // ricalcolo di tutte le BOM e relativi prezzi... foreach (var item in listCalc) { // chiedo BOM e immagine await callBomUpdate(item); } // chiudo blocco forza parametri isForceParamMode = false; } private async Task DoRecalcOffer(bool forceResetCalc) { isLoading = true; if (forceResetCalc) { await setAwaitPrice(true, false); UpdateTable(); await InvokeAsync(StateHasChanged); await Task.Delay(300); } await OService.UpdateCostAsync(OfferID); if (forceResetCalc) { await Task.Delay(300); await setAwaitPrice(false, true); } // rileggo dati await ReloadDataAsync(); UpdateTable(); isLoading = false; await EC_Updated.InvokeAsync(true); } /// /// Salvataggio edit record + reload /// /// /// private async Task DoSave(OfferRowModel curRec) { isLoading = true; // salvo record modificato... await ORService.UpsertAsync(curRec); // reset CurrEditMode = EditMode.None; EditRecord = null; await OService.FlushCacheOffersAsync(); await ReloadDataAsync(); UpdateTable(); isLoading = false; } /// /// Salva record ed avanza compilazione /// /// /// private async Task DoSave(OfferModel updRec) { mTitle = "Salvataggio offerta"; mMessage = "Confermi di voler salvare parametri per l'offerta?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; // salvo record await OService.UpsertAsync(updRec); } /// /// Seleziono riga senza cambiare modalit� editing /// /// private void DoSelect(OfferRowModel curRec) { // imposto edit record EditRecord = curRec; /// modalit�edit: gestione valori campi record CurrEditMode = EditMode.None; } private async Task DoSelectItem() { addFromTemplate = true; ListCataloghi = ListAllCatalog.Where(x => x.Envir == CurrRecord.Envir).ToList(); } /// /// Imposta modalita edit ciclo di lavoro /// /// private Task DoSwapJobCycle(OfferRowModel currRow) { CurrEditMode = EditMode.JobCycle; return selectBom(currRow); } /// /// Imposta modalita ad edit BOM /// /// private Task DoSwapMat(OfferRowModel currRow) { CurrEditMode = EditMode.BOM; return selectBom(currRow); } /// /// Aggiorna valore corrente reqPending x salvare poi con JWD su richiesta /// /// /// private void DoUpdatePend(Dictionary newPendReq) { CurrPendReq = newPendReq; #if false if (EditRecord != null) { // aggiorno record richieste! await ORService.UpdatePendReqAsync(EditRecord.OfferRowID, newPendReq); //await ReloadDataAsync(); //UpdateTable(); } await Task.Delay(10); #endif } /// /// Prepara URL x download file JWD /// /// /// /// /// private string DownloadUrl(string currUid, string objType = "SOR", EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW) { return $"{apiUrl}/file/{currUid}?objType={objType}&env={envir}"; } /// /// Salvataggio del JWD aggiornato nella mia riga di offerta /// /// private async Task ExecRequest(Dictionary CurrArgs) { // Proseguo solo se sono in interattivo (NO prerender pagina) if (isInteractive) { // ...se ho editing if (EditRecord != null) { // SE contiene il mio Jwd... if (CurrArgs.ContainsKey("SerializedData")) { string serStruct = CurrArgs["SerializedData"]; // controllo SE variato... if (!prevJwd.Equals(serStruct) || !EgwCoreLib.Utils.DictUtils.DictAreEqual(reqDict, CurrArgs)) { // aggiorno val prev reqDict = CurrArgs; prevJwd = serStruct; // aggiorno live data CurrData.CurrJwd = serStruct; // chiamo richiesta update CalcRequestDTO calcRequestDTO = new CalcRequestDTO(); calcRequestDTO.EnvType = EditRecord.Envir; calcRequestDTO.DictExec = reqDict; // chiamo la chiamata POST alla API, che manda la richiesta via REDIS await ICService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO); #if false // salvo su DB! await DLService.OffertRowUpdateSerStruct(EditRecord.OfferRowID, serStruct); #endif } } } } } /// /// Path da parent record /// /// /// private string FolderPath(int objID) { return $"SO-{objID:X8}"; } /// /// Svuota cache corrente + rilegge dati /// private async Task ForceReloadData() { isLoading = true; CurrEditMode = EditMode.None; EditRecord = null; await Task.Delay(20); await OService.FlushCacheOffersAsync(); await Task.Delay(20); await ReloadDataAsync(); UpdateTable(); isLoading = false; } /// /// Display fileSize scalato /// /// /// private string fSize(long size) { return EgwCoreLib.Utils.FileHelpers.SizeSuffix(size, 1); } /// /// Formattazione testo come html x display /// private MarkupString HtmlConv(string rawData) { return (MarkupString)rawData.Replace(Environment.NewLine, "
").Replace("\n", "
");//.Replace(" ", " "); } /// /// Calcola src immagine direttamente da modello dati + base path /// /// /// private string imgSrc(string imgUrl) { return $"{apiUrl}/{imgBasePath}/{imgUrl}"; } /// /// Aggiornamento costing completo: /// - verifica UID /// - ricalcolo BOM /// - update prezzi /// /// private async Task OfferUpdateAllCosting() { mTitle = "Attenzione"; mMessage = "Confermi di voler ricalcolare/validare in toto l'offerta?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; // metto a waiting tutte le righe con bom... var listCalc = SorListCalc(); foreach (var item in listCalc) { await ORService.UpdateAwaitStateAsync(item.OfferRowID, true, true); } await InvokeAsync(StateHasChanged); // verifica preliminare UID await ORService.FixUidAsync(OfferID); await ORService.FixImgTypeAsync(OfferID); // fixme todo da riverificare con calcolo BOM funzionante #if false // rileggo i record... await ReloadDataAsync(); #endif // ricalcolo di tutte le BOM e relativi prezzi... foreach (var item in listCalc) { // verifico SE sia calcolabile... if (item.CalcEnabled) { // chiedo BOM e immagine await callBomUpdate(item); } } #if false await DoRecalcOffer(); #endif } /// /// Verifica e ricalcolo dei prezzi degli items nell'offerta /// /// private async Task OfferUpdatePrices() { mTitle = "Attenzione"; mMessage = "Confermi di voler ricalcolare a costi correnti offerta?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; await DoRecalcOffer(true); } /// /// Ricevuto HwOpt, processo /// /// /// private async void PipeHwOpt_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.Equals($"{chHwOpt}:{EditRecord.OfferRowUID}")) { // se non è vuoto deserializzo if (currArgs.newMessage.Count() > 2) { var rawDict = JsonConvert.DeserializeObject>(currArgs.newMessage) ?? new Dictionary(); currHwOption = rawDict; } else { currHwOption = new Dictionary(); } // salvo in live data... CurrData.DictOptionsXml = currHwOption; } await InvokeAsync(StateHasChanged); } } } private async void PipePng_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.Equals($"{chPng}:{EditRecord.OfferRowUID}")) { currPng = currArgs.newMessage; // non devo passarlo al componente... #if false // salvo in live data... CurrData.SvgPreview = currSvg; #endif } await InvokeAsync(StateHasChanged); } } } /// /// Ricevuto ProfElem, processo /// /// /// private async void PipeProfElement_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.StartsWith($"{chProfElem}:{EditRecord.OfferRowUID}")) { // deserializzo il dizionario delle risposte... var rawDict = JsonConvert.DeserializeObject>(currArgs.newMessage); currAreaProfiles = rawDict ?? new List(); CurrData.ProfElementList = currAreaProfiles; } await InvokeAsync(StateHasChanged); } } } /// /// Ricevuta profile list, processo /// /// /// private async void PipeProfList_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.Equals($"{chProfList}:{EditRecord.OfferRowUID}")) { try { // nuova gestione da DB: attendo 500ms che il DB sia aggiornato await Task.Delay(500); // rileggo DB x info profili AvailProfileList = await CPService.GetAllAsync(); // converto i profili nel nuovo formato x payload... var profList = AvailProfileList.Select(x => new ProfilePayload() { ProfileName = x.Code, ThresholdList = x.ThresholdList, ParameterDict = x.ProfileDataDict }).ToList(); SetupList.ProfileList = profList; } catch { } } await InvokeAsync(StateHasChanged); } } } /// /// Ricevuta shape, procersso /// /// /// private async void PipeShape_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { #if false // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.Equals($"{shapeChannel}:{EditRecord.OfferRowUID}")) { currGroupShape = currArgs.newMessage; // salvo in live data... CurrData.DictShape = currGroupShape; } await InvokeAsync(StateHasChanged); } #endif // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.StartsWith($"{chShape}:{EditRecord.OfferRowUID}")) { // deserializzo il dizionario delle risposte... var rawDict = JsonConvert.DeserializeObject>(currArgs.newMessage); #if false int groupId = 0; // verifica del groupID... int.TryParse(currArgs.msgUid.Replace($"{shapeChannel}:{windowUid}:", ""), out groupId); if (currGroupShape.ContainsKey(groupId)) { currGroupShape[groupId] = currArgs.newMessage; } else { currGroupShape.Add(groupId, currArgs.newMessage); } #endif currGroupShape = rawDict ?? new Dictionary(); CurrData.DictShape = currGroupShape; } await InvokeAsync(StateHasChanged); } } } /// /// Ricevuto SVG, se � il mio lo aggiorno... /// /// /// private async void PipeSvg_EA_NewMessage(object? sender, EventArgs e) { // vale SOLO SE sono in editing... if (EditRecord != null) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { if (currArgs.msgUid.Equals($"{chSvg}:{EditRecord.OfferRowUID}")) { currSvg = currArgs.newMessage; // salvo in live data... CurrData.SvgPreview = currSvg; } await InvokeAsync(StateHasChanged); } } } /// /// Task verifica update ricevuti /// /// /// private async void PipeUpdate_EA_NewMessage(object? sender, EventArgs e) { // aggiorno visualizzazione PubSubEventArgs currArgs = (PubSubEventArgs)e; // conversione on-the-fly SVG da mostrare if (!string.IsNullOrEmpty(currArgs.newMessage)) { // cerco se faccia parte dei record correnti... var recFound = AllRecords.Any(x => x.OfferRowUID == currArgs.newMessage); if (recFound) { isLoading = true; await Task.Delay(1); // se si tratta dell'UID corrente --> fa update await DoRecalcOffer(false); await InvokeAsync(StateHasChanged); } } await Task.Delay(1); } /// /// Preparazione dati x componente edit JWD /// /// private void PrepareWindowData(string currJwd) { // converto i profili nel nuovo formato x payload... var profList = AvailProfileList.Select(x => new ProfilePayload() { ProfileName = x.Code, ThresholdList = x.ThresholdList, ParameterDict = x.ProfileDataDict }).ToList(); // preparo conf oggetti x controllo SetupList = new BaseListPayload() { ColorMaterial = AvailColorMaterialList, FamilyHardware = AvailFamilyHardwareList, Glass = AvailGlassList, Hardware = AvailHardwareList, Wood = AvailWoodList, ProfileList = profList }; CurrData = new LivePayload() { CurrJwd = currJwd, SvgPreview = "" //currSvg }; } /// /// init classi configurazione /// /// private async Task ReloadBaseList() { // leggo cataloghi e template relativi... ListAllCatalog = await TService.GetAllAsync(); ListTemplateAll = await TRService.GetAllAsync(); // lettura config setup varie da DB/Cache Redis AllConfEnvir = await EPService.GetAllAsync(); AllConfGlass = await CGService.GetAllAsync(); AvailProfileList = await CPService.GetAllAsync(); // FixMe Todo: eliminare da REDIS e usare elenco DB solamente... var rawProfiles = await CDService.ProfileListAsync(cEnvir, "Default"); // se fosse vuoto chiamo update... if (rawProfiles == null || rawProfiles.Count == 0) { await callRefreshProfList(); // aspetto 200ms... e richiedo! await Task.Delay(200); rawProfiles = await CDService.ProfileListAsync(cEnvir, "Default"); } AvailProfileListOld = rawProfiles; var rawHw = await CDService.HwModelListAsync(cEnvir, "HW.AGB"); // hw filtro solo validi... AllConfHardware = rawHw .Where(x => !x.FamilyName.Equals(x.Description, StringComparison.OrdinalIgnoreCase)) .ToList(); AllConfWood = await CWService.GetAllAsync(); AllColors = await GVService.GetFiltAsync("WoodCol"); // conversione tipi AvailGlassList = AllConfGlass .Select(x => x.Description) .ToList(); AvailFamilyHardwareList = AllConfHardware .DistinctBy(x => x.FamilyName) .OrderBy(x => x.FamilyName) .Select(x => x.FamilyName) .ToList(); AvailHardwareList = AllConfHardware .Select(x => new Egw.Window.Data.Hardware(x.Id, x.FamilyName, x.Description, x.OpeningType, x.Shape, x.SashQty, x.SashPosition)) .ToList(); AvailWoodList = AllConfWood .Select(x => x.Description) .ToList(); // FixMe Todo: aggiunta profili (manca anche nel costruttore...) AvailColorMaterialList = AllColors .OrderBy(x => x.Index) .Select(x => x.ValString) .ToList(); } /// /// Legge i dati dei record completi /// private async Task ReloadDataAsync() { if (OfferID > 0) { AllRecords = await ORService.GetByParentAsync(OfferID); totalCount = AllRecords.Count(); } } private Task ReqEditRow() { return EC_Action.InvokeAsync("EditRow"); } /// /// Lancia la richiesta di ricaolo della BOM dal JWD (o equivalente) /// /// private async Task RequestBom(OfferRowModel currRec) { mTitle = "Attenzione"; mMessage = "Confermi di voler ricalcolare la BOM?"; mMode = BootstrapModal.ModalMode.Confirm; modalOpt = new(); modalOpt.Add(true, "Si"); modalOpt.Add(false, "No"); if (!await Modal!.ShowAsync()) return; await callBomUpdate(currRec); } /// /// Css di verifica riga selezionata /// /// /// private string RowClass(OfferRowModel selRow) { return EditRecord != null && EditRecord.OfferRowID == selRow.OfferRowID ? "table-info" : ""; } /// /// Salvataggio dei dati del file caricato /// /// Dizionario info file private void SaveFile(Dictionary fileDict) { // verifico di essere in edit... if (EditRecord != null) { string folderPath = FolderPath(EditRecord.OfferID); // verifico di avere parametri... if (fileDict != null && fileDict.Count > 0) { string secureName = ""; string content = ""; if (fileDict.ContainsKey("secureName")) { secureName = fileDict["secureName"]; } if (fileDict.ContainsKey("content")) { content = fileDict["content"]; } if (!string.IsNullOrEmpty(folderPath) && !string.IsNullOrEmpty(secureName) && !string.IsNullOrEmpty(content)) { // salvo! FService.SaveFileContent(folderPath, secureName, content); } } // altrimenti signifca cleanup eventuale vecchio file... else { FService.DeleteOldFile(folderPath, EditRecord.FileResource); } } } /// /// Selezione e fix dati BOM /// /// /// private async Task selectBom(OfferRowModel currRow) { EditRecord = currRow; //CurrBomList = DLService.OffertGetBomList(EditRecord); CurrBomList = BomCalculator.GetBomList(EditRecord.ItemBOM); if (CurrBomList.Any(x => x.ItemID == 0)) { CurrBomList = await ItemService.GetBomFixItemIdAsync(CurrBomList); } } private async Task setAwaitPrice(bool awaitPrice, bool flushCache) { foreach (var item in AllRecords) { item.AwaitPrice = awaitPrice; await ORService.UpdateAwaitStateAsync(item.OfferRowID, null, awaitPrice, flushCache); } } /// /// Verifia ammissibilit� display btn ricalcolo BOM da item /// /// /// private bool ShowBom(OfferRowModel reqItem) { bool answ = false; if (DisplayMode == EgwCoreLib.Lux.Core.Enums.DisplayMode.Edit) { switch (reqItem.Envir) { case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW: answ = !string.IsNullOrEmpty(reqItem.SerStruct) && reqItem.SerStruct.Length > 2; break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WALL: case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.CABINET: // da cambiare con ricerca file su disco?!? answ = !string.IsNullOrEmpty(reqItem.FileResource) && !string.IsNullOrEmpty(reqItem.FileName); break; case EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.NULL: default: break; } } return answ; } /// /// Elenco SalesOfferRows calcolabili: /// - contengono serializzazione come JWD /// - contengono file come BTL /// private List SorListCalc() { var rawList = AllRecords .Where(x => (!string.IsNullOrEmpty(x.SerStruct) && x.SerStruct.Length > 2) || (!string.IsNullOrEmpty(x.FileName) && !string.IsNullOrEmpty(x.FileResource))) .ToList(); return rawList ?? new List(); } /// /// Toggle visibilit� modifica file indicando ID della OfferRow corrente (o zero se deselect) /// private void ToggleFileEdit(OfferRowModel? currRec) { CurrEditMode = currRec == null ? EditMode.None : EditMode.File; EditRecord = currRec; } private void ToggleForceParam() { isForceParamMode = !isForceParamMode; } #if false /// /// Forza parametri generali selezionati nell'offerta /// /// private async Task OfferForceParameters() { // recupero obj dizionario x i parametri compresi... ParamDict CurrSel = new ParamDict(CurrRecord.DictPresel); // metto a waiting tutte le righe con bom... var listCalc = SorListCalc(); foreach (var item in listCalc) { await ORService.UpdateAwaitStateAsync(item.OfferRowID, true, true); // poich� non � gestito evento ritorno update window interno si "scassa" --> try catch/ if FALSE try { string rColor = CurrSel.GetVal("Color"); string rGlass = CurrSel.GetVal("Glass"); string rProfile = CurrSel.GetVal("Profile"); string rWood = CurrSel.GetVal("Wood"); var newSerStruct = SerialMan.MassUpdate((string)item.SerStruct, null, null, rColor, rWood, rGlass, rProfile); await ORService.UpdateSerStructAsync(item.OfferRowID, newSerStruct); } catch { } } await InvokeAsync(StateHasChanged); // verifica preliminare UID await ORService.FixUidAsync(OfferID); await ORService.FixImgTypeAsync(OfferID); // ricalcolo di tutte le BOM e relativi prezzi... foreach (var item in listCalc) { // chiedo BOM e immagine await callBomUpdate(item); } //await DoRecalcTemplate(); } #endif #if false /// /// Aggiunge una nuova riga ordine in coda... /// /// protected async Task DoAddOrderRowFromSellItem(int sellItemID) { int numRow = AllRecords.Count + 1; if (EditRecord != null) { numRow = EditRecord.RowNum + 1; } OfferRowModel newSOR = new OfferRowModel() { AwaitBom = true, AwaitPrice = true, OfferID = CurrRecord.OfferID, Envir = CurrRecord.Envir, Inserted = DateTime.Now, RowNum = numRow, OfferRowUID = "", Qty = 1, SellingItemID = sellItemID, BomCost = 0, BomPrice = 0, StepCost = 0, StepPrice = 0 }; // se è window aggiungo "{}" come serStruct sennò non la prende bene... if (CurrRecord.Envir == EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW) { newSOR.SerStruct = "{}"; } addFromTemplate = false; await DLService.OffertRowUpsert(newSOR); await ReloadDataAsync(); UpdateTable(); } #endif #if false /// /// Calcolo URL immagine /// /// /// /// protected string imgUrl(string imgUid, string env) { // cast string su env.. EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS envir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW; Enum.TryParse(env, out envir); return ICService.ImageUrl($"{apiUrl}/{imgBasePath}", false, imgUid, envir); } #endif /// /// Salva nel record corrente la BOM aggiornata e poi ricalcola importo... /// /// /// private async Task UpdateBom(List newBomList) { if (EditRecord != null) { // salvo BOM nel record corrente... bool fatto = await ORService.UpdateBomAsync(EditRecord.OfferRowID, newBomList); // ricalcolo offerta completa await ReloadDataAsync(); UpdateTable(); // rilegge il record da elenco appena rinfrescato... int offerRowId = EditRecord.OfferRowID; var updRec = AllRecords.FirstOrDefault(x => x.OfferRowID == offerRowId); if (updRec != null) { CurrBomList = new List(); // fa refresh dei dati della BOM visualizzata await selectBom(updRec); await InvokeAsync(StateHasChanged); } } } /// /// Filtro e paginazione /// private void UpdateTable() { // fix paginazione ListRecords = AllRecords .OrderBy(x => x.RowNum) .Skip(numRecord * (currPage - 1)) .Take(numRecord) .ToList(); isLoading = false; } /// /// Esegue lettura file + invio richiesta specifica /// /// /// private async Task UploadFile(InputFileChangeEventArgs e) { // Proseguo solo se sono in interattivo (NO prerender pagina) if (isInteractive) { if (EditRecord != null) { // init dizionari arg richiesta update Dictionary fileArgs = new Dictionary(); // leggo il contenuto del PRIMO (singolo) file IBrowserFile file = e.File; // limite file size (al momento 10 MB) var maxAllowedSize = 10 * 1024 * 1024; using var stream = file.OpenReadStream(maxAllowedSize); using var reader = new StreamReader(stream); string rawContent = await reader.ReadToEndAsync(); // calcolo il nome del file trusted... string trustedFileName = Path.GetRandomFileName(); EditRecord.FileResource = trustedFileName; EditRecord.FileName = file.Name; EditRecord.FileSize = rawContent.LongCount(); // salvo sul DB i dati (nome, nome sicuro, size...) await ORService.UpdateFileDataAsync(EditRecord); // parametri richiesta fileArgs.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}"); fileArgs.Add("SubMode", "2"); fileArgs.Add("FileName", $"{file.Name}"); fileArgs.Add("Height", "1200"); fileArgs.Add("Width", "1800"); //fileArgs.Add("Btl", rawContent); fileArgs.Add("SerializedData", rawContent); // invio! CalcRequestDTO calcRequestDTO = new CalcRequestDTO(); calcRequestDTO.EnvType = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM; calcRequestDTO.DictExec = fileArgs; await ICService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{EditRecord.OfferRowUID}", calcRequestDTO); // ora chiedo anche la BOM! #if false // salvo in locale il file: SISTEMARE PERMESSI saveFileContent(EditFileRecord.OfferRowUID, trustedFileName, rawContent); #endif } } } #endregion Private Methods } }