JSPM

@rivtor/invoice

1.1.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 3
  • Score
    100M100P100Q21827F
  • License MIT

EU VAT Invoice Generator with country-specific requirements and PDF rendering

Package Exports

  • @rivtor/invoice
  • @rivtor/invoice/server

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/invoice

Quick Start

1. Create and Generate Invoice

import { createInvoice } from '@rivtor/invoice/server';

const invoice = await createInvoice({
  customer: {
    name: 'Acme Corporation',
    email: 'billing@acme.com',
    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 false

3. 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: 'billing@yourcompany.com',
      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

  1. Validate Customer VAT IDs: Always validate VAT IDs before issuing invoices
  2. Use Sequential Numbering: Maintain sequential invoice numbers for audit purposes
  3. Store for 10 Years: EU regulations require 10-year invoice retention
  4. Generate EN 16931 XML: Use for e-invoicing compliance
  5. Handle Reverse Charge: Apply 0% VAT for cross-border B2B transactions

License

MIT


Made with ❤️ by Rivtor