Add PartnerDepot to ShippingDocument, EKÁER grid updates

- Introduced PartnerDepotId and PartnerDepot navigation to ShippingDocument and IShippingDocument, loaded only for EKÁER scenarios.
- Updated ShippingToEkaerMapper to leave CarrierName empty for outgoing shipments.
- Enhanced GridShippingDocument.razor: added PartnerDepot column with edit/display templates, improved Partner selection with cascade depot logic, and optimized partner lookup.
- Fixed Ekaer tab and count logic to use NeedsCompletion filter.
- Minor code and comment cleanups.
This commit is contained in:
Loretta 2026-06-16 12:02:24 +02:00
parent 47fd53cc4e
commit 2221d4a68e
7 changed files with 95 additions and 27 deletions

View File

@ -73,7 +73,8 @@
"WebFetch(domain:www.ekaer-feladas.hu)", "WebFetch(domain:www.ekaer-feladas.hu)",
"WebFetch(domain:net.jogtar.hu)", "WebFetch(domain:net.jogtar.hu)",
"WebFetch(domain:www.itrack.hu)", "WebFetch(domain:www.itrack.hu)",
"WebFetch(domain:docplayer.hu)" "WebFetch(domain:docplayer.hu)",
"WebFetch(domain:supportcenter.devexpress.com)"
] ]
} }
} }

View File

