@rivtor/invoice
v1.1.0
Published
EU VAT Invoice Generator with country-specific requirements and PDF rendering
Maintainers
Readme
@rivtor/invoice
Production-ready EU VAT invoice generator with country-specific requirements and PDF rendering
Features
- Country-Specific Requirements: Built-in support for DACH, France, Italy, and other EU countries
- VAT Rate Engine: Real-time VAT rates via EU VAT API with B2B reverse charge support
- PDF Rendering: Professional invoice PDFs with EN 16931 XML support
- Invoice Number Sequencer: Distributed locks for conflict-free sequential numbering
- 10-Year Archival: Storage integration for long-term invoice retention
- Multi-Currency: Support for EUR, USD, GBP, and other currencies
Installation
npm install @rivtor/invoice
# or
yarn add @rivtor/invoice
# or
pnpm add @rivtor/invoiceQuick Start
1. Create and Generate Invoice
import { createInvoice } from '@rivtor/invoice/server';
const invoice = await createInvoice({
customer: {
name: 'Acme Corporation',
email: '[email protected]',
vatId: 'DE123456789',
address: {
line1: 'Musterstraße 1',
line2: 'Aufgang 3',
city: 'Berlin',
postalCode: '10115',
country: 'DE',
state: 'Berlin'
}
},
lineItems: [
{
description: 'Web Development Services',
quantity: 10,
unitPrice: 100,
vatRate: 19,
discount: 0
},
{
description: 'Server Setup',
quantity: 1,
unitPrice: 500,
vatRate: 19
}
],
countryOfSale: 'DE',
currency: 'EUR',
issueDate: '2024-01-15',
dueDate: '2024-02-15',
paymentTerms: 'Net 30',
notes: 'Thank you for your business!'
}, { generatePdf: true });
console.log(invoice);
// {
// id: 'inv-123',
// invoiceNumber: 'INV-2024-001',
// subtotal: 1500,
// vatAmount: 285,
// totalAmount: 1785,
// pdfUrl: 'https://storage.example.com/invoices/inv-123.pdf'
// }2. Get VAT Rate
import { getVatRate, isValidVatId } from '@rivtor/invoice/server';
// Get standard VAT rate for Germany
const rate = await getVatRate('DE');
// Returns: { country: 'DE', code: 'standard', rate: 19, ... }
// Get reduced VAT rate for Germany
const reducedRate = await getVatRate('DE', null, 'reduced');
// Returns: { country: 'DE', code: 'reduced', rate: 7, ... }
// B2B reverse charge (0% VAT)
const b2bRate = await getVatRate('DE', 'DE123456789');
// Returns: { rate: 0, description: 'Reverse charge applies' }
// Validate VAT ID
const valid = await isValidVatId('DE', 'DE123456789');
// Returns: true or false3. Get Country Requirements
import { getCountryRequirements } from '@rivtor/invoice/server';
const requirements = getCountryRequirements('DE');
// Returns: {
// requiresVatId: true,
// requiresSequentialNumbering: true,
// requiresSpecificDateFormat: true,
// minimumRetentionYears: 10,
// vatRates: { standard: 19, reduced: 7 },
// requiredFields: ['vat_id', 'invoice_number', 'issue_date']
// }4. Download Invoice PDF
import { getInvoicePdf, getInvoiceUrl } from '@rivtor/invoice/server';
// Get PDF buffer
const pdfBuffer = await getInvoicePdf('inv-123');
// Get public URL
const url = await getInvoiceUrl('inv-123');
// Returns: 'https://storage.example.com/invoices/inv-123.pdf'API Reference
Server Functions
import {
createInvoice,
updateInvoice,
deleteInvoice,
getInvoice,
listInvoices
} from '@rivtor/invoice/server';
// Create invoice
await createInvoice(data: CreateInvoiceInput, options?: {
generatePdf?: boolean;
storePdf?: boolean;
}): Promise<Invoice>;
// Update invoice
await updateInvoice(id: string, data: UpdateInvoiceInput): Promise<Invoice>;
// Delete invoice
await deleteInvoice(id: string): Promise<boolean>;
// Get invoice by ID
await getInvoice(id: string): Promise<Invoice | null>;
// List invoices
await listInvoices(filters?: {
customerId?: string;
status?: string;
countryOfSale?: string;
dateFrom?: string;
dateTo?: string;
}): Promise<Invoice[]>;VAT Management
import {
getVatRate,
getAllVatRates,
isValidVatId,
validateVatId
} from '@rivtor/invoice/server';
// Get VAT rate for country
await getVatRate(
countryCode: string,
customerVatId?: string, // For B2B reverse charge
rateType?: 'standard' | 'reduced'
): Promise<VatRate>;
// Get all VAT rates
await getAllVatRates(): Promise<Record<string, VatRate>>;
// Validate VAT ID format
isValidVatId(countryCode: string, vatId: string): boolean;
// Validate VAT ID via VIES
await validateVatId('DE', 'DE123456789');
// Returns: { valid: true, name: 'Company Name', address: '...' }Country Requirements
import {
getCountryRequirements,
getAllCountryRequirements,
isInvoiceCompliant
} from '@rivtor/invoice/server';
// Get requirements for specific country
await getCountryRequirements(countryCode: string): Promise<CountryRequirements>;
// Get all country requirements
await getAllCountryRequirements(): Promise<Record<string, CountryRequirements>>;
// Check if invoice is compliant
await isInvoiceCompliant(invoice: Invoice): Promise<{
compliant: boolean;
errors: string[];
warnings: string[];
}>;Invoice Numbering
import {
getNextInvoiceNumber,
resetInvoiceSequence,
setInvoiceSequence
} from '@rivtor/invoice/server';
// Get next invoice number
await getNextInvoiceNumber(prefix?: string): Promise<string>;
// Reset sequence
await resetInvoiceSequence(prefix?: string): Promise<void>;
// Set sequence value
await setInvoiceSequence(prefix: string, value: number): Promise<void>;PDF Generation
import {
generateInvoicePdf,
getInvoicePdf,
getInvoiceUrl,
generateEn16931Xml
} from '@rivtor/invoice/server';
// Generate PDF from invoice
await generateInvoicePdf(invoice: Invoice): Promise<Buffer>;
// Get stored PDF
await getInvoicePdf(invoiceId: string): Promise<Buffer>;
// Get public URL
await getInvoiceUrl(invoiceId: string): Promise<string>;
// Generate EN 16931 XML
await generateEn16931Xml(invoice: Invoice): Promise<string>;Database Schema
CREATE TABLE invoices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
invoice_number TEXT UNIQUE NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
customer_id UUID,
customer_name TEXT NOT NULL,
customer_email TEXT NOT NULL,
customer_vat_id TEXT,
seller_vat_id TEXT NOT NULL,
subtotal NUMERIC NOT NULL,
vat_amount NUMERIC NOT NULL,
vat_rate NUMERIC NOT NULL,
total_amount NUMERIC NOT NULL,
currency TEXT NOT NULL DEFAULT 'EUR',
country_code TEXT NOT NULL,
country_of_sale TEXT NOT NULL,
issue_date TIMESTAMPTZ NOT NULL,
due_date TIMESTAMPTZ,
service_date TIMESTAMPTZ,
paid_date TIMESTAMPTZ,
line_items JSONB NOT NULL,
notes TEXT,
payment_terms TEXT,
metadata JSONB,
pdf_url TEXT,
xml_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Sequence for invoice numbering
CREATE TABLE invoice_sequences (
prefix TEXT PRIMARY KEY,
counter INTEGER NOT NULL DEFAULT 1
);
-- Indexes for efficient queries
CREATE INDEX idx_invoices_number ON invoices(invoice_number);
CREATE INDEX idx_invoices_customer ON invoices(customer_id);
CREATE INDEX idx_invoices_status ON invoices(status);
CREATE INDEX idx_invoices_country_of_sale ON invoices(country_of_sale);
CREATE INDEX idx_invoices_issue_date ON invoices(issue_date);
CREATE INDEX idx_invoices_due_date ON invoices(due_date);Configuration
import { configureInvoice } from '@rivtor/invoice/server';
configureInvoice({
// Seller information
seller: {
name: 'Your Company GmbH',
vatId: 'DE123456789',
address: {
line1: 'Company Street 1',
city: 'Berlin',
postalCode: '10115',
country: 'DE'
},
contact: {
email: '[email protected]',
phone: '+49 30 12345678',
website: 'https://yourcompany.com'
}
},
// Invoice numbering
numbering: {
prefix: 'INV-',
format: '{prefix}{year}-{sequence}',
sequenceLength: 4
},
// VAT settings
vat: {
defaultRate: 19,
autoDetectCountry: true,
enableReverseCharge: true
},
// PDF generation
pdf: {
template: 'standard',
logo: '/assets/logo.png',
fontSize: 10,
fontFamily: 'Helvetica'
},
// Storage
storage: {
provider: 's3',
bucket: 'invoices',
region: 'eu-central-1'
}
});Country-Specific Requirements
Germany (DE)
- Requires sequential invoice numbers
- 10-year retention period
- VAT ID required for B2B invoices
- Specific date format: DD.MM.YYYY
France (FR)
- Requires SIRET number
- 10-year retention period
- Mentions légales required
- French language mandatory
Italy (IT)
- Requires Codice Fiscale/P.IVA
- Electronic invoicing (SDI) required
- 10-year retention period
- Split payment for public entities
Best Practices
- Validate Customer VAT IDs: Always validate VAT IDs before issuing invoices
- Use Sequential Numbering: Maintain sequential invoice numbers for audit purposes
- Store for 10 Years: EU regulations require 10-year invoice retention
- Generate EN 16931 XML: Use for e-invoicing compliance
- Handle Reverse Charge: Apply 0% VAT for cross-border B2B transactions
License
MIT
Made with ❤️ by Rivtor
