npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vocoweb/invoice

v1.1.0

Published

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

Readme

@vocoweb/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 @vocoweb/invoice
# or
yarn add @vocoweb/invoice
# or
pnpm add @vocoweb/invoice

Quick Start

1. Create and Generate Invoice

import { createInvoice } from '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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 '@vocoweb/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

  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 VocoWeb