EKÁER integration, product search, and schema updates

- Added EKÁER (NAV) service registrations and config to PluginNopStartup
- Extended MgProductDto/IMgProductDto with Sku property
- Raised min search term length to 3 in autocomplete endpoints
- Increased ProductSearchAutoComplete maxResults to 300
- Refactored product search to filter by AvailableQuantity > 0
- Closed GTIN/VTSZ issue in EKAER_ISSUES.md, moved to data model topic
- Added SCHEMA.md with Toon domain model, including Currency property for partners
This commit is contained in:
Loretta 2026-06-03 16:59:23 +02:00
parent d6c828262d
commit 6b105bb5ed
4 changed files with 75 additions and 31 deletions

View File

@ -969,8 +969,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[CheckPermission(StandardPermission.Customers.CUSTOMERS_VIEW)]
public virtual async Task<IActionResult> CustomerSearchAutoComplete(string term)
{
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
return Json(new List<object>());
if (string.IsNullOrWhiteSpace(term) || term.Length < 3) return Json(new List<object>());
const int maxResults = 15;
@ -1037,39 +1036,29 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
public virtual async Task<IActionResult> ProductSearchAutoComplete(string term)
{
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
return Json(new List<object>());
if (string.IsNullOrWhiteSpace(term) || term.Length < 3) return Json(new List<object>());
const int maxResults = 30;
const int maxResults = 300;
// Search products by name or SKU
var products = await _productService.SearchProductsAsync(
keywords: term,
pageIndex: 0,
pageSize: maxResults);
var products = await _productService.SearchProductsAsync(keywords: term, pageIndex: 0, pageSize: maxResults);
var result = new List<object>();
var productDtosById = await _dbContext.ProductDtos.GetAllByIds(products.Select(p => p.Id)).ToDictionaryAsync(k => k.Id, v => v);
//var productDtosById = await _dbContext.ProductDtos.GetAllByIds(products.Select(p => p.Id)).Where(x => x.AvailableQuantity > 0).ToDictionaryAsync(k => k.Id, v => v);
var productDtos = await _dbContext.ProductDtos.GetAllByIds(products.Select(p => p.Id)).ToArrayAsync();
foreach (var product in products)
{
var productDto = productDtosById[product.Id];
if (productDto != null)
{
if (productDto.AvailableQuantity > 0)
foreach (var productDto in productDtos.Where(x => x.AvailableQuantity > 0))
{
result.Add(new
{
label = $"{product.Name} [RENDELHETŐ: {productDto.AvailableQuantity} (R:{productDto.StockQuantity}/K:{productDto.IncomingQuantity})] [ÁR: {product.Price}]",
value = product.Id,
sku = product.Sku,
price = product.Price,
stockQuantity = product.StockQuantity,
label = $"{productDto.Name} [RENDELHETŐ: {productDto.AvailableQuantity} (R:{productDto.StockQuantity}/K:{productDto.IncomingQuantity})] [ÁR: {productDto.Price}]",
value = productDto.Id,
sku = productDto.Sku,
price = productDto.Price,
stockQuantity = productDto.StockQuantity,
availableQuantity = productDto.AvailableQuantity,
});
}
}
}
return Json(result);
}
@ -1078,8 +1067,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
public virtual async Task<IActionResult> PreOrderProductSearchAutoComplete(string term)
{
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
return Json(new List<object>());
if (string.IsNullOrWhiteSpace(term) || term.Length < 3) return Json(new List<object>());
const int maxResults = 30;
var today = DateTime.UtcNow.Date;
@ -1151,8 +1139,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
public virtual async Task<IActionResult> ProductSearchUnfilteredAutoComplete(string term)
{
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
return Json(new List<object>());
if (string.IsNullOrWhiteSpace(term) || term.Length < 3) return Json(new List<object>());
const int maxResults = 30;

View File

@ -41,6 +41,11 @@ using Nop.Web.Areas.Admin.Models.Catalog;
using Nop.Web.Areas.Admin.Models.Orders;
using System.Net.Http.Headers;
using Mango.Nop.Core.Loggers;
using AyCode.Services.Nav;
using AyCode.Services.Nav.Ekaer;
using FruitBank.Common.Services.Ekaer;
using FruitBank.Common.Server.Services.Ekaer;
using System.Security.Authentication;
namespace Nop.Plugin.Misc.FruitBankPlugin.Infrastructure;
@ -162,6 +167,44 @@ public class PluginNopStartup : INopStartup
services.AddScoped<FileStorageService>();
services.AddScoped<ICustomerCreditService, CustomerCreditService>();
// ── EKÁER (NAV közúti áruforgalom-bejelentés): map → validate → send ──────────
services.AddScoped<IShippingToEkaerMapper, ShippingToEkaerMapper>(); // FruitBank.Common — leképezés
services.AddScoped<IEkaerTradeCardValidator, EkaerTradeCardValidator>(); // AyCode.Services — NAV-validáció
services.AddScoped<IEkaerSubmitService, EkaerSubmitService>(); // AyCode.Services — validate→send
services.AddScoped<IFruitBankEkaerService, FruitBankEkaerService>(); // FruitBank.Common.Server — fogyasztó
services.AddHttpClient<EkaerManageService>() // AyCode.Services — NAV HTTP
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { SslProtocols = SslProtocols.Tls12 }); // a NAV TLS 1.2-t vár
// A NAV-fiók hitelesítő adatai + a saját telephely/raktár — konfigurációból (appsettings "Ekaer" szekció).
// ⚠️ Titkos kulcsok: secret store / appsettings, SOHA NEM a forráskódba.
services.AddSingleton<INavCredentials>(sp =>
{
var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer:Credentials");
return new NavCredentials
{
User = c["User"] ?? "",
Password = c["Password"] ?? "",
SigningKey = c["SigningKey"] ?? "",
TaxNumber = c["TaxNumber"] ?? "",
BaseUrl = c["BaseUrl"] ?? "https://import-test-b.ekaer.nav.gov.hu", // TEST; PROD: https://import.ekaer.nav.gov.hu
};
});
services.AddSingleton(sp =>
{
var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer:Company");
return new EkaerCompanyInfo
{
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.
};
});
services.AddControllersWithViews(options =>
{
options.Filters.AddService<PendingMeasurementCheckoutFilter>();

View File

@ -9,7 +9,10 @@ Scope: a FruitBank EKÁER-bejelentés szerver-oldali / nopCommerce-integrációs
## MGFBANKPLUG-EKAER-I-T3X8: A `Product.Gtin` átmenetileg a VTSZ-t tárolja — szétválasztandó
**Status:** Open · **Priority:** P3 · **Type:** I (adatmodell / átmeneti megoldás)
**Status:** Closed (2026-06-02) — superseded · **Priority:** P3 · **Type:** I (adatmodell / átmeneti megoldás)
### Resolution
**Áthelyezve** az általános adatmodell-topicba: **`FBANKAPP-DMODEL-I-P6X4`** (`FruitBank.Common/docs/DATAMODEL/DATAMODEL_ISSUES.md#fbankapp-dmodel-i-p6x4`). Indok: a GTIN/VTSZ szétválasztás **általános** adatmodell-kérdés, nem EKÁER-specifikus — az EKÁER csak felhasználója. A követés ott folytatódik; az alábbi leírás referencia.
Az EKÁER `tradeCardItem.productVtsz` (kötelező, 8 jegyű vámtarifaszám) forrása jelenleg a nopCommerce **`Product.Gtin`** oszlop (a `ProductDto.Gtin`-en keresztül). A GTIN és a VTSZ **fogalmilag különböző**:
- **GTIN** — globális kereskedelmi cikkszám (vonalkód-azonosító, EAN/UPC).

View File

@ -1,3 +1,10 @@
# Domain Model Schema (Toon Format)
> Part of `Nop.Plugin.Misc.FruitBankPlugin`. See `README.md` for project overview.
> Full domain model in Toon (Token-Oriented Object Notation) format — see `AyCode.Core/Serializers/Toons/README.md` (in AyCode.Core solution).
> This is the authoritative schema for entities, DTOs, and enums in the FruitBank domain.
> For behavioral documentation (workflows, lifecycles, event cascades) see `docs/DOMAIN_MODEL.md`.
@meta {
version = "1.0"
format = "toon"
@ -313,6 +320,8 @@
CountryCode: string
County: string
Created: DateTime
Currency: string
purpose: "ISO 4217 currency code the company trades and settles in with this partner (e.g. EUR, HUF). For supplier partners it is the source currency for converting shipping-item values to example: HUF in NAV EKÁER reporting."
Modified: DateTime
Name: string
PostalCode: string
@ -555,6 +564,8 @@
CountryCode: string
County: string
Created: DateTime
Currency: string
purpose: "ISO 4217 currency code the company trades and settles in with this partner (e.g. EUR, HUF). For supplier partners it is the source currency for converting shipping-item values to example: HUF in NAV EKÁER reporting."
Modified: DateTime
Name: string
PostalCode: string