@stafyniaksacha/facturx

0.3.0 • Public • Published

Factur-X and Order-X JS library

Generate and extract Factur-X and Order-X invoices in TypeScript, using pdf-lib and libxmljs.

Usage

CLI

# Usage (Node.js)
npx @stafyniaksacha/facturx --help

# Usage (Bun)
bunx @stafyniaksacha/facturx --help
# Generate a Factur-X/Order-X PDF-A/3 invoice
npx @stafyniaksacha/facturx generate \
  --pdf input.pdf \
  --xml input.xml \
  --output output.pdf

# Extract a Factur-X/Order-X XML from a PDF
npx @stafyniaksacha/facturx extract input.pdf > output.xml

# Check a Factur-X/Order-X XML file, display validation errors
npx @stafyniaksacha/facturx check input.xml \
  --flavor facturx \ # autodetects the flavor if not provided
  --level en16931 # autodetects the level if not provided

Node.js

npm install @stafyniaksacha/facturx
import { readFile } from 'node:fs/promises'

import { check, extract, generate } from '@stafyniaksacha/facturx'

const pdf = await readFile('/path/to/input.pdf')
const xml = await readFile('/path/to/input.xml')

// Generate a Factur-X/Order-X PDF-A/3 invoice
const buffer = await generate({
  pdf, // string, buffer or PDFDocument
  xml, // string, buffer or XMLDocument

  // Optional
  check: true, // set to false to disable the check
  flavor: 'facturx', // autodetects the flavor if not provided
  level: 'en16931', // autodetects the level if not provided
  language: 'en-GB',
  meta: { // extracted from xml if not provided
    author: 'John Doe',
    title: 'John Doe',
    subject: 'John Doe',
    keywords: ['John', 'Doe'],
    date: new Date(),
  },
})

// Extract a Factur-X/Order-X XML from a PDF
const { filename, xml, flavor, level } = await extract({
  pdf, // string, buffer or PDFDocument

  // Optional
  check: true, // set to false to disable the check
  flavor: 'facturx', // autodetects the flavor if not provided
  level: 'en16931', // autodetects the level if not provided
})

// Extract a Factur-X/Order-X XML from a PDF
const { valid, errors, flavor, level } = await check({
  xml, // string, buffer or XMLDocument

  // Optional
  flavor: 'facturx', // autodetects the flavor if not provided
  level: 'en16931', // autodetects the level if not provided
})
import { invoiceToXml } from '@stafyniaksacha/facturx'
import {
  AmountType,
  CountryIDType,
  CrossIndustryInvoiceType,
  CurrencyCodeType,
  DateTimeType,
  DocumentCodeType,
  DocumentContextParameterType,
  ExchangedDocumentContextType,
  ExchangedDocumentType,
  HeaderTradeAgreementType,
  HeaderTradeDeliveryType,
  HeaderTradeSettlementType,
  IDType,
  SupplyChainTradeTransactionType,
  TaxCategoryCodeType,
  TaxTypeCodeType,
  TextType,
  TradeAddressType,
  TradePartyType,
  TradeSettlementHeaderMonetarySummationType,
  TradeTaxType,
} from '@stafyniaksacha/facturx/models'

// Create a FacturX model (minimum version)
const guidelineID = new IDType({ value: 'urn:factur-x.eu:1p0:minimum' })
const guidelineParameter = new DocumentContextParameterType({ id: guidelineID })
const documentContext = new ExchangedDocumentContextType({
  guidelineSpecifiedDocumentContextParameter: guidelineParameter
})

// Document
const invoiceID = new IDType({ value: 'INV-2023-001' })
const typeCode = new DocumentCodeType({ value: '380' })
const issueDT = new DateTimeType({ dateTimeString: '20230415', format: '102' })
const document = new ExchangedDocumentType({
  id: invoiceID,
  typeCode,
  issueDateTime: issueDT
})

// Seller and buyer
const sellerName = new TextType({ value: 'Acme Corporation' })
const sellerAddress = new TradeAddressType({
  countryID: new CountryIDType({ value: 'FR' })
})
const sellerParty = new TradePartyType({
  name: sellerName,
  postalTradeAddress: sellerAddress
})

const buyerName = new TextType({ value: 'Sample Customer' })
const buyerAddress = new TradeAddressType({
  countryID: new CountryIDType({ value: 'FR' })
})
const buyerParty = new TradePartyType({
  name: buyerName,
  postalTradeAddress: buyerAddress
})

// Trade agreement
const tradeAgreement = new HeaderTradeAgreementType({
  sellerTradeParty: sellerParty,
  buyerTradeParty: buyerParty
})

// Trade delivery
const tradeDelivery = new HeaderTradeDeliveryType({})

// Trade settlement
const currencyCode = new CurrencyCodeType({ value: 'EUR' })
const tradeTax = new TradeTaxType({
  categoryCode: new TaxCategoryCodeType({ value: 'S' }),
  typeCode: new TaxTypeCodeType({ value: 'VAT' }),
  rateApplicablePercent: { value: 20 }
})

const taxBasisTotalAmount = new AmountType({ value: 100, currencyID: 'EUR' })
const taxTotalAmount = new AmountType({ value: 20, currencyID: 'EUR' })
const grandTotalAmount = new AmountType({ value: 120, currencyID: 'EUR' })
const duePayableAmount = new AmountType({ value: 120, currencyID: 'EUR' })

const summation = new TradeSettlementHeaderMonetarySummationType({
  lineTotalAmount: new AmountType({ value: 100, currencyID: 'EUR' }),
  taxBasisTotalAmount: [taxBasisTotalAmount],
  taxTotalAmount: [taxTotalAmount],
  grandTotalAmount: [grandTotalAmount],
  duePayableAmount
})

const tradeSettlement = new HeaderTradeSettlementType({
  invoiceCurrencyCode: currencyCode,
  applicableTradeTax: [tradeTax],
  specifiedTradeSettlementHeaderMonetarySummation: summation
})

const transaction = new SupplyChainTradeTransactionType({
  applicableHeaderTradeAgreement: tradeAgreement,
  applicableHeaderTradeDelivery: tradeDelivery,
  applicableHeaderTradeSettlement: tradeSettlement
})

const invoice = new CrossIndustryInvoiceType({
  exchangedDocumentContext: documentContext,
  exchangedDocument: document,
  supplyChainTradeTransaction: transaction
})

// Convert the model to XML
const xml = await invoiceToXml(invoice)
const xmlString = xml.toString()

Usefull links

License

Based on original work of akretion/factur-x python library by Alexis de Lattre

/@stafyniaksacha/facturx/

    Package Sidebar

    Install

    npm i @stafyniaksacha/facturx

    Weekly Downloads

    16

    Version

    0.3.0

    License

    MIT

    Unpacked Size

    152 kB

    Total Files

    16

    Last publish

    Collaborators

    • stafyniaksacha