almeira-web/facturacion-dian-hub/backend/src/domain/value-objects/Nit.ts

71 lines
2.0 KiB
TypeScript

import { ValidacionFiscalException } from '../exceptions/ValidacionFiscalException.js';
/**
* Value Object para el Número de Identificación Tributaria (NIT) en Colombia
* Incluye algoritmo de Módulo 11 oficial de la DIAN para cálculo y verificación del DV.
*/
export class Nit {
private readonly _numero: string;
private readonly _dv: string;
private static readonly PRIMOS_DIAN = [3, 7, 13, 17, 19, 23, 29, 37, 41, 43, 47, 53, 59, 67, 71];
constructor(numero: string, dv?: string) {
const cleanNumber = numero.replace(/\D/g, '');
if (!cleanNumber || cleanNumber.length < 5 || cleanNumber.length > 15) {
throw new ValidacionFiscalException(`El NIT suministrado "${numero}" no es válido. Debe contener entre 5 y 15 dígitos.`);
}
this._numero = cleanNumber;
const computedDv = Nit.calcularDigitoVerificacion(cleanNumber);
if (dv !== undefined && dv !== null && dv !== '') {
const cleanDv = dv.toString().trim();
if (cleanDv !== computedDv) {
throw new ValidacionFiscalException(
`Dígito de verificación inválido para el NIT ${cleanNumber}. Calculado: ${computedDv}, Proporcionado: ${cleanDv}`
);
}
this._dv = cleanDv;
} else {
this._dv = computedDv;
}
}
public static calcularDigitoVerificacion(nitLimpio: string): string {
let suma = 0;
const len = nitLimpio.length;
for (let i = 0; i < len; i++) {
const digito = parseInt(nitLimpio.charAt(len - 1 - i), 10);
suma += digito * Nit.PRIMOS_DIAN[i];
}
const residuo = suma % 11;
if (residuo === 0 || residuo === 1) {
return residuo.toString();
}
return (11 - residuo).toString();
}
public get numero(): string {
return this._numero;
}
public get dv(): string {
return this._dv;
}
public get nitCompleto(): string {
return `${this._numero}-${this._dv}`;
}
public toString(): string {
return this.nitCompleto;
}
public equals(otro: Nit): boolean {
return this._numero === otro._numero && this._dv === otro._dv;
}
}