From 9f6aa1b903845343aa96865766bc43a0e3f49298 Mon Sep 17 00:00:00 2001 From: Loretta Date: Fri, 10 Jul 2026 15:37:05 +0200 Subject: [PATCH] =?UTF-8?q?EK=C3=81ER:=20depot,=20isEkaer,=20grid=20layout?= =?UTF-8?q?,=20doc=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switch to DEV DB and split company address street/number - CustomerCreditWidget: isEkaer now nullable, defaults to true - FruitBankDataController: add grid layout CRUD endpoints - EKÁER: require depot for inbound, batch isEkaer for outbound - Update docs: stricter foreign reporting, depot handling, isEkaer logic, mapping rules, and close depot-grouping issue --- .../CustomerCreditWidgetViewComponent.cs | 4 +- .../Controllers/FruitBankDataController.cs | 74 ++++++++++++- .../docs/EKAER/EKAER_ISSUES.md | 50 +++++++++ .../docs/EKAER/EKAER_TODO.md | 103 ++++++++++++++---- Nop.Plugin.Misc.AIPlugin/docs/EKAER/README.md | 12 +- 5 files changed, 218 insertions(+), 25 deletions(-) diff --git a/Nop.Plugin.Misc.AIPlugin/Components/CustomerCreditWidgetViewComponent.cs b/Nop.Plugin.Misc.AIPlugin/Components/CustomerCreditWidgetViewComponent.cs index 0cafbfa..142c31f 100644 --- a/Nop.Plugin.Misc.AIPlugin/Components/CustomerCreditWidgetViewComponent.cs +++ b/Nop.Plugin.Misc.AIPlugin/Components/CustomerCreditWidgetViewComponent.cs @@ -35,8 +35,10 @@ public class CustomerCreditWidgetViewComponent : NopViewComponent var credit = await _customerCreditService.GetByCustomerIdAsync(customerId); var outstanding = await _customerCreditService.GetOutstandingBalanceAsync(customerId); + // Hiányzó attribútum (null) = köteles — a kapu is így dönt (CreateMissingEkaerHistories: csak az explicit + // false mentesít); a checkbox pipáltat mutat, amíg explicit ki nem veszik. var isEkaer = await _attributeService - .GetGenericAttributeValueAsync(customerId, FruitBankPluginConst.IsEkaerAttribute, storeId: 0); + .GetGenericAttributeValueAsync(customerId, FruitBankPluginConst.IsEkaerAttribute, storeId: 0) ?? true; var model = new CustomerCreditWidgetModel { diff --git a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs index 13c4002..24883d0 100644 --- a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs @@ -49,6 +49,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers : BasePluginController, IFruitBankDataControllerServer { private const int LastShippingDays = 15; + private const string GridLayoutKeyGroup = "GridLayout"; private readonly ILogger _logger = new Logger(logWriters.ToArray()); [SignalR(SignalRTags.ProcessAndSaveFullShippingJson)] @@ -77,6 +78,53 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers return await ctx.GenericAttributeDtos.GetByIdAsync(genericAttributeDto.Id); } + private async Task GetGridLayoutAttributeAsync(string key, int userId) + => (await ctx.GetGenericAttributeDtosByEntityIdAndKeyGroup(userId, GridLayoutKeyGroup, 0)) + .FirstOrDefault(x => x.Key == key); + + [SignalR(SignalRTags.GetGridLayoutTag)] + public async Task GetGridLayout(string key, int userId) + { + return (await GetGridLayoutAttributeAsync(key, userId))?.Value; + } + + [SignalR(SignalRTags.SaveGridLayoutTag)] + public async Task SaveGridLayout(string key, string layoutJson, int userId) + { + var attribute = await GetGridLayoutAttributeAsync(key, userId); + + if (attribute == null) + { + await ctx.GenericAttributeDtos.InsertAsync(new GenericAttributeDto + { + EntityId = userId, + KeyGroup = GridLayoutKeyGroup, + Key = key, + Value = layoutJson, + StoreId = 0, + CreatedOrUpdatedDateUTC = DateTime.UtcNow + }); + } + else + { + attribute.Value = layoutJson; + attribute.CreatedOrUpdatedDateUTC = DateTime.UtcNow; + await ctx.GenericAttributeDtos.UpdateAsync(attribute); + } + + return true; + } + + [SignalR(SignalRTags.DeleteGridLayoutTag)] + public async Task DeleteGridLayout(string key, int userId) + { + var attribute = await GetGridLayoutAttributeAsync(key, userId); + if (attribute == null) return false; + + await ctx.GenericAttributeDtos.DeleteAsync(attribute); + return true; + } + [SignalR(SignalRTags.GetStockQuantityHistoryDtos)] public async Task> GetStockQuantityHistoryDtos() { @@ -340,7 +388,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers if (!ekaerHistory.IsOutgoing) { // 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(); + // A PartnerDepot szándékosan NINCS a default loadRelations-ben — csak itt kell (felrakodási hely = depot címe). + var documents = await ctx.ShippingDocuments.GetAll(true) + .LoadWith(sd => sd.PartnerDepot) + .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); @@ -433,6 +484,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ő, de nincs telephely → NEM jön létre sor, üzenet a popupba (T-D8R4: a PartnerDepot az + // EKÁER-hez kötelező — a felrakodási hely a depot címe); pótlás után a következő futás felveszi. + // A depot-check a kötelezettség-döntés UTÁN: küszöb alatti belföldi csoportra nem szólunk feleslegesen. + if (group.Key.PartnerDepotId is null) + { + messages.Add($"{docs[0].Partner?.Name} (Szállítás #{group.Key.ShippingId}): nincs telephely (PartnerDepot) — EKÁER-bejelentéshez kötelező, a sor nem jött létre."); + continue; + } + // 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); @@ -455,10 +515,22 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers var missingOrders = missingOrderIds.Count == 0 ? [] : await ctx.OrderDtos.GetAllByIds(missingOrderIds, true).ToListAsync(); + // Vevő-szintű mentesség (isEkaer GenericAttribute — mint a Partner.IsEkaer) EGY batch-queryvel a ciklus előtt: + // ami nincs a dictionary-ben = nincs beállítva = köteles (fail-safe; a widget-checkbox is pipáltat mutat rá), + // CSAK az explicit false mentesít. + var customerIds = missingOrders.Select(o => o.CustomerId).Distinct().ToList(); + Dictionary isEkaerByCustomer = customerIds.Count == 0 ? new() : (await ctx.GenericAttributes.Table + .Where(ga => ga.KeyGroup == nameof(Customer) && ga.Key == FruitBankPluginConst.IsEkaerAttribute + && ga.StoreId == 0 && customerIds.Contains(ga.EntityId)) + .ToListAsync()) + .ToDictionary(ga => ga.EntityId, ga => CommonHelper.To(ga.Value)); + foreach (var order in missingOrders) { try { + if (isEkaerByCustomer.TryGetValue(order.CustomerId, out var isEkaer) && !isEkaer) continue; + var result = fruitBankEkaerService.EvaluateObligation(order); if (result.Obligation == EkaerObligation.DataError) { messages.UnionWith(result.Errors); continue; } if (result.Obligation == EkaerObligation.NotRequired) continue; diff --git a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md index f4ee6b5..64be8d1 100644 --- a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md +++ b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md @@ -27,6 +27,56 @@ Egy termékhez a kettő nem azonos; a `Gtin` oszlop VTSZ-ként való használata - `FruitBank.Common/Dtos/ProductDto.cs` → `Gtin` property (a `[Column(nameof(Product.Gtin))]` jelöléssel, summary-ban megjelölve) - jövőbeli: `ShippingToEkaerMapper` (`FruitBank.Common/Services/Ekaer/`) — a `productVtsz`-t a külön `Vtsz` mezőből olvassa +## MGFBANKPLUG-EKAER-I-F5D9: Küszöb alatti külföldi reláció kötelezőként kezelve — ügyfél-kérés, a hatályos jognál szigorúbb + +**Status:** Open · **Priority:** P2 · **Type:** I (üzleti szabály vs jogszabály) · **2026-07-10** + +**A hatályos jogszabály:** a 13/2020. (XII. 23.) PM rendelet **4. § i)** pontja szerint mentes a fuvarozás, ha „egy fuvarozás keretében egymillió forint értéket és ötszáz kilogramm bruttó tömeget meg nem haladó, bejelentésköteles termékkel ugyanazon feladótól ugyanazon címzett részére ugyanazon gépjárművel" történik — a mentesség szövegében **nincs irány-megkötés**, tehát a küszöb alatti fuvar **importnál/exportnál SEM** kötelező (szó szerint ellenőrizve, net.jogtar, 2026-07-10). + +**A jelenlegi viselkedés — KIMONDOTTAN AZ ÜGYFÉL KÉRÉSE:** minden külföldről bejövő szállítmány **küszöbtől függetlenül** bejelentendő (`EkaerReportability` — küszöb alatt is `Required`, ha a feladó és a címzett országkódja eltér). Ez a jogszabályi minimumnál szigorúbb, önkéntes bejelentés — az ügyfél tudatos döntése. + +**Miért van nyitva:** az ügyfél várhatóan utánanéz a kérdésnek, és **nem kizárt, hogy a közeljövőben az eredeti (hatályos) jogszabályi szabályra kell átírni**: küszöb alatt → `NotRequired` iránytól függetlenül; ezzel a kapu országkód-alapú `DataError`-ága is okafogyottá válik (az országkód-hibát a generate-validálás úgyis blokkolja), az `EkaerReportability` jelentősen egyszerűsödik. + +**Ha az átírás megtörténik, VELE EGYÜTT kezelendő:** [`EKAER_TODO.md`](EKAER_TODO.md) **T-H7F2** (depot-szintű küszöb-aggregálás — szintén vállalt eltérés a jogszabályi aggregálási egységtől, ugyanahhoz a kapu+UI átíráshoz tartozik). + +**Affected:** +- `FruitBank.Common/Services/Ekaer/EkaerReportability.cs` — a (2)-(3) ág (országkód-validálás + külföldi → Required) +- `docs/EKAER/README.md` — kapu-szakasz (a döntés után a bullet frissítendő) +- Kapcsolódik: `EKAER_TODO.md` K7Q2 #11 (Closed — ide mutat) + +## MGFBANKPLUG-EKAER-I-B2W7: Partner/PartnerDepot: nincs Phone/Email mező — magyar felrakodási helynél kellene + +**Status:** Open · **Priority:** P3 · **Type:** I (adatmodell — jelenleg NEM aktuális) · **2026-07-10** + +A NAV a **magyarországi felrakodási helynél** a `LocationType` `phone`/`e-mail` mezőit is kéri (a séma szerint opcionálisak, a NAV-felület kéri) — a bejövő felrakodási hely nálunk a `Partner`/`PartnerDepot` címe, ezekben az entitásokban viszont **nincs Phone/Email mező**. + +**Miért nem aktuális (2026-07-10, user):** Partner és PartnerDepot **jelenleg csak külföldi beszállítóhoz** létezik → a felrakodási hely mindig külföldi → phone/email nem kell. A hiány csak akkor válik valóssá, ha (a) **belföldi** beszállító kerül a rendszerbe (magyar felrakodási hely), VAGY (b) az **automata NAV-beadás** bekötésekor a teljes cím-adatkör kötelezővé válik. Addig: kézi beadásnál az ügyintéző a NAV-felületen pótolhatja. + +**Javítási irány (ha aktuális lesz):** `Phone` + `Email` oszlop a `PartnerDepot`-on (és/vagy Partneren), admin-felület, `ShippingToEkaerMapper.BuildLocation` bekötés. + +**Affected:** +- `FruitBank.Common/Entities/PartnerDepot.cs` (+ `Partner.cs`) — jövőbeli Phone/Email oszlopok +- `ShippingToEkaerMapper.BuildLocation` (`FruitBank.Common/Services/Ekaer/`) — a mezők leképezése +- Kapcsolódik: `EKAER_TODO.md` K7Q2 #6 (seller-mezők) + +## MGFBANKPLUG-EKAER-I-K9J4: Kimenő lerakodási hely: hiányzó házszám (streetNumber) és Phone/Email — az automata beadás előfeltétele + +**Status:** Open · **Priority:** P2 · **Type:** I (adatmodell + mapping) · **2026-07-10** + +> ⚠️ **Az automata NAV-beadás KÖVETELMÉNY lesz** (user, 2026-07-10) — a kézi XML-feladás csak átmenet, amíg nincs teszt-hozzáférés. Ezek a hiányok tehát nem „későbbi finomítások", hanem az automata út előfeltételei. + +A kimenő (belföldi vevős, `D` irányú) tradeCard **lerakodási helye** az `OrderSiteSnapshot`-ból (fallback: a vevő fő címéből) épül. Ami megvan: név, adószám, országkód, irányítószám, város, utca. Ami **hiányzik**: + +1. **Külön házszám (`streetNumber`)** — a NAV a `street` + `streetNumber` mezőpárt (vagy helyrajzi számot: `lotNumber`) várja kötelezően; nálunk a házszám a `Street` stringben egyben van („Fő utca 12."). Utólagos string-parse megbízhatatlan → **a snapshot-készítéskor** kell külön mezőbe tenni (a nop `Address.Address1/Address2` bontásából), és a `BuildSiteLocation` / `BuildCustomerLocation` képezze le. +2. **Phone/Email** — a snapshotban és a vevő-cím leképezésben sincs; a sémában opcionális, a NAV-felület kéri. A forrásadat létezik: nop **`Address.PhoneNumber`** → a snapshot bővíthető vele felvételkor. + +Kézi beadásnál egyik sem blokkol (az ügyintéző a NAV-felületen pótolja) — az automata beadásnál a hiányos cím `TC_LOCATION_NOT_COMPLETE`-et adhat. + +**Affected:** +- `FruitBank.Common/Entities/OrderSiteSnapshot.cs` — `StreetNumber` + `Phone` (+ `Email`?) mezők; a snapshot-készítő kód (Address → snapshot) +- `ShippingToEkaerMapper.BuildSiteLocation` / `BuildCustomerLocation` (`FruitBank.Common/Services/Ekaer/`) — leképezés +- Kapcsolódik: [`I-B2W7`](EKAER_ISSUES.md) (bejövő oldali Phone/Email — ott Partner/Depot), `EKAER_TODO.md` K7Q2 #5 (MapShipping-drift — szintén automata-előfeltétel) + ## MGFBANKPLUG-EKAER-I-M6Y2: A számla-pénznem a Partnerből jön — jobb lenne a ShippingDocument-en **Status:** Open · **Priority:** P3 · **Type:** I (adatmodell) diff --git a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md index 81502de..f4d6afd 100644 --- a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md +++ b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md @@ -18,13 +18,15 @@ A `ShippingToEkaerMapper` megírása előtt tisztázandó pontok. Minden sor a j | 1 | `tradeType` (E/I/D irány) | ✅ **Frissítve (2026-06):** a feladó és a címzett **országkódjának összevetése** dönt: egyezik → **D** (belföld); eltér → kimenőnél **E** (export), bejövőnél **I** (import). (Korábban: HU ↔ nem-HU.) | A `Customer.CountryId` → ISO-feloldás kimenőnél még TODO (#7) → addig a vevő HU, ezért a kimenő ténylegesen D marad. | | 2 | Célhely / telephely | ✅ **Megoldva:** configból (`Ekaer:Company` + `Site` al-szekció) → `IEkaerSettings.Company` + `EkaerCompanyInfo.Site` (`LocationType`, fel-/lerakodás). Nincs hardcode. | Több telephely (mint a `PartnerDepot`) — későbbi, ha kell. | | 3 | Érték (`value`, HUF) | ✅ **Megoldva:** `UnitPriceOnDocument × QuantityOnDocument × árfolyam` (`EkaerValueCalculator`). Pénznem a `Partner.Currency`-ből; EUR-HUF árfolyam configból (`Ekaer:ExchangeRate:EurHuf`, MNB középárfolyam). | Árfolyam dinamizálása (jelenleg „drót" config-érték) — későbbi. | -| 4 | Tömeg (`weight`) | `ShippingItem.MeasuredGrossWeight` (bruttó). | Az EKÁER **bruttó** tömeget vár — megerősíteni, hogy ez bruttó és nem nettó. | -| 5 | Granularitás | 1 `ShippingDocument` → 1 `tradeCard`; 1 `Shipping` → több művelet (több dokumentum). | Megerősíteni: tényleg dokumentumonként egy tradeCard? | -| 6 | Eladó (`seller*`) mezők | `ShippingDocument.Partner`-ből: `Name`, `TaxId` (első 8 jegy = adószám-törzs), `CountryCode`, `City`, `Street`, `PostalCode`. | A `Partner` (`PartnerBase`) pontos mezőnevei/forrásai — megerősíteni. | +| 4 | Tömeg (`weight`) | ✅ **Frissítve (2026-07):** `GrossWeightOnDocument` — a **szállítólevélen szereplő** bruttó (a value-hoz hasonlóan minden adat a kísérő dokumentumról; a bejelentés nem függ a későbbi mérlegeléstől). Korábban: `MeasuredGrossWeight`. | ✅ **Megoldva (2026-07):** a validátor tételenként blokkoló `weight > 0` szabályt kényszerít (0 = a doksiról hiányzó súly → ValidationError). A **kapu** tételenkénti súly-előfeltétele külön teendő: **#12**. | +| 5 | Granularitás | ✅ **Frissítve (2026-06/07):** a kézi út CSOPORT-alapú — bejövő: a `(Shipping, Partner, PartnerDepot)` csoport ÖSSZES dokumentuma = **1 tradeCard** (1 `EkaerHistory` + N mapping); kimenő: 1 rendelés = 1 tradeCard. | ⚠️ A `MapShipping` (automata `SubmitShippingAsync`-út) még **dokumentumonként** gyárt tradeCard-ot — a granularitás ELTÉR a kézi úttól; az automata beadás bekötése ELŐTT a kézi (csoport-alapú) granularitásra egységesítendő. | +| 6 | Eladó (`seller*`) mezők | `ShippingDocument.Partner`-ből: `Name`, `TaxId` (első 8 jegy = adószám-törzs), `CountryCode`, `City`, `Street`, `PostalCode`. | A `Partner` (`PartnerBase`) pontos mezőnevei/forrásai — megerősíteni. Továbbá: a NAV **magyar** felrakodási helynél `phone`/`email` mezőt is kér — a `Partner`/`PartnerDepot` entitásban nincs ilyen (entitás+DB bővítés kell; importnál — külföldi feladó — nem játszik). | | 7 | Kimenő irány (`Order` → EKÁER) | ✅ **Implementálva** (`ToConsignment(OrderDto)`): mi=eladó, vevő=címzett; tömeg `OrderItemDto.GrossWeight`, érték nettó (UnitPriceExclTax × qty), jelenleg minden HUF (rate 1). **`carrierText` kimenőnél NEM töltve** (üres) — a vevő maga viszi el, ő már a címzett; a carrierText opcionális (2026-06). | **Nyitott:** a vonó jármű (**rendszám**) forrása — a `Customer`-hez még nincs bekötve (üres → warning, a felrakodás megkezdéséig pótolandó). Külföldi deviza + `Customer.CountryId` ISO-feloldás (export `E`): későbbi. | | 8 | `productVtsz` forrás | `ProductDto.Gtin` (átmeneti). | GTIN ≠ VTSZ — lásd [`MGFBANKPLUG-EKAER-I-T3X8`](EKAER_ISSUES.md); hosszú távon külön `Vtsz` mező. A VTSZ-**csoportosítás** + `productName` **külön** kérdés: **T-V9G3**. | -| 9 | tétel `tradeReason` | `A` (beszerzés) — a mapper ezt használja bejövő árura. | Korábban tévesen `S`-nek feltételezve; az enum valójában `S`=értékesítés, `A`=beszerzés. Üzletileg megerősítendő. | -| 10 | `value` mező | ✅ **Megoldva:** a value most töltve (lásd #3); 0 / ismeretlen ár esetén `null` marad. | A NAV 2021-től `value > 0`-t vár — a validátorban a `value > 0` üzleti szabály még hozzáadandó. | +| 9 | tétel `tradeReason` | ✅ **Megoldva (2026-07-10, common.xsd-ből ellenőrizve):** MINDKÉT irány **`S`**-t küld (= „Termék értékesítés / beszerzés" — egy kód). A korábbi bejövő `A` HIBÁS volt: az `A` = „Saját tulajdonú termék — törvényváltozás miatt KIVEZETVE" (nem „beszerzés"!), és belföldön (`D`) csak `S` adható. Lásd AyCode `EKAER_TRADECARD.md` → tradeReason. | — | +| 10 | `value` mező | ✅ **Megoldva:** a value most töltve (lásd #3); 0 / ismeretlen ár esetén `null` marad. | ✅ **Megoldva (2026-07):** a validátor tételenként blokkoló `value > 0` szabályt kényszerít (hiányzó/0 érték → ValidationError). | +| 11 | Külföldi reláció a küszöb-kapuban | ✅ **Lezárva — kimondottan az ügyfél kérése (megerősítve 2026-07-10):** az `EkaerReportability` küszöb alatt is kötelezőnek veszi a külföldi relációt; a hatályos jog ennél kevesebbet követel (13/2020 PM 4.§ i — a mentesség irány-független). | A lehetséges jövőbeni átírás (jogszabályi szabályra) követése: [`EKAER_ISSUES.md`](EKAER_ISSUES.md) **I-F5D9**. | +| 12 | Kapu: tételenkénti bruttó súly-előfeltétel | ✅ **Megoldva (2026-07-10):** az `EkaerReportability.Evaluate` (0). lépése MINDEN ág előtt — bármely tétel `WeightKg <= 0` → `DataError` tételes üzenettel (a sor nem jön létre; pótlás után a következő futás felveszi). Korábban a 0 súlyú tétel (kitöltetlen `GrossWeightOnDocument` / méretlen rendelés) csendben alul-számolta a küszöb-summát → alul-jelentési kockázat volt. | — | **Affected:** - `ShippingToEkaerMapper` (`FruitBank.Common/Services/Ekaer/`) — a leképezés @@ -32,25 +34,20 @@ A `ShippingToEkaerMapper` megírása előtt tisztázandó pontok. Minden sor a j - `FruitBankEkaerService` (`FruitBank.Common.Server/Services/Ekaer/`) — a szerver-oldali fogyasztó (map → submit) - Forrás-entitások (`FruitBank.Common`): `Shipping`, `ShippingDocument`, `ShippingItem`, `ProductDto.Gtin` -## MGFBANKPLUG-EKAER-T-W3R8: EKÁER küszöb-kategória a terméknél (enum, DB-ben tárolva) +## MGFBANKPLUG-EKAER-T-W3R8: Termék-szintű EKÁER-besorolás (kockázatos-lista tagság) + hatályos küszöb -**Status:** Open · **Priority:** P2 · **Type:** T (adatmodell + mapping) +**Status:** Open · **Priority:** P2 · **Type:** T (adatmodell + mapping + config) -Az EKÁER bejelentés-kötelezettség küszöbe **termékfüggő** — a VTSZ kockázati besorolása dönti el, melyik értékhatár vonatkozik rá: +> ⚠️ **ÚJRAÍRVA (2026-07-10, webről ellenőrizve — net.jogtar 13/2020 PM + 51/2014 NGM):** a korábban itt szereplő 3-kategóriás küszöb-tábla (200 kg/250 eFt élelmiszer · 500 kg/1 MFt egyéb · 2500 kg/5 MFt nem-kockázatos) a **2021 ELŐTTI** rezsim (5/2015 NGM) volt — **NEM hatályos**. -| Kategória | Tömeg | Érték (nettó) | -|---|---|---| -| Kockázatos élelmiszer | 200 kg | 250 000 Ft | -| Kockázatos egyéb (nem élelmiszer) | 500 kg | 1 000 000 Ft | -| Nem kockázatos | 2500 kg | 5 000 000 Ft | +**Hatályos szabály (2021-01-01 óta, 13/2020. (XII. 23.) PM rendelet):** +- **CSAK a kockázatos (bejelentésköteles) termék** EKÁER-köteles — a lista az **51/2014. (XII. 31.) NGM rendelet 1. melléklete** (VTSZ-alapú). A listán NEM szereplő termék **egyáltalán nem** jelentendő. +- **Egyetlen, egységes küszöb (4. § i):** mentes a fuvarozás, ha az **500 kg bruttó tömeget ÉS az 1 000 000 Ft adó nélküli értéket** sem haladja meg → kötelező, ha **bármelyik** átlépi (a feladó→címzett→jármű relációra aggregálva). +- **Lista-módosítás — 25/2025. (VIII. 18.) NGM (2025-08-18-tól):** egyes kertészeti termékek KIKERÜLTEK a kockázatos-listából: vöröshagyma-dughagyma (07031011), friss borsó (07081000), friss bab (07082000), csemegekukorica (07099960), egyes vetőmagok (07129011, 07131010, 07133310). Ha ilyet forgalmazunk, rá bejelentés sem kell. -> **A fenti értékek a 13/2020. (XII. 23.) PM rendelet kategóriái — REFERENCIA, 2026-06 állapot.** Az alkalmazott küszöb a kódba **NINCS drótozva** (`appsettings` → `Ekaer:Thresholds`), és a jogszabály/beállítás változásával módosul; ezt a táblát csak tájékoztatásul, **dátumozva** tartjuk. -> -> ⚠️ **A túljelentés is bírság** (megerősítve, 2026-06): a globális „legszigorúbb" küszöb egy **nem-élelmiszer** terméket (ami csak a magasabb határnál kötelező) **túljelentene**. Fruit-importőrnél (minden áru kockázatos élelmiszer) ez nem gond — de amint nem-élelmiszer is bekerül, ez a per-termék kategória **szükségessé** válik (nem csak P2). +**Jelenlegi állapot — ✅ CONFIG RENDBEN (2026-07-10, user megerősítve):** a `CreateMissingEkaerHistories` globális küszöbe a configból (`Ekaer:Thresholds`) jön, és a **hatályos 500 kg / 1 000 000 Ft van beállítva** az appsettings-ben. A kapu aggregálási/irány-szabályainak ismert nyitott pontjai: **K7Q2 #11** (külföldi küszöb nélkül — túljelent), **#12** (0-súlyú tétel), **T-H7F2** (depot-szintű summa — alul-jelenthet). -**Jelenlegi (átmeneti):** a `CreateMissingEkaerHistories` egyetlen, globális küszöböt használ az `EkaerSettings`-ből — **configból** (`Ekaer:Thresholds`), a kódba **NEM drótozva**. Jelenleg a **kockázatos-élelmiszer** határ van beállítva (a fruit-importőr profil miatt, a legszigorúbb a beszerzéseknél). A kapu a küszöböt `(Shipping, Partner)`-re **aggregálva** nézi, külföldinél küszöb nélkül kötelez (lásd README → kapu-szakasz). - -**Cél:** a **terméknél (Product) kézzel megadható** küszöb-**KATEGÓRIA** (nem a konkrét érték!) — egy **enum** (pl. `EkaerThresholdCategory { RiskyFood, RiskyOther, NonRisky }`) —, **DB-ben tárolva** (Product oszlop / GenericAttribute). A konkrét kg/Ft értékeket a **kód** rendeli a kategóriához → értékhatár-változáskor egy helyen módosul. A küszöb-kapu dokumentumonként a tételek kategóriái szerint alkalmazza a megfelelő határt. (Későbbi lépés: a kategória automatikus levezetése a NAV kockázatos-VTSZ-listákból.) +**Cél (a per-termék rész):** NEM küszöb-kategória-enum kell (az a régi jogszabályhoz készült terv), hanem termék-szintű **„kockázatos-e" besorolás** — a termék rajta van-e az 51/2014-es VTSZ-listán —, DB-ben tárolva (Product oszlop / GenericAttribute), kézzel karbantartva. A kapu a NEM kockázatos terméket a küszöb-summából (és a bejelentésből) teljesen kihagyja. (Későbbi lépés: automatikus levezetés a VTSZ-listából — a lista-módosításokat, mint a 25/2025 NGM, követni kell.) **Affected:** - `FruitBank.Common/Dtos/ProductDto.cs` — küszöb-kategória mező (enum, DB-ben) @@ -82,7 +79,7 @@ A NAV felé **egy VTSZ-re egy `tradeCardItem`** kell — több azonos-VTSZ-ű te ## MGFBANKPLUG-EKAER-T-D8R4: Kapu-csoportosítás (Shipping, PartnerDepot)-re; a PartnerDepotId kötelező -**Status:** Open · **Priority:** P2 · **Type:** T (kapu) · **2026-06** +**Status:** Closed (2026-07-10) · **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. @@ -96,3 +93,69 @@ A `CreateMissingEkaerHistories` bejövő kapuja jelenleg `(ShippingId, PartnerId - `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 + +### Resolution (2026-07-10) + +Implementálva, EGY tudatos eltéréssel: a **kulcs-egyszerűsítés NEM készült el** — a csoportkulcs szándékosan `(ShippingId, PartnerId, PartnerDepotId)` maradt, mert null `PartnerDepotId`-nál a `(ShippingId, PartnerDepotId)` kulcs KÜLÖNBÖZŐ partnerek dokumentumait vonná egy csoportba (null == null), és a kötelezettség-aggregátum partnerek közt keveredne; nem-null depotnál a `PartnerId` redundáns, de ártalmatlan (ugyanazt a csoportosítást adja). + +- **Kapu:** kötelező bejelentésnél hiányzó `PartnerDepotId` → üzenet a popupba, sor NEM jön létre. A depot-check a kötelezettség-döntés UTÁN fut — küszöb alatti belföldi csoportra nem szól feleslegesen. +- **Generate:** `LoadWith(sd => sd.PartnerDepot)` (a lazy asszociáció csak itt töltve) + `FruitBankEkaerService.TryDepotError` guard → hiányzó/betöltetlen depotnál `ValidationError`, XML nem generálódik. +- **Mapper:** `LoadLocation = BuildLocation(PartnerDepot ?? partner)` — a felrakodási hely a **depot címe**; a `TaxId` Partner-passthrough (`depot.Partner ??= partner`). A kapu-útvonalon a depot nincs betöltve → partner-cím, ami ott nem számít (a kötelezettség-döntés címet nem használ). + +## MGFBANKPLUG-EKAER-T-H7F2: Küszöb-aggregálás szintje: (Shipping, Partner) — a depot-bontás küszöb-rést nyit + +**Status:** Open (parkolva — vállalt kockázat, ügyfél-döntés 2026-07-10; az I-F5D9-cel együtt kezelendő) · **Priority:** P3 · **Type:** T (kapu) · **2026-07-10** + +A T-D8R4 óta a kapu `(ShippingId, PartnerId, PartnerDepotId)`-re csoportosít, és a küszöb-summa is **ezen a csoporton** fut. A jogszabályi aggregálási egység viszont a **feladó→címzett→jármű** reláció, telephelytől FÜGGETLENÜL — a 13/2020 PM **§12 (4)** szó szerint: „Egy EKAER számot **kell** megállapítani akkor is, ha egy gépjárművel **egy feladó több felrakodási címéről**, egy címzett több kirakodási címére történik a közúti fuvarozás." + +**A rés (alul-jelentés!):** ugyanaz a Partner két telephelyről 300–300 kg-ot ad fel ugyanarra a kamionra → a kapu két külön 300 kg-os csoportot lát → mindkettő „küszöb alatt, nem kell" — pedig jogilag 600 kg = **kötelező**. (A gyűjtőfuvar KÜLÖNBÖZŐ feladókkal viszont jogszerűen mentes relációnként — az egység nem a kamion.) + +**Döntés (2026-07-10, ügyfél): MARAD ÍGY — vállalt kockázat, az entry NYITVA tartva.** Indoklás: (1) nagyon ritkán fordulhat elő; (2) az érintett tételek egyenként definíció szerint küszöb alattiak, így a kitettség kicsi; (3) a `loadDate` csak **nap-pontosságú** (időpont nélküli, opcionális) — a bejelentési adatokból két azonos napi külön fuvar megkülönböztethetetlen, így a NAV felé „két külön alkalommal vitte el" magyarázat adható. **Egyben kezelendő az [`I-F5D9`](EKAER_ISSUES.md)-cel (bejövő külföldi szabály):** ha az ügyfél a hatályos jogszabályi szabályra váltat, a kapu+UI úgyis átírandó — AKKOR ezt is meg kell oldani az alábbi opciók egyikével; addig parkolva. + +**Döntési opciók (a jövőbeni átíráskor):** +- **(a) Jogilag tiszta:** kötelezettség `(Shipping, Partner)`-szinten; **EGY** EkaerHistory/tradeCard partnerenként, a telephelyek külön **`deliveryPlan`**-ekként. **Séma-oldalról megerősítve (2026-07-10, ekaermanagement.xsd):** a `deliveryPlans` 1..N `deliveryPlan`-t enged, mindegyik saját `loadLocation`+`unloadLocation`+tételekkel; a `loadDate` viszont EGY van (fejrész-szintű, opcionális) — „több loadDate" sémaszinten nem létezik, a NAV az egész túrát egy fuvarnak modellezi (érkezés a finalize-nál). Mapper-bővítés kell — most csak 1 fel-/lerakodási helyet képezünk, deliveryPlan nélkül. +- **(b) Pragmatikus:** a kötelezettség-summa `(Shipping, Partner)`-szinten fut (ha átlépi, a partner MINDEN depot-csoportja sort kap); a deklaráció marad depot-onként. A küszöb-rés eltűnik; formailag több EKÁER-szám lesz ott, ahol a §12 (4) egyet mond — kockázata tisztázandó. + +**Affected:** +- `FruitBankDataController.CreateMissingEkaerHistories` (plugin) — a kötelezettség-kiértékelés szintje vs a csoport/EkaerHistory-képzés szétválasztása +- `ShippingToEkaerMapper` (`FruitBank.Common/Services/Ekaer/`) — (a) opciónál deliveryPlan-többes leképezés +- Kapcsolódik: T-D8R4 (Closed — a depot-csoportosítás bevezetése), K7Q2 #11/#12 (küszöb-szabályok) + +## MGFBANKPLUG-EKAER-T-C3M8: Customer `isEkaer` mentesség a kimenő kapuban + null-szemantika + +**Status:** Closed (2026-07-10) · **Priority:** P2 · **Type:** T (kapu + admin-widget) · **2026-07** + +A vevő-oldali EKÁER-mentesség attribútum **létezik** (`GenericAttribute`: `Key="isEkaer"`, `KeyGroup="Customer"`, StoreId=0; admin CustomerCredit-widget checkbox „Ekáer köteles", `SetEkaer` endpoint menti), de a **kimenő kapu (`CreateMissingEkaerHistories`) nem nézi** — a nagybani piacos (közúti fuvar nélküli) vevők rendelései is tévesen kapnak `Pending` sort. Bejövő oldalon a `Partner.IsEkaer` mentesség működik (csendes kihagyás). + +**Blokkoló döntés — mi legyen a SOHA be nem állított vevővel (nincs attribútum-sora)?** A csapda: az olvasó (`GetGenericAttributeValueAsync`) hiányzó sornál `false`-t ad, így a widgetben a „be nem állított" és az „explicit mentesített" vevő megkülönböztethetetlen (mindkettő üres checkbox); a Partner-oldalon a default fordított (`IsEkaer = true`, csak explicit `false` mentesít). + +| Opció | Kapu-viselkedés | Kockázat | Plusz teendő | +|---|---|---|---| +| **A) hiányzó = KÖTELES** (Partner-konzisztens, fail-safe) | csak az explicit `false` sor mentesít | felesleges Pending az új vevőknél (látható, törölhető) | a widget default-ját igazítani (hiányzó → pipált), különben félrevezet | +| **B) hiányzó = MENTES** (a mostani widget-megjelenítés) | csak explicit `true`-ra jön létre sor | **új/be-nem-állított vevő csendben kimarad → alul-jelentés** | minden köteles vevőt pipálni kell | + +**Implementáció (a döntés után):** a kimenő ágban csendes kihagyás (mint a `Partner.IsEkaer`), az attribútum StoreId=0-val olvasva; a rendelés-lista kicsi (dátum-szűrt), per-customer olvasás vagy batch `GenericAttribute`-query. + +**Affected:** +- `FruitBankDataController.CreateMissingEkaerHistories` (plugin) — kimenő ág: `isEkaer`-szűrés +- `CustomerCreditWidgetViewComponent` + `CustomerCreditWidget.cshtml` (plugin) — A) opciónál a hiányzó-attribútum default igazítása +- `docs/EKAER/README.md` — kapu-szakasz mentesség-bullet kiegészítése a döntés után + +### Resolution (2026-07-10) + +Az **A) szemantika** valósult meg, `bool?`-olvasással (a user javaslata — a tri-state az adatszinten explicit, a UI kétállapotú maradt): +- **Kapu (kimenő ág):** az érintett vevők `isEkaer` attribútumai **EGY batch-queryvel** a ciklus előtt dictionary-be (`ctx.GenericAttributes.Table`, `KeyGroup="Customer"`, `StoreId=0`, a `CustomerId`-kra szűrve — nincs ciklusonkénti DB-hívás); a ciklusban `TryGetValue`: **hiányzó kulcs = nincs beállítva = köteles** (fail-safe, Partner-konzisztens), **explicit `false` → csendes kihagyás** (mint a `Partner.IsEkaer`). +- **Widget:** az olvasó `bool?` + **`?? true`** — a soha be nem állított vevőnél a checkbox pipáltat mutat, pontosan azt, amit a kapu csinál; tri-state UI nem készült (a null a felületen nem külön állapot). +- `CommonHelper.To` forrásból ellenőrizve: `NullableConverter` kezeli a `"True"/"False"` konverziót; hiányzó sor → null. + +## MGFBANKPLUG-EKAER-T-N4E7: EkaerHistory delete/újragenerálás végpont + +**Status:** Open · **Priority:** P2 · **Type:** T (folyamat) · **2026-07** + +A dizájn-cél a „törölhető + újragenerálható" deklaráció (az EKÁER-kapcsolat az `EkaerHistoryMapping` junctionben él, a core adatokat nem érinti), de törlő végpont NINCS — a grid delete tiltva, a hibásan/elavultan létrejött sorok (pl. a T-D8R4 szigorítás ELŐTT létrejött, null-depotos `Pending`-ek) csak kézzel, DB-ből takaríthatók. + +**Cél:** SignalR-endpoint az `EkaerHistory` + mapping sorok **atomikus** törlésére; a grid delete engedélyezése. Üzletileg tisztázandó: `Sent` státuszú (a NAV-nál már bejelentett) sor törölhető-e (valószínűleg csak `Pending` / `Generated*` / `ValidationError`). + +**Affected:** +- `FruitBankDataController` (plugin) — delete endpoint (EkaerHistory + mappingek atomikusan) +- EkaerHistory grid (kliens) — delete gomb engedélyezése diff --git a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/README.md b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/README.md index 52dbfaf..93e2007 100644 --- a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/README.md +++ b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/README.md @@ -42,10 +42,12 @@ Validációs hiba → **hibalista** (`EkaerSubmitResult.Invalid`), nem megy ki k | `seller*` (feladó) | `ShippingDocument.Partner` (a beszállító) | | `carrierText` (szállítmányozó — **opcionális**, NAV nem követeli) | **bejövő:** `Shipping.CargoPartner.Name` (valós, külön fuvarozó); **kimenő:** — **üres** (a vevő maga viszi el, ő már a címzett; nincs külön fuvarozónk) | | `vehicle` / `vehicle2` | `Shipping.CargoTruck` / `CargoTrailer` (`LicencePlate` + `CountryCode`, normalizálva) | +| `loadLocation` (felrakodás) | **bejövő:** a `ShippingDocument.PartnerDepot` **címe** (kötelező — **T-D8R4** kész; a `VatNumber` a Partneré, a depot `TaxId`-ja passthrough); **kimenő:** `company.Site` (configból) | +| `unloadLocation` (lerakodás) | **bejövő:** `company.Site`; **kimenő:** az `order.Site` snapshot (fallback: a vevő fő címe) | | tétel `productVtsz` | `ShippingItem.ProductDto.Gtin` (átmenetileg — lásd `EKAER_ISSUES.md`) | | tétel `productName` | `ShippingItem.ProductName` (a VTSZ-szerinti csoportosítás + a csoport-név `Category`-ből: TODO **T-V9G3**) | -| tétel `weight` | `ShippingItem.MeasuredGrossWeight` (bruttó) | -| tétel `tradeReason` | `A` (beszerzés — bejövő áru). ⚠️ Az enum: `S`=értékesítés, `A`=beszerzés, `W`=bérmunka, `O`=egyéb. | +| tétel `weight` | `ShippingItem.GrossWeightOnDocument` — a **szállítólevélen szereplő** bruttó (a bejelentés a kísérő dokumentum adatairól megy, mint a value; NEM a későbbi mérlegelésről) | +| tétel `tradeReason` | **`S` MINDKÉT irányra** — az XSD szerint `S` = „Termék értékesítés / beszerzés" (egy kód), `A` = saját tulajdonú termék (**kivezetve**), `W`=bérmunka, `O`=egyéb; belföldön (`D`) csak `S` adható. (A bejövő korábban tévesen `A`-t küldött — javítva 2026-07-10.) | A nyitott pontok (tradeType, destination, value/deviza, granularitás, kimenő irány): [`EKAER_TODO.md`](EKAER_TODO.md). @@ -57,10 +59,14 @@ A kézi NAV-beadás munkafolyamat: a **kapu** létrehozza a sorokat → a user * `Pending` (a kapu hozta létre) → **Generate** → `Generated` / `GeneratedWithWarning` (küldhető, de pótlandó — pl. hiányzó rendszám) / `ValidationError` (blokkoló) → kézi NAV-beadás után `Sent` / `SentWithMissingData` (pótlásra vár). A grid 3 tabja ezekre szűr (szerver-oldali `EkaerHistoryFilter`), a warning ⚠️ / error ⛔ ikonnal megkülönböztetve. **Bejelentés-kötelezettség** — `EkaerReportability.Evaluate(consignment, settings)`, a kapu hívja egységenként: +- **tételenkénti előfeltétel (2026-07):** minden tétel **bruttó súlya > 0** kell legyen — 0 súlyú tétel (kitöltetlen dokumentum-súly / méretlen rendelés) → `DataError` tételes üzenettel, a csoport sora NEM jön létre (a 0 csendben alul-számolná a küszöb-summát); pótlás után a következő futás felveszi; - **egység:** bejövőnél a `(Shipping, Partner, PartnerDepot)` csoport (= feladó+telephely→címzett→jármű) — a tömeg/érték a csoport **ÖSSZES** dokumentumára **aggregálva** (13/2020 PM rendelet), és a csoport EGY `EkaerHistory`-t kap; kimenőnél egy rendelés; + ℹ️ **Ismert, VÁLLALT eltérés (T-H7F2, ügyfél-döntés 2026-07-10):** a jogszabályi aggregálási egység telephely NÉLKÜLI (feladó→címzett→jármű, §12 (4)) — a depot-szintű summa a több-telephelyes belföldi partnernél elvileg alul-jelenthet (2×300 kg két depotról). Vállalva, mert ritka, tételenként küszöb alatti, és a nap-pontosságú `loadDate` mellett két külön fuvarként számolható el; az [`I-F5D9`](EKAER_ISSUES.md)-cel együtt kezelendő, ha jogszabályi szabályra váltás jön — lásd `EKAER_TODO.md` T-H7F2; - **küszöb felett** → kötelező, az országkódtól **függetlenül** (az országkód-hibát a generate-validálás jelzi); - **küszöb alatt** → csak a **külföldi** reláció kötelező (a feladó és a címzett országkódja **eltér**); belföld → nem; hiányzó/érvénytelen (nem ISO-2) országkód → **`DataError`** (a sor kimarad + üzenet a popupban); -- **mentesség:** `Partner.IsEkaer = false`, és a Partner nélküli (félkész) szállítólevél — csendben kimaradnak. + ℹ️ **Kimondottan az ügyfél kérése (megerősítve 2026-07-10):** a hatályos jog szerint a küszöb-mentesség irány-független (13/2020 PM 4.§ i — küszöb alatti import SEM lenne kötelező), az ügyfél viszont **minden külföldről bejövőt jelenteni kér** — a minimumnál szigorúbb, önkéntes bejelentés. Nem kizárt, hogy a közeljövőben a jogszabályi szabályra kell átírni — a követés: [`EKAER_ISSUES.md`](EKAER_ISSUES.md) **I-F5D9**; +- **mentesség:** bejövőnél `Partner.IsEkaer = false`, és a Partner nélküli (félkész) szállítólevél; kimenőnél a **Customer `isEkaer` GenericAttribute explicit `false`** értéke (T-C3M8, 2026-07: `bool?`-olvasás, a hiányzó attribútum = köteles — fail-safe; az admin-widget checkboxa is pipáltat mutat, amíg explicit ki nem veszik) — mind csendben kimaradnak; +- **telephely-kapu (T-D8R4, 2026-07):** kötelező bejelentésnél a `PartnerDepotId` is kötelező — hiányzó telephelyű csoportra NEM jön létre sor, üzenet a popupba (a depot-check a kötelezettség-döntés UTÁN fut: küszöb alatti belföldi csoportra nem szól feleslegesen). A generate-út `LoadWith(sd => sd.PartnerDepot)`-tal tölt, és a `FruitBankEkaerService.TryDepotError` guard hiányzó/betöltetlen depotnál `ValidationError`-t ad (nincs partner-központi-címes, félrevezető XML). A csoportkulcsban a `PartnerId` **szándékosan** benne marad: null-depotnál különben különböző partnerek dokumentumai egy csoportba olvadnának. > **A küszöb configból jön** (`Ekaer:Thresholds:WeightKg` / `:ValueHuf`) — a kódba **NINCS drótozva**, mert a NAV-jogszabály és a beállítás szerint **változhat**. A konkrét, dátumozott értékeket és a termékkategóriákat lásd [`EKAER_TODO.md`](EKAER_TODO.md) **T-W3R8**. A NAV **mindkét irányban büntet** (alul- ÉS túljelentés), ezért a küszöbnek pontosnak kell lennie.