EKAER: add site config, outgoing support, refactor settings

- Added "Site" section to appsettings.json for company location.
- Increased EKAER threshold values; updated phone format.
- Refactored to use IEkaerSettings DI and registered in startup.
- GenerateEkaerXmlDocument now supports both incoming and outgoing (order) documents.
- Updated EKAER_TODO.md to reflect config and outgoing mapping changes.
This commit is contained in:
Loretta 2026-06-12 07:54:33 +02:00
parent f1db5a9a99
commit cd49b572ad
3 changed files with 43 additions and 16 deletions

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using AyCode.Core.Extensions; using AyCode.Core.Extensions;
using AyCode.Core.Loggers; using AyCode.Core.Loggers;
using AyCode.Services.Nav.Ekaer;
using AyCode.Services.SignalRs; using AyCode.Services.SignalRs;
using DocumentFormat.OpenXml.Office2010.Excel; using DocumentFormat.OpenXml.Office2010.Excel;
using FruitBank.Common.Dtos; using FruitBank.Common.Dtos;
@ -44,7 +45,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
ILocalizationService localizationService, ILocalizationService localizationService,
PreOrderConversionService preorderConversionService, PreOrderConversionService preorderConversionService,
IFruitBankEkaerService fruitBankEkaerService, IFruitBankEkaerService fruitBankEkaerService,
EkaerSettings ekaerSettings, IEkaerSettings ekaerSettings,
IEnumerable<IAcLogWriterBase> logWriters) IEnumerable<IAcLogWriterBase> logWriters)
: BasePluginController, IFruitBankDataControllerServer : BasePluginController, IFruitBankDataControllerServer
{ {
@ -313,17 +314,27 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Controllers
} }
[SignalR(SignalRTags.GenerateEkaerXmlDocument)] [SignalR(SignalRTags.GenerateEkaerXmlDocument)]
public async Task<EkaerHistory> GenerateEkaerXmlDocument(int shippingDocumentId) public async Task<EkaerHistory> GenerateEkaerXmlDocument(int foreignKey, bool isOutgoing)
{ {
_logger.Detail($"GenerateEkaerXmlDocument invoked; shippingDocumentId: {shippingDocumentId}"); _logger.Detail($"GenerateEkaerXmlDocument invoked; foreignKey: {foreignKey}; isOutgoing: {isOutgoing}");
// A GetAll(true) a mapperhez kellő teljes gráfot tölti: Partner, Items+ProductDto, Shipping→járművek/fuvarozó. // Upsert: (ForeignKey + IsOutgoing) párra EGY rekord — az újragenerálás nem duplikál.
var shippingDocument = await ctx.ShippingDocuments.GetByIdAsync(shippingDocumentId, true) var ekaerHistory = await ctx.EkaerHistories.GetByForeignKey(foreignKey).FirstOrDefaultAsync(eh => eh.IsOutgoing == isOutgoing);
?? throw new ArgumentException($"ShippingDocument not found; id: {shippingDocumentId}", nameof(shippingDocumentId));
// Upsert: dokumentumonként EGY bejövő rekord (ForeignKey + IsOutgoing) — az újragenerálás nem duplikál. if (!isOutgoing)
var ekaerHistory = await ctx.EkaerHistories.GetByForeignKey(shippingDocumentId).FirstOrDefaultAsync(eh => !eh.IsOutgoing); {
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(shippingDocument, ekaerHistory); // 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);
}
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));
ekaerHistory = fruitBankEkaerService.GenerateEkaerXmlDocument(order, ekaerHistory);
}
if (ekaerHistory.Id > 0) await ctx.EkaerHistories.UpdateAsync(ekaerHistory); if (ekaerHistory.Id > 0) await ctx.EkaerHistories.UpdateAsync(ekaerHistory);
else await ctx.EkaerHistories.InsertAsync(ekaerHistory); else await ctx.EkaerHistories.InsertAsync(ekaerHistory);

View File

