From d2bdf7e4ebfcfccfddb4fdd872266c948733273e Mon Sep 17 00:00:00 2001 From: Loretta Date: Wed, 3 Jun 2026 16:58:47 +0200 Subject: [PATCH] =?UTF-8?q?Refactor=20EK=C3=81ER=20mapping:=20unify=20comp?= =?UTF-8?q?any=20info,=20doc=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced ICompanyInfoBase for standardized company/partner data; refactored PartnerBase and interfaces to implement it - Replaced EkaerMappingOptions with EkaerCompanyInfo; updated all usages, constructors, and tests - Refactored EKÁER mapping logic to use ICompanyInfoBase; improved normalization and address handling - Added regex/validation for plate numbers and country codes; new error codes - Added Currency to PartnerBase; updated grids to display it - Updated ProductDto doc for GTIN/VTSZ data model issue - Enabled validation in CargoTruck grid - Added DMODEL topic docs: TOPIC_CODES.md, DATAMODEL_ISSUES.md, README.md - Removed obsolete files and updated settings.local.json - General code and doc improvements for maintainability --- .../TestModels/StockTakingTestModels.cs | 4 +++ AyCode.Core/Consts/AcErrorCode.cs | 2 ++ AyCode.Core/Consts/AcRegExpression.cs | 20 ++++++++++++ AyCode.Core/Consts/AcValidate.cs | 30 ++++++++++++++++++ AyCode.Core/Interfaces/ICompanyInfoBase.cs | 31 +++++++++++++++++++ AyCode.Services/Nav/Ekaer/EkaerCompanyInfo.cs | 27 ++++++++++++++++ AyCode.Services/Nav/NavCredentials.cs | 14 +++++++++ 7 files changed, 128 insertions(+) create mode 100644 AyCode.Core/Interfaces/ICompanyInfoBase.cs create mode 100644 AyCode.Services/Nav/Ekaer/EkaerCompanyInfo.cs create mode 100644 AyCode.Services/Nav/NavCredentials.cs diff --git a/AyCode.Core.Tests/TestModels/StockTakingTestModels.cs b/AyCode.Core.Tests/TestModels/StockTakingTestModels.cs index 3259f32..cb8fe4f 100644 --- a/AyCode.Core.Tests/TestModels/StockTakingTestModels.cs +++ b/AyCode.Core.Tests/TestModels/StockTakingTestModels.cs @@ -252,6 +252,8 @@ public interface IMgProductDto : IEntityInt string ShortDescription { get; set; } string FullDescription { get; set; } + public string Sku { get; set; } + int WarehouseId { get; set; } decimal Price { get; set; } int StockQuantity { get; set; } @@ -290,6 +292,8 @@ public abstract class MgProductDto : MgEntityBase, /*Product,*/ IMgProductDto//I public string ShortDescription { get; set; } public string FullDescription { get; set; } + public string Sku { get; set; } + public int WarehouseId { get; set; } public decimal Price { get; set; } public int StockQuantity { get; set; } diff --git a/AyCode.Core/Consts/AcErrorCode.cs b/AyCode.Core/Consts/AcErrorCode.cs index 4a14088..fda0909 100644 --- a/AyCode.Core/Consts/AcErrorCode.cs +++ b/AyCode.Core/Consts/AcErrorCode.cs @@ -52,6 +52,8 @@ DisableAddUser = 44, PhoneNumberFormatIsNotValid = 45, + PlateNumberFormatIsNotValid = 46, + CountryCodeFormatIsNotValid = 47, RefreshTokenUpdateError = 50, EmailIsNullOrEmpty = 55, diff --git a/AyCode.Core/Consts/AcRegExpression.cs b/AyCode.Core/Consts/AcRegExpression.cs index 87ffd41..7f73af8 100644 --- a/AyCode.Core/Consts/AcRegExpression.cs +++ b/AyCode.Core/Consts/AcRegExpression.cs @@ -12,9 +12,29 @@ public static partial class AcRegExpression //public const string PhoneNumberMask = @"\+(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\W*\d\W*\d\W*\d\W*\d\W*\d\W*\d\W*\d\W*\d\W*(\d{1,2})"; public const string PhoneNumberMask = @"\+(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\W*\d(\W*\d){1,14}"; + /// Rendszám (EKÁER-kompatibilis): nagybetű + szám (+ Ö Ő Ü Ű), 4–15 karakter — kötőjel/szóköz NÉLKÜL. + public const string PlateNumberMask = "[A-Z0-9ÖŐÜŰ]{4,15}"; + + /// Országkód, ISO 3166-1 alpha-2: pontosan 2 nagybetű (pl. HU). + public const string CountryCodeAlpha2Mask = "[A-Z]{2}"; + + /// Országkód: 1–3 nagybetű (pl. jármű-országkód az EKÁER-ben). + public const string CountryCodeMax3Mask = "[A-Z]{1,3}"; + [GeneratedRegex(AcRegExpression.EmailMask)] public static partial Regex EmailRegex(); [GeneratedRegex(AcRegExpression.PhoneNumberMask)] public static partial Regex PhoneNumberRegex(); + + // A Regex-eket horgonyozzuk (^…$): a const Mask nyers (a [RegularExpression] attribútum maga + // teljes-string egyezést vár), de az IsMatch különben részstringet is elfogadna. + [GeneratedRegex("^" + AcRegExpression.PlateNumberMask + "$")] + public static partial Regex PlateNumberRegex(); + + [GeneratedRegex("^" + AcRegExpression.CountryCodeAlpha2Mask + "$")] + public static partial Regex CountryCodeAlpha2Regex(); + + [GeneratedRegex("^" + AcRegExpression.CountryCodeMax3Mask + "$")] + public static partial Regex CountryCodeMax3Regex(); } \ No newline at end of file diff --git a/AyCode.Core/Consts/AcValidate.cs b/AyCode.Core/Consts/AcValidate.cs index f7c6b05..2d88393 100644 --- a/AyCode.Core/Consts/AcValidate.cs +++ b/AyCode.Core/Consts/AcValidate.cs @@ -68,6 +68,36 @@ namespace AyCode.Core.Consts return isValid; } + public static bool IsValidPlateNumberFormat(string? plateNumber, out AcErrorCode acErrorCode) + { + acErrorCode = AcErrorCode.Unset; + + var isValid = !string.IsNullOrWhiteSpace(plateNumber) && AcRegExpression.PlateNumberRegex().IsMatch(plateNumber); + + if (!isValid) acErrorCode = AcErrorCode.PlateNumberFormatIsNotValid; + return isValid; + } + + public static bool IsValidCountryCodeAlpha2Format(string? countryCode, out AcErrorCode acErrorCode) + { + acErrorCode = AcErrorCode.Unset; + + var isValid = !string.IsNullOrWhiteSpace(countryCode) && AcRegExpression.CountryCodeAlpha2Regex().IsMatch(countryCode); + + if (!isValid) acErrorCode = AcErrorCode.CountryCodeFormatIsNotValid; + return isValid; + } + + public static bool IsValidCountryCodeMax3Format(string? countryCode, out AcErrorCode acErrorCode) + { + acErrorCode = AcErrorCode.Unset; + + var isValid = !string.IsNullOrWhiteSpace(countryCode) && AcRegExpression.CountryCodeMax3Regex().IsMatch(countryCode); + + if (!isValid) acErrorCode = AcErrorCode.CountryCodeFormatIsNotValid; + return isValid; + } + public static bool IsValidUserTokenFormat(string verificationToken, out AcErrorCode acErrorCode) { acErrorCode = AcErrorCode.Unset; diff --git a/AyCode.Core/Interfaces/ICompanyInfoBase.cs b/AyCode.Core/Interfaces/ICompanyInfoBase.cs new file mode 100644 index 0000000..5f4c272 --- /dev/null +++ b/AyCode.Core/Interfaces/ICompanyInfoBase.cs @@ -0,0 +1,31 @@ +namespace AyCode.Core.Interfaces; + +/// +/// Postai címmel és adóazonosítóval azonosított fél (cég vagy partner) — általános, alsó-rétegű szerződés. +/// Bárhol használható, ahol egy felet név + adószám + postai cím alapján kell egységesen kezelni (számlázás, +/// szállítás, hatósági bejelentés stb.). Több, eltérő rétegű típus is implementálhatja (egy domain-entitás és +/// egy konfig-objektum egyaránt), így a feldolgozó kód egyetlen interfészen dolgozhat, a konkrét típus ismerete nélkül. +/// +public interface ICompanyInfoBase +{ + string Name { get; } + string TaxId { get; } + string CountryCode { get; } + string PostalCode { get; } + string City { get; } + string Street { get; } + + /// A + + egy sorba fűzve (a nem üres részekből). + string FullAddress { get; } +} + +/// Közös segédműveletek — hogy a FullAddress logikája EGY helyen éljen. +public static class CompanyInfoBaseExtensions +{ + /// A PostalCode + City + Street összefűzése egy sorba (a nem üres részekből); üres → null. + public static string? ComposeFullAddress(this ICompanyInfoBase company) + { + var joined = string.Join(" ", new[] { company.PostalCode, company.City, company.Street }.Where(p => !string.IsNullOrWhiteSpace(p))).Trim(); + return string.IsNullOrWhiteSpace(joined) ? null : joined; + } +} diff --git a/AyCode.Services/Nav/Ekaer/EkaerCompanyInfo.cs b/AyCode.Services/Nav/Ekaer/EkaerCompanyInfo.cs new file mode 100644 index 0000000..e7500f3 --- /dev/null +++ b/AyCode.Services/Nav/Ekaer/EkaerCompanyInfo.cs @@ -0,0 +1,27 @@ +using AyCode.Core.Interfaces; +using AyCode.Entities; +using AyCode.Services.Nav.Ekaer.Models; + +namespace AyCode.Services.Nav.Ekaer; + +/// +/// A NAV-bejelentő saját cégadatai — általános (bármely bejelentő), a DI konfigurációból tölti. +/// A leképezés -ként kezeli, ugyanúgy, mint a beszállító Partner-t. +/// +public sealed class EkaerCompanyInfo : ICompanyInfoBase +{ + public string? Name { get; set; } + public string? TaxId { get; set; } + + /// A bejelentő országkódja. A NAV EKÁER magyar rendszer → gyakorlatilag mindig "HU". + public string? CountryCode { get; set; } = "HU"; + + public string? PostalCode { get; set; } + public string? City { get; set; } + public string? Street { get; set; } + + public string? FullAddress => this.ComposeFullAddress(); + + /// A saját telephely / lerakodási hely. Magyar címnél a NAV a Name/VatNumber/Phone/Email-t is megköveteli. + public LocationType? UnloadLocation { get; set; } +} diff --git a/AyCode.Services/Nav/NavCredentials.cs b/AyCode.Services/Nav/NavCredentials.cs new file mode 100644 index 0000000..68f1706 --- /dev/null +++ b/AyCode.Services/Nav/NavCredentials.cs @@ -0,0 +1,14 @@ +namespace AyCode.Services.Nav; + +/// +/// Az egyszerű, beállítható megvalósítása — a hívó (DI) konfigurációból +/// (appsettings / secret store) tölti. ⚠️ Soha NE hardcode-old a kulcsokat a forráskódba. +/// +public sealed class NavCredentials : INavCredentials +{ + public string User { get; set; } = ""; + public string Password { get; set; } = ""; + public string SigningKey { get; set; } = ""; + public string TaxNumber { get; set; } = ""; + public string BaseUrl { get; set; } = ""; +}