UNPKG

@openade/common

Version:

Common types, validators, and XML builders for Italian fiscal receipts

196 lines 7.75 kB
"use strict"; 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