@ -44,6 +44,7 @@ using System.Net.Http.Headers;
using Mango.Nop.Core.Loggers; using Mango.Nop.Core.Loggers;
using AyCode.Services.Nav; using AyCode.Services.Nav;
using AyCode.Services.Nav.Ekaer; using AyCode.Services.Nav.Ekaer;
using AyCode.Services.Nav.Ekaer.Models;
using FruitBank.Common.Services.Ekaer; using FruitBank.Common.Services.Ekaer;
using FruitBank.Common.Server.Services.Ekaer; using FruitBank.Common.Server.Services.Ekaer;
using System.Security.Authentication; using System.Security.Authentication;
@ -193,12 +194,14 @@ public class PluginNopStartup : INopStartup
BaseUrl = c["BaseUrl"] ?? "https://import-test-b.ekaer.nav.gov.hu", // TEST; PROD: https://import.ekaer.nav.gov.hu 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. // EKÁER konfiguráció (cégadat + telephely + küszöb + árfolyam), configból (appsettings "Ekaer").
services.AddSingleton(sp => // IEkaerSettings-ként kötjük be (AyCode-kontraktus) — a fogyasztók az interfészt kérik, mint az INavCredentials-t.
services.AddSingleton<IEkaerSettings>(sp =>
{ {
var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer"); var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer");
var co = c.GetSection("Company"); var co = c.GetSection("Company");
var site = co.GetSection("Site");
return new EkaerSettings return new EkaerSettings
{ {
Company = new EkaerCompanyInfo Company = new EkaerCompanyInfo
@ -209,13 +212,26 @@ public class PluginNopStartup : INopStartup
PostalCode = co["PostalCode"], PostalCode = co["PostalCode"],
City = co["City"], City = co["City"],
Street = co["Street"], 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. // Telephely = fizikai fel-/lerakodási hely (bejövőnél lerakodás, kimenőnél felrakodás).
// Magyar címnél a NAV Name/VatNumber/Phone/Email-t is megköveteli. Ha = székhely, ugyanaz a cím.
Site = new LocationType
{
Name = co["Name"],
VatNumber = co["TaxId"],
Country = co["CountryCode"] ?? "HU",
ZipCode = site["PostalCode"],
City = site["City"],
Street = site["Street"],
Phone = co["Phone"],
Email = co["Email"],
},
}, },
// 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, // 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. // a 0 küszöb „mindent jelentünk" — egyik sem számol elavult, kódba égetett értékkel.
EurHufRate = c.GetValue<double>("ExchangeRate:EurHuf"), EurHufRate = c.GetValue<double>("ExchangeRate:EurHuf"),
ThresholdWeightKg = c.GetValue<double>("Thresholds:WeightKg"), ThresholdWeightKg = c.GetValue<double>("Thresholds:WeightKg"),
ThresholdValueHuf = c.GetValue<long>("Thresholds:ValueHuf"), ThresholdValueHuf = c.GetValue<int>("Thresholds:ValueHuf"),
}; };
}); });

View File

@ -16,12 +16,12 @@ A `ShippingToEkaerMapper` megírása előtt tisztázandó pontok. Minden sor a j
| # | Téma | Jelenlegi feltételezés | Nyitott kérdés | | # | Téma | Jelenlegi feltételezés | Nyitott kérdés |
|---|------|------------------------|----------------| |---|------|------------------------|----------------|
| 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). | | 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)? | | 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. | | 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ó. | | 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? | | 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. | | 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? | | 7 | Kimenő irány (`Order` → EKÁER) | **Implementálva** (`MapOrder`): belföldi értékesítés (**D**/**S**), mi=eladó, vevő=címzett; tömeg `OrderItemDto.GrossWeight`, érték nettó (UnitPriceExclTax × qty), jelenleg minden HUF (rate 1). | **Nyitott:** a vonó jármű / fuvarozó forrása — az Orderen NINCS; az OrderDto-ba bekötendő (a `MapOrder` `Vehicle`/`CarrierText` TODO-ja). Export (**E**) + külföldi deviza + a `Customer.CountryId` ISO-feloldása: 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ő. | | 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ő. | | 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ó. | | 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ó. |