@openade/fe
Version:
Fatturazione Elettronica - Electronic Invoicing for Sistema di Interscambio (SDI)
317 lines • 11.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CrossBorderManager = void 0;
exports.createCrossBorderManager = createCrossBorderManager;
exports.validateCrossBorderInvoice = validateCrossBorderInvoice;
exports.validateEUVATNumber = validateEUVATNumber;
const euvat_service_1 = require("./euvat.service");
class CrossBorderManager {
constructor(config = {}) {
this.config = {
enableEUVATValidation: true,
enableCountryValidation: true,
enableCurrencyValidation: true,
defaultCurrency: 'EUR',
euCountries: [
'AT',
'BE',
'BG',
'HR',
'CY',
'CZ',
'DK',
'EE',
'FI',
'FR',
'DE',
'GR',
'HU',
'IE',
'IT',
'LV',
'LT',
'LU',
'MT',
'NL',
'PL',
'PT',
'RO',
'SK',
'SI',
'ES',
'SE',
],
vatRates: {
IT: 22,
DE: 19,
FR: 20,
ES: 21,
NL: 21,
BE: 21,
AT: 20,
PT: 23,
FI: 24,
IE: 23,
GR: 24,
LU: 17,
MT: 18,
CY: 19,
EE: 20,
LV: 21,
LT: 21,
PL: 23,
CZ: 21,
SK: 20,
HU: 27,
SI: 22,
HR: 25,
RO: 19,
BG: 20,
DK: 25,
SE: 25,
},
...config,
};
this.euVatService = new euvat_service_1.EUVATService();
}
async validateCrossBorderInvoice(invoice) {
const errors = [];
const warnings = [];
const recommendations = [];
try {
const supplier = invoice.fatturaElettronicaHeader.cedentePrestatore;
const customer = invoice.fatturaElettronicaHeader.cessionarioCommittente;
const supplierCountry = this.extractCountry(supplier);
const customerCountry = this.extractCountry(customer);
if (this.config.enableCountryValidation) {
const countryValidation = this.validateCountries(supplierCountry, customerCountry);
errors.push(...countryValidation.errors);
warnings.push(...countryValidation.warnings);
}
const crossBorderData = this.determineCrossBorderData(supplier, customer, supplierCountry, customerCountry);
if (this.config.enableEUVATValidation && this.isEUCountry(customerCountry)) {
const vatValidation = await this.validateEUVATNumber(customer);
if (!vatValidation.valid) {
errors.push(`Invalid EU VAT number: ${vatValidation.error}`);
}
else {
crossBorderData.vatNumberValid = true;
}
}
if (this.config.enableCurrencyValidation) {
const currencyValidation = this.validateCurrency(invoice, crossBorderData);
errors.push(...currencyValidation.errors);
warnings.push(...currencyValidation.warnings);
}
recommendations.push(...this.generateRecommendations(crossBorderData));
return {
valid: errors.length === 0,
errors,
warnings,
crossBorderData,
recommendations,
};
}
catch (error) {
return {
valid: false,
errors: [error instanceof Error ? error.message : 'Unknown error'],
warnings,
recommendations,
};
}
}
async validateEUVATNumber(customer) {
try {
const vatNumber = customer.datiAnagrafici.idFiscaleIVA?.idCodice;
const countryCode = customer.datiAnagrafici.idFiscaleIVA?.idPaese;
if (!vatNumber || !countryCode) {
return {
valid: false,
validationDate: new Date().toISOString(),
error: 'VAT number or country code missing',
};
}
return await this.euVatService.validateVATNumber(vatNumber, countryCode);
}
catch (error) {
return {
valid: false,
validationDate: new Date().toISOString(),
error: error instanceof Error ? error.message : 'Unknown error',
};
}
}
getVATRate(countryCode) {
return this.config.vatRates?.[countryCode] || null;
}
isEUCountry(countryCode) {
return this.config.euCountries?.includes(countryCode) || false;
}
extractCountry(taxpayer) {
return taxpayer.sede?.nazione || 'IT';
}
validateCountries(supplierCountry, customerCountry) {
const errors = [];
const warnings = [];
if (!supplierCountry) {
errors.push('Supplier country is required');
}
if (!customerCountry) {
errors.push('Customer country is required');
}
if (supplierCountry && customerCountry) {
if (supplierCountry === customerCountry) {
warnings.push('Domestic transaction detected');
}
if (supplierCountry !== 'IT' && customerCountry !== 'IT') {
warnings.push('Third-country transaction detected');
}
}
return { errors, warnings };
}
determineCrossBorderData(supplier, customer, supplierCountry, customerCountry) {
const hasVATNumber = !!customer.DatiAnagrafici.idFiscaleIVA?.idCodice;
const isSupplierIT = supplierCountry === 'IT';
const isCustomerIT = customerCountry === 'IT';
const isCustomerEU = this.isEUCountry(customerCountry);
let transactionType = 'B2C';
let vatTreatment;
if (hasVATNumber) {
transactionType = 'B2B';
}
if (isSupplierIT && isCustomerIT) {
vatTreatment = 'DOMESTIC';
}
else if (isSupplierIT && isCustomerEU && hasVATNumber) {
vatTreatment = 'EU_B2B';
}
else if (isSupplierIT && isCustomerEU && !hasVATNumber) {
vatTreatment = 'EU_B2C';
}
else if (isSupplierIT && !isCustomerEU) {
vatTreatment = 'EXPORT';
}
else if (!isSupplierIT && isCustomerIT) {
vatTreatment = 'IMPORT';
}
else {
vatTreatment = 'EXPORT';
}
return {
supplierCountry,
customerCountry,
transactionType,
vatTreatment,
currency: this.config.defaultCurrency,
vatNumberValid: hasVATNumber,
};
}
validateCurrency(invoice, crossBorderData) {
const errors = [];
const warnings = [];
const invoiceCurrency = this.extractCurrency(invoice);
if (!invoiceCurrency) {
if (crossBorderData.vatTreatment !== 'DOMESTIC') {
warnings.push('Currency not specified for international transaction');
}
}
else {
if (crossBorderData.vatTreatment === 'DOMESTIC' && invoiceCurrency !== 'EUR') {
warnings.push('Non-EUR currency for domestic transaction');
}
}
return { errors, warnings };
}
extractCurrency(invoice) {
const body = Array.isArray(invoice.fatturaElettronicaBody)
? invoice.fatturaElettronicaBody[0]
: invoice.fatturaElettronicaBody;
if (body.datiPagamento && body.datiPagamento.length > 0) {
return body.datiPagamento[0].condizioniPagamento || null;
}
return null;
}
validateVATNumberFormat(vatNumber, countryCode) {
const patterns = {
IT: /^IT\d{11}$/,
DE: /^DE\d{9}$/,
FR: /^FR[A-Z0-9]{2}\d{9}$/,
ES: /^ES[A-Z0-9]\d{7}[A-Z0-9]$/,
NL: /^NL\d{9}B\d{2}$/,
BE: /^BE\d{10}$/,
AT: /^ATU\d{8}$/,
PT: /^PT\d{9}$/,
FI: /^FI\d{8}$/,
IE: /^IE\d[A-Z0-9+*]\d{5}[A-Z]$/,
GR: /^GR\d{9}$/,
LU: /^LU\d{8}$/,
MT: /^MT\d{8}$/,
CY: /^CY\d{8}[A-Z]$/,
EE: /^EE\d{9}$/,
LV: /^LV\d{11}$/,
LT: /^LT(\d{9}|\d{12})$/,
PL: /^PL\d{10}$/,
CZ: /^CZ\d{8,10}$/,
SK: /^SK\d{10}$/,
HU: /^HU\d{8}$/,
SI: /^SI\d{8}$/,
HR: /^HR\d{11}$/,
RO: /^RO\d{2,10}$/,
BG: /^BG\d{9,10}$/,
DK: /^DK\d{8}$/,
SE: /^SE\d{12}$/,
};
const pattern = patterns[countryCode];
return pattern ? pattern.test(vatNumber) : false;
}
formatAddress(address) {
return `${address.indirizzo}, ${address.cap} ${address.comune}, ${address.nazione}`;
}
generateRecommendations(crossBorderData) {
const recommendations = [];
if (crossBorderData.vatTreatment === 'EU_B2B') {
recommendations.push('Consider using reverse charge mechanism for EU B2B transactions');
}
if (crossBorderData.vatTreatment === 'EXPORT') {
recommendations.push('Verify export documentation requirements');
}
if (crossBorderData.vatTreatment === 'IMPORT') {
recommendations.push('Check import VAT obligations');
}
if (crossBorderData.currency !== 'EUR') {
recommendations.push('Consider currency exchange rate documentation');
}
return recommendations;
}
}
exports.CrossBorderManager = CrossBorderManager;
function createCrossBorderManager(config = {}) {
return new CrossBorderManager(config);
}
async function validateCrossBorderInvoice(invoice, config = {}) {
const manager = createCrossBorderManager(config);
return await manager.validateCrossBorderInvoice(invoice);
}
async function validateEUVATNumber(vatNumber, countryCode, config = {}) {
const manager = createCrossBorderManager(config);
const customer = {
DatiAnagrafici: {
idFiscaleIVA: {
idPaese: countryCode,
idCodice: vatNumber,
},
anagrafica: {
denominazione: 'Unknown',
},
},
sede: {
indirizzo: 'Unknown',
cap: '00000',
comune: 'Unknown',
nazione: countryCode,
},
};
return await manager.validateEUVATNumber(customer);
}
//# sourceMappingURL=crossborder.manager.js.map