11 KiB
EKÁER — NAV közúti áruforgalom-bejelentés (FruitBank áttekintő)
Topic EKAER, prefix MGFBANKPLUG → entry ID-k MGFBANKPLUG-EKAER-I-<RAND> (issue) / -T- (TODO) / -B- (bug).
A FruitBank EKÁER-bejelentés szerver-oldali (a kliens már standalone WASM, böngészőből CORS miatt nem hívhat NAV-ot — a kliens SignalR-en triggerel). A tényleges logika viszont rétegekre bontva él, nem ebben a pluginban; ez a folder a belépő/áttekintő + a FruitBank-/nopCommerce-specifikus nyitott döntések.
A NAV-protokoll részletei (transport, auth, üzenet-szerkezet) az
AyCode.Corerepóban:AyCode.Services/Nav/docs/EKAER_*.md. Itt nem ismételjük.
A teljes EKÁER-kép (4 réteg)
| Réteg | Hol | Mit ad |
|---|---|---|
| NAV/EKÁER framework | AyCode.Services/Nav/ + Nav/Ekaer/ (AyCode.Core) |
transport, auth (SHA-512), generált modellek, EkaerTradeCardValidator (NAV-séma + üzleti szabályok), EkaerSubmitService (validate→send), EkaerManageService (HTTP). Általános — nem FruitBank-specifikus. |
| FruitBank leképezés | FruitBank.Common/Services/Ekaer/ |
ShippingToEkaerMapper — a forrást (bejövő ShippingDocument-csoport / kimenő OrderDto) egy irány-független EkaerConsignment-re képezi (ToConsignment), abból épül a tradeCard (BuildTradeCard). EkaerReportability — bejelentés-kötelezettség (küszöb/külföld). EkaerValueCalculator — tétel-érték HUF-ban. |
| FruitBank fogyasztó | FruitBank.Common.Server/Services/Ekaer/ |
FruitBankEkaerService — szerver-oldali orchestráció: credentials/options + map → submit |
| nopCommerce-integráció (ez a plugin) | ez a folder + a plugin kód | DI-bekötés, NAV-fiók settings, a VTSZ-forrás (Product.Gtin), SignalR-trigger a kliens felől, és ez a doksi |
A beküldési lánc
FruitBankEkaerService.SubmitShippingAsync(shipping) // FruitBank.Common.Server
→ ShippingToEkaerMapper.MapShipping(...) // FruitBank.Common (map)
→ EkaerSubmitService.SubmitAsync(ops) // AyCode.Services (validate → send)
→ EkaerTradeCardValidator.Validate(ops) // hibalista → ha nem üres: NEM küld
→ EkaerManageService.ManageAsync(request) // auth + HTTP POST a NAV-nak
Validációs hiba → hibalista (EkaerSubmitResult.Invalid), nem megy ki kérés. NAV-oldali hiba → NavReportException propagál.
További belépési pontok (a kézi NAV-beadás munkafolyamathoz — FruitBankDataController SignalR-endpointok, kliens felől triggerelve):
CreateMissingEkaerHistories(fromDate)— reconciliation-kapu: a dátumtól még le nem fedett szállítólevelekre/rendelésekrePendingEkaerHistorysorokat hoz létre, csak ha kötelező (lásd a kapu-szakaszt lentebb). Bejövőnél a(Shipping, Partner, PartnerDepot)csoport EGYEkaerHistory-t kap, a csoport dokumentumai pedigEkaerHistoryMappingsorokként kapcsolódnak; kimenőnél egy rendelés = egyEkaerHistory+ egy mapping.EkaerCreateResult-ot ad vissza (létrehozott szám + a kihagyott tételek üzenetei).GenerateEkaerXmlDocument(ekaerHistoryId)— egyEkaerHistory-deklarációra (a mappingben rögzített forrás-dokumentumok ÖSSZES tételét egy tradeCard-ba vonva) legenerálja + validálja a tradeCard XML-t (NEM küld NAV-ot); aStatus/ErrorText/ConversionRatetöltődik, az XML vágólapra másolható a kézi beadáshoz.
Companion fájlok
EKAER_ISSUES.md— ismert problémák (pl.MGFBANKPLUG-EKAER-I-T3X8: GTIN≠VTSZ).EKAER_TODO.md— aShipping → tradeCardmapping nyitott üzleti döntései.
A leképezés (tisztázott pontok)
| EKÁER | FruitBank forrás |
|---|---|
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.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.
Reconciliation-kapu, kötelezettség, életciklus (2026-06)
A kézi NAV-beadás munkafolyamat: a kapu létrehozza a sorokat → a user generál + másol/beküld → kézzel rögzíti a NAV-választ.
EkaerHistory életciklus (egy sor = egy tradeCard; a forrás-rekordok az EkaerHistoryMapping junctionön át kapcsolódnak — bejövő: a (Shipping, Partner, PartnerDepot) csoport N ShippingDocument-je, kimenő: 1 Order —, az EkaerHistory maga nem hordoz forrás-kulcsot, az irány az IsOutgoing; a Status int-oszlop + [NotColumn] enum-nézet; a mapping a GetAll(loadRelations: true)-zal töltődik a Mappings asszociációba):
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) →
DataErrorté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 EGYEkaerHistory-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úloadDatemellett két külön fuvarként számolható el; azI-F5D9-cel együtt kezelendő, ha jogszabályi szabályra váltás jön — lásdEKAER_TODO.mdT-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); ℹ️ 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.mdI-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 CustomerisEkaerGenericAttribute explicitfalseé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
PartnerDepotIdis 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-útLoadWith(sd => sd.PartnerDepot)-tal tölt, és aFruitBankEkaerService.TryDepotErrorguard hiányzó/betöltetlen depotnálValidationError-t ad (nincs partner-központi-címes, félrevezető XML). A csoportkulcsban aPartnerIdszá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ásdEKAER_TODO.mdT-W3R8. A NAV mindkét irányban büntet (alul- ÉS túljelentés), ezért a küszöbnek pontosnak kell lennie.
Egyesített leképezés: a bejövő/kimenő forrás egy EkaerConsignment köztes modellre képződik (két adapter), és abból épül MIND a tradeCard (BuildTradeCard), MIND a kötelezettség-döntés — így az irányfüggő rész két adapterre szorul, a közös logika egy helyen van. A MapDocument / MapOrder / MapShipping ennek vékony wrappere.
Bekötés a szerveren (DI)
A plugin (vagy a szerver-startup) köti be a láncot — a NAV-fiók titkos adatai a szerver config/secret store-jából:
services.AddSingleton<INavCredentials>(/* NAV-fiók: User/Password/SigningKey/TaxNumber/BaseUrl */);
services.AddHttpClient<EkaerManageService>(/* NAV TLS 1.2 */);
services.AddScoped<IEkaerTradeCardValidator, EkaerTradeCardValidator>();
services.AddScoped<IEkaerSubmitService, EkaerSubmitService>();
services.AddScoped<IShippingToEkaerMapper, ShippingToEkaerMapper>();
services.AddSingleton<IEkaerSettings>(/* EkaerSettings: Ekaer:Company + Site + küszöbök + EUR-HUF árfolyam — configból */);
services.AddScoped<IFruitBankEkaerService, FruitBankEkaerService>();