Files
lux/Lux.UI/Components/Compo/OfferRowMan.razor.cs
T
Samuele Locatelli 42a8d8fdc6 Refresh componente
2025-10-07 16:14:15 +02:00

656 lines
23 KiB
C#

using EgwCoreLib.Lux.Core;
using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data;
using EgwCoreLib.Lux.Data.DbModel.Config;
using EgwCoreLib.Lux.Data.DbModel.Sales;
using EgwCoreLib.Lux.Data.DbModel.Utils;
using EgwCoreLib.Lux.Data.Services;
using EgwMultiEngineManager.Data;
using Lux.UI.Components.Compo.Config;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.EntityFrameworkCore.Storage.Json;
using Microsoft.JSInterop;
using NLog.LayoutRenderers;
using WebWindowComplex.DTO;
using static EgwCoreLib.Lux.Core.Enums;
namespace Lux.UI.Components.Compo
{
public partial class OfferRowMan : IDisposable
{
#region Public Properties
[Parameter]
public DisplayMode DisplayMode { get; set; } = DisplayMode.Standard;
[Parameter]
public EventCallback<bool> EC_Updated { get; set; }
[Parameter]
public int OfferID { get; set; } = 0;
#endregion Public Properties
#region Public Methods
/// <summary>
/// Dispose sottoscrizione canale
/// </summary>
public void Dispose()
{
DLService.UpdatePipe.EA_NewMessage -= UpdatePipe_EA_NewMessage;
DLService.CalcDonePipe.EA_NewMessage -= CalcDonePipe_EA_NewMessage;
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Predisposizione valori live SVG/JWD
/// </summary>
protected LivePayload CurrData = new LivePayload();
/// <summary>
/// Configurazione elenchi anagrafiche
/// </summary>
protected BaseListPayload SetupList = new BaseListPayload();
#endregion Protected Fields
#region Protected Properties
[Inject]
protected ConfigDataService CDService { get; set; } = null!;
[Inject]
protected IConfiguration Config { get; set; } = null!;
[Inject]
protected CalcRequestService CService { get; set; } = null!;
[Inject]
protected DataLayerServices DLService { get; set; } = null!;
[Inject]
protected IWebHostEnvironment HostEnv { get; set; } = null!;
[Inject]
protected ImageCacheService ICService { get; set; } = null!;
[Inject]
protected IJSRuntime JSRuntime { get; set; } = null!;
#endregion Protected Properties
#region Protected Methods
protected void ClosePopup()
{
showChangeMat = false;
EditBomRecord = null;
}
protected void DoEdit(OfferRowModel curRec)
{
// preparazione dati da record corrente
PrepareWindowData(curRec.SerStruct);
// reset prev
prevJwd = "";
// imposto edit record
EditRecord = curRec;
}
/// <summary>
/// Versione precedente JWD x test e confronto
/// </summary>
private string prevJwd = "";
/// <summary>
/// Versione originale (pre edit)
/// </summary>
private string origJwd = "";
protected void DoSwapMat(OfferRowModel currRow)
{
showChangeMat = true;
EditBomRecord = currRow;
CurrBomList = DLService.OffertGetBomList(EditBomRecord);
}
/// <summary>
/// Display fileSize scalato
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
protected string fSize(long size)
{
return EgwCoreLib.Utils.FileHelpers.SizeSuffix(size, 1);
}
/// <summary>
/// Calcolo URL immagine
/// </summary>
/// <param name="imgUid"></param>
/// <param name="env"></param>
/// <returns></returns>
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);
}
/// <summary>
/// init obj
/// </summary>
protected override async Task OnInitializedAsync()
{
ConfInit();
prevJwd = "";
await ReloadBaseList();
DLService.UpdatePipe.EA_NewMessage += UpdatePipe_EA_NewMessage;
DLService.CalcDonePipe.EA_NewMessage += CalcDonePipe_EA_NewMessage;
}
protected override async Task OnParametersSetAsync()
{
await ReloadData();
UpdateTable();
}
protected async Task RecalcOffer()
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare importi offerta?"))
return;
await DoRecalcOffer();
}
/// <summary>
/// Lancia la richiesta di ricaolo della BOM dal JWD (o equivalente)
/// </summary>
/// <returns></returns>
protected async Task RequestBom(OfferRowModel currRec)
{
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler ricalcolare la BOM?"))
return;
await reqBomUpdate(currRec);
}
/// <summary>
/// Effettua vera richiesta della BOM
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
private async Task reqBomUpdate(OfferRowModel currRec)
{
// salvo richiesta BOM su record
currRec.AwaitBom = true;
currRec.AwaitPrice = true;
await DLService.OffertUpdateAwaitState(currRec.OfferRowID, true, true);
// preparo la domanda serializzata
Dictionary<string, string> DictExec = new Dictionary<string, string>();
// verifico parametri da conf envir...
var envRec = AllConfEnvir.FirstOrDefault(x => x.EnvirID == currRec.Envir);
string serKey = envRec != null ? envRec.SerStrucKey : "Jwd";
// cablata la BOM
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}");
//DictExec.Add("Mode", "2");
// UID cablato x ora...
DictExec.Add("UID", currRec.OfferRowUID);
DictExec.Add(serKey, currRec.SerStruct);
CalcRequestDTO req = new CalcRequestDTO()
{
EnvType = currRec.Envir,
//EnvType = currEnv,
DictExec = DictExec
};
await InvokeAsync(StateHasChanged);
// chiamo la chiamata POST alla API, che manda la richiesta via REDIS
await CService.CallRestPost($"{apiUrl}/{genBasePath}", $"{calcTag}/{currRec.OfferRowUID}", req);
}
#endregion Protected Methods
#region Private Fields
private List<GenValueModel> AllColors = new();
private List<EnvirParamModel> AllConfEnvir = new();
private List<GlassModel> AllConfGlass = new();
private List<HardwareModel> AllConfHardware = new();
private List<ProfileModel> AllConfProfile = new();
private List<WoodModel> AllConfWood = new();
private List<OfferRowModel> AllRecords = new List<OfferRowModel>();
private string apiUrl = "";
private List<string> AvailColorMaterialList = new List<string>();
private List<string> AvailFamilyHardwareList = new List<string>();
private List<string> AvailGlassList = new List<string>();
private List<Egw.Window.Data.Hardware> AvailHardwareList = new();
private List<string> AvailMaterialList = new List<string>();
private string calcTag = "calc";
private List<BomItemDTO>? CurrBomList = null;
private int currPage = 1;
private string currSvg = "";
private OfferRowModel? EditBomRecord = null;
private OfferRowModel? EditFileRecord = null;
private OfferRowModel? EditRecord = null;
private string genBasePath = "generic";
private string GenericBasePath = "";
private string imgBasePath = "";
private bool isLoading = false;
private List<OfferRowModel> ListRecords = new List<OfferRowModel>();
private int numRecord = 10;
private bool showChangeMat = false;
private string subChannel = "";
private int totalCount = 0;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Ricevuto SVG, se è il mio lo aggiorno...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void CalcDonePipe_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($"{subChannel}:{EditRecord.OfferRowUID}"))
{
currSvg = currArgs.newMessage;
// salvo in live data...
CurrData.SvgPreview = currSvg;
}
await InvokeAsync(StateHasChanged);
}
}
}
private void ConfInit()
{
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
imgBasePath = Config.GetValue<string>("ServerConf:ImageBaseUrl") ?? "";
GenericBasePath = Config.GetValue<string>("ServerConf:GenericBaseUrl") ?? "";
calcTag = Config.GetValue<string>("ServerConf:CalcTag") ?? "calc";
subChannel = Config.GetValue<string>("ServerConf:SvgChannel") ?? "";
}
private async Task DoRecalcOffer()
{
isLoading = true;
// ciclo sulle righe...
await setAwaitPrice(true, false);
await Task.Delay(50);
await DLService.OffertUpdateCost(OfferID);
await Task.Delay(50);
await setAwaitPrice(false, true);
await ReloadData();
UpdateTable();
isLoading = false;
await EC_Updated.InvokeAsync(true);
}
/// <summary>
/// Svuota cache corrente + rilegge dati
/// </summary>
private async Task ForceReloadData()
{
isLoading = true;
await Task.Delay(20);
await DLService.FlushCacheOffersAsync();
await Task.Delay(20);
await ReloadData();
UpdateTable();
isLoading = false;
}
/// <summary>
/// Preparazione dati x componente edit JWD
/// </summary>
/// <param name="currJwd"></param>
private void PrepareWindowData(string currJwd)
{
// preparo conf oggetti x controllo
SetupList = SetupList = new BaseListPayload()
{
ColorMaterial = AvailColorMaterialList,
FamilyHardware = AvailFamilyHardwareList,
Glass = AvailGlassList,
Hardware = AvailHardwareList,
Material = AvailMaterialList,
TemplateDTO = null
};
CurrData = new LivePayload()
{
CurrJwd = currJwd,
SvgPreview = currSvg
};
}
/// <summary>
/// init classi configurazione
/// </summary>
/// <returns></returns>
private async Task ReloadBaseList()
{
// lettura config setup varie da DB/Cache Redis
AllConfEnvir = await DLService.ConfEnvirParamGetAllAsync();
AllConfGlass = await DLService.ConfGlassGetAllAsync();
var rawHw = CDService.ElencoHw(EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, "HW.AGB");
// hw filtro solo validi...
AllConfHardware = rawHw
.Where(x => !x.FamilyName.Equals(x.Description, StringComparison.OrdinalIgnoreCase))
.ToList();
AllConfProfile = await DLService.ConfProfileGetAllAsync();
AllConfWood = await DLService.ConfWoodGetAllAsync();
AllColors = await DLService.GenValGetFiltAsync("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();
AvailMaterialList = AllConfWood
.Select(x => x.Description)
.ToList();
// FixMe Todo: aggiunta profili (manca anche nel costruttore...)
AvailColorMaterialList = AllColors
.OrderBy(x => x.Ordinal)
.Select(x => x.ValString)
.ToList();
}
/// <summary>
/// Legge i dati dei record completi
/// </summary>
private async Task ReloadData()
{
if (OfferID > 0)
{
AllRecords = await DLService.OfferRowGetByOffer(OfferID);
totalCount = AllRecords.Count();
}
}
/// <summary>
/// Esegue salvataggio del file ricevuto
/// </summary>
/// <param name="secureName">Nome secure da impiegare</param>
/// <param name="content">Contenuto file</param>
private bool saveFileContent(string folderPath, string secureName, string content)
{
bool answ = false;
if (!string.IsNullOrEmpty(folderPath))
{
// calcolo path file...
string filePath = Path.Combine("unsafe_uploads", folderPath, secureName);
File.WriteAllText(filePath, content);
}
return answ;
}
/// <summary>
/// Salvataggio del JWD aggiornato nella mia riga di offerta
/// </summary>
/// <param name="args"></param>
private async void SaveJWD(Dictionary<string, string> args)
{
// ...se ho editing
if (EditRecord != null)
{
// SE contiene il mio Jwd...
if (args.ContainsKey("Jwd"))
{
reqDict = args;
string serStruct = reqDict["Jwd"];
// controllo SE variato...
if (!prevJwd.Equals(serStruct))
{
// aggiorno val prev
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
}
}
}
}
/// <summary>
/// Dizionario richieste
/// </summary>
private Dictionary<string, string> reqDict = new Dictionary<string, string>();
private async Task setAwaitPrice(bool awaitPrice, bool flushCache)
{
foreach (var item in AllRecords)
{
await DLService.OffertUpdateAwaitState(item.OfferRowID, false, awaitPrice, flushCache);
}
}
/// <summary>
/// Toggle visibilità modifica file indicando ID della OfferRow corrente (o zero se deselect)
/// </summary>
private void ToggleFileEdit(OfferRowModel? currRec)
{
EditFileRecord = currRec;
}
/// <summary>
/// salva nel record corrente la BOM aggiornata e poi ricalcola importo...
/// </summary>
/// <param name="newBomList"></param>
/// <exception cref="NotImplementedException"></exception>
private async void UpdateBom(List<BomItemDTO> newBomList)
{
if (EditBomRecord != null)
{
// salvo BOM nel record corrente...
bool fatto = await DLService.OffertRowUpdateBom(EditBomRecord.OfferRowID, newBomList);
// ricalcolo offerta completa
await ReloadData();
UpdateTable();
}
}
/// <summary>
/// Task verifica update ricevuti
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private async void UpdatePipe_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 aprte dei record correnti...
var recFound = AllRecords.Any(x => x.OfferRowUID == currArgs.newMessage);
if (recFound)
{
isLoading = true;
await Task.Delay(10);
#if false
if (currArgs.msgUid.Equals($"{subChannel}:{windowUid}"))
{
}
#endif
// se si tratta dell'UID corrente --> fa update
await DoRecalcOffer();
await InvokeAsync(StateHasChanged);
}
}
await Task.Delay(1);
}
/// <summary>
/// Filtro e paginazione
/// </summary>
private void UpdateTable()
{
// fix paginazione
ListRecords = AllRecords
.Skip(numRecord * (currPage - 1))
.Take(numRecord)
.ToList();
isLoading = false;
}
/// <summary>
/// Esegue lettura file + invio richiesta specifica
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
private async Task UploadFile(InputFileChangeEventArgs e)
{
if (EditFileRecord != null)
{
// init dizionari arg richiesta update
Dictionary<string, string> fileArgs = new Dictionary<string, string>();
// 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();
EditFileRecord.FileResource = trustedFileName;
EditFileRecord.FileName = file.Name;
EditFileRecord.FileSize = rawContent.LongCount();
// salvo sul DB i dati (nome, nome sicuro, size...)
await DLService.OffertRowUpdateFileData(EditFileRecord);
// parametri richiesta
fileArgs.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}");
fileArgs.Add("Btl", rawContent);
// invio!
CalcRequestDTO calcRequestDTO = new CalcRequestDTO();
calcRequestDTO.EnvType = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.BEAM;
calcRequestDTO.DictExec = fileArgs;
await ICService.CallRestPost($"{apiUrl}/{GenericBasePath}", $"{calcTag}/{EditFileRecord.OfferRowUID}", calcRequestDTO);
#if false
// salvo in locale il file: SISTEMARE PERMESSI
saveFileContent(EditFileRecord.OfferRowUID, trustedFileName, rawContent);
#endif
}
}
/// <summary>
/// Chiude edit andando eventualmente a salvare
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
private async Task CloseEdit(bool doSave)
{
// ...se ho editing
if (EditRecord != null)
{
bool updateBom = false;
// SE richiesto salvataggio...
if (doSave)
{
// salvo su DB!
await DLService.OffertRowUpdateSerStruct(EditRecord.OfferRowID, prevJwd);
updateBom = true;
}
else
// altrimenti ricalcolo valore salvato
{
prevJwd = EditRecord.SerStruct;
CurrData.CurrJwd = EditRecord.SerStruct;
}
if(updateBom)
{
await reqBomUpdate(EditRecord);
}
// aggiorno nel dizionari
if (reqDict.ContainsKey("Jwd"))
{
reqDict["Jwd"] = 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;
}
}
#endregion Private Methods
}
}