feat(compliance): improve compliance
This commit is contained in:
@ -354,8 +354,8 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
const categoryId = invoice.reverseCharge ? 'AE' : 'S';
|
||||
this.appendElement(doc, taxCategoryNode, 'cbc:ID', categoryId);
|
||||
|
||||
// Add percent
|
||||
this.appendElement(doc, taxCategoryNode, 'cbc:Percent', rate.toString());
|
||||
// Add percent with 2 decimal places
|
||||
this.appendElement(doc, taxCategoryNode, 'cbc:Percent', rate.toFixed(2));
|
||||
|
||||
// Add tax exemption reason if reverse charge
|
||||
if (invoice.reverseCharge) {
|
||||
@ -474,8 +474,8 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
const categoryId = invoice.reverseCharge ? 'AE' : 'S';
|
||||
this.appendElement(doc, classifiedTaxCategoryNode, 'cbc:ID', categoryId);
|
||||
|
||||
// Tax percent
|
||||
this.appendElement(doc, classifiedTaxCategoryNode, 'cbc:Percent', item.vatPercentage.toString());
|
||||
// Tax percent with 2 decimal places
|
||||
this.appendElement(doc, classifiedTaxCategoryNode, 'cbc:Percent', item.vatPercentage.toFixed(2));
|
||||
|
||||
// Tax scheme
|
||||
const taxSchemeNode = doc.createElement('cac:TaxScheme');
|
||||
@ -637,6 +637,13 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
paymentMeans.appendChild(paymentId);
|
||||
}
|
||||
|
||||
// Add PaymentDueDate
|
||||
if (paymentInfo.paymentDueDate && !paymentMeans.getElementsByTagName('cbc:PaymentDueDate')[0]) {
|
||||
const dueDate = doc.createElement('cbc:PaymentDueDate');
|
||||
dueDate.textContent = paymentInfo.paymentDueDate;
|
||||
paymentMeans.appendChild(dueDate);
|
||||
}
|
||||
|
||||
// Add IBAN and BIC
|
||||
if (paymentInfo.iban || paymentInfo.bic) {
|
||||
let payeeAccount = paymentMeans.getElementsByTagName('cac:PayeeFinancialAccount')[0];
|
||||
@ -652,37 +659,42 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
payeeAccount.appendChild(iban);
|
||||
}
|
||||
|
||||
// Add BIC
|
||||
if (paymentInfo.bic) {
|
||||
// Add account name (must come after ID but before FinancialInstitutionBranch)
|
||||
if (paymentInfo.accountName && !payeeAccount.getElementsByTagName('cbc:Name')[0]) {
|
||||
const accountName = doc.createElement('cbc:Name');
|
||||
accountName.textContent = paymentInfo.accountName;
|
||||
// Insert after ID but before FinancialInstitutionBranch
|
||||
const id = payeeAccount.getElementsByTagName('cbc:ID')[0];
|
||||
const finInstBranch = payeeAccount.getElementsByTagName('cac:FinancialInstitutionBranch')[0];
|
||||
if (finInstBranch) {
|
||||
payeeAccount.insertBefore(accountName, finInstBranch);
|
||||
} else if (id && id.nextSibling) {
|
||||
payeeAccount.insertBefore(accountName, id.nextSibling);
|
||||
} else {
|
||||
payeeAccount.appendChild(accountName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add BIC and bank name
|
||||
if (paymentInfo.bic || paymentInfo.bankName) {
|
||||
let finInstBranch = payeeAccount.getElementsByTagName('cac:FinancialInstitutionBranch')[0];
|
||||
if (!finInstBranch) {
|
||||
finInstBranch = doc.createElement('cac:FinancialInstitutionBranch');
|
||||
payeeAccount.appendChild(finInstBranch);
|
||||
}
|
||||
|
||||
let finInst = finInstBranch.getElementsByTagName('cac:FinancialInstitution')[0];
|
||||
if (!finInst) {
|
||||
finInst = doc.createElement('cac:FinancialInstitution');
|
||||
finInstBranch.appendChild(finInst);
|
||||
// Add BIC as branch ID
|
||||
if (paymentInfo.bic && !finInstBranch.getElementsByTagName('cbc:ID')[0]) {
|
||||
const bicElement = doc.createElement('cbc:ID');
|
||||
bicElement.textContent = paymentInfo.bic;
|
||||
finInstBranch.appendChild(bicElement);
|
||||
}
|
||||
|
||||
if (!finInst.getElementsByTagName('cbc:ID')[0]) {
|
||||
const bic = doc.createElement('cbc:ID');
|
||||
bic.textContent = paymentInfo.bic;
|
||||
finInst.appendChild(bic);
|
||||
}
|
||||
}
|
||||
|
||||
// Add account name
|
||||
if (paymentInfo.accountName && !payeeAccount.getElementsByTagName('cbc:Name')[0]) {
|
||||
const accountName = doc.createElement('cbc:Name');
|
||||
accountName.textContent = paymentInfo.accountName;
|
||||
// Insert after ID
|
||||
const id = payeeAccount.getElementsByTagName('cbc:ID')[0];
|
||||
if (id && id.nextSibling) {
|
||||
payeeAccount.insertBefore(accountName, id.nextSibling);
|
||||
} else {
|
||||
payeeAccount.appendChild(accountName);
|
||||
// Add bank name
|
||||
if (paymentInfo.bankName && !finInstBranch.getElementsByTagName('cbc:Name')[0]) {
|
||||
const bankNameElement = doc.createElement('cbc:Name');
|
||||
bankNameElement.textContent = paymentInfo.bankName;
|
||||
finInstBranch.appendChild(bankNameElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -773,10 +785,69 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
*/
|
||||
private enhancePartyInformationUBL(doc: Document, invoice: TInvoice): void {
|
||||
// Enhance supplier party
|
||||
this.addContactToPartyUBL(doc, 'cac:AccountingSupplierParty', (invoice.from as any)?.metadata?.contactInformation);
|
||||
this.enhancePartyUBL(doc, 'cac:AccountingSupplierParty', invoice.from);
|
||||
|
||||
// Enhance customer party
|
||||
this.addContactToPartyUBL(doc, 'cac:AccountingCustomerParty', (invoice.to as any)?.metadata?.contactInformation);
|
||||
this.enhancePartyUBL(doc, 'cac:AccountingCustomerParty', invoice.to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances a party with GLN, additional identifiers, and contact info
|
||||
* @param doc XML document
|
||||
* @param partySelector Party selector
|
||||
* @param partyData Party data
|
||||
*/
|
||||
private enhancePartyUBL(doc: Document, partySelector: string, partyData: any): void {
|
||||
if (!partyData) return;
|
||||
|
||||
const partyContainer = doc.getElementsByTagName(partySelector)[0];
|
||||
if (!partyContainer) return;
|
||||
|
||||
const party = partyContainer.getElementsByTagName('cac:Party')[0];
|
||||
if (!party) return;
|
||||
|
||||
// Add GLN if available
|
||||
if (partyData.gln && !party.getElementsByTagName('cbc:EndpointID')[0]) {
|
||||
const endpointNode = doc.createElement('cbc:EndpointID');
|
||||
endpointNode.setAttribute('schemeID', '0088'); // GLN scheme ID
|
||||
endpointNode.textContent = partyData.gln;
|
||||
|
||||
// Insert as first child
|
||||
if (party.firstChild) {
|
||||
party.insertBefore(endpointNode, party.firstChild);
|
||||
} else {
|
||||
party.appendChild(endpointNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Add additional identifiers
|
||||
if (partyData.additionalIdentifiers && Array.isArray(partyData.additionalIdentifiers)) {
|
||||
for (const identifier of partyData.additionalIdentifiers) {
|
||||
const partyId = doc.createElement('cac:PartyIdentification');
|
||||
const id = doc.createElement('cbc:ID');
|
||||
if (identifier.scheme) {
|
||||
id.setAttribute('schemeID', identifier.scheme);
|
||||
}
|
||||
id.textContent = identifier.value;
|
||||
partyId.appendChild(id);
|
||||
|
||||
// Insert after EndpointID or at beginning
|
||||
const endpoint = party.getElementsByTagName('cbc:EndpointID')[0];
|
||||
if (endpoint && endpoint.nextSibling) {
|
||||
party.insertBefore(partyId, endpoint.nextSibling);
|
||||
} else if (party.firstChild) {
|
||||
party.insertBefore(partyId, party.firstChild);
|
||||
} else {
|
||||
party.appendChild(partyId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add contact information from metadata if not already present
|
||||
const contactInfo = partyData.metadata?.contactInformation;
|
||||
if (contactInfo) {
|
||||
this.addContactToPartyUBL(doc, partySelector, contactInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -847,6 +918,34 @@ export class UBLEncoder extends UBLBaseEncoder {
|
||||
|
||||
if (!itemMetadata) continue;
|
||||
|
||||
// Add OrderLineReference if available
|
||||
if (itemMetadata.orderLineReference && !line.getElementsByTagName('cac:OrderLineReference')[0]) {
|
||||
const orderLineRef = doc.createElement('cac:OrderLineReference');
|
||||
const lineId = doc.createElement('cbc:LineID');
|
||||
lineId.textContent = itemMetadata.orderLineReferenceId || '1';
|
||||
orderLineRef.appendChild(lineId);
|
||||
|
||||
if (itemMetadata.orderLineReference) {
|
||||
const orderRef = doc.createElement('cac:OrderReference');
|
||||
const orderId = doc.createElement('cbc:ID');
|
||||
orderId.textContent = itemMetadata.orderLineReference;
|
||||
orderRef.appendChild(orderId);
|
||||
orderLineRef.appendChild(orderRef);
|
||||
}
|
||||
|
||||
// Insert after ID
|
||||
const invoiceLineId = line.getElementsByTagName('cbc:ID')[0];
|
||||
if (invoiceLineId && invoiceLineId.nextSibling) {
|
||||
line.insertBefore(orderLineRef, invoiceLineId.nextSibling);
|
||||
} else {
|
||||
// Insert before InvoicedQuantity
|
||||
const quantity = line.getElementsByTagName('cbc:InvoicedQuantity')[0];
|
||||
if (quantity) {
|
||||
line.insertBefore(orderLineRef, quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const itemElement = line.getElementsByTagName('cac:Item')[0];
|
||||
if (!itemElement) continue;
|
||||
|
||||
|
Reference in New Issue
Block a user