@casoon/invoice-generator
Version:
Generate PDF invoices, XRechnung XML, and ZUGFeRD PDF/A-3 from JSON data with TypeScript support
192 lines • 9.37 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.XRechnungGenerator = void 0;
exports.generateXRechnungXML = generateXRechnungXML;
exports.generateXRechnungXMLFile = generateXRechnungXMLFile;
const fs_1 = __importDefault(require("fs"));
class XRechnungGenerator {
constructor(invoice) {
this.invoice = invoice;
}
/**
* Generiert eine XRechnung-XML-Datei nach dem deutschen E-Rechnungs-Standard
*/
generateXML() {
const xml = `
<rsm:CrossIndustryInvoice xmlns:rsm="urn:ferd:CrossIndustryDocument:invoice:2p0" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:TestIndicator>
<udt:Indicator>false</udt:Indicator>
</ram:TestIndicator>
<ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.3</ram:ID>
</ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:cen.eu:en16931:2017</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>${this.escapeXml(this.invoice.details.invoiceNumber)}</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">${this.formatDate(this.invoice.details.invoiceDate)}</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:IncludedSupplyChainTradeLineItem>
${this.generateLineItems()}
</ram:IncludedSupplyChainTradeLineItem>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>${this.escapeXml(this.invoice.sender.name)}</ram:Name>
<ram:PostalTradeAddress>
<ram:LineOne>${this.escapeXml(this.invoice.sender.address.street)}</ram:LineOne>
<ram:CityName>${this.escapeXml(this.invoice.sender.address.city)}</ram:CityName>
<ram:PostcodeCode>${this.escapeXml(this.invoice.sender.address.postalCode)}</ram:PostcodeCode>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="FC">${this.escapeXml(this.invoice.sender.vatId || '')}</ram:ID>
</ram:SpecifiedTaxRegistration>
<ram:DefinedTradeContact>
<ram:PersonName>${this.escapeXml(this.invoice.sender.businessOwner || '')}</ram:PersonName>
<ram:TelephoneUniversalCommunication>
<ram:CompleteNumber>${this.escapeXml(this.invoice.sender.contactInfo.phone)}</ram:CompleteNumber>
</ram:TelephoneUniversalCommunication>
<ram:EmailURIUniversalCommunication>
<ram:URIID>${this.escapeXml(this.invoice.sender.contactInfo.email)}</ram:URIID>
</ram:EmailURIUniversalCommunication>
</ram:DefinedTradeContact>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>${this.escapeXml(this.invoice.recipient.name)}</ram:Name>
<ram:PostalTradeAddress>
<ram:LineOne>${this.escapeXml(this.invoice.recipient.address.street)}</ram:LineOne>
<ram:CityName>${this.escapeXml(this.invoice.recipient.address.city)}</ram:CityName>
<ram:PostcodeCode>${this.escapeXml(this.invoice.recipient.address.postalCode)}</ram:PostcodeCode>
<ram:CountryID>DE</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
<ram:BuyerOrderReferencedDocument>
<ram:IssuerAssignedID>${this.escapeXml(this.invoice.details.customerNumber)}</ram:IssuerAssignedID>
</ram:BuyerOrderReferencedDocument>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeDelivery>
<ram:ActualDeliverySupplyChainEvent>
<ram:OccurrenceDateTime>
<udt:DateTimeString format="102">${this.formatDate(this.invoice.details.deliveryDate)}</udt:DateTimeString>
</ram:OccurrenceDateTime>
</ram:ActualDeliverySupplyChainEvent>
</ram:ApplicableHeaderTradeDelivery>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>${this.invoice.totals.currency}</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementPaymentMeans>
<ram:TypeCode>30</ram:TypeCode>
<ram:Information>${this.escapeXml(this.invoice.paymentInfo.bank)}</ram:Information>
<ram:PayerPartyDebtorFinancialAccount>
<ram:IBANID>${this.escapeXml(this.invoice.paymentInfo.iban)}</ram:IBANID>
</ram:PayerPartyDebtorFinancialAccount>
<ram:PayerSpecifiedDebtorFinancialInstitution>
<ram:BICID>${this.escapeXml(this.invoice.paymentInfo.bic)}</ram:BICID>
</ram:PayerSpecifiedDebtorFinancialInstitution>
</ram:SpecifiedTradeSettlementPaymentMeans>
<ram:ApplicableTradeSettlementFinancialMonetarySummation>
<ram:LineTotalAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:LineTotalAmount>
<ram:TaxBasisTotalAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="${this.invoice.totals.currency}">${this.invoice.totals.vatAmount.toFixed(2)}</ram:TaxTotalAmount>
<ram:GrandTotalAmount>${this.invoice.totals.grossAmount.toFixed(2)}</ram:GrandTotalAmount>
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount>
<ram:DuePayableAmount>${this.invoice.totals.grossAmount.toFixed(2)}</ram:DuePayableAmount>
</ram:ApplicableTradeSettlementFinancialMonetarySummation>
<ram:ApplicableTradeTax>
<ram:CalculatedAmount>${this.invoice.totals.vatAmount.toFixed(2)}</ram:CalculatedAmount>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:BasisAmount>${this.invoice.totals.netAmount.toFixed(2)}</ram:BasisAmount>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>${this.invoice.totals.vatRate.toFixed(2)}</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>`;
return xml;
}
/**
* Generiert die XML-Datei und speichert sie
*/
async generateXMLFile(outputPath) {
const xml = this.generateXML();
return new Promise((resolve, reject) => {
fs_1.default.writeFile(outputPath, xml, 'utf8', (err) => {
if (err) {
reject(err);
}
else {
resolve();
}
});
});
}
generateLineItems() {
return this.invoice.items.map((item, index) => `
<ram:AssociatedDocumentLineDocument>
<ram:LineID>${index + 1}</ram:LineID>
</ram:AssociatedDocumentLineDocument>
<ram:SpecifiedTradeProduct>
<ram:Name>${this.escapeXml(item.description)}</ram:Name>
</ram:SpecifiedTradeProduct>
<ram:SpecifiedLineTradeAgreement>
<ram:GrossPriceProductTradePrice>
<ram:ChargeAmount>${item.unitPrice.toFixed(2)}</ram:ChargeAmount>
<ram:BasisQuantity unitCode="${item.unit}">${item.quantity}</ram:BasisQuantity>
</ram:GrossPriceProductTradePrice>
</ram:SpecifiedLineTradeAgreement>
<ram:SpecifiedLineTradeDelivery>
<ram:BilledQuantity unitCode="${item.unit}">${item.quantity}</ram:BilledQuantity>
</ram:SpecifiedLineTradeDelivery>
<ram:SpecifiedLineTradeSettlement>
<ram:ApplicableTradeTax>
<ram:TypeCode>VAT</ram:TypeCode>
<ram:CategoryCode>S</ram:CategoryCode>
<ram:RateApplicablePercent>${item.vatRate.toFixed(2)}</ram:RateApplicablePercent>
</ram:ApplicableTradeTax>
<ram:SpecifiedTradeSettlementLineMonetarySummation>
<ram:LineTotalAmount>${item.total.toFixed(2)}</ram:LineTotalAmount>
</ram:SpecifiedTradeSettlementLineMonetarySummation>
</ram:SpecifiedLineTradeSettlement>`).join('');
}
escapeXml(text) {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
formatDate(dateString) {
// Konvertiert "DD.MM.YY" zu "YYYYMMDD"
const parts = dateString.split('.');
if (parts.length === 3) {
const day = parts[0].padStart(2, '0');
const month = parts[1].padStart(2, '0');
const year = '20' + parts[2]; // Annahme: YY ist 20YY
return `${year}${month}${day}`;
}
return dateString;
}
}
exports.XRechnungGenerator = XRechnungGenerator;
/**
* Convenience-Funktionen für einfache Verwendung
*/
async function generateXRechnungXML(invoice) {
const generator = new XRechnungGenerator(invoice);
return generator.generateXML();
}
async function generateXRechnungXMLFile(invoice, outputPath) {
const generator = new XRechnungGenerator(invoice);
return generator.generateXMLFile(outputPath);
}
//# sourceMappingURL=xrechnung-generator.js.map