einvoice/test/suite/einvoice_standards-compliance/test.std-06.fatturapa-12.ts

546 lines
23 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as path from 'path';
import { EInvoice } from '../../../ts/index.js';
import { PerformanceTracker } from '../../helpers/performance.tracker.instance.js';
import { CorpusLoader } from '../../helpers/corpus.loader.js';
tap.test('STD-06: FatturaPA 1.2 Compliance - should validate FatturaPA 1.2 standard compliance', async () => {
const einvoice = new EInvoice();
const performanceTracker = new PerformanceTracker('STD-06: FatturaPA 1.2 Compliance');
// Test 1: FatturaPA document structure validation
const documentStructure = await performanceTracker.measureAsync(
'fatturapa-document-structure',
async () => {
const fatturaPAStructure = {
rootElement: 'p:FatturaElettronica',
namespaces: {
'p': 'http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2',
'ds': 'http://www.w3.org/2000/09/xmldsig#',
'xsi': 'http://www.w3.org/2001/XMLSchema-instance',
},
version: '1.2',
mainSections: [
'FatturaElettronicaHeader', // Header with transmission and parties
'FatturaElettronicaBody', // Body with invoice details
],
headerSubsections: [
'DatiTrasmissione', // Transmission data
'CedentePrestatore', // Seller/Provider
'RappresentanteFiscale', // Tax representative (optional)
'CessionarioCommittente', // Buyer/Customer
'TerzoIntermediarioOSoggettoEmittente', // Third party intermediary (optional)
'SoggettoEmittente', // Issuing party
],
bodySubsections: [
'DatiGenerali', // General invoice data
'DatiBeniServizi', // Goods and services data
'DatiVeicoli', // Vehicle data (optional)
'DatiPagamento', // Payment data
'Allegati', // Attachments (optional)
],
};
return {
version: fatturaPAStructure.version,
namespaceCount: Object.keys(fatturaPAStructure.namespaces).length,
mainSectionCount: fatturaPAStructure.mainSections.length,
headerSubsectionCount: fatturaPAStructure.headerSubsections.length,
bodySubsectionCount: fatturaPAStructure.bodySubsections.length,
rootElement: fatturaPAStructure.rootElement,
};
}
);
expect(documentStructure.version).toEqual('1.2');
expect(documentStructure.rootElement).toEqual('p:FatturaElettronica');
// Test 2: Italian tax identifier validation
const taxIdentifierValidation = await performanceTracker.measureAsync(
'italian-tax-identifiers',
async () => {
const italianTaxRules = {
// Partita IVA (VAT number) validation
partitaIVA: {
pattern: /^IT[0-9]{11}$/,
description: 'Italian VAT number: IT + 11 digits',
algorithm: 'Luhn check digit',
example: 'IT12345678901',
},
// Codice Fiscale validation (individuals)
codiceFiscale: {
personalPattern: /^[A-Z]{6}[0-9]{2}[A-Z][0-9]{2}[A-Z][0-9]{3}[A-Z]$/,
companyPattern: /^[0-9]{11}$/,
description: 'Italian tax code for individuals (16 chars) or companies (11 digits)',
examples: ['RSSMRA85M01H501Z', '12345678901'],
},
// Codice Destinatario (recipient code)
codiceDestinatario: {
pattern: /^[A-Z0-9]{7}$/,
description: '7-character alphanumeric code for electronic delivery',
example: 'ABCDEFG',
fallback: '0000000', // For PEC delivery
},
// PEC (Certified email) validation
pecEmail: {
pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
description: 'Certified email for invoice delivery',
domain: '.pec.it domain preferred',
},
};
return {
ruleCount: Object.keys(italianTaxRules).length,
partitaIVAPattern: italianTaxRules.partitaIVA.pattern.toString(),
codiceFiscalePersonalLength: 16,
codiceFiscaleCompanyLength: 11,
codiceDestinatarioLength: 7,
fallbackCodiceDestinatario: italianTaxRules.codiceDestinatario.fallback,
};
}
);
expect(taxIdentifierValidation.codiceFiscalePersonalLength).toEqual(16);
expect(taxIdentifierValidation.fallbackCodiceDestinatario).toEqual('0000000');
// Test 3: FatturaPA document types and purposes
const documentTypeValidation = await performanceTracker.measureAsync(
'fatturapa-document-types',
async () => {
const documentTypes = {
// TipoDocumento values
tipoDocumento: {
'TD01': 'Fattura', // Invoice
'TD02': 'Acconto/Anticipo su fattura', // Advance payment
'TD03': 'Acconto/Anticipo su parcella', // Advance on fees
'TD04': 'Nota di Credito', // Credit note
'TD05': 'Nota di Debito', // Debit note
'TD06': 'Parcella', // Professional fee invoice
'TD16': 'Integrazione fattura reverse charge interno', // Reverse charge integration
'TD17': 'Integrazione/autofattura per acquisto servizi dall\'estero', // Self-billing for foreign services
'TD18': 'Integrazione per acquisto di beni intracomunitari', // Intra-EU goods integration
'TD19': 'Integrazione/autofattura per acquisto di beni ex art.17 c.2 DPR 633/72', // Self-billing art.17
'TD20': 'Autofattura per regolarizzazione e integrazione delle fatture', // Self-billing for regularization
'TD21': 'Autofattura per splafonamento', // Self-billing for threshold breach
'TD22': 'Estrazione beni da Deposito IVA', // Goods extraction from VAT warehouse
'TD23': 'Estrazione beni da Deposito IVA con versamento dell\'IVA', // VAT warehouse with VAT payment
'TD24': 'Fattura differita di cui all\'art.21 c.4 lett. a)', // Deferred invoice art.21
'TD25': 'Fattura differita di cui all\'art.21 c.4 lett. b)', // Deferred invoice art.21 (b)
'TD26': 'Cessione di beni ammortizzabili e per passaggi interni', // Transfer of depreciable goods
'TD27': 'Fattura per autoconsumo o per cessioni gratuite senza rivalsa', // Self-consumption invoice
},
// Causale values for credit/debit notes
causale: [
'Sconto/maggiorazione', // Discount/surcharge
'Reso', // Return
'Omesso/errato addebito IVA', // Missing/incorrect VAT charge
'Correzione dati fattura', // Invoice data correction
'Operazione inesistente', // Non-existent operation
],
};
return {
documentTypeCount: Object.keys(documentTypes.tipoDocumento).length,
causaleCount: documentTypes.causale.length,
mainTypes: ['TD01', 'TD04', 'TD05', 'TD06'], // Most common types
selfBillingTypes: ['TD17', 'TD18', 'TD19', 'TD20', 'TD21'], // Self-billing scenarios
};
}
);
expect(documentTypeValidation.documentTypeCount).toEqual(18);
expect(documentTypeValidation.mainTypes).toContain('TD01');
// Test 4: Italian VAT rules and rates
const vatRuleValidation = await performanceTracker.measureAsync(
'italian-vat-rules',
async () => {
const italianVATRules = {
// Standard VAT rates in Italy
vatRates: {
standard: '22.00', // Standard rate
reduced1: '10.00', // Reduced rate 1
reduced2: '5.00', // Reduced rate 2 (super-reduced)
reduced3: '4.00', // Reduced rate 3 (minimum)
zero: '0.00', // Zero rate
},
// VAT nature codes (Natura IVA)
naturaCodes: {
'N1': 'Escluse ex art.15', // Excluded per art.15
'N2': 'Non soggette', // Not subject to VAT
'N3': 'Non imponibili', // Not taxable
'N4': 'Esenti', // Exempt
'N5': 'Regime del margine', // Margin scheme
'N6': 'Inversione contabile', // Reverse charge
'N7': 'IVA assolta in altro stato UE', // VAT paid in other EU state
},
// Split payment scenarios
splitPayment: {
description: 'PA (Public Administration) split payment mechanism',
codes: ['S'], // SplitPayment = 'S'
application: 'Public sector invoices',
},
// Withholding tax (Ritenuta d\'Acconto)
withholding: {
types: ['RT01', 'RT02', 'RT03', 'RT04', 'RT05', 'RT06'],
rates: ['20.00', '23.00', '26.00', '4.00'],
causals: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
},
};
return {
standardVATRate: italianVATRules.vatRates.standard,
vatRateCount: Object.keys(italianVATRules.vatRates).length,
naturaCodeCount: Object.keys(italianVATRules.naturaCodes).length,
withholdingTypeCount: italianVATRules.withholding.types.length,
withholdingCausalCount: italianVATRules.withholding.causals.length,
splitPaymentSupported: true,
};
}
);
expect(vatRuleValidation.standardVATRate).toEqual('22.00');
expect(vatRuleValidation.splitPaymentSupported).toBeTrue();
// Test 5: Italian payment methods and terms
const paymentValidation = await performanceTracker.measureAsync(
'italian-payment-methods',
async () => {
const italianPaymentMethods = {
// Modalità Pagamento codes
paymentMethods: {
'MP01': 'Contanti', // Cash
'MP02': 'Assegno', // Check
'MP03': 'Assegno circolare', // Cashier's check
'MP04': 'Contanti presso Tesoreria', // Cash at Treasury
'MP05': 'Bonifico', // Bank transfer
'MP06': 'Vaglia cambiario', // Promissory note
'MP07': 'Bollettino bancario', // Bank bulletin
'MP08': 'Carta di pagamento', // Payment card
'MP09': 'RID', // Direct debit
'MP10': 'RID utenze', // Utility direct debit
'MP11': 'RID veloce', // Fast direct debit
'MP12': 'RIBA', // Bank collection
'MP13': 'MAV', // Payment slip
'MP14': 'Quietanza erario', // Tax office receipt
'MP15': 'Giroconto su conti di contabilità speciale', // Special accounting transfer
'MP16': 'Domiciliazione bancaria', // Bank domiciliation
'MP17': 'Domiciliazione postale', // Postal domiciliation
'MP18': 'Bollettino di c/c postale', // Postal current account
'MP19': 'SEPA Direct Debit', // SEPA DD
'MP20': 'SEPA Direct Debit CORE', // SEPA DD CORE
'MP21': 'SEPA Direct Debit B2B', // SEPA DD B2B
'MP22': 'Trattenuta su somme già riscosse', // Withholding on amounts already collected
},
// Payment terms validation
paymentTerms: {
maxDays: 60, // Maximum payment terms for PA
standardDays: 30, // Standard payment terms
latePenalty: 'Legislative Decree 231/2002', // Late payment interest
},
// IBAN validation for Italian banks
ibanValidation: {
pattern: /^IT[0-9]{2}[A-Z][0-9]{10}[0-9A-Z]{12}$/,
length: 27,
countryCode: 'IT',
},
};
return {
paymentMethodCount: Object.keys(italianPaymentMethods.paymentMethods).length,
maxPaymentDays: italianPaymentMethods.paymentTerms.maxDays,
ibanLength: italianPaymentMethods.ibanValidation.length,
sepaMethodCount: Object.keys(italianPaymentMethods.paymentMethods).filter(k => k.includes('SEPA')).length,
};
}
);
expect(paymentValidation.paymentMethodCount).toBeGreaterThan(20);
expect(paymentValidation.maxPaymentDays).toEqual(60);
// Test 6: Stamp duty (Bollo) requirements
const stampDutyValidation = await performanceTracker.measureAsync(
'stamp-duty-validation',
async () => {
const bolloRequirements = {
// When stamp duty applies
threshold: 77.47, // Euro threshold for stamp duty
rate: 2.00, // Euro amount for stamp duty
applicability: [
'Professional services (TD06)',
'Invoices > €77.47 to individuals',
'B2C transactions above threshold',
],
// Bollo payment methods
paymentMethods: {
virtual: 'Bollo virtuale', // Virtual stamp
physical: 'Marca da bollo fisica', // Physical stamp
},
// Exemptions
exemptions: [
'B2B transactions',
'VAT-liable customers',
'Public administration',
'Companies with VAT number',
],
// XML representation
xmlElement: 'DatiBollo',
fields: ['BolloVirtuale', 'ImportoBollo'],
};
return {
threshold: bolloRequirements.threshold,
rate: bolloRequirements.rate,
paymentMethodCount: Object.keys(bolloRequirements.paymentMethods).length,
exemptionCount: bolloRequirements.exemptions.length,
xmlElement: bolloRequirements.xmlElement,
};
}
);
expect(stampDutyValidation.threshold).toEqual(77.47);
expect(stampDutyValidation.rate).toEqual(2.00);
// Test 7: Administrative and geographic codes
const administrativeCodeValidation = await performanceTracker.measureAsync(
'administrative-codes',
async () => {
const italianCodes = {
// Province codes (Codice Provincia)
provinceCodes: [
'AG', 'AL', 'AN', 'AO', 'AR', 'AP', 'AT', 'AV', 'BA', 'BT', 'BL', 'BN', 'BG', 'BI', 'BO', 'BZ', 'BS', 'BR',
'CA', 'CL', 'CB', 'CI', 'CE', 'CT', 'CZ', 'CH', 'CO', 'CS', 'CR', 'KR', 'CN', 'EN', 'FM', 'FE', 'FI', 'FG',
'FC', 'FR', 'GE', 'GO', 'GR', 'IM', 'IS', 'SP', 'AQ', 'LT', 'LE', 'LC', 'LI', 'LO', 'LU', 'MC', 'MN', 'MS',
'MT', 'VS', 'ME', 'MI', 'MO', 'MB', 'NA', 'NO', 'NU', 'OG', 'OT', 'OR', 'PD', 'PA', 'PR', 'PV', 'PG', 'PU',
'PE', 'PC', 'PI', 'PT', 'PN', 'PZ', 'PO', 'RG', 'RA', 'RC', 'RE', 'RI', 'RN', 'RM', 'RO', 'SA', 'SS', 'SV',
'SI', 'SR', 'SO', 'TA', 'TE', 'TR', 'TO', 'TP', 'TN', 'TV', 'TS', 'UD', 'VA', 'VE', 'VB', 'VC', 'VR', 'VV',
'VI', 'VT'
],
// Italian municipalities (sample)
municipalities: [
'Roma', 'Milano', 'Napoli', 'Torino', 'Palermo', 'Genova', 'Bologna', 'Firenze', 'Bari', 'Catania'
],
// Country codes for foreign entities
countryCodes: ['IT', 'FR', 'DE', 'ES', 'US', 'CH', 'GB', 'CN', 'JP'],
// Currency codes (mainly EUR for Italy)
currencies: ['EUR', 'USD', 'GBP', 'CHF'],
// Professional order codes (Albo Professionale)
professionalOrders: [
'Avvocati', 'Commercialisti', 'Ingegneri', 'Architetti', 'Medici', 'Farmacisti', 'Notai'
],
};
return {
provinceCodeCount: italianCodes.provinceCodes.length,
municipalityCount: italianCodes.municipalities.length,
countryCodeCount: italianCodes.countryCodes.length,
currencyCount: italianCodes.currencies.length,
professionalOrderCount: italianCodes.professionalOrders.length,
mainCurrency: 'EUR',
};
}
);
expect(administrativeCodeValidation.provinceCodeCount).toBeGreaterThan(100);
expect(administrativeCodeValidation.mainCurrency).toEqual('EUR');
// Test 8: FatturaPA business rules
const businessRuleValidation = await performanceTracker.measureAsync(
'fatturapa-business-rules',
async () => {
const businessRules = {
// Mandatory fields validation
mandatoryFields: [
'Partita IVA or Codice Fiscale for seller',
'Codice Fiscale for buyer (individuals)',
'Partita IVA for buyer (companies)',
'Codice Destinatario or PEC',
'Progressive invoice number',
'Invoice date',
'Document type (TipoDocumento)',
],
// Cross-field validation rules
crossFieldRules: [
'If Natura IVA is specified, VAT rate must be 0',
'Split payment only for PA customers',
'Stamp duty required for B2C > €77.47',
'Withholding tax details must be complete',
'Payment method must match payment details',
'Currency must be consistent throughout document',
],
// Format validation rules
formatRules: [
'Amounts with 2-8 decimal places',
'Dates in YYYY-MM-DD format',
'Progressive number must be unique per year',
'VAT rates as percentages (0.00-100.00)',
'Quantities with up to 8 decimal places',
],
// Electronic delivery rules
deliveryRules: [
'Codice Destinatario for electronic delivery',
'PEC email as fallback for delivery',
'XML signature for legal validity',
'Sistema di Interscambio (SDI) compliance',
],
};
const totalRules = Object.values(businessRules).reduce((sum, rules) => sum + rules.length, 0);
return {
totalRules,
mandatoryFieldCount: businessRules.mandatoryFields.length,
crossFieldRuleCount: businessRules.crossFieldRules.length,
formatRuleCount: businessRules.formatRules.length,
deliveryRuleCount: businessRules.deliveryRules.length,
};
}
);
expect(businessRuleValidation.totalRules).toBeGreaterThan(20);
expect(businessRuleValidation.mandatoryFieldCount).toBeGreaterThanOrEqual(7);
// Test 9: Corpus validation - FatturaPA files
const corpusValidation = await performanceTracker.measureAsync(
'corpus-validation',
async () => {
const results = {
total: 0,
bySource: {
eigor: 0,
official: 0,
},
byType: {
invoice: 0,
creditNote: 0,
},
fileTypes: {
xml: 0,
}
};
// Process FatturaPA corpus files
const eigorFiles = await CorpusLoader.loadPattern('**/*.xml', 'FATTURAPA_EIGOR');
const officialFiles = await CorpusLoader.loadPattern('**/*.xml', 'FATTURAPA_OFFICIAL');
results.bySource.eigor = eigorFiles.length;
results.bySource.official = officialFiles.length;
results.total = eigorFiles.length + officialFiles.length;
results.fileTypes.xml = results.total;
// Analyze file types
const allFiles = [...eigorFiles, ...officialFiles];
for (const file of allFiles) {
const filename = path.basename(file.path);
if (filename.includes('Credit') || filename.includes('creditnote')) {
results.byType.creditNote++;
} else {
results.byType.invoice++;
}
}
return results;
}
);
expect(corpusValidation.total).toBeGreaterThanOrEqual(0);
expect(corpusValidation.bySource.official).toBeGreaterThanOrEqual(0);
// Test 10: Sistema di Interscambio (SDI) integration
const sdiIntegration = await performanceTracker.measureAsync(
'sdi-integration',
async () => {
const sdiRequirements = {
// SDI endpoints
endpoints: {
production: 'https://ivaservizi.agenziaentrate.gov.it/ser/sdi/',
test: 'https://testservizi.agenziaentrate.gov.it/ser/sdi/',
},
// File naming convention
fileNaming: {
pattern: /^IT[0-9]{11}_[0-9A-Z]{5}\.(xml|xml\.p7m)$/,
example: 'IT12345678901_00001.xml',
description: 'Partita IVA + progressive number + extension',
},
// Response types from SDI
responseTypes: [
'RC - Ricevuta di Consegna', // Delivery receipt
'NS - Notifica di Scarto', // Rejection notification
'MC - Mancata Consegna', // Failed delivery
'NE - Notifica Esito', // Outcome notification
'DT - Decorrenza Termini', // Time expiry
],
// Digital signature requirements
digitalSignature: {
format: 'CAdES (PKCS#7)',
extension: '.p7m',
requirement: 'Optional but recommended',
certificateType: 'Qualified certificate',
},
// Size and format limits
limits: {
maxFileSize: '5MB',
maxAttachmentSize: '5MB',
encoding: 'UTF-8',
compression: 'ZIP allowed',
},
};
return {
endpointCount: Object.keys(sdiRequirements.endpoints).length,
responseTypeCount: sdiRequirements.responseTypes.length,
maxFileSize: sdiRequirements.limits.maxFileSize,
signatureFormat: sdiRequirements.digitalSignature.format,
fileNamingPattern: sdiRequirements.fileNaming.pattern.toString(),
};
}
);
expect(sdiIntegration.responseTypeCount).toBeGreaterThanOrEqual(5);
expect(sdiIntegration.maxFileSize).toEqual('5MB');
// Generate summary
console.log('\n📊 FatturaPA 1.2 Compliance Test Summary:');
console.log(`✅ Total operations: 10`);
console.log(`🇮🇹 Document structure: v${documentStructure.version} with ${documentStructure.namespaceCount} namespaces`);
console.log(`🆔 Tax identifiers: Partita IVA, Codice Fiscale, ${taxIdentifierValidation.ruleCount} validation rules`);
console.log(`📄 Document types: ${documentTypeValidation.documentTypeCount} types including self-billing`);
console.log(`💰 VAT rates: ${vatRuleValidation.standardVATRate}% standard, ${vatRuleValidation.vatRateCount} rates total`);
console.log(`💳 Payment methods: ${paymentValidation.paymentMethodCount} methods, max ${paymentValidation.maxPaymentDays} days`);
console.log(`📮 Stamp duty: €${stampDutyValidation.rate} above €${stampDutyValidation.threshold} threshold`);
console.log(`🗺️ Geographic codes: ${administrativeCodeValidation.provinceCodeCount} provinces`);
console.log(`✅ Business rules: ${businessRuleValidation.totalRules} rules across all categories`);
console.log(`📁 Corpus files: ${corpusValidation.total} FatturaPA files (${corpusValidation.bySource.official} official)`);
console.log(`🏛️ SDI integration: ${sdiIntegration.responseTypeCount} response types, ${sdiIntegration.maxFileSize} limit`);
// Test completed
});
// Start the test
tap.start();
// Export for test runner compatibility
export default tap;