using System.Globalization; using System.Text.RegularExpressions; using GeradoresService.Core; namespace GeradoresService.Generators.Financeiro; /// /// Referência Multibanco: entidade (5) + referência (9) + montante. /// Os 2 últimos dígitos da referência são controlo: 98 - (Σ dígito × peso mod 97), /// calculado sobre entidade(5) + referência(7) + montante em cêntimos(8). /// public sealed partial class ReferenciaMultibancoGenerator : GeneratorBase { private static readonly int[] Weights = [51, 73, 17, 89, 38, 62, 45, 53, 15, 50, 5, 49, 34, 81, 76, 27, 90, 9, 30, 3]; public override string Id => "multibanco"; public override string Name => "Referência Multibanco"; public override string Category => Categories.Financeiro; public override string Description => "Entidade, referência e montante com dígitos de controlo válidos."; public override GeneratedItem Generate(GeneratorContext context) { var entity = (10000 + context.Random.Next(90000)).ToString(); var cents = 100 + context.Random.Next(50000); var reference = context.Digits(7); reference += CheckDigits(entity, reference, cents); var amount = (cents / 100m).ToString("0.00", CultureInfo.InvariantCulture); return new GeneratedItem($"{entity} {FormatReference(reference)} {amount}", new Dictionary { ["Entidade"] = entity, ["Referência"] = FormatReference(reference), ["Montante"] = $"{amount} €", }); } public static string CheckDigits(string entity, string reference7, long cents) { var chk = entity + reference7 + cents.ToString("D8"); var sum = 0; for (var i = 0; i < 20; i++) sum += (chk[i] - '0') * Weights[i]; return (98 - sum % 97).ToString("D2"); } private static string FormatReference(string r) => $"{r[..3]} {r[3..6]} {r[6..]}"; public override ValidationResult Validate(string value) { var m = Pattern().Match(value.Trim()); if (!m.Success) return ValidationResult.WrongFormat("Formato esperado: entidade (5 dígitos), referência (9 dígitos) e montante, ex.: 12345 123 456 789 10.00"); var entity = m.Groups["ent"].Value; var reference = Checksums.Clean(m.Groups["ref"].Value); var amount = decimal.Parse(m.Groups["val"].Value.Replace(',', '.'), CultureInfo.InvariantCulture); var cents = (long)Math.Round(amount * 100); if (cents > 99_999_999) return ValidationResult.WrongFormat("Montante demasiado elevado."); var expected = CheckDigits(entity, reference[..7], cents); var details = new List { $"Entidade: {entity}", $"Montante: {amount.ToString("0.00", CultureInfo.InvariantCulture)} €", $"Dígitos de controlo esperados: {expected}", }; return reference[7..] == expected ? ValidationResult.Valid($"{entity} {FormatReference(reference)} {amount.ToString("0.00", CultureInfo.InvariantCulture)}", "Referência Multibanco válida.", details) : ValidationResult.BadChecksum($"Dígitos de controlo inválidos: esperado {expected}, encontrado {reference[7..]}.", details); } [GeneratedRegex(@"^(?\d{5})[\s|;]+(?\d{3}\s?\d{3}\s?\d{3})[\s|;]+(?\d{1,6}(?:[.,]\d{1,2})?)\s*€?$")] private static partial Regex Pattern(); }