@openade/common
Version:
Common types, validators, and XML builders for Italian fiscal receipts
196 lines • 7.75 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.validatePartitaIVA = validatePartitaIVA;
exports.validateCodiceFiscale = validateCodiceFiscale;
exports.validateAmount = validateAmount;
exports.validateVATRate = validateVATRate;
exports.validateISODate = validateISODate;
exports.validateISODateTime = validateISODateTime;
exports.validateCorrispettivi = validateCorrispettivi;
exports.validateDocumentoCommerciale = validateDocumentoCommerciale;
function validatePartitaIVA(partitaIVA) {
const cleaned = partitaIVA.replace(/\s/g, '').toUpperCase();
const regex = /^IT\d{11}$/;
return regex.test(cleaned);
}
function validateCodiceFiscale(codiceFiscale) {
const cleaned = codiceFiscale.replace(/\s/g, '').toUpperCase();
if (cleaned.length !== 16) {
return false;
}
const regex = /^[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]$/;
return regex.test(cleaned);
}
function validateAmount(amount) {
if (amount < 0) {
return false;
}
const decimals = (amount.toString().split('.')[1] || '').length;
return decimals <= 2;
}
function validateVATRate(rate) {
return rate >= 0 && rate <= 100;
}
function validateISODate(date) {
const regex = /^\d{4}-\d{2}-\d{2}$/;
if (!regex.test(date)) {
return false;
}
try {
const date_obj = new Date(date);
return !isNaN(date_obj.getTime());
}
catch {
return false;
}
}
function validateISODateTime(dateTime) {
const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$/;
if (!regex.test(dateTime)) {
return false;
}
try {
const date = new Date(dateTime);
return !isNaN(date.getTime());
}
catch {
return false;
}
}
function validateCorrispettivi(corrispettivi) {
const errors = [];
if (!corrispettivi.versione) {
errors.push('Version is required');
}
if (!corrispettivi.contribuente?.partitaIVA) {
errors.push('Taxpayer VAT number is required');
}
else if (!validatePartitaIVA(corrispettivi.contribuente.partitaIVA)) {
errors.push('Invalid taxpayer VAT number');
}
if (!corrispettivi.identificativoPEM) {
errors.push('Emission point ID is required');
}
if (!corrispettivi.dataRiferimento) {
errors.push('Reference date is required');
}
else if (!validateISODate(corrispettivi.dataRiferimento)) {
errors.push('Invalid reference date format');
}
if (!corrispettivi.riepilogoIVA || corrispettivi.riepilogoIVA.length === 0) {
errors.push('VAT summary is required');
}
else {
corrispettivi.riepilogoIVA.forEach((riepilogo, index) => {
if (riepilogo.aliquotaIVA === undefined && !riepilogo.natura) {
errors.push(`VAT summary ${index}: Either VAT rate or nature code is required`);
}
if (riepilogo.imponibile === undefined || riepilogo.imponibile < 0) {
errors.push(`VAT summary ${index}: Invalid amount`);
}
if (riepilogo.aliquotaIVA !== undefined && !validateVATRate(riepilogo.aliquotaIVA)) {
errors.push(`VAT summary ${index}: Invalid VAT rate`);
}
});
}
if (corrispettivi.importoTotale === undefined || !validateAmount(corrispettivi.importoTotale)) {
errors.push('Invalid total amount');
}
const vatSumTotal = corrispettivi.riepilogoIVA.reduce((sum, riepilogo) => sum + (riepilogo.imponibile || 0) + (riepilogo.imposta || 0), 0);
const roundedVatSum = Math.round(vatSumTotal * 100) / 100;
const roundedTotal = Math.round(corrispettivi.importoTotale * 100) / 100;
if (Math.abs(roundedVatSum - roundedTotal) > 0.01) {
errors.push(`Total amount mismatch: expected ${roundedVatSum}, got ${roundedTotal}`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined,
};
}
function validateDocumentoCommerciale(documento) {
const errors = [];
if (!documento.versione) {
errors.push('Version is required');
}
if (!documento.contribuente?.partitaIVA) {
errors.push('Taxpayer VAT number is required');
}
else if (!validatePartitaIVA(documento.contribuente.partitaIVA)) {
errors.push('Invalid taxpayer VAT number');
}
if (!documento.identificativoPEM) {
errors.push('Emission point ID is required');
}
if (!documento.datiGenerali) {
errors.push('General data is required');
}
else {
if (!documento.datiGenerali.numero) {
errors.push('Document number is required');
}
if (!documento.datiGenerali.dataOra) {
errors.push('Date and time are required');
}
else if (!validateISODateTime(documento.datiGenerali.dataOra)) {
errors.push('Invalid date/time format');
}
if (!documento.datiGenerali.tipoDocumento) {
errors.push('Document type is required');
}
}
if (!documento.dettaglioLinee || documento.dettaglioLinee.length === 0) {
errors.push('At least one line item is required');
}
else {
documento.dettaglioLinee.forEach((linea, index) => {
if (!linea.descrizione) {
errors.push(`Line ${index + 1}: Description is required`);
}
if (linea.quantita === undefined || linea.quantita <= 0) {
errors.push(`Line ${index + 1}: Invalid quantity`);
}
if (linea.prezzoUnitario === undefined || !validateAmount(linea.prezzoUnitario)) {
errors.push(`Line ${index + 1}: Invalid unit price`);
}
if (linea.aliquotaIVA === undefined && !linea.natura) {
errors.push(`Line ${index + 1}: Either VAT rate or nature code is required`);
}
if (linea.aliquotaIVA !== undefined && !validateVATRate(linea.aliquotaIVA)) {
errors.push(`Line ${index + 1}: Invalid VAT rate`);
}
});
}
if (!documento.datiRiepilogo || documento.datiRiepilogo.length === 0) {
errors.push('VAT summary is required');
}
else {
documento.datiRiepilogo.forEach((riepilogo, index) => {
if (riepilogo.aliquotaIVA === undefined && !riepilogo.natura) {
errors.push(`VAT summary ${index}: Either VAT rate or nature code is required`);
}
if (riepilogo.imponibile === undefined || riepilogo.imponibile < 0) {
errors.push(`VAT summary ${index}: Invalid taxable amount`);
}
if (riepilogo.imposta === undefined || riepilogo.imposta < 0) {
errors.push(`VAT summary ${index}: Invalid tax amount`);
}
if (riepilogo.aliquotaIVA !== undefined && !validateVATRate(riepilogo.aliquotaIVA)) {
errors.push(`VAT summary ${index}: Invalid VAT rate`);
}
});
}
if (documento.importoTotale === undefined || !validateAmount(documento.importoTotale)) {
errors.push('Invalid total amount');
}
const vatSumTotal = documento.datiRiepilogo.reduce((sum, riepilogo) => sum + (riepilogo.imponibile || 0) + (riepilogo.imposta || 0), 0);
const roundedVatSum = Math.round(vatSumTotal * 100) / 100;
const roundedTotal = Math.round(documento.importoTotale * 100) / 100;
if (Math.abs(roundedVatSum - roundedTotal) > 0.01) {
errors.push(`Total amount mismatch: expected ${roundedVatSum}, got ${roundedTotal}`);
}
return {
valid: errors.length === 0,
errors: errors.length > 0 ? errors : undefined,
};
}
//# sourceMappingURL=index.js.map