feat(core): improve in-memory validation, FatturaPA detection coverage, and published type compatibility
This commit is contained in:
@@ -1,443 +1,310 @@
|
||||
# @fin.cx/einvoice 🚀
|
||||
# @fin.cx/einvoice ⚡
|
||||
|
||||
**The Ultimate TypeScript E-Invoicing Library for Europe** - Now with **100% EN16931 Compliance** ✅
|
||||
TypeScript e-invoicing for the real world: load invoice XML or hybrid PDFs, detect the format, map it into a typed invoice model, validate it, convert it, and embed XML back into PDFs.
|
||||
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](https://www.cen.eu/work/areas/ict/ebusiness/pages/einvoicing.aspx)
|
||||
[](https://github.com/fin-cx/einvoice)
|
||||
[](./license)
|
||||
`@fin.cx/einvoice` is built for programmers who need a practical toolkit around European e-invoice formats without writing a parser zoo from scratch.
|
||||
|
||||
Transform the chaos of European e-invoicing into pure TypeScript elegance. **@fin.cx/einvoice** is your battle-tested solution for creating, validating, and converting electronic invoices across all major European standards - with blazing fast performance and enterprise-grade reliability.
|
||||
## Issue Reporting and Security
|
||||
|
||||
## 🎯 Why @fin.cx/einvoice?
|
||||
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
||||
|
||||
- **🏆 100% EN16931 Compliant**: Full implementation of all 162 Business Terms and 32 Business Groups
|
||||
- **⚡ Blazing Fast**: Validate invoices in ~2.2ms, convert formats in ~0.6ms
|
||||
- **🔐 Enterprise Security**: XXE prevention, resource limits, path traversal protection
|
||||
- **🌍 Multi-Standard Support**: ZUGFeRD, Factur-X, XRechnung, PEPPOL BIS 3.0, UBL, and more
|
||||
- **💎 Decimal Precision**: Arbitrary precision arithmetic for perfect financial calculations
|
||||
- **🔄 Lossless Conversion**: 100% data preservation in round-trip conversions
|
||||
- **📦 PDF Magic**: Extract and embed XML in PDF/A-3 documents seamlessly
|
||||
- **🛠️ TypeScript First**: Fully typed with IntelliSense support throughout
|
||||
## Why this library?
|
||||
|
||||
## 🚀 Quick Start
|
||||
- 🚀 Load invoices from XML strings, files, or PDFs with embedded XML.
|
||||
- 🧭 Detect `ubl`, `xrechnung`, `cii`, `facturx`, `zugferd`, and `fatturapa` documents.
|
||||
- 🧾 Work on a typed in-memory invoice model based on `@tsclass/tsclass`.
|
||||
- ✅ Validate invoices on syntax, semantic, and business-rule levels.
|
||||
- 🔄 Export invoices as `facturx`, `zugferd`, `xrechnung`, `ubl`, or `cii`.
|
||||
- 📎 Extract XML from invoice PDFs and attach XML back into existing PDFs.
|
||||
- 🧱 Use the high-level `EInvoice` class or lower-level decoders, encoders, validators, and PDF extractors directly.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
# Using pnpm (recommended)
|
||||
pnpm add @fin.cx/einvoice
|
||||
|
||||
# Using npm
|
||||
npm install @fin.cx/einvoice
|
||||
|
||||
# Using yarn
|
||||
yarn add @fin.cx/einvoice
|
||||
```
|
||||
|
||||
### One-Minute Example
|
||||
`postinstall` will try to download Schematron validation resources on a best-effort basis when the package is installed in a normal online environment. Install will not fail if the resources cannot be downloaded.
|
||||
|
||||
```typescript
|
||||
import { EInvoice } from '@fin.cx/einvoice';
|
||||
Useful commands:
|
||||
|
||||
// Load from any source
|
||||
const invoice = await EInvoice.fromFile('invoice.xml'); // From file
|
||||
const invoice2 = await EInvoice.fromXml(xmlString); // From XML string
|
||||
const invoice3 = await EInvoice.fromPdf(pdfBuffer); // From PDF with embedded XML
|
||||
|
||||
// Validate with comprehensive EN16931 rules
|
||||
const validation = await invoice.validate();
|
||||
console.log(`Valid: ${validation.valid}`);
|
||||
|
||||
// Convert between any formats - losslessly!
|
||||
const xrechnung = await invoice.exportXml('xrechnung'); // For German B2G
|
||||
const peppol = await invoice.exportXml('ubl'); // For PEPPOL network
|
||||
const facturx = await invoice.exportXml('facturx'); // For France/Germany
|
||||
const zugferd = await invoice.exportXml('zugferd'); // For German standard
|
||||
|
||||
// Embed into PDF for hybrid invoices
|
||||
const pdfWithXml = await invoice.exportPdf('facturx');
|
||||
```bash
|
||||
pnpm download-schematron
|
||||
pnpm download-test-samples
|
||||
```
|
||||
|
||||
## 🏗️ Complete Invoice Creation
|
||||
Useful environment flag:
|
||||
|
||||
```typescript
|
||||
```bash
|
||||
EINVOICE_SKIP_RESOURCES=1
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
|
||||
|
||||
const invoice = await EInvoice.fromFile('./invoice.xml');
|
||||
|
||||
console.log(invoice.getFormat());
|
||||
console.log(invoice.id);
|
||||
console.log(invoice.from.name, '->', invoice.to.name);
|
||||
|
||||
const validation = await invoice.validate(ValidationLevel.BUSINESS);
|
||||
if (!validation.valid) {
|
||||
console.log(validation.errors);
|
||||
}
|
||||
|
||||
const xrechnungXml = await invoice.exportXml('xrechnung');
|
||||
```
|
||||
|
||||
## What it can do
|
||||
|
||||
### Load and inspect invoices
|
||||
|
||||
```ts
|
||||
import { EInvoice } from '@fin.cx/einvoice';
|
||||
|
||||
const fromXml = await EInvoice.fromXml(xmlString);
|
||||
const fromFile = await EInvoice.fromFile('./invoice.xml');
|
||||
const fromPdf = await EInvoice.fromPdf(pdfBuffer);
|
||||
|
||||
console.log(fromXml.subject);
|
||||
console.log(fromXml.items.length);
|
||||
console.log(fromXml.currency);
|
||||
```
|
||||
|
||||
### Create invoices in code
|
||||
|
||||
```ts
|
||||
import { EInvoice } from '@fin.cx/einvoice';
|
||||
|
||||
// Create a fully compliant invoice from scratch
|
||||
const invoice = new EInvoice();
|
||||
|
||||
// Essential metadata
|
||||
invoice.accountingDocId = 'INV-2025-001';
|
||||
invoice.issueDate = new Date('2025-01-15');
|
||||
invoice.accountingDocType = 'invoice';
|
||||
invoice.accountingDocId = 'INV-2026-001';
|
||||
invoice.issueDate = new Date('2026-04-16');
|
||||
invoice.currency = 'EUR';
|
||||
invoice.dueInDays = 30;
|
||||
invoice.dueInDays = 14;
|
||||
|
||||
// Seller information
|
||||
invoice.from = {
|
||||
type: 'company',
|
||||
name: 'Tech Solutions GmbH',
|
||||
name: 'Sender GmbH',
|
||||
description: '',
|
||||
status: 'active',
|
||||
foundedDate: { year: 2020, month: 1, day: 1 },
|
||||
address: {
|
||||
streetName: 'Innovation Street',
|
||||
houseNumber: '42',
|
||||
streetName: 'Example Street',
|
||||
houseNumber: '1',
|
||||
city: 'Berlin',
|
||||
postalCode: '10115',
|
||||
country: 'DE'
|
||||
country: 'DE',
|
||||
},
|
||||
registrationDetails: {
|
||||
vatId: 'DE123456789',
|
||||
registrationId: 'HRB 123456',
|
||||
registrationName: 'Tech Solutions GmbH'
|
||||
registrationName: 'Sender GmbH',
|
||||
},
|
||||
status: 'active'
|
||||
};
|
||||
|
||||
// Buyer information
|
||||
invoice.to = {
|
||||
type: 'company',
|
||||
name: 'Customer Corp SAS',
|
||||
name: 'Receiver SAS',
|
||||
description: '',
|
||||
status: 'active',
|
||||
foundedDate: { year: 2020, month: 1, day: 1 },
|
||||
address: {
|
||||
streetName: 'Rue de la Paix',
|
||||
streetName: 'Rue Example',
|
||||
houseNumber: '10',
|
||||
city: 'Paris',
|
||||
postalCode: '75001',
|
||||
country: 'FR'
|
||||
country: 'FR',
|
||||
},
|
||||
registrationDetails: {
|
||||
vatId: 'FR987654321',
|
||||
registrationId: 'RCS Paris 987654321'
|
||||
}
|
||||
vatId: 'FR12345678901',
|
||||
registrationId: 'RCS 123456789',
|
||||
registrationName: 'Receiver SAS',
|
||||
},
|
||||
};
|
||||
|
||||
// Payment details - SEPA ready
|
||||
invoice.paymentAccount = {
|
||||
iban: 'DE89370400440532013000',
|
||||
bic: 'COBADEFFXXX',
|
||||
accountName: 'Tech Solutions GmbH',
|
||||
institutionName: 'Commerzbank'
|
||||
};
|
||||
|
||||
// Line items with automatic calculations
|
||||
invoice.items = [
|
||||
{
|
||||
position: 1,
|
||||
name: 'Cloud Infrastructure Services',
|
||||
description: 'Monthly cloud hosting and support',
|
||||
articleNumber: 'CLOUD-PRO-001',
|
||||
unitQuantity: 1,
|
||||
unitNetPrice: 2500.00,
|
||||
name: 'Implementation work',
|
||||
articleNumber: 'IMPL-001',
|
||||
unitType: 'HUR',
|
||||
unitQuantity: 8,
|
||||
unitNetPrice: 120,
|
||||
vatPercentage: 19,
|
||||
unitType: 'MON' // Month
|
||||
},
|
||||
{
|
||||
position: 2,
|
||||
name: 'Professional Consulting',
|
||||
description: 'Architecture review and optimization',
|
||||
articleNumber: 'CONSULT-001',
|
||||
unitQuantity: 16,
|
||||
unitNetPrice: 150.00,
|
||||
vatPercentage: 19,
|
||||
unitType: 'HUR' // Hour
|
||||
}
|
||||
];
|
||||
|
||||
// Export to any format you need
|
||||
const zugferdXml = await invoice.exportXml('zugferd');
|
||||
const pdfWithXml = await invoice.exportPdf('facturx');
|
||||
const xml = await invoice.exportXml('facturx');
|
||||
```
|
||||
|
||||
## 🎨 Supported Standards & Formats
|
||||
### Validate at different levels
|
||||
|
||||
| Standard | Version | Status | Use Case |
|
||||
|----------|---------|--------|----------|
|
||||
| **EN16931** | 2017 | ✅ 100% Complete | Core European standard |
|
||||
| **ZUGFeRD** | 1.0, 2.0, 2.1 | ✅ Full Support | German B2B/B2C |
|
||||
| **Factur-X** | 1.0 (all profiles) | ✅ Full Support | France/Germany |
|
||||
| **XRechnung** | 2.0, 3.0 | ✅ Full Support | German public sector |
|
||||
| **PEPPOL BIS 3.0** | 3.0 | ✅ Full Support | Cross-border B2G |
|
||||
| **UBL** | 2.1 | ✅ Full Support | International |
|
||||
| **CII** | D16B | ✅ Full Support | Cross Industry |
|
||||
```ts
|
||||
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
|
||||
|
||||
### 📋 Factur-X Profile Support
|
||||
const invoice = await EInvoice.fromXml(xmlString);
|
||||
|
||||
```typescript
|
||||
// Automatic profile detection and validation
|
||||
const profiles = {
|
||||
MINIMUM: 'Essential fields only (BT-1, BT-2, BT-3)',
|
||||
BASIC: 'Core invoice with line items',
|
||||
BASIC_WL: 'Basic without lines (summary invoices)',
|
||||
EN16931: 'Full EN16931 compliance',
|
||||
EXTENDED: 'Additional structured data'
|
||||
};
|
||||
```
|
||||
|
||||
## 🔥 Power Features
|
||||
|
||||
### 🧮 Decimal Precision for Financial Accuracy
|
||||
|
||||
No more floating-point errors! Built-in arbitrary precision arithmetic:
|
||||
|
||||
```typescript
|
||||
// Perfect financial calculations every time
|
||||
const calculator = new DecimalCurrencyCalculator('EUR');
|
||||
const result = calculator.calculateLineNet(
|
||||
'3.14159', // Quantity
|
||||
'999.99', // Unit price
|
||||
'0' // Discount (optional)
|
||||
);
|
||||
// Result: 3141.56 (correctly rounded for EUR)
|
||||
```
|
||||
|
||||
### 🔍 Multi-Level Validation
|
||||
|
||||
```typescript
|
||||
// Three-layer validation with detailed diagnostics
|
||||
const syntaxResult = await invoice.validate(ValidationLevel.SYNTAX);
|
||||
const semanticResult = await invoice.validate(ValidationLevel.SEMANTIC);
|
||||
const businessResult = await invoice.validate(ValidationLevel.BUSINESS);
|
||||
|
||||
// Get specific rule violations
|
||||
businessResult.errors.forEach(error => {
|
||||
console.log(`Rule ${error.ruleId}: ${error.message}`);
|
||||
console.log(`Business Term: ${error.btReference}`);
|
||||
console.log(`Field: ${error.field}`);
|
||||
const syntax = await invoice.validate(ValidationLevel.SYNTAX);
|
||||
const semantic = await invoice.validate(ValidationLevel.SEMANTIC);
|
||||
const business = await invoice.validate(ValidationLevel.BUSINESS, {
|
||||
featureFlags: ['EN16931_BUSINESS_RULES', 'CODE_LIST_VALIDATION'],
|
||||
reportOnly: true,
|
||||
});
|
||||
|
||||
console.log(business.valid);
|
||||
console.log(business.errors);
|
||||
console.log(business.warnings ?? []);
|
||||
```
|
||||
|
||||
### 🔄 Format Detection & Conversion
|
||||
### Convert between supported XML targets
|
||||
|
||||
```typescript
|
||||
// Automatic format detection
|
||||
const format = FormatDetector.detectFormat(xmlString);
|
||||
console.log(`Detected: ${format}`); // 'zugferd', 'facturx', 'xrechnung', etc.
|
||||
```ts
|
||||
import { EInvoice } from '@fin.cx/einvoice';
|
||||
|
||||
// Intelligent conversion preserves all data
|
||||
const zugferd = await EInvoice.fromFile('zugferd.xml');
|
||||
const xrechnung = await zugferd.exportXml('xrechnung');
|
||||
const backToZugferd = await EInvoice.fromXml(xrechnung);
|
||||
// All data preserved through round-trip!
|
||||
const invoice = await EInvoice.fromFile('./source.xml');
|
||||
|
||||
const facturxXml = await invoice.exportXml('facturx');
|
||||
const zugferdXml = await invoice.exportXml('zugferd');
|
||||
const xrechnungXml = await invoice.exportXml('xrechnung');
|
||||
const ublXml = await invoice.exportXml('ubl');
|
||||
const ciiXml = await invoice.exportXml('cii');
|
||||
```
|
||||
|
||||
### 📄 PDF Operations
|
||||
### Work with PDFs
|
||||
|
||||
```typescript
|
||||
// Extract XML from PDF invoices
|
||||
const extractor = new PDFExtractor();
|
||||
const result = await extractor.extractXml(pdfBuffer);
|
||||
if (result.success) {
|
||||
console.log(`Found ${result.format} invoice`);
|
||||
const invoice = await EInvoice.fromXml(result.xml);
|
||||
```ts
|
||||
import { EInvoice, PDFExtractor } from '@fin.cx/einvoice';
|
||||
|
||||
const extracted = await new PDFExtractor().extractXml(pdfBuffer);
|
||||
if (extracted.success && extracted.xml) {
|
||||
const invoice = await EInvoice.fromXml(extracted.xml);
|
||||
console.log(extracted.format, invoice.id);
|
||||
}
|
||||
|
||||
// Embed XML into PDF for hybrid invoices
|
||||
const embedder = new PDFEmbedder();
|
||||
const pdfWithXml = await embedder.createPdfWithXml(
|
||||
existingPdf,
|
||||
xmlContent,
|
||||
'factur-x.xml',
|
||||
'Factur-X Invoice'
|
||||
);
|
||||
const invoice = await EInvoice.fromXml(xmlString);
|
||||
const pdfWithXml = await invoice.embedInPdf(existingPdfBuffer, 'facturx');
|
||||
```
|
||||
|
||||
## 🌍 Country-Specific Requirements
|
||||
## Supported formats
|
||||
|
||||
### 🇩🇪 German XRechnung
|
||||
| Format | Detect | Import | Export | Validation | Notes |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| `ubl` | Yes | Yes | Yes | Yes | Generic UBL flow |
|
||||
| `xrechnung` | Yes | Yes | Yes | Yes | UBL-based profile |
|
||||
| `cii` | Yes | Yes | Yes | Partial | Generic CII export currently uses the Factur-X encoder path |
|
||||
| `facturx` | Yes | Yes | Yes | Yes | Main CII-based generation path |
|
||||
| `zugferd` | Yes | Yes | Yes | Partial | Input supports v1 and v2+, export focuses on the current encoder |
|
||||
| `fatturapa` | Yes | No | No | Basic placeholder | Detection exists, decoder/encoder do not |
|
||||
|
||||
```typescript
|
||||
invoice.metadata = {
|
||||
customizationId: 'urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0',
|
||||
extensions: {
|
||||
leitwegId: '991-12345-67', // Required routing ID
|
||||
buyerReference: 'DE-BUYER-REF', // Mandatory
|
||||
sellerContact: {
|
||||
name: 'Max Mustermann',
|
||||
phone: '+49 30 12345678',
|
||||
email: 'invoice@company.de'
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
### Important format notes
|
||||
|
||||
### 🇪🇺 PEPPOL BIS 3.0
|
||||
- There is no dedicated root-level `peppol` export format. PEPPOL-specific checks exist, but the XML export targets are still `ubl` / `xrechnung`.
|
||||
- `FatturaPA` should be documented as detection-only right now.
|
||||
- `creditnote` / `debitnote` handling exists in parts of the lower-level code, but the high-level `invoiceType` setter on `EInvoice` only accepts `'invoice'`.
|
||||
|
||||
```typescript
|
||||
invoice.metadata = {
|
||||
profileId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0',
|
||||
extensions: {
|
||||
endpointId: '0088:1234567890128', // GLN with checksum
|
||||
documentTypeId: 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017',
|
||||
processId: 'urn:fdc:peppol.eu:2017:poacc:billing:01:1.0'
|
||||
}
|
||||
};
|
||||
```
|
||||
## Public API overview
|
||||
|
||||
### 🇫🇷 French Chorus Pro
|
||||
Top-level exports from `@fin.cx/einvoice` include:
|
||||
|
||||
```typescript
|
||||
invoice.metadata = {
|
||||
extensions: {
|
||||
siret: '12345678901234',
|
||||
serviceCode: 'SERVICE-2025',
|
||||
engagementNumber: 'ENG-123456',
|
||||
marketReference: 'MARKET-REF-001'
|
||||
}
|
||||
};
|
||||
```
|
||||
- `EInvoice`
|
||||
- `createEInvoice()`
|
||||
- `validateXml(xml, level?)`
|
||||
- `FormatDetector`
|
||||
- `PDFExtractor`, `PDFEmbedder`
|
||||
- `DecoderFactory`, `EncoderFactory`, `ValidatorFactory`
|
||||
- `BaseDecoder`, `BaseEncoder`, `BaseValidator`
|
||||
- `UBLBase*` and `CIIBase*` extension classes
|
||||
- `FacturX*` and `ZUGFeRD*` format-specific classes
|
||||
- `ValidationLevel`, `InvoiceFormat`
|
||||
- exported types like `TInvoice`, `ValidationResult`, `ValidationError`, `ExportFormat`, `IPdf`, `EInvoiceOptions`
|
||||
|
||||
## ⚡ Performance Metrics
|
||||
## Validation resources and install-time behavior
|
||||
|
||||
Lightning-fast operations with minimal memory footprint:
|
||||
The package includes an install bootstrap in `ts_install/` that tries to download validation resources into `assets_downloaded/schematron`.
|
||||
|
||||
| Operation | Speed | Memory |
|
||||
|-----------|-------|--------|
|
||||
| **Format Detection** | ~0.1ms | Minimal |
|
||||
| **XML Parsing** | ~0.5ms | ~100KB |
|
||||
| **Full Validation** | ~2.2ms | ~136KB |
|
||||
| **Format Conversion** | ~0.6ms | ~150KB |
|
||||
| **PDF Extraction** | ~5ms | ~1MB |
|
||||
| **PDF Embedding** | ~10ms | ~2MB |
|
||||
Behavior to know:
|
||||
|
||||
## 🏗️ Architecture
|
||||
- It only runs in a real `postinstall` lifecycle.
|
||||
- It skips cleanly when offline.
|
||||
- It skips when `dist_ts/` is not present yet.
|
||||
- It can be disabled in CI with `EINVOICE_SKIP_RESOURCES=1`.
|
||||
- Missing resources do not break install, but they can reduce advanced validation coverage.
|
||||
|
||||
### Plugin-Based Design
|
||||
|
||||
```typescript
|
||||
// Factory pattern for extensibility
|
||||
DecoderFactory.getDecoder(format) → BaseDecoder
|
||||
EncoderFactory.getEncoder(format) → BaseEncoder
|
||||
ValidatorFactory.getValidator(format) → BaseValidator
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Input (XML/PDF) → Format Detection → Decoder → EInvoice Model
|
||||
↓
|
||||
Validation
|
||||
↓
|
||||
Encoder → Output (XML/PDF)
|
||||
```
|
||||
|
||||
## 🔒 Security Features
|
||||
|
||||
- **XXE Prevention**: External entities disabled by default
|
||||
- **Resource Limits**: Max 100MB XML, max 100 nesting levels
|
||||
- **Path Traversal Protection**: Sanitized filenames in PDFs
|
||||
- **SSRF Mitigation**: Entity blocking in XML processing
|
||||
- **Input Validation**: Comprehensive input sanitization
|
||||
|
||||
## 🧪 Testing
|
||||
If you need the resources manually:
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pnpm test
|
||||
|
||||
# Run specific test suites
|
||||
pnpm test test/test.peppol-validator.ts
|
||||
pnpm test test/test.facturx-validator.ts
|
||||
pnpm test test/test.semantic-model.ts
|
||||
|
||||
# Run with verbose output
|
||||
pnpm test -- --verbose
|
||||
pnpm download-schematron
|
||||
pnpm download-test-samples
|
||||
```
|
||||
|
||||
## 📚 Advanced Examples
|
||||
## Architecture in one screen
|
||||
|
||||
### Batch Processing with Concurrency Control
|
||||
|
||||
```typescript
|
||||
import pLimit from 'p-limit';
|
||||
|
||||
const limit = pLimit(5); // Max 5 concurrent operations
|
||||
const files = ['invoice1.xml', 'invoice2.xml', /* ... */];
|
||||
|
||||
const results = await Promise.all(
|
||||
files.map(file =>
|
||||
limit(async () => {
|
||||
const invoice = await EInvoice.fromFile(file);
|
||||
const validation = await invoice.validate();
|
||||
return { file, valid: validation.valid };
|
||||
})
|
||||
)
|
||||
);
|
||||
```text
|
||||
XML / PDF
|
||||
-> format detection
|
||||
-> decoder
|
||||
-> EInvoice model
|
||||
-> validation / editing
|
||||
-> encoder
|
||||
-> XML / PDF with embedded XML
|
||||
```
|
||||
|
||||
### REST API Integration
|
||||
This repo also exposes the lower-level pieces separately so you can plug into the pipeline at the stage you actually need.
|
||||
|
||||
```typescript
|
||||
app.post('/api/invoice/convert', async (req, res) => {
|
||||
try {
|
||||
const { xml, targetFormat } = req.body;
|
||||
const invoice = await EInvoice.fromXml(xml);
|
||||
|
||||
// Validate before conversion
|
||||
const validation = await invoice.validate();
|
||||
if (!validation.valid) {
|
||||
return res.status(400).json({
|
||||
error: 'Invalid invoice',
|
||||
violations: validation.errors
|
||||
});
|
||||
}
|
||||
|
||||
const converted = await invoice.exportXml(targetFormat);
|
||||
res.json({ success: true, xml: converted });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
## Good things to know before using it
|
||||
|
||||
- `embedInPdf()` attaches XML to an existing PDF buffer. It does not render invoice PDFs from scratch.
|
||||
- `saveToFile('something.pdf', ...)` only works when `invoice.pdf` already exists, for example after loading from a PDF first.
|
||||
- Validation coverage is useful and broad, but this README deliberately does not claim blanket certification or “100% compliance”.
|
||||
- Schematron integration and richer business-rule layers exist, but they depend on downloaded resources and are not the same as guaranteed certification against every profile.
|
||||
|
||||
## Example workflow
|
||||
|
||||
```ts
|
||||
import { EInvoice, ValidationLevel } from '@fin.cx/einvoice';
|
||||
|
||||
const invoice = await EInvoice.fromFile('./incoming.pdf');
|
||||
|
||||
const validation = await invoice.validate(ValidationLevel.BUSINESS, {
|
||||
featureFlags: ['EN16931_BUSINESS_RULES', 'CODE_LIST_VALIDATION'],
|
||||
reportOnly: false,
|
||||
});
|
||||
|
||||
if (!validation.valid) {
|
||||
throw new Error(validation.errors.map((error) => error.message).join('\n'));
|
||||
}
|
||||
|
||||
const xmlForGermanB2G = await invoice.exportXml('xrechnung');
|
||||
```
|
||||
|
||||
## 🎯 What Makes Us Different
|
||||
## Module layout
|
||||
|
||||
### 🏆 100% EN16931 Compliance
|
||||
- All 162 Business Terms implemented
|
||||
- All 32 Business Groups structured
|
||||
- Complete semantic model with BT/BG validation
|
||||
- Official Schematron rules integrated
|
||||
|
||||
### 💎 Production Excellence
|
||||
- **500+ test cases** ensuring reliability
|
||||
- **Battle-tested** with real-world invoice corpus
|
||||
- **Memory efficient** - handles 1000+ line items
|
||||
- **Thread-safe** for concurrent processing
|
||||
|
||||
### 🚀 Developer Experience
|
||||
- **IntelliSense everywhere** - fully typed API
|
||||
- **Detailed error messages** with recovery hints
|
||||
- **Static factory methods** for intuitive usage
|
||||
- **Comprehensive documentation** with real examples
|
||||
|
||||
## 📦 Installation Requirements
|
||||
|
||||
- Node.js 18+ or modern browser
|
||||
- TypeScript 5.0+ (for TypeScript projects)
|
||||
- ~15MB installed size
|
||||
- Zero native dependencies
|
||||
|
||||
## 🤝 Standards Compliance
|
||||
|
||||
This library implements:
|
||||
- **EN 16931-1:2017** - Core invoice model
|
||||
- **CEN/TS 16931-3** - Syntax bindings
|
||||
- **ISO 4217** - Currency codes
|
||||
- **ISO 3166** - Country codes
|
||||
- **UN/ECE Rec 20** - Units of measure
|
||||
- **ISO 6523** - Organization identifiers
|
||||
- `ts/`: main published API and implementation
|
||||
- `ts_install/`: install-time resource bootstrap and download helpers
|
||||
- `assets_downloaded/`: downloaded validation resources
|
||||
- `test/`: corpus-heavy validation, format, PDF, performance, and security coverage
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
|
||||
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file.
|
||||
|
||||
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
||||
|
||||
### Trademarks
|
||||
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
|
||||
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
||||
|
||||
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
||||
|
||||
### Company Information
|
||||
|
||||
Task Venture Capital GmbH
|
||||
Registered at District court Bremen HRB 35230 HB, Germany
|
||||
Registered at District Court Bremen HRB 35230 HB, Germany
|
||||
|
||||
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
|
||||
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
||||
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
||||
|
||||
Reference in New Issue
Block a user