From f1db5a9a99f8fb621ef2b0e6aa2adb4fdf46a5cc Mon Sep 17 00:00:00 2001 From: Loretta Date: Thu, 11 Jun 2026 17:40:42 +0200 Subject: [PATCH] =?UTF-8?q?Update=20EK=C3=81ER=20config,=20thresholds,=20a?= =?UTF-8?q?nd=20value=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Switched DB connection to FruitBank_PROD in appsettings.json - Added Ekaer config section: company info, thresholds, exchange rate - Refactored CreateMissingEkaerHistories to use new settings, load related data, and apply exemption/threshold logic - Updated controller DI to use EkaerSettings - Centralized EKÁER config binding in PluginNopStartup - Documented currency source issue and new value/category logic in EKAER_ISSUES.md and EKAER_TODO.md --- .../Controllers/FruitBankDataController.cs | 27 +++++++++++++++-- .../Infrastructure/PluginNopStartup.cs | 30 +++++++++++++------ .../docs/EKAER/EKAER_ISSUES.md | 15 ++++++++++ .../docs/EKAER/EKAER_TODO.md | 25 ++++++++++++++-- 4 files changed, 83 insertions(+), 14 deletions(-) diff --git a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs index e485604..961128f 100644 --- a/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Controllers/FruitBankDataController.cs @@ -14,6 +14,7 @@ using FruitBank.Common.Models; using FruitBank.Common.Server; using FruitBank.Common.Server.Interfaces; using FruitBank.Common.Server.Services.Ekaer; +using FruitBank.Common.Services.Ekaer; using FruitBank.Common.SignalRs; using LinqToDB; using Mango.Nop.Core.Dtos; @@ -43,6 +44,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers ILocalizationService localizationService, PreOrderConversionService preorderConversionService, IFruitBankEkaerService fruitBankEkaerService, + EkaerSettings ekaerSettings, IEnumerable logWriters) : BasePluginController, IFruitBankDataControllerServer { @@ -369,13 +371,32 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers { _logger.Detail($"CreateMissingEkaerHistories invoked; fromDate: {fromDate:yyyy-MM-dd}"); - // Bejövő: rekord nélküli szállítólevelek a dátumtól. - var missingInbound = await ctx.ShippingDocuments.GetAll(false) + // Bejövő: rekord nélküli szállítólevelek a dátumtól — Partnerrel és tételekkel betöltve a kapuhoz + // (IsEkaer-mentesség + tömeg/érték küszöb). A fájl-blobokat NEM töltjük (csak Partner + Items). + var candidates = await ctx.ShippingDocuments.GetAll() .Where(sd => sd.ShippingDate >= fromDate) .Where(sd => !ctx.EkaerHistories.Table.Any(eh => eh.ForeignKey == sd.Id && !eh.IsOutgoing)) - .Select(sd => new EkaerHistory { ForeignKey = sd.Id, IsOutgoing = false, StatusId = (int)EkaerStatus.Pending }) + .LoadWith(sd => sd.Partner) + .LoadWith(sd => sd.ShippingItems) .ToListAsync(); + var missingInbound = new List(); + foreach (var doc in candidates) + { + // Explicit mentesítés (pl. nagybani piac, azonos cím — nincs közúti fuvar a partnerek között). + if (doc.Partner?.IsEkaer == false) continue; + + var items = doc.ShippingItems ?? []; + var totalWeight = items.Sum(i => i.MeasuredGrossWeight); + var rateToHuf = EkaerValueCalculator.ResolveRateToHuf(doc.Partner?.Currency, ekaerSettings.EurHufRate); + var totalValueHuf = items.Sum(EkaerValueCalculator.ItemLineValue) * rateToHuf; + + // Küszöb alatt (tömeg ÉS érték is) → nem kötelező EKÁER → nem hozunk létre sort. + if (totalWeight < ekaerSettings.ThresholdWeightKg && totalValueHuf < ekaerSettings.ThresholdValueHuf) continue; + + missingInbound.Add(new EkaerHistory { ForeignKey = doc.Id, IsOutgoing = false, StatusId = (int)EkaerStatus.Pending }); + } + // Kimenő: rekord nélküli, lezárt (Complete) rendelések, DateOfReceipt a dátumtól (jövőbeli is — előre-bejelentés). var missingOutgoing = await ctx.OrderDtos.GetAllByOrderStatus(OrderStatus.Complete, false) .Where(o => o.GenericAttributes.Any(ga => ga.Key == nameof(OrderDto.DateOfReceipt) && DateTime.Parse(ga.Value) >= fromDate.Date)) diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index 6dc44bd..6a09561 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -21,6 +21,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection.Extensions; +using Nop.Core; using Nop.Core.Domain.Orders; using Nop.Core.Infrastructure; using Nop.Data; @@ -192,18 +193,29 @@ public class PluginNopStartup : INopStartup BaseUrl = c["BaseUrl"] ?? "https://import-test-b.ekaer.nav.gov.hu", // TEST; PROD: https://import.ekaer.nav.gov.hu }; }); + // EKÁER konfiguráció — egyetlen objektum (cégadat + küszöb + árfolyam), configból (appsettings "Ekaer"). + // A bejelentő cégadata: a NAV-helyes 11-jegyű adószám + telefon/e-mail itt adható meg, a webshop Store-tól függetlenül. services.AddSingleton(sp => { - var c = sp.GetRequiredService().GetSection("Ekaer:Company"); - return new EkaerCompanyInfo + var c = sp.GetRequiredService().GetSection("Ekaer"); + var co = c.GetSection("Company"); + return new EkaerSettings { - Name = c["Name"], - TaxId = c["TaxId"], - CountryCode = c["CountryCode"] ?? "HU", // a NAV EKÁER magyar - PostalCode = c["PostalCode"], - City = c["City"], - Street = c["Street"], - // UnloadLocation (saját telephely): magyar címnél a NAV Name/VatNumber/Phone/Email-t is kér — TODO configból összeállítani. + Company = new EkaerCompanyInfo + { + Name = co["Name"], + TaxId = co["TaxId"], + CountryCode = co["CountryCode"] ?? "HU", // a NAV EKÁER magyar + PostalCode = co["PostalCode"], + City = co["City"], + Street = co["Street"], + // UnloadLocation (saját telephely): magyar címnél a NAV Name/VatNumber/Phone/Email-t is kér — TODO összeállítani. + }, + // Default ÉRTÉK szándékosan nincs: be nem töltött config → 0. A 0 árfolyam a számításkor hibát dob, + // a 0 küszöb „mindent jelentünk" — egyik sem számol elavult, kódba égetett értékkel. + EurHufRate = c.GetValue("ExchangeRate:EurHuf"), + ThresholdWeightKg = c.GetValue("Thresholds:WeightKg"), + ThresholdValueHuf = c.GetValue("Thresholds:ValueHuf"), }; }); diff --git a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md index 760715e..a01943f 100644 --- a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md +++ b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_ISSUES.md @@ -27,3 +27,18 @@ Egy termékhez a kettő nem azonos; a `Gtin` oszlop VTSZ-ként való használata **Affected:** - `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-M6Y2: A számla-pénznem a Partnerből jön — jobb lenne a ShippingDocument-en + +**Status:** Open · **Priority:** P3 · **Type:** I (adatmodell) + +Az EKÁER tétel-érték HUF-ra számításához a forrás-pénznem jelenleg a **`Partner.Currency`**-ből jön (a beszállító alap-devizája). **Most 100%-ban megbízható** — a partner egyértelműen meghatározza a devizát. + +**Hatás:** működik; de a pénznem **konceptuálisan a szállítmányhoz / dokumentumhoz tartozik**, nem a partnerhez (egy partner elvileg többféle devizában is számlázhat). + +**Javítási irány:** külön **`Currency` oszlop a `ShippingDocument`-en**, és az `EkaerValueCalculator` / `ShippingToEkaerMapper` onnan olvasson; a `Partner.Currency` maradjon fallback. **Nem prioritás** — a jelenlegi partner-alapú forrás elegendő. + +**Affected:** +- `FruitBank.Common/Entities/ShippingDocument.cs` — jövőbeli `Currency` oszlop +- `FruitBank.Common/Services/Ekaer/EkaerValueCalculator.cs` + `ShippingToEkaerMapper.cs` — a deviza-forrás +- jelenlegi forrás: `PartnerBase.Currency` diff --git a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md index e78a6ca..4bf7412 100644 --- a/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md +++ b/Nop.Plugin.Misc.AIPlugin/docs/EKAER/EKAER_TODO.md @@ -17,17 +17,38 @@ A `ShippingToEkaerMapper` megírása előtt tisztázandó pontok. Minden sor a j |---|------|------------------------|----------------| | 1 | `tradeType` (E/I/D irány) | Bejövő = beszerzés. HU feladó → **D** (belföldi), egyébként → **I** (import). A feladó országát a `ShippingDocument.Partner` / `Country` adja. | Automatikusan az országból dőljön el, vagy kézzel (UI) állítható? A kimenő (**E**, export) egyelőre hatókörön kívül (lásd #7). | | 2 | Célhely (destination) | Hardcode-olt FruitBank-telephely (cím). | Ne hardcode legyen — konfigból/beállításból jöjjön. Honnan (plugin settings / nopCommerce warehouse)? | -| 3 | Érték (`value`, HUF) | `UnitPriceOnDocument × MeasuredQuantity`. | A bejövő tételek tipikusan **EUR**-ban — deviza + árfolyam (FX) konverzió kell HUF-ra. Árfolyam forrása/időpontja? Vevőnként egyeztetni. | +| 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. | | 7 | Kimenő irány (`Order` → EKÁER) | Egyelőre nincs. A vevő gyakran maga viszi el (saját fuvar) → nem mi jelentünk. | Mikor kell mégis nekünk export/kimenő (E) tradeCard-ot küldeni? | | 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ő. | | 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ő | Egyelőre **üresen hagyva** (opcionális mező) a deviza tisztázásáig (#3). | Megerősíteni, hogy a NAV elfogadja érték nélkül bejövő relációban, vagy kötelező-e. | +| 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ó. | **Affected:** - `ShippingToEkaerMapper` (`FruitBank.Common/Services/Ekaer/`) — a leképezés - `EkaerTradeCardValidator` / `EkaerSubmitService` / `EkaerManageService` (`AyCode.Services/Nav/Ekaer/`) — validáció + beküldés + HTTP (általános NAV-réteg) - `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) + +**Status:** Open · **Priority:** P2 · **Type:** T (adatmodell + mapping) + +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á: + +| 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 | + +**Jelenlegi (átmeneti):** a `CreateMissingEkaerHistories` egyetlen, globális küszöböt használ az `EkaerSettings`-ből (config: 200 kg / 250 000 Ft — a legszigorúbb, hogy ne legyen aluljelentés). + +**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.) + +**Affected:** +- `FruitBank.Common/Dtos/ProductDto.cs` — küszöb-kategória mező (enum, DB-ben) +- `EkaerSettings` / `EkaerValueCalculator` (`FruitBank.Common/Services/Ekaer/`) — kategória → küszöbérték leképezés +- `FruitBankDataController.CreateMissingEkaerHistories` — a kapu a tételek kategóriái szerint