using GeradoresService.Core; namespace GeradoresService.Generators.Financeiro; /// /// IBAN português: PT + 2 dígitos de controlo ISO 7064 (MOD 97-10) + NIB. /// A validação aceita IBAN de qualquer país (verifica comprimento conhecido e MOD 97). /// public sealed class IbanGenerator : NibGenerator { private static readonly Dictionary Lengths = new() { ["PT"] = 25, ["ES"] = 24, ["FR"] = 27, ["DE"] = 22, ["IT"] = 27, ["GB"] = 22, ["IE"] = 22, ["NL"] = 18, ["BE"] = 16, ["LU"] = 20, ["CH"] = 21, ["AT"] = 20, ["DK"] = 18, ["FI"] = 18, ["SE"] = 24, ["NO"] = 15, ["PL"] = 28, ["CZ"] = 24, ["GR"] = 27, ["HR"] = 21, ["RO"] = 24, ["MT"] = 31, ["CY"] = 28, ["SK"] = 24, ["SI"] = 19, ["HU"] = 28, ["BG"] = 22, ["EE"] = 20, ["LT"] = 20, ["LV"] = 21, ["IS"] = 26, ["LI"] = 21, ["MC"] = 27, ["SM"] = 27, ["BR"] = 29, }; public override string Id => "iban"; public override string Name => "IBAN"; public override string Description => "IBAN português (PT50 + NIB). Valida IBAN de qualquer país."; public override IReadOnlyList Options => [ .. base.Options, Option("formato", "Formato", "espacos", ("espacos", "PT50 0000 0000 …"), ("compacto", "PT500000…")), ]; public override GeneratedItem Generate(GeneratorContext context) { var iban = FromBban("PT", BuildNib(context)); return new GeneratedItem(context.Option(Options[1]) == "compacto" ? iban : Format(iban)); } public static string FromBban(string country, string bban) { var check = 98 - Checksums.Mod97(Checksums.LettersToDigits(bban + country) + "00"); return $"{country}{check:D2}{bban}"; } public static string Format(string iban) => string.Join(' ', iban.Chunk(4).Select(c => new string(c))); public override ValidationResult Validate(string value) { var iban = Checksums.Clean(value).ToUpperInvariant(); if (iban.Length < 15 || iban.Length > 34 || !char.IsAsciiLetter(iban[0]) || !char.IsAsciiLetter(iban[1]) || !char.IsAsciiDigit(iban[2]) || !char.IsAsciiDigit(iban[3]) || !iban.All(char.IsAsciiLetterOrDigit)) return ValidationResult.WrongFormat("Um IBAN tem código de país (2 letras), 2 dígitos de controlo e o BBAN."); var country = iban[..2]; if (Lengths.TryGetValue(country, out var expectedLength) && iban.Length != expectedLength) return ValidationResult.WrongFormat($"Um IBAN {country} tem {expectedLength} caracteres (tem {iban.Length})."); var rearranged = Checksums.LettersToDigits(iban[4..] + iban[..4]); if (Checksums.Mod97(rearranged) != 1) { var expected = FromBban(country, iban[4..])[2..4]; return ValidationResult.BadChecksum( $"Dígitos de controlo do IBAN inválidos: esperado {expected}, encontrado {iban[2..4]}.", ["Controlo: ISO 7064 MOD 97-10"]); } if (country == "PT") { var nibResult = ValidateNib(iban[4..], "IBAN"); return nibResult with { Normalized = nibResult.IsValid ? Format(iban) : null }; } return ValidationResult.Valid(Format(iban), $"IBAN válido ({country}).", ["Controlo: ISO 7064 MOD 97-10"]); } }