@ -15,6 +15,7 @@ public sealed class ShippingDocument : MgEntityBase, IShippingDocument
{ {
public int PartnerId { get; set; } public int PartnerId { get; set; }
public int? ShippingId { get; set; } public int? ShippingId { get; set; }
public int? PartnerDepotId { get; set; }
public string DocumentIdNumber { get; set; } public string DocumentIdNumber { get; set; }
public string PdfFileName { get; set; } public string PdfFileName { get; set; }
@ -32,6 +33,12 @@ public sealed class ShippingDocument : MgEntityBase, IShippingDocument
[Association(ThisKey = nameof(PartnerId), OtherKey = nameof(Partner.Id), CanBeNull = true)] [Association(ThisKey = nameof(PartnerId), OtherKey = nameof(Partner.Id), CanBeNull = true)]
public Partner? Partner { get; set; } public Partner? Partner { get; set; }
// Csak az EKÁER felrakodási címhez kell. SZÁNDÉKOSAN nincs benne semelyik alapértelmezett loadRelations-ben:
// kizárólag a generálás/reconciliation kéri explicit `LoadWith(sd => sd.PartnerDepot)`-tal, így a többi
// lekérdezést/sorosítást nem terheli (betöltetlenül null marad). Legacy szállítólevélnél PartnerDepotId = null.
[Association(ThisKey = nameof(PartnerDepotId), OtherKey = nameof(PartnerDepot.Id), CanBeNull = true)]
public PartnerDepot? PartnerDepot { get; set; }
[Association(ThisKey = nameof(Id), OtherKey = nameof(ShippingItem.ShippingDocumentId), CanBeNull = true)] [Association(ThisKey = nameof(Id), OtherKey = nameof(ShippingItem.ShippingDocumentId), CanBeNull = true)]
public List<ShippingItem>? ShippingItems { get; set; } public List<ShippingItem>? ShippingItems { get; set; }

View File

@ -9,6 +9,7 @@ public interface IShippingDocument: IEntityInt, ITimeStampInfo//, IMeasured
{ {
public int PartnerId { get; set; } public int PartnerId { get; set; }
public int? ShippingId { get; set; } public int? ShippingId { get; set; }
public int? PartnerDepotId { get; set; }
public string DocumentIdNumber { get; set; } public string DocumentIdNumber { get; set; }
public string PdfFileName { get; set; } public string PdfFileName { get; set; }

View File

@ -147,8 +147,9 @@ public sealed class ShippingToEkaerMapper : IShippingToEkaerMapper
LoadLocation = company.Site, LoadLocation = company.Site,
UnloadLocation = BuildCustomerLocation(customer), UnloadLocation = BuildCustomerLocation(customer),
Lines = lines, Lines = lines,
// Kimenőnél a VEVŐ veszi át / viszi el az árut → ő a fuvarozó. // Kimenőnél NEM töltjük a szállítmányozót: a vevő maga viszi el, és a vevő MÁR a címzett (destinationName) —
CarrierName = customer?.Company, // külön fuvarozót nem tartunk nyilván; a carrierText opcionális (a NAV nem követeli) → üresen hagyjuk.
CarrierName = null,
// A vonó jármű (rendszám) a customer-hez még nincs bekötve → üresen marad, a felrakodás megkezdéséig pótolandó // A vonó jármű (rendszám) a customer-hez még nincs bekötve → üresen marad, a felrakodás megkezdéséig pótolandó
// (a validátor warningolja). Amint bekötik, a Vehicle is innen jön. // (a validátor warningolja). Amint bekötik, a Vehicle is innen jön.
}; };

View File

@ -1,4 +1,4 @@
@using AyCode.Blazor.Components.Components.Grids @using AyCode.Blazor.Components.Components.Grids
@using AyCode.Utils.Extensions @using AyCode.Utils.Extensions
@using FruitBank.Common.Dtos @using FruitBank.Common.Dtos
@using FruitBank.Common.Entities @using FruitBank.Common.Entities
@ -26,24 +26,65 @@
<Columns> <Columns>
<MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true"/> <MgGridDataColumn FieldName="Id" SortIndex="0" SortOrder="GridColumnSortOrder.Descending" ReadOnly="true"/>
<DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner"> <DxGridDataColumn FieldName="PartnerId" Caption="Partner" Visible="@(!ParentDataItemIsPartner)" ReadOnly="@ParentDataItemIsPartner">
<EditSettings> <CellDisplayTemplate>
<DxComboBoxSettings Data="Partners" @{
ValueFieldName="Id" var sdPd = (ShippingDocument)context.DataItem;
TextFieldName="Name" <text>@(PartnersDictById.GetValueOrDefault(sdPd.PartnerId)?.Name)</text>
DropDownBodyCssClass="dd-body-class" }
ListRenderMode="ListRenderMode.Entire" </CellDisplayTemplate>
SearchMode="ListSearchMode.AutoSearch" <CellEditTemplate>
SearchFilterCondition="ListSearchFilterCondition.Contains" @{
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto"> var sdPe = (ShippingDocument)context.EditModel;
<Columns> }
<DxListEditorColumn FieldName="@nameof(Partner.Id)" /> <DxComboBox TData="Partner" TValue="int"
<DxListEditorColumn FieldName="@nameof(Partner.Name)" /> Data="@Partners"
<DxListEditorColumn FieldName="@nameof(Partner.TaxId)" /> Value="@sdPe.PartnerId"
</Columns> ValueChanged="@((int v) => OnPartnerChanged(sdPe, v))"
</DxComboBoxSettings> ValueExpression="@(() => sdPe.PartnerId)"
</EditSettings> ValueFieldName="@nameof(Partner.Id)"
TextFieldName="@nameof(Partner.Name)"
DropDownBodyCssClass="dd-body-class"
ListRenderMode="ListRenderMode.Entire"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(Partner.Id)" />
<DxListEditorColumn FieldName="@nameof(Partner.Name)" />
<DxListEditorColumn FieldName="@nameof(Partner.TaxId)" />
</Columns>
</DxComboBox>
</CellEditTemplate>
</DxGridDataColumn> </DxGridDataColumn>
<DxGridDataColumn FieldName="Shipping.ShippingDate" Caption="Beérkezés" ReadOnly="true" /> <DxGridDataColumn FieldName="PartnerDepotId" Caption="Telephely">
<CellDisplayTemplate>
@{
var sdD = (ShippingDocument)context.DataItem;
var depotD = PartnersDictById.GetValueOrDefault(sdD.PartnerId)?.PartnerDepots?.FirstOrDefault(d => d.Id == sdD.PartnerDepotId);
<div style="text-align:left">@depotD?.Name</div>
}
</CellDisplayTemplate>
<CellEditTemplate>
@{
var sdE = (ShippingDocument)context.EditModel;
var depotsE = PartnersDictById.GetValueOrDefault(sdE.PartnerId)?.PartnerDepots ?? new List<PartnerDepot>();
}
<DxComboBox TData="PartnerDepot" TValue="int?"
Data="@depotsE"
@bind-Value="sdE.PartnerDepotId"
ValueFieldName="@nameof(PartnerDepot.Id)"
TextFieldName="@nameof(PartnerDepot.Name)"
NullText="(telephely…)"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
<Columns>
<DxListEditorColumn FieldName="@nameof(PartnerDepot.Name)" Caption="Név" />
<DxListEditorColumn FieldName="@nameof(PartnerDepot.FullAddress)" Caption="Cím" />
</Columns>
</DxComboBox>
</CellEditTemplate>
</DxGridDataColumn>
<DxGridDataColumn FieldName="Shipping.ShippingDate" Caption="Beérkezés" ReadOnly="true" />
<DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping"> <DxGridDataColumn FieldName="ShippingId" Caption="Shipping" Visible="@(!ParentDataItemIsShipping)" ReadOnly="@ParentDataItemIsShipping">
<EditSettings> <EditSettings>
<DxComboBoxSettings Data="Shippings" <DxComboBoxSettings Data="Shippings"
@ -185,7 +226,10 @@
private LoggerClient<GridShippingDocument> _logger = null!; private LoggerClient<GridShippingDocument> _logger = null!;
private int _activeTabIndex; private int _activeTabIndex;
protected override async Task OnInitializedAsync() // Partner-lookup ID szerint a cella-templatekhez (a ReloadDataFromDb tölti) — renderenkénti lineáris keresés helyett.
private Dictionary<int, Partner> PartnersDictById = new();
protected override async Task OnInitializedAsync()
{ {
_logger = new LoggerClient<GridShippingDocument>(LogWriters.ToArray()); _logger = new LoggerClient<GridShippingDocument>(LogWriters.ToArray());
@ -223,9 +267,13 @@
{ {
if (Shippings == null && ParentDataItem is Shipping shippingParent) Shippings = [shippingParent]; if (Shippings == null && ParentDataItem is Shipping shippingParent) Shippings = [shippingParent];
if (Partners == null && ParentDataItem is Partner partnerParent) Partners = [partnerParent]; if (Partners == null && ParentDataItem is Partner partnerParent) Partners = [partnerParent];
return;
} }
// Partner-lookup ID szerint a cella-templatekhez (O(1) a renderenkénti FirstOrDefault helyett), a Partners véglegesülése után.
PartnersDictById = Partners?.ToDictionary(p => p.Id) ?? new();
if (!IsMasterGrid) return;
if (Grid == null) return; if (Grid == null) return;
using (await ObjectLock.GetSemaphore<ShippingDocument>().UseWaitAsync()) using (await ObjectLock.GetSemaphore<ShippingDocument>().UseWaitAsync())
@ -269,6 +317,16 @@
EditItemsEnabled = true; EditItemsEnabled = true;
} }
// Cascade: a Partner-combo ValueChanged-je. Beírjuk az új partnert; ha a partnernek PONTOSAN EGY telephelye van,
// arra auto-állunk, különben nullázzuk (a régi nem az új partneré); majd újrarenderelünk → a Telephely-combo frissül.
void OnPartnerChanged(ShippingDocument sd, int partnerId)
{
sd.PartnerId = partnerId;
var depots = PartnersDictById.GetValueOrDefault(partnerId)?.PartnerDepots;
sd.PartnerDepotId = depots?.Count == 1 ? depots[0].Id : null;
StateHasChanged();
}
protected async Task OnActiveTabChanged(int activeTabIndex) protected async Task OnActiveTabChanged(int activeTabIndex)
{ {
_activeTabIndex = activeTabIndex; _activeTabIndex = activeTabIndex;

View File

@ -20,7 +20,7 @@
<GridEkaerHistory @ref="gridEkaerHistoryPending" Filter="EkaerHistoryFilter.ToSubmit" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory> <GridEkaerHistory @ref="gridEkaerHistoryPending" Filter="EkaerHistoryFilter.ToSubmit" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory>
</DxTabPage> </DxTabPage>
<DxTabPage Text="@NeedsCompletionTabText"> <DxTabPage Text="@NeedsCompletionTabText">
<GridEkaerHistory @ref="gridEkaerHistoryNeedsCompletion" Filter="EkaerHistoryFilter.ToSubmit" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory> <GridEkaerHistory @ref="gridEkaerHistoryNeedsCompletion" Filter="EkaerHistoryFilter.NeedsCompletion" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory>
</DxTabPage> </DxTabPage>
<DxTabPage Text="Elküldött"> <DxTabPage Text="Elküldött">
<GridEkaerHistory @ref="gridEkaerHistorySent" Filter="EkaerHistoryFilter.Sent" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory> <GridEkaerHistory @ref="gridEkaerHistorySent" Filter="EkaerHistoryFilter.Sent" OnDataChanged="RefreshNeedsCompletionCountAsync" OnBusyChanged="OnGridBusyChanged"></GridEkaerHistory>

View File

@ -58,8 +58,8 @@ public partial class Ekaer : ComponentBase
{ {
try try
{ {
//_needsCompletionCount = await FruitBankSignalRClient.GetEkaerHistoryCount(EkaerHistoryFilter.NeedsCompletion); _needsCompletionCount = await FruitBankSignalRClient.GetEkaerHistoryCount(EkaerHistoryFilter.NeedsCompletion);
_needsCompletionCount = await FruitBankSignalRClient.GetEkaerHistoryCount(EkaerHistoryFilter.ToSubmit); //_needsCompletionCount = await FruitBankSignalRClient.GetEkaerHistoryCount(EkaerHistoryFilter.ToSubmit);
StateHasChanged(); StateHasChanged();
} }
catch (Exception ex) catch (Exception ex)