Compare commits
2 Commits
1d56eba8ec
...
fdde2b3c6b
| Author | SHA1 | Date |
|---|---|---|
|
|
fdde2b3c6b | |
|
|
c71bf2fcd8 |
|
|
@ -275,7 +275,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
public async Task<List<EkaerHistory>> GetEkaerHistories(EkaerHistoryFilter ekaerHistoryFilter)
|
||||
{
|
||||
_logger.Detail($"GetEkaerHistories invoked; ekaerHistoryFilter: {ekaerHistoryFilter}");
|
||||
return await ctx.EkaerHistories.GetByFilter(ekaerHistoryFilter).ToListAsync();
|
||||
return await ctx.EkaerHistories.GetByFilter(ekaerHistoryFilter, true).ToListAsync();
|
||||
}
|
||||
|
||||
[SignalR(SignalRTags.GetEkaerHistoryCount)]
|
||||
|
|
@ -289,14 +289,19 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
public async Task<EkaerHistory> GetEkaerHistoryById(int id)
|
||||
{
|
||||
_logger.Detail($"GetEkaerHistoryById invoked; id: {id}");
|
||||
return await ctx.EkaerHistories.GetByIdAsync(id);
|
||||
return await ctx.EkaerHistories.GetByIdAsync(id, true);
|
||||
}
|
||||
|
||||
[SignalR(SignalRTags.GetEkaerHistoriesByForeignKey)]
|
||||
public async Task<List<EkaerHistory>> GetEkaerHistoriesByForeignKey(int foreignKey)
|
||||
{
|
||||
_logger.Detail($"GetEkaerHistoriesByForeignKey invoked; foreignKey: {foreignKey}");
|
||||
return await ctx.EkaerHistories.GetByForeignKey(foreignKey).ToListAsync();
|
||||
|
||||
// A forrás-rekordot (ShippingDocument/Order Id) lefedő EKÁER-deklaráció(k), a Mappings-szel betöltve — EGY query
|
||||
// (EXISTS a mapping-táblára), nem kettő.
|
||||
return await ctx.EkaerHistories.GetAll(true)
|
||||
.Where(eh => ctx.EkaerHistoryMappings.Table.Any(m => m.EkaerHistoryId == eh.Id && m.ForeignKey == foreignKey))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
[SignalR(SignalRTags.AddEkaerHistory)]
|
||||
|
|
@ -307,7 +312,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
_logger.Detail($"AddEkaerHistory invoked; id: {ekaerHistory.Id}");
|
||||
|
||||
await ctx.EkaerHistories.InsertAsync(ekaerHistory);
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id);
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id, true);
|
||||
}
|
||||
|
||||
[SignalR(SignalRTags.UpdateEkaerHistory)]
|
||||
|
|
@ -318,64 +323,82 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
_logger.Detail($"UpdateEkaerHistory invoked; id: {ekaerHistory.Id}");
|
||||
|
||||
await ctx.EkaerHistories.UpdateAsync(ekaerHistory);
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id);
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id, true);
|
||||
}
|
||||
|
||||
[SignalR(SignalRTags.GenerateEkaerXmlDocument)]
|
||||
public async Task<EkaerHistory> GenerateEkaerXmlDocument(int foreignKey, bool isOutgoing)
|
||||
public async Task<EkaerHistory> GenerateEkaerXmlDocument(int ekaerHistoryId)
|
||||
{
|
||||
_logger.Detail($"GenerateEkaerXmlDocument invoked; foreignKey: {foreignKey}; isOutgoing: {isOutgoing}");
|
||||
_logger.Detail($"GenerateEkaerXmlDocument invoked; ekaerHistoryId: {ekaerHistoryId}");
|
||||
|
||||
// Upsert: (ForeignKey + IsOutgoing) párra EGY rekord — az újragenerálás nem duplikál.
|
||||
var ekaerHistory = await ctx.EkaerHistories.GetByForeignKey(foreignKey).FirstOrDefaultAsync(eh => eh.IsOutgoing == isOutgoing);
|
||||
var ekaerHistory = await ctx.EkaerHistories.GetByIdAsync(ekaerHistoryId, true)
|
||||
?? throw new ArgumentException($"EkaerHistory not found; id: {ekaerHistoryId}", nameof(ekaerHistoryId));
|
||||
|
||||
if (!isOutgoing)
|
||||
// A deklarációhoz tartozó forrás-rekordok a BETÖLTÖTT mappingből (bejövő: N szállítólevél; kimenő: 1 rendelés).
|
||||
var foreignKeys = ekaerHistory.Mappings?.Select(m => m.ForeignKey).ToList() ?? [];
|
||||
if (foreignKeys.Count == 0) throw new InvalidOperationException($"EkaerHistory #{ekaerHistoryId} has no mapped source records.");
|
||||
|
||||
if (!ekaerHistory.IsOutgoing)
|
||||
{
|
||||
// Bejövő: a GetAll(true) a mapperhez kellő teljes gráfot tölti (Partner, Items+ProductDto, Shipping→járművek).
|
||||
var shippingDocument = await ctx.ShippingDocuments.GetByIdAsync(foreignKey, true)
|
||||
?? throw new ArgumentException($"ShippingDocument not found; id: {foreignKey}", nameof(foreignKey));
|
||||
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(shippingDocument, ekaerHistory);
|
||||
// Bejövő: a csoport ÖSSZES szállítólevele EGY tradeCard-dá (összevont tömeg/érték); GetAll(true) = teljes gráf.
|
||||
var documents = await ctx.ShippingDocuments.GetAll(true).Where(sd => foreignKeys.Contains(sd.Id)).ToListAsync();
|
||||
if (documents.Count == 0) throw new InvalidOperationException($"EkaerHistory #{ekaerHistoryId}: no ShippingDocuments found for the mapped ids.");
|
||||
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(documents, ekaerHistory);
|
||||
|
||||
// Összegző (ShippingDate + Partner) frissítése — a csoporton belül invariáns, így bármelyik dok jó.
|
||||
fruitBankEkaerService.SetSummary(ekaerHistory, documents[0].ShippingDate, documents[0].Partner?.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Kimenő: a GetByIdAsync(true) betölti a Customer-t, tételeket, palettákat (GrossWeight) és a ProductDto-t.
|
||||
var order = await ctx.OrderDtos.GetByIdAsync(foreignKey, true)
|
||||
?? throw new ArgumentException($"Order not found; id: {foreignKey}", nameof(foreignKey));
|
||||
// Kimenő: egy rendelés (a GetByIdAsync(true) tölti a Customer-t, tételeket, palettákat (GrossWeight), ProductDto-t).
|
||||
var order = await ctx.OrderDtos.GetByIdAsync(foreignKeys[0], true)
|
||||
?? throw new ArgumentException($"Order not found; id: {foreignKeys[0]}");
|
||||
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(order, ekaerHistory);
|
||||
|
||||
fruitBankEkaerService.SetSummary(ekaerHistory, order.DateOfReceipt, order.Customer?.Company);
|
||||
}
|
||||
|
||||
if (ekaerHistory.Id > 0) await ctx.EkaerHistories.UpdateAsync(ekaerHistory);
|
||||
else await ctx.EkaerHistories.InsertAsync(ekaerHistory);
|
||||
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id) ?? ekaerHistory;
|
||||
await ctx.EkaerHistories.UpdateAsync(ekaerHistory);
|
||||
// A friss sort adjuk vissza (frissített Modified + a betöltött Mappings); a sor biztosan létezik (épp updateltük).
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Idempotens rekord-létrehozás: ha a (foreignKey, isOutgoing) párra már van EkaerHistory, azt adja vissza
|
||||
/// érintetlenül; különben új Pending rekordot hoz létre. A generálás (XmlDoc) külön lépés: GenerateEkaerXmlDocument.
|
||||
/// Idempotens rekord-létrehozás: ha a forrás-rekordra (foreignKey + isOutgoing) már van mapping → a hozzá tartozó
|
||||
/// EkaerHistory-t adja vissza érintetlenül; különben új Pending rekordot + EGY mapping sort hoz létre (atomikusan).
|
||||
/// A generálás (XmlDoc) külön lépés: GenerateEkaerXmlDocument(ekaerHistoryId).
|
||||
/// </summary>
|
||||
[SignalR(SignalRTags.CreateEkaerHistory)]
|
||||
public async Task<EkaerHistory> CreateEkaerHistory(int foreignKey, bool isOutgoing)
|
||||
{
|
||||
_logger.Detail($"CreateEkaerHistory invoked; foreignKey: {foreignKey}; isOutgoing: {isOutgoing}");
|
||||
|
||||
var existing = await ctx.EkaerHistories.GetByForeignKey(foreignKey).FirstOrDefaultAsync(eh => eh.IsOutgoing == isOutgoing);
|
||||
// Idempotencia: a forrás-rekordra (azonos irányban) már létező mapping → a hozzá tartozó EkaerHistory,
|
||||
// a Mappings-szel betöltve (GetAll(true)) — EGY query (EXISTS a mappingre), nincs külön újraolvasás.
|
||||
var existing = await LinqToDB.AsyncExtensions.FirstOrDefaultAsync(
|
||||
ctx.EkaerHistories.GetAll(true).Where(eh => eh.IsOutgoing == isOutgoing
|
||||
&& ctx.EkaerHistoryMappings.Table.Any(m => m.EkaerHistoryId == eh.Id && m.ForeignKey == foreignKey)));
|
||||
|
||||
if (existing != null) return existing;
|
||||
|
||||
// A forrás-entitás léte irányfüggő: bejövő → ShippingDocument, kimenő → Order.
|
||||
// A forrás-entitás létét ellenőrizzük és betöltjük (relations) az összegző mezőkhöz (ShippingDate + Partner).
|
||||
var ekaerHistory = new EkaerHistory { IsOutgoing = isOutgoing, Status = EkaerStatus.Pending };
|
||||
if (!isOutgoing)
|
||||
{
|
||||
_ = await ctx.ShippingDocuments.GetByIdAsync(foreignKey, false) ?? throw new ArgumentException($"ShippingDocument not found; id: {foreignKey}", nameof(foreignKey));
|
||||
var doc = await ctx.ShippingDocuments.GetByIdAsync(foreignKey, true) ?? throw new ArgumentException($"ShippingDocument not found; id: {foreignKey}", nameof(foreignKey));
|
||||
fruitBankEkaerService.SetSummary(ekaerHistory, doc.ShippingDate, doc.Partner?.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = await ctx.OrderDtos.GetByIdAsync(foreignKey, false) ?? throw new ArgumentException($"Order not found; id: {foreignKey}", nameof(foreignKey));
|
||||
var order = await ctx.OrderDtos.GetByIdAsync(foreignKey, true) ?? throw new ArgumentException($"Order not found; id: {foreignKey}", nameof(foreignKey));
|
||||
fruitBankEkaerService.SetSummary(ekaerHistory, order.DateOfReceipt, order.Customer?.Company);
|
||||
}
|
||||
|
||||
var ekaerHistory = new EkaerHistory { ForeignKey = foreignKey, IsOutgoing = isOutgoing, Status = EkaerStatus.Pending };
|
||||
await ctx.EkaerHistories.InsertAsync(ekaerHistory);
|
||||
// EGY EkaerHistory + EGY mapping sor (egyelemű csoport), atomikusan.
|
||||
ekaerHistory = await ctx.AddEkaerHistoryWithMappingsAsync(ekaerHistory, [foreignKey]);
|
||||
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id) ?? ekaerHistory;
|
||||
// A friss sort adjuk vissza, a betöltött Mappings-szel; a sor biztosan létezik (épp beszúrtuk).
|
||||
return await ctx.EkaerHistories.GetByIdAsync(ekaerHistory.Id, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -388,21 +411,21 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
{
|
||||
_logger.Detail($"CreateMissingEkaerHistories invoked; fromDate: {fromDate:yyyy-MM-dd}");
|
||||
|
||||
var toCreate = new List<EkaerHistory>();
|
||||
var toCreate = new List<(EkaerHistory History, List<int> ForeignKeys)>();
|
||||
var messages = new HashSet<string>(); // dedup: ugyanaz a partner (pl. hiányzó országkód) több csoportban is előjöhet
|
||||
|
||||
// ── Bejövő ── Rekord nélküli szállítólevelek a dátumtól, Partnerrel + tételekkel betöltve a kötelezettség-kapuhoz.
|
||||
// A bejelentés-egység (Shipping, Partner) = feladó→címzett→jármű: a küszöb a csoport ÖSSZES dokumentumára aggregál.
|
||||
// A bejelentés-egység (Shipping, Partner, PartnerDepot) = feladó(+telephely)→címzett→jármű: EGY EkaerHistory/csoport, a küszöb a csoport ÖSSZES dokumentumára aggregál.
|
||||
var candidates = await ctx.ShippingDocuments.GetAll()
|
||||
.Where(sd => sd.ShippingDate >= fromDate)
|
||||
.Where(sd => !ctx.EkaerHistories.Table.Any(eh => eh.ForeignKey == sd.Id && !eh.IsOutgoing))
|
||||
.Where(sd => !ctx.EkaerHistoryMappings.Table.Any(m => m.ForeignKey == sd.Id && ctx.EkaerHistories.Table.Any(eh => eh.Id == m.EkaerHistoryId && !eh.IsOutgoing)))
|
||||
.LoadWith(sd => sd.Partner)
|
||||
.LoadWith(sd => sd.ShippingItems)
|
||||
.ToListAsync();
|
||||
|
||||
// Partner nélküli (még nincs rendesen felvett) szállítólevél CSENDBEN kihagyva — nincs feladó, nem kész;
|
||||
// a mentes partner (IsEkaer=false) is. A maradék (Shipping, Partner) csoportban (a dokumentum mindig kap Shippinget).
|
||||
foreach (var group in candidates.Where(sd => sd.Partner != null && sd.Partner.IsEkaer != false).GroupBy(sd => (sd.ShippingId, sd.PartnerId)))
|
||||
// a mentes partner (IsEkaer=false) is. A maradék (Shipping, Partner, PartnerDepot) csoportban (a dokumentum mindig kap Shippinget).
|
||||
foreach (var group in candidates.Where(sd => sd.Partner != null && sd.Partner.IsEkaer != false).GroupBy(sd => (sd.ShippingId, sd.PartnerId, sd.PartnerDepotId)))
|
||||
{
|
||||
var docs = group.ToList();
|
||||
try
|
||||
|
|
@ -411,14 +434,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
if (result.Obligation == EkaerObligation.DataError) { messages.UnionWith(result.Errors); continue; }
|
||||
if (result.Obligation == EkaerObligation.NotRequired) continue;
|
||||
|
||||
// Kötelező → a csoport minden dokumentumára egy-egy Pending sor (a rekord-szerkezet per-dokumentum marad).
|
||||
foreach (var doc in docs)
|
||||
toCreate.Add(new EkaerHistory { ForeignKey = doc.Id, IsOutgoing = false, StatusId = (int)EkaerStatus.Pending });
|
||||
// Kötelező → EGY EkaerHistory a csoportra (összegző: ShippingDate + Partner — invariáns) + a dokumentumok mapping-foreignKey-ként.
|
||||
var history = new EkaerHistory { IsOutgoing = false, StatusId = (int)EkaerStatus.Pending };
|
||||
fruitBankEkaerService.SetSummary(history, docs[0].ShippingDate, docs[0].Partner?.Name);
|
||||
toCreate.Add((history, docs.Select(d => d.Id).ToList()));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"CreateMissingEkaerHistories; inbound evaluate failed; ShippingId: {group.Key.ShippingId}; PartnerId: {group.Key.PartnerId}", ex);
|
||||
messages.Add($"Szállítólevél-csoport (Shipping #{group.Key.ShippingId}, Partner #{group.Key.PartnerId}): a kötelezettség nem dönthető el — {ex.Message}");
|
||||
_logger.Error($"CreateMissingEkaerHistories; inbound evaluate failed; ShippingId: {group.Key.ShippingId}; PartnerId: {group.Key.PartnerId}; PartnerDepotId: {group.Key.PartnerDepotId}", ex);
|
||||
messages.Add($"Szállítólevél-csoport (Shipping #{group.Key.ShippingId}, Partner #{group.Key.PartnerId}, Telephely #{group.Key.PartnerDepotId?.ToString() ?? "—"}): a kötelezettség nem dönthető el — {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +450,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
// Előbb csak az azonosítók (könnyű szűrés), majd EGY batch-betöltés a teljes gráffal (GetAllByIds) — nincs N+1.
|
||||
var missingOrderIds = await ctx.OrderDtos.GetAllByOrderStatus(OrderStatus.Complete, false)
|
||||
.Where(o => o.GenericAttributes.Any(ga => ga.Key == nameof(OrderDto.DateOfReceipt) && DateTime.Parse(ga.Value) >= fromDate.Date))
|
||||
.Where(o => !ctx.EkaerHistories.Table.Any(eh => eh.ForeignKey == o.Id && eh.IsOutgoing))
|
||||
.Where(o => !ctx.EkaerHistoryMappings.Table.Any(m => m.ForeignKey == o.Id && ctx.EkaerHistories.Table.Any(eh => eh.Id == m.EkaerHistoryId && eh.IsOutgoing)))
|
||||
.Select(o => o.Id)
|
||||
.ToListAsync();
|
||||
|
||||
|
|
@ -440,7 +464,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
if (result.Obligation == EkaerObligation.DataError) { messages.UnionWith(result.Errors); continue; }
|
||||
if (result.Obligation == EkaerObligation.NotRequired) continue;
|
||||
|
||||
toCreate.Add(new EkaerHistory { ForeignKey = order.Id, IsOutgoing = true, StatusId = (int)EkaerStatus.Pending });
|
||||
var history = new EkaerHistory { IsOutgoing = true, StatusId = (int)EkaerStatus.Pending };
|
||||
fruitBankEkaerService.SetSummary(history, order.DateOfReceipt, order.Customer?.Company);
|
||||
toCreate.Add((history, [order.Id]));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -451,16 +477,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
|
|||
|
||||
// ── Beszúrás ──
|
||||
var createdCount = 0;
|
||||
foreach (var ekaerHistory in toCreate)
|
||||
foreach (var (history, foreignKeys) in toCreate)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ctx.EkaerHistories.InsertAsync(ekaerHistory);
|
||||
// EkaerHistory + a forrás-mapping sorok atomikusan (a junction tartja a kapcsolatot, nem a rendszer-tábla).
|
||||
await ctx.AddEkaerHistoryWithMappingsAsync(history, foreignKeys);
|
||||
createdCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error($"CreateMissingEkaerHistories; insert failed; ForeignKey: {ekaerHistory.ForeignKey}; IsOutgoing: {ekaerHistory.IsOutgoing}", ex);
|
||||
_logger.Error($"CreateMissingEkaerHistories; insert failed; IsOutgoing: {history.IsOutgoing}; foreignKeys: [{string.Join(",", foreignKeys)}]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,16 @@ public class EkaerHistoryDbTable : MgDbTableBase<EkaerHistory>
|
|||
{
|
||||
}
|
||||
|
||||
// History tábla: legújabb elöl. Nincs asszociáció → nincs LoadWith / loadRelations.
|
||||
// History tábla: legújabb elöl.
|
||||
public override IOrderedQueryable<EkaerHistory> GetAll() => base.GetAll().OrderByDescending(p => p.Id);
|
||||
|
||||
public Task<EkaerHistory> GetByIdAsync(int id) => GetAll().FirstOrDefaultAsync(p => p.Id == id);
|
||||
public IQueryable<EkaerHistory> GetByForeignKey(int foreignKey) => GetAll().Where(p => p.ForeignKey == foreignKey);
|
||||
// loadRelations → a deklarációt lefedő forrás-mapping sorok (Mappings) is töltődnek (a kliensnek kellenek).
|
||||
public IQueryable<EkaerHistory> GetAll(bool loadRelations)
|
||||
=> loadRelations ? GetAll().LoadWith(eh => eh.Mappings) : GetAll();
|
||||
|
||||
// FirstOrDefaultAsync kétértelmű (System.Linq.Async + LinqToDB) az IQueryable-ön (a GetAll(loadRelations) LoadWith
|
||||
// miatt már nem IOrderedQueryable) → explicit a LinqToDB-é, ugyanúgy mint a CountByFilterAsync-nél.
|
||||
public Task<EkaerHistory> GetByIdAsync(int id, bool loadRelations) => LinqToDB.AsyncExtensions.FirstOrDefaultAsync(GetAll(loadRelations), p => p.Id == id);
|
||||
|
||||
// A NAV-nál lévő partíciók fix StatusId-k; minden más (a jövőbeni új státuszok is) „beküldésre váró".
|
||||
private static readonly int[] ToSubmitStatusIds = [.. Enum.GetValues<EkaerStatus>().Select(s => (int)s).Except([(int)EkaerStatus.Sent, (int)EkaerStatus.SentWithMissingData])];
|
||||
|
|
@ -27,20 +32,20 @@ public class EkaerHistoryDbTable : MgDbTableBase<EkaerHistory>
|
|||
/// <summary>Flag-alapú szűrés a tabokhoz/count-hoz. <see cref="EkaerHistoryFilter.All"/> (= 0) → minden;
|
||||
/// egyébként a beállított flag-ek diszjunkt StatusId-partícióinak UNIÓJA (IN). A státusz-helperek (IsSent stb.)
|
||||
/// nem fordulnak SQL-re, ezért közvetlen StatusId-halmazzal szűrünk.</summary>
|
||||
public IQueryable<EkaerHistory> GetByFilter(EkaerHistoryFilter filter)
|
||||
public IQueryable<EkaerHistory> GetByFilter(EkaerHistoryFilter filter, bool loadRelations)
|
||||
{
|
||||
// HasFlag(All) mindig true (0 bit), ezért előbb az All-ágat zárjuk rövidre.
|
||||
if (filter == EkaerHistoryFilter.All) return GetAll();
|
||||
if (filter == EkaerHistoryFilter.All) return GetAll(loadRelations);
|
||||
|
||||
var statusIds = new List<int>();
|
||||
if (filter.HasFlag(EkaerHistoryFilter.ToSubmit)) statusIds.AddRange(ToSubmitStatusIds);
|
||||
if (filter.HasFlag(EkaerHistoryFilter.Sent)) statusIds.Add((int)EkaerStatus.Sent);
|
||||
if (filter.HasFlag(EkaerHistoryFilter.NeedsCompletion)) statusIds.Add((int)EkaerStatus.SentWithMissingData);
|
||||
|
||||
return GetAll().Where(eh => statusIds.Contains(eh.StatusId));
|
||||
return GetAll(loadRelations).Where(eh => statusIds.Contains(eh.StatusId));
|
||||
}
|
||||
|
||||
// A CountAsync kétértelmű (a System.Linq.Async ÉS a LinqToDB is hozza ugyanazt a nevet, no-arg hívásnál mindkettő illik)
|
||||
// → explicit a LinqToDB-é. (A ToListAsync nem ütközik, azt a hívó közvetlenül használhatja.)
|
||||
public Task<int> CountByFilterAsync(EkaerHistoryFilter filter) => LinqToDB.AsyncExtensions.CountAsync(GetByFilter(filter));
|
||||
public Task<int> CountByFilterAsync(EkaerHistoryFilter filter) => LinqToDB.AsyncExtensions.CountAsync(GetByFilter(filter, false));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
using FruitBank.Common.Entities;
|
||||
using LinqToDB;
|
||||
using Mango.Nop.Data.Repositories;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
|
||||
public class EkaerHistoryMappingDbTable : MgDbTableBase<EkaerHistoryMapping>
|
||||
{
|
||||
public EkaerHistoryMappingDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings)
|
||||
: base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings)
|
||||
{
|
||||
}
|
||||
|
||||
// loadRelations → a szülő EkaerHistory is töltődik (a junction → deklaráció navigáció).
|
||||
public IQueryable<EkaerHistoryMapping> GetAll(bool loadRelations) => loadRelations ? base.GetAll().LoadWith(m => m.EkaerHistory) : base.GetAll();
|
||||
|
||||
// Egy EKÁER-deklaráció forrás-rekordjai (bejövő: N ShippingDocument; kimenő: 1 Order).
|
||||
public IQueryable<EkaerHistoryMapping> GetByEkaerHistoryId(int ekaerHistoryId, bool loadRelations) => GetAll(loadRelations).Where(m => m.EkaerHistoryId == ekaerHistoryId);
|
||||
|
||||
// Egy forrás-rekordhoz (ShippingDocument/Order Id) tartozó mapping sor(ok) — a dedup-hoz / a sorhoz tartozó EKÁER kereséséhez.
|
||||
public IQueryable<EkaerHistoryMapping> GetByForeignKey(int foreignKey, bool loadRelations) => GetAll(loadRelations).Where(m => m.ForeignKey == foreignKey);
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
public PartnerDbTable Partners { get; set; }
|
||||
public PartnerDepotDbTable PartnerDepots { get; set; }
|
||||
public EkaerHistoryDbTable EkaerHistories { get; set; }
|
||||
public EkaerHistoryMappingDbTable EkaerHistoryMappings { get; set; }
|
||||
public CargoPartnerDbTable CargoPartners { get; set; }
|
||||
public CargoTruckDbTable CargoTrucks{ get; set; }
|
||||
|
||||
|
|
@ -83,7 +84,7 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
public IRepository<StockQuantityHistoryExt> StockQuantityHistoriesExt { get; set; }
|
||||
|
||||
public FruitBankDbContext(INopDataProvider dataProvider, ILockService lockService, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext,
|
||||
CargoPartnerDbTable cargoPartnerDbTable, CargoTruckDbTable cargoTruckDbTable, PartnerDbTable partnerDbTable, PartnerDepotDbTable partnerDepotDbTable, EkaerHistoryDbTable ekaerHistoryDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, ShippingItemDbTable shippingItemDbTable,
|
||||
CargoPartnerDbTable cargoPartnerDbTable, CargoTruckDbTable cargoTruckDbTable, PartnerDbTable partnerDbTable, PartnerDepotDbTable partnerDepotDbTable, EkaerHistoryDbTable ekaerHistoryDbTable, EkaerHistoryMappingDbTable ekaerHistoryMappingDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, ShippingItemDbTable shippingItemDbTable,
|
||||
ShippingItemPalletDbTable shippingItemPalletDbTable, FilesDbTable filesDbTable, ShippingDocumentToFilesDbTable shippingDocumentToFilesDbTable,
|
||||
ProductDtoDbTable productDtoDbTable, OrderDtoDbTable orderDtoDbTable, OrderItemDtoDbTable orderItemDtoDbTable, OrderItemPalletDbTable orderItemPalletDbTable,
|
||||
StockQuantityHistoryDtoDbTable stockQuantityHistoryDtos, CustomerCreditDbTable customerCreditDbTable,
|
||||
|
|
@ -112,6 +113,7 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
Partners = partnerDbTable;
|
||||
PartnerDepots = partnerDepotDbTable;
|
||||
EkaerHistories = ekaerHistoryDbTable;
|
||||
EkaerHistoryMappings = ekaerHistoryMappingDbTable;
|
||||
CargoPartners = cargoPartnerDbTable;
|
||||
CargoTrucks = cargoTruckDbTable;
|
||||
|
||||
|
|
@ -767,6 +769,25 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
return list;
|
||||
}
|
||||
|
||||
/// <summary>EKÁER-deklaráció (EkaerHistory) + a hozzá tartozó forrás-mapping sorok ATOMIKUS létrehozása.
|
||||
/// A mapping tartja a kapcsolatot a forrás-rekordokhoz (bejövő: N ShippingDocument egy (Shipping, Partner,
|
||||
/// PartnerDepot) csoportból; kimenő: 1 Order) — így a deklaráció a rendszer-tábláktól FÜGGETLENÜL törölhető
|
||||
/// és újragenerálható. Az InsertAsync az identity Id-t visszaírja, így a mapping sorok már a friss Id-ra mutatnak.</summary>
|
||||
public async Task<EkaerHistory> AddEkaerHistoryWithMappingsAsync(EkaerHistory ekaerHistory, IEnumerable<int> foreignKeys)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(ekaerHistory);
|
||||
|
||||
await TransactionSafeAsync(async _ =>
|
||||
{
|
||||
await EkaerHistories.InsertAsync(ekaerHistory);
|
||||
foreach (var foreignKey in foreignKeys)
|
||||
await EkaerHistoryMappings.InsertAsync(new EkaerHistoryMapping { EkaerHistoryId = ekaerHistory.Id, ForeignKey = foreignKey });
|
||||
return true;
|
||||
});
|
||||
|
||||
return ekaerHistory;
|
||||
}
|
||||
|
||||
public async Task<CustomerAddressMapping?> AddCustomerAddressMappingAsync(int customerId, int addressId)
|
||||
{
|
||||
var customerAddressMapping = new CustomerAddressMapping
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ public class PluginNopStartup : INopStartup
|
|||
services.AddScoped<PartnerDbTable>();
|
||||
services.AddScoped<PartnerDepotDbTable>();
|
||||
services.AddScoped<EkaerHistoryDbTable>();
|
||||
services.AddScoped<EkaerHistoryMappingDbTable>();
|
||||
services.AddScoped<CargoPartnerDbTable>();
|
||||
services.AddScoped<CargoTruckDbTable>();
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ public partial class NameCompatibility : INameCompatibility
|
|||
|
||||
{ typeof(PartnerDepot), FruitBankConstClient.PartnerDepotDbTableName},
|
||||
{ typeof(EkaerHistory), FruitBankConstClient.EkaerHistoryDbTableName},
|
||||
{ typeof(EkaerHistoryMapping), FruitBankConstClient.EkaerHistoryMappingDbTableName},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -79,3 +79,20 @@ A NAV felé **egy VTSZ-re egy `tradeCardItem`** kell — több azonos-VTSZ-ű te
|
|||
- `FruitBank.Common.Server/Services/Ekaer/{IFruitBankEkaerService,FruitBankEkaerService}.cs` — a `vtszNames` átadása a generate-en
|
||||
- `FruitBankDataController.GenerateEkaerXmlDocument` (plugin) — `(productId, categoryId)` tuple + batch `Category`-load → `Dictionary<vtsz, name>`
|
||||
- nopCommerce `Category` (MetaKeywords = VTSZ, Name = productName) — adat/üzemeltetés
|
||||
|
||||
## MGFBANKPLUG-EKAER-T-D8R4: Kapu-csoportosítás (Shipping, PartnerDepot)-re; a PartnerDepotId kötelező
|
||||
|
||||
**Status:** Open · **Priority:** P2 · **Type:** T (kapu) · **2026-06**
|
||||
|
||||
A `CreateMissingEkaerHistories` bejövő kapuja jelenleg `(ShippingId, PartnerId, PartnerDepotId)`-re csoportosít. Mivel a **`PartnerDepot` egyértelműen egy `Partner`-hez tartozik** (a depó implikálja a feladót), a `PartnerId` a kulcsban **redundáns** → elég a `(ShippingId, PartnerDepotId)` csoportosítás.
|
||||
|
||||
**Cél:**
|
||||
- A csoportosítási kulcs `(ShippingId, PartnerDepotId)` (a `PartnerId` kivehető).
|
||||
- A **`PartnerDepotId` kötelező** az EKÁER-hez: ha egy szállítólevélen **nincs** `PartnerDepotId` → **hiba**, és **NE generálódjon** rá deklaráció. A kapu a hiányzó-telephelyű dokumentumokat kihagyja + üzenettel jelzi (mint a `DataError`-nál); a generate `ValidationError`-t ad (nem küldhető), nem üres/téves tradeCard-ot.
|
||||
|
||||
**Megj.:** szigorítás a jelenlegihez képest, ahol a `null` `PartnerDepotId` még külön (null-telephelyű) csoportként kezelődik. Bevezetés előtt a meglévő szállítólevelek `PartnerDepotId`-ját pótolni kell, különben a kapu kihagyja őket.
|
||||
|
||||
**Affected:**
|
||||
- `FruitBankDataController.CreateMissingEkaerHistories` (plugin) — a `GroupBy` kulcs `(ShippingId, PartnerDepotId)`-re + a hiányzó-`PartnerDepotId` hiba-ág (kihagyás + üzenet)
|
||||
- `FruitBankDataController.GenerateEkaerXmlDocument` / `FruitBankEkaerService` — hiányzó telephelynél `ValidationError`, nincs generálás
|
||||
- `ShippingToEkaerMapper` / `EkaerReportability` (`FruitBank.Common/Services/Ekaer/`) — ha a feladó-cím a `PartnerDepot`-ból jön, a hiányzó telephely validációs hiba
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue