94 lines
4.5 KiB
TypeScript
94 lines
4.5 KiB
TypeScript
/**
|
|
* ════════════════════════════════════════════════════════════
|
|
* مُصادَق (Musadaq) — UBL 2.1 Generator Service
|
|
* ════════════════════════════════════════════════════════════
|
|
* يقوم بإنشاء ملفات XML المتوافقة مع معيار UBL 2.1 المطلوبة من
|
|
* دائرة ضريبة الدخل والمبيعات الأردنية (ISTD).
|
|
* ════════════════════════════════════════════════════════════
|
|
*/
|
|
|
|
import { Injectable } from '@nestjs/common';
|
|
import { create } from 'xmlbuilder2';
|
|
import { Invoice } from './entities/invoice.entity';
|
|
|
|
@Injectable()
|
|
export class UBLGeneratorService {
|
|
/**
|
|
* توليد UBL 2.1 XML لفاتورة مبيعات
|
|
*/
|
|
generateXML(invoice: Invoice, company: any): string {
|
|
const doc = create({ version: '1.0', encoding: 'UTF-8' })
|
|
.ele('Invoice', {
|
|
xmlns: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
|
|
'xmlns:cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
|
'xmlns:cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
|
})
|
|
.ele('cbc:UBLVersionID').txt('2.1').up()
|
|
.ele('cbc:CustomizationID').txt('TRX-1.0').up()
|
|
.ele('cbc:ID').txt(invoice.invoice_number || 'N/A').up()
|
|
.ele('cbc:IssueDate').txt(invoice.invoice_date?.toISOString().split('T')[0] || '').up()
|
|
.ele('cbc:InvoiceTypeCode').txt(invoice.ubl_type_code).up()
|
|
.ele('cbc:DocumentCurrencyCode').txt(invoice.currency_code).up()
|
|
|
|
// ── AccountingSupplierParty (المُصدر) ───────────────
|
|
.ele('cac:AccountingSupplierParty')
|
|
.ele('cac:Party')
|
|
.ele('cac:PartyIdentification')
|
|
.ele('cbc:ID').txt(company.tax_identification_number).up()
|
|
.up()
|
|
.ele('cac:PartyName')
|
|
.ele('cbc:Name').txt(company.name).up()
|
|
.up()
|
|
.ele('cac:PostalAddress')
|
|
.ele('cbc:StreetName').txt(company.address || '').up()
|
|
.ele('cac:Country')
|
|
.ele('cbc:IdentificationCode').txt('JO').up()
|
|
.up()
|
|
.up()
|
|
.up()
|
|
.up()
|
|
|
|
// ── AccountingCustomerParty (المشتري) ───────────────
|
|
.ele('cac:AccountingCustomerParty')
|
|
.ele('cac:Party')
|
|
.ele('cac:PartyIdentification')
|
|
.ele('cbc:ID').txt(invoice.buyer_tin || invoice.buyer_national_id || '').up()
|
|
.up()
|
|
.ele('cac:PartyName')
|
|
.ele('cbc:Name').txt(invoice.buyer_name || '').up()
|
|
.up()
|
|
.up()
|
|
.up()
|
|
|
|
// ── TaxTotal ───────────────────────────────────────
|
|
.ele('cac:TaxTotal')
|
|
.ele('cbc:TaxAmount', { currencyID: invoice.currency_code }).txt(invoice.tax_amount.toString()).up()
|
|
.up()
|
|
|
|
// ── LegalMonetaryTotal ─────────────────────────────
|
|
.ele('cac:LegalMonetaryTotal')
|
|
.ele('cbc:LineExtensionAmount', { currencyID: invoice.currency_code }).txt(invoice.subtotal.toString()).up()
|
|
.ele('cbc:TaxExclusiveAmount', { currencyID: invoice.currency_code }).txt(invoice.subtotal.toString()).up()
|
|
.ele('cbc:TaxInclusiveAmount', { currencyID: invoice.currency_code }).txt(invoice.grand_total.toString()).up()
|
|
.ele('cbc:PayableAmount', { currencyID: invoice.currency_code }).txt(invoice.grand_total.toString()).up()
|
|
.up();
|
|
|
|
// ── InvoiceLines ─────────────────────────────────────
|
|
invoice.lines.forEach((line) => {
|
|
doc.ele('cac:InvoiceLine')
|
|
.ele('cbc:ID').txt(line.line_number.toString()).up()
|
|
.ele('cbc:InvoicedQuantity', { unitCode: 'PCE' }).txt(line.quantity.toString()).up()
|
|
.ele('cbc:LineExtensionAmount', { currencyID: invoice.currency_code }).txt(line.line_total.toString()).up()
|
|
.ele('cac:Item')
|
|
.ele('cbc:Description').txt(line.description).up()
|
|
.up()
|
|
.ele('cac:Price')
|
|
.ele('cbc:PriceAmount', { currencyID: invoice.currency_code }).txt(line.unit_price.toString()).up()
|
|
.up()
|
|
.up();
|
|
});
|
|
|
|
return doc.end({ prettyPrint: true });
|
|
}
|
|
}
|