update
This commit is contained in:
@ -0,0 +1,552 @@
|
||||
import { tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as path from 'path';
|
||||
import { EInvoice } from '../../../ts/index.js';
|
||||
import { PerformanceTracker } from '../../helpers/performance.tracker.js';
|
||||
import { CorpusLoader } from '../../helpers/corpus.loader.js';
|
||||
|
||||
tap.test('STD-06: FatturaPA 1.2 Compliance - should validate FatturaPA 1.2 standard compliance', async (t) => {
|
||||
const einvoice = new EInvoice();
|
||||
const corpusLoader = new CorpusLoader();
|
||||
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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(documentStructure.result.version === '1.2', 'Should use FatturaPA version 1.2');
|
||||
t.ok(documentStructure.result.rootElement === 'p:FatturaElettronica', 'Should use correct root element');
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(taxIdentifierValidation.result.codiceFiscalePersonalLength === 16, 'Should support 16-char personal tax codes');
|
||||
t.ok(taxIdentifierValidation.result.fallbackCodiceDestinatario === '0000000', 'Should use correct PEC fallback code');
|
||||
|
||||
// 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
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(documentTypeValidation.result.documentTypeCount > 20, 'Should support all FatturaPA document types');
|
||||
t.ok(documentTypeValidation.result.mainTypes.includes('TD01'), 'Should support standard invoice type');
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(vatRuleValidation.result.standardVATRate === '22.00', 'Should use correct Italian standard VAT rate');
|
||||
t.ok(vatRuleValidation.result.splitPaymentSupported, 'Should support split payment mechanism');
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(paymentValidation.result.paymentMethodCount > 20, 'Should support all Italian payment methods');
|
||||
t.ok(paymentValidation.result.maxPaymentDays === 60, 'Should enforce PA payment term limits');
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(stampDutyValidation.result.threshold === 77.47, 'Should use correct stamp duty threshold');
|
||||
t.ok(stampDutyValidation.result.rate === 2.00, 'Should use correct stamp duty rate');
|
||||
|
||||
// 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',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(administrativeCodeValidation.result.provinceCodeCount > 100, 'Should support all Italian province codes');
|
||||
t.ok(administrativeCodeValidation.result.mainCurrency === 'EUR', 'Should use EUR as main currency');
|
||||
|
||||
// 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,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(businessRuleValidation.result.totalRules > 20, 'Should have comprehensive business rules');
|
||||
t.ok(businessRuleValidation.result.mandatoryFieldCount >= 7, 'Should enforce mandatory fields');
|
||||
|
||||
// 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.findFiles('fatturaPA/eigor', '**/*.xml');
|
||||
const officialFiles = await corpusLoader.findFiles('fatturaPA/official', '**/*.xml');
|
||||
|
||||
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);
|
||||
if (filename.includes('Credit') || filename.includes('creditnote')) {
|
||||
results.byType.creditNote++;
|
||||
} else {
|
||||
results.byType.invoice++;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(corpusValidation.result.total > 0, 'Should find FatturaPA corpus files');
|
||||
t.ok(corpusValidation.result.bySource.official > 0, 'Should have official FatturaPA samples');
|
||||
|
||||
// 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(),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
t.ok(sdiIntegration.result.responseTypeCount >= 5, 'Should support all SDI response types');
|
||||
t.ok(sdiIntegration.result.maxFileSize === '5MB', 'Should enforce SDI file size limits');
|
||||
|
||||
// Generate performance summary
|
||||
const summary = performanceTracker.getSummary();
|
||||
|
||||
console.log('\n📊 FatturaPA 1.2 Compliance Test Summary:');
|
||||
console.log(`✅ Total operations: ${summary.totalOperations}`);
|
||||
console.log(`⏱️ Total duration: ${summary.totalDuration}ms`);
|
||||
console.log(`🇮🇹 Document structure: v${documentStructure.result.version} with ${documentStructure.result.namespaceCount} namespaces`);
|
||||
console.log(`🆔 Tax identifiers: Partita IVA, Codice Fiscale, ${taxIdentifierValidation.result.ruleCount} validation rules`);
|
||||
console.log(`📄 Document types: ${documentTypeValidation.result.documentTypeCount} types including self-billing`);
|
||||
console.log(`💰 VAT rates: ${vatRuleValidation.result.standardVATRate}% standard, ${vatRuleValidation.result.vatRateCount} rates total`);
|
||||
console.log(`💳 Payment methods: ${paymentValidation.result.paymentMethodCount} methods, max ${paymentValidation.result.maxPaymentDays} days`);
|
||||
console.log(`📮 Stamp duty: €${stampDutyValidation.result.rate} above €${stampDutyValidation.result.threshold} threshold`);
|
||||
console.log(`🗺️ Geographic codes: ${administrativeCodeValidation.result.provinceCodeCount} provinces`);
|
||||
console.log(`✅ Business rules: ${businessRuleValidation.result.totalRules} rules across all categories`);
|
||||
console.log(`📁 Corpus files: ${corpusValidation.result.total} FatturaPA files (${corpusValidation.result.bySource.official} official)`);
|
||||
console.log(`🏛️ SDI integration: ${sdiIntegration.result.responseTypeCount} response types, ${sdiIntegration.result.maxFileSize} limit`);
|
||||
|
||||
console.log('\n🔍 Performance breakdown:');
|
||||
summary.operations.forEach(op => {
|
||||
console.log(` - ${op.name}: ${op.duration}ms`);
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
// Export for test runner compatibility
|
||||
export default tap;
|
Reference in New Issue
Block a user