@using AyCode.Blazor.Components.Components.Grids @using AyCode.Core.Helpers @using AyCode.Core.Interfaces @using AyCode.Core.Loggers @using AyCode.Services.Nav @using AyCode.Services.Nav.Ekaer.Models @using AyCode.Utils.Extensions @using FruitBank.Common.Entities @using FruitBankHybrid.Shared.Databases @using FruitBankHybrid.Shared.Services.Loggers @using FruitBankHybrid.Shared.Services.SignalRs @inject IEnumerable LogWriters @inject FruitBankSignalRClient FruitBankSignalRClient @inject IJSRuntime JS @* A kézi NAV-beadás fázisában a Status / EKÁER szám / SentDate kézzel szerkeszthető. *@ @* Audit: a value-számításhoz alkalmazott árfolyam (HUF feladónál 1). ReadOnly — generáláskor töltődik. *@ @* Üzenet-oszlop: warningot ÉS errort is mutat. Ikon a súlyosság szerint: ⛔ blokkoló hiba, ⚠️ warning (pótlandó). *@ @if (!string.IsNullOrWhiteSpace(context.DisplayText)) { var isError = ((EkaerHistory)context.DataItem).Status.IsError(); @context.DisplayText } @{ var row = (EkaerHistory)context.DataItem; } @{ var ekaerHistory = (EkaerHistory)context.DataItem; TradeCardType? tradeCard = null; if (!string.IsNullOrWhiteSpace(ekaerHistory.XmlDoc)) { try { tradeCard = NavXmlHelper.Deserialize(ekaerHistory.XmlDoc); } catch (Exception ex) { _logger.Error($"EkaerHistory XmlDoc deserialize error; id: {ekaerHistory.Id}", ex); } } } @if (IsMasterGrid) { @* EKÁER-rekord a toolbar "sorok létrehozása" gombbal készül (new/delete tiltva — nincs automata érzékelés, a létrehozás explicit és idempotens). Az Edit a kézi NAV-beadás fázisában engedélyezett: Status / EKÁER szám / SentDate kézi rögzítéséhez. *@ @* A dátumválasztó editor csak Template-ben mehet toolbarba; a gomb natív DxToolbarItem, hogy a toolbar stílusát/igazítását kapja (lásd: "Ai process..." minta a GridShippingDocument-ben). *@ @* dxbl-toolbar-edit: a DevExpress téma saját opt-in osztálya editor-t hordozó toolbar-itemre — ettől kapja a beépített középre igazítást és a toolbarba simuló editor-stílust. *@ } @code { [Inject] public required DatabaseClient Database { get; set; } [Parameter] public AcObservableCollection? EkaerHistories { get; set; } const string ExportFileName = "ExportResult"; string GridSearchText = ""; bool EditItemsEnabled { get; set; } int FocusedRowVisibleIndex { get; set; } public GridEkaerHistoryBase Grid { get; set; } string GridCss => !IsMasterGrid ? "hide-toolbar" : string.Empty; [Parameter] public IId? ParentDataItem { get; set; } /// Szerver-oldali szűrő (a tabok adják): ToSubmit / Sent / NeedsCompletion. Default All = minden. [Parameter] public EkaerHistoryFilter Filter { get; set; } = EkaerHistoryFilter.All; /// A szülő (oldal) értesítése, ha egy művelet státuszt válthatott (generálás / kézi mentés / jövőbeni beküldés) — /// hogy a fül-számlálók azonnal frissüljenek, mert a sor átkerülhet másik tabra. [Parameter] public EventCallback OnDataChanged { get; set; } public bool IsMasterGrid => ParentDataItem == null; private LoggerClient _logger; protected override async Task OnInitializedAsync() { _logger = new LoggerClient(LogWriters.ToArray()); await ReloadDataFromDb(false); } private async Task ReloadDataFromDb(bool forceReload = false) { if (!IsMasterGrid) return; if (Grid == null) return; using (await ObjectLock.GetSemaphore().UseWaitAsync()) if (forceReload) await Grid.ReloadDataSourceAsync(); if (forceReload) Grid.Reload(); } async Task Grid_FocusedRowChanged(GridFocusedRowChangedEventArgs args) { if (Grid == null) return; if (Grid.IsEditing() && !Grid.IsEditingNewRow()) { await Grid.SaveChangesAsync(); await NotifyDataChanged(); // kézi szerkesztés (pl. Status → SentWithMissingData) → fül-szám frissítése } FocusedRowVisibleIndex = args.VisibleIndex; EditItemsEnabled = true; } private readonly HashSet _generatingIds = []; // Elküldött (NAV-nál lévő) bejelentést nem generálunk újra némán; minden más állapot újragenerálható. private bool CanGenerate(EkaerHistory ekaerHistory) => !ekaerHistory.Status.IsSent() && !_generatingIds.Contains(ekaerHistory.Id); // Csak BLOKKOLÓ hiba (ValidationError) esetén NEM másolható; a warning (pl. hiányzó rendszám) küldhető → másolható. private static bool CanCopy(EkaerHistory ekaerHistory) => ekaerHistory.Status.IsSubmittable() && !string.IsNullOrWhiteSpace(ekaerHistory.XmlDoc); // A szülő-oldal (fül-számlálók) értesítése egy státuszt érintő művelet után. Üres delegáltnál no-op. private Task NotifyDataChanged() => OnDataChanged.HasDelegate ? OnDataChanged.InvokeAsync() : Task.CompletedTask; private async Task OnGenerateClick(EkaerHistory ekaerHistory) { if (!_generatingIds.Add(ekaerHistory.Id)) return; try { var updated = await FruitBankSignalRClient.GenerateEkaerXmlDocument(ekaerHistory.ForeignKey, ekaerHistory.IsOutgoing); if (updated == null) { _logger.Error($"GenerateEkaerXmlDocument null választ adott; ForeignKey: {ekaerHistory.ForeignKey}"); return; } // A sor frissítése helyben — a grid ugyanazt a példányt mutatja. ekaerHistory.Status = updated.Status; ekaerHistory.XmlDoc = updated.XmlDoc; ekaerHistory.ConversionRate = updated.ConversionRate; ekaerHistory.ErrorText = updated.ErrorText; ekaerHistory.EkaerNumber = updated.EkaerNumber; ekaerHistory.SentDate = updated.SentDate; ekaerHistory.Modified = updated.Modified; } catch (Exception ex) { _logger.Error($"GenerateEkaerXmlDocument hiba; ForeignKey: {ekaerHistory.ForeignKey}", ex); } finally { _generatingIds.Remove(ekaerHistory.Id); Grid?.Reload(); await NotifyDataChanged(); } } DateTime CreateFromDate { get; set; } = DateTime.Today.AddDays(-3); private bool _creatingMissing; private async Task OnCreateMissingClick() { if (_creatingMissing) return; _creatingMissing = true; try { var createdCount = await FruitBankSignalRClient.CreateMissingEkaerHistories(CreateFromDate); _logger.Info($"CreateMissingEkaerHistories; created: {createdCount}; fromDate: {CreateFromDate:yyyy.MM.dd}"); if (createdCount > 0) await ReloadDataFromDb(true); } catch (Exception ex) { _logger.Error($"CreateMissingEkaerHistories hiba; fromDate: {CreateFromDate:yyyy.MM.dd}", ex); } finally { _creatingMissing = false; } } private async Task OnCopyClick(EkaerHistory ekaerHistory) { if (!CanCopy(ekaerHistory)) return; try { await JS.InvokeVoidAsync("navigator.clipboard.writeText", ekaerHistory.XmlDoc); } catch (Exception ex) { _logger.Error($"XmlDoc vágólapra másolása sikertelen; EkaerHistory.Id: {ekaerHistory.Id}", ex); } } }