Files
brazil-swift-ops/packages/utils/src/validation.ts
defiQUG 8c771da399 Initial implementation: Brazil SWIFT Operations Platform
- Complete monorepo structure with pnpm workspaces and Turborepo
- All packages implemented: types, utils, rules-engine, iso20022, treasury, risk-models, audit
- React web application with TypeScript and Tailwind CSS
- Full Brazil regulatory compliance (BCB requirements)
- ISO 20022 message support (pacs.008, pacs.009, pain.001)
- Treasury and subledger management
- Risk, capital, and liquidity stress allocation
- Audit logging and BCB reporting
- E&O +10% uplift implementation
2026-01-23 14:51:10 -08:00

152 lines
4.5 KiB
TypeScript

/**
* Brazilian ID validation utilities (CPF/CNPJ)
*/
/**
* Validate CPF (Cadastro de Pessoa Física) format and checksum
* CPF format: XXX.XXX.XXX-XX (11 digits)
*/
export function validateCPF(cpf: string): { valid: boolean; formatted?: string; error?: string } {
// Remove non-numeric characters
const cleaned = cpf.replace(/\D/g, '');
// Check length
if (cleaned.length !== 11) {
return { valid: false, error: 'CPF must have 11 digits' };
}
// Check for invalid patterns (all same digits)
if (/^(\d)\1{10}$/.test(cleaned)) {
return { valid: false, error: 'CPF cannot have all same digits' };
}
// Validate checksum digits
let sum = 0;
let remainder: number;
// Validate first check digit
for (let i = 1; i <= 9; i++) {
sum += parseInt(cleaned.substring(i - 1, i)) * (11 - i);
}
remainder = (sum * 10) % 11;
if (remainder === 10 || remainder === 11) remainder = 0;
if (remainder !== parseInt(cleaned.substring(9, 10))) {
return { valid: false, error: 'Invalid CPF checksum (first digit)' };
}
// Validate second check digit
sum = 0;
for (let i = 1; i <= 10; i++) {
sum += parseInt(cleaned.substring(i - 1, i)) * (12 - i);
}
remainder = (sum * 10) % 11;
if (remainder === 10 || remainder === 11) remainder = 0;
if (remainder !== parseInt(cleaned.substring(10, 11))) {
return { valid: false, error: 'Invalid CPF checksum (second digit)' };
}
// Format CPF
const formatted = `${cleaned.substring(0, 3)}.${cleaned.substring(3, 6)}.${cleaned.substring(6, 9)}-${cleaned.substring(9, 11)}`;
return { valid: true, formatted };
}
/**
* Validate CNPJ (Cadastro Nacional da Pessoa Jurídica) format and checksum
* CNPJ format: XX.XXX.XXX/XXXX-XX (14 digits)
*/
export function validateCNPJ(cnpj: string): { valid: boolean; formatted?: string; error?: string } {
// Remove non-numeric characters
const cleaned = cnpj.replace(/\D/g, '');
// Check length
if (cleaned.length !== 14) {
return { valid: false, error: 'CNPJ must have 14 digits' };
}
// Check for invalid patterns (all same digits)
if (/^(\d)\1{13}$/.test(cleaned)) {
return { valid: false, error: 'CNPJ cannot have all same digits' };
}
// Validate checksum digits
let length = cleaned.length - 2;
let numbers = cleaned.substring(0, length);
const digits = cleaned.substring(length);
let sum = 0;
let pos = length - 7;
// Validate first check digit
for (let i = length; i >= 1; i--) {
sum += parseInt(numbers.charAt(length - i)) * pos--;
if (pos < 2) pos = 9;
}
let result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
if (result !== parseInt(digits.charAt(0))) {
return { valid: false, error: 'Invalid CNPJ checksum (first digit)' };
}
// Validate second check digit
length = length + 1;
numbers = cleaned.substring(0, length);
sum = 0;
pos = length - 7;
for (let i = length; i >= 1; i--) {
sum += parseInt(numbers.charAt(length - i)) * pos--;
if (pos < 2) pos = 9;
}
result = sum % 11 < 2 ? 0 : 11 - (sum % 11);
if (result !== parseInt(digits.charAt(1))) {
return { valid: false, error: 'Invalid CNPJ checksum (second digit)' };
}
// Format CNPJ
const formatted = `${cleaned.substring(0, 2)}.${cleaned.substring(2, 5)}.${cleaned.substring(5, 8)}/${cleaned.substring(8, 12)}-${cleaned.substring(12, 14)}`;
return { valid: true, formatted };
}
/**
* Validate Brazilian tax ID (CPF or CNPJ)
*/
export function validateBrazilianTaxId(taxId: string): {
valid: boolean;
type?: 'CPF' | 'CNPJ';
formatted?: string;
error?: string;
} {
const cleaned = taxId.replace(/\D/g, '');
if (cleaned.length === 11) {
const cpfResult = validateCPF(taxId);
return {
...cpfResult,
type: cpfResult.valid ? 'CPF' : undefined,
};
} else if (cleaned.length === 14) {
const cnpjResult = validateCNPJ(taxId);
return {
...cnpjResult,
type: cnpjResult.valid ? 'CNPJ' : undefined,
};
}
return {
valid: false,
error: 'Tax ID must be 11 digits (CPF) or 14 digits (CNPJ)',
};
}
/**
* Format tax ID for display (CPF or CNPJ)
*/
export function formatTaxId(taxId: string): string {
const cleaned = taxId.replace(/\D/g, '');
if (cleaned.length === 11) {
return `${cleaned.substring(0, 3)}.${cleaned.substring(3, 6)}.${cleaned.substring(6, 9)}-${cleaned.substring(9, 11)}`;
} else if (cleaned.length === 14) {
return `${cleaned.substring(0, 2)}.${cleaned.substring(2, 5)}.${cleaned.substring(5, 8)}/${cleaned.substring(8, 12)}-${cleaned.substring(12, 14)}`;
}
return taxId;
}