using AyCode.Services.Nav.Ekaer; namespace FruitBank.Common.Services.Ekaer; /// Egy szállítmány EKÁER bejelentés-kötelezettsége. public enum EkaerObligation { /// Kötelező bejelenteni (sort kell létrehozni). Required, /// Nem kötelező (belföld, küszöb alatt) — NEM hiba, nincs üzenet. NotRequired, /// A döntés nem hozható meg adathiány/formátumhiba miatt (pl. érvénytelen országkód) — NINCS sor, az okok az üzenetekben. DataError, } /// A kötelezettség-kiértékelés eredménye: a döntés + (csak DataError esetén) a felhasználónak szóló okok. public sealed class EkaerObligationResult { public EkaerObligation Obligation { get; private init; } public IReadOnlyList Errors { get; private init; } = []; public static EkaerObligationResult Required { get; } = new() { Obligation = EkaerObligation.Required }; public static EkaerObligationResult NotRequired { get; } = new() { Obligation = EkaerObligation.NotRequired }; public static EkaerObligationResult DataError(IReadOnlyList errors) => new() { Obligation = EkaerObligation.DataError, Errors = errors }; } /// /// EKÁER bejelentés-kötelezettség eldöntése egy normalizált -re — KÖZÖS a bejövő /// (document-csoport) és a kimenő (order) ágon. A NAV mindkét irányban büntet (a felesleges bejelentés ÉS a kimaradás /// is bírság), ezért: a bizonytalan adatot (érvénytelen országkód) NEM döntjük el magától → . /// /// /// Sorrend: (1) az AGGREGÁLT tömeg/érték küszöb FELETT → Required, az országkódtól FÜGGETLENÜL — belföld+küszöb /// felett és külföld egyaránt kötelező; az országkód-hibát ilyenkor a generate-validálás jelzi, a sor létrejön; /// (2) küszöb ALATT → itt számít az ország: hiányzó/érvénytelen országkód → DataError (a foreign-vs-belföld nem /// dönthető el); (3) küszöb alatt, érvényes országkódok: külföld (eltér) → Required, belföld (egyezik) → NotRequired. /// A 13/2020. (XII. 23.) PM rendelet alapján a küszöb feladó→címzett→jármű relációra aggregált — a hívó már /// így állítja össze a -t (bejövőnél a (Shipping, Partner) csoport tételei). /// public static class EkaerReportability { public static EkaerObligationResult Evaluate(EkaerConsignment consignment, IEkaerSettings settings) { ArgumentNullException.ThrowIfNull(consignment); ArgumentNullException.ThrowIfNull(settings); // (1) Küszöb FELETT → kötelező, FÜGGETLENÜL az országkódtól: belföld+küszöb felett ÉS külföld egyaránt kötelező. // Az országkód-hibát ilyenkor NEM itt blokkoljuk — a sor létrejön, a hibát a generate-validálás jelzi. var totalWeight = consignment.Lines.Sum(l => l.WeightKg); var totalValueHuf = consignment.Lines.Sum(l => l.ValueHuf ?? 0L); if (totalWeight >= settings.ThresholdWeightKg || totalValueHuf >= settings.ThresholdValueHuf) return EkaerObligationResult.Required; // (2) Küszöb ALATT: itt MÁR az ország dönt — csak a külföldi (eltérő országkód) reláció kötelező. Ehhez érvényes // ISO-2 országkódok kellenek; hiányzó/érvénytelen → a foreign-vs-belföld nem dönthető el → DataError. var subject = Subject(consignment); var errors = new List(); if (!IsValidCountry(consignment.Seller.CountryCode)) errors.Add($"{subject}: a feladó országkódja hiányzik vagy érvénytelen ('{consignment.Seller.CountryCode}')."); if (!IsValidCountry(consignment.Buyer.CountryCode)) errors.Add($"{subject}: a címzett országkódja hiányzik vagy érvénytelen ('{consignment.Buyer.CountryCode}')."); if (errors.Count > 0) return EkaerObligationResult.DataError(errors); // (3) Küszöb alatt, érvényes országkódok: külföld (eltér) → kötelező, belföld (egyezik) → nem kötelező (üzenet nélkül). return CountryEquals(consignment.Seller.CountryCode, consignment.Buyer.CountryCode) ? EkaerObligationResult.NotRequired : EkaerObligationResult.Required; } /// Érvényes EKÁER országkód: pontosan 2 ASCII betű (ISO-2). Üres / más hosszúságú → érvénytelen. private static bool IsValidCountry(string? code) { var c = code?.Trim(); return c is { Length: 2 } && c.All(char.IsAsciiLetter); } private static bool CountryEquals(string? a, string? b) => string.Equals(a?.Trim(), b?.Trim(), StringComparison.OrdinalIgnoreCase); private static string Subject(EkaerConsignment c) => c.IsOutgoing ? $"Rendelés #{c.ForeignKey}" : string.IsNullOrWhiteSpace(c.Seller.Name) ? $"Szállítólevél #{c.ForeignKey}" : c.Seller.Name!; }