Skip to main content

py-invoices

Framework-agnostic invoice management with pluggable storage backends.

Features

  • Pluggable Storage Backends: Swap between SQLite, PostgreSQL, MySQL or in-memory storage
  • E-Invoicing Ready: Built-in support for Factur-X (ZUGFeRD) and UBL standards
  • Audit Logging: Integrated audit trail for tracking all invoice lifecycles
  • Framework-Agnostic: Designed for integration into any Python application
  • Type-Safe: Built on pydantic-invoices schemas with full type hints
  • Flexible: Optional dependencies - install only what you need
  • In-Memory Operations: HTML generation and byte-stream output supported out-of-the-box

Installation

Requires Python 3.14 or later.

# Basic installation (includes HTML generation)
pip install py-invoices

# With CLI support (recommended for interactive use)
pip install "py-invoices[cli]"

# With SQLite support
pip install py-invoices[sqlite]

# With PostgreSQL support
pip install py-invoices[postgres]

# With MySQL support
pip install py-invoices[mysql]

# With PDF generation
pip install py-invoices[pdf]

# With .env file support (for setup command)
pip install py-invoices[dotenv]

# Everything
pip install py-invoices[all]

PDF Generation Requirements

PDF generation uses WeasyPrint, which requires system-level libraries for rendering (Pango, GObject, Cairo).

macOS:

brew install pango libffi
# You may also need to set the library path:
export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_FALLBACK_LIBRARY_PATH

Linux/Ubuntu:

sudo apt-get install python3-pip python3-cffi python3-brotli libpango-1.0-0 libharfbuzz0b libpangoft2-1.0-0

For more details, see the WeasyPrint installation guide.

CLI Usage

The package includes a configured CLI tool py-invoices.

Quick Start:

# 1. Setup configuration (interactive wizard)
py-invoices setup

# 2. Initialize the backend
py-invoices init

# 3. Create an invoice (and auto-create client)
py-invoices invoices create --client-name "Acme Corp" --amount 1500.00 --description "Consulting"

For full documentation on available commands (listing, generating PDFs, backend configuration), see the CLI Documentation.

Quick Start

from datetime import datetime
from py_invoices import RepositoryFactory
from pydantic_invoices.schemas import ClientCreate, InvoiceCreate, InvoiceLineCreate, InvoiceStatus

# Initialize with memory backend
factory = RepositoryFactory(backend="memory")

# Get repositories
client_repo = factory.create_client_repository()
invoice_repo = factory.create_invoice_repository()

# Create a client
client = client_repo.create(ClientCreate(
    name="Acme Corp",
    address="123 Business St",
    tax_id="12-3456789"
))

# Create an invoice
invoice = invoice_repo.create(InvoiceCreate(
    number="INV-2025-0001",
    issue_date=datetime.now(),
    status=InvoiceStatus.UNPAID,
    client_id=client.id,
    client_name_snapshot=client.name,
    client_address_snapshot=client.address,
    client_tax_id_snapshot=client.tax_id,
    lines=[
        InvoiceLineCreate(
            description="Professional Services",
            quantity=40,
            unit_price=150.0
        )
    ]
))

print(f"Created invoice {invoice.number} for ${invoice.total_amount}")

Configuration

Environment Variables

Configure backends using environment variables or .env files:

# Set environment variables
export INVOICES_BACKEND=sqlite
export INVOICES_DATABASE_URL=sqlite:///invoices.db
export INVOICES_DATABASE_ECHO=false
from py_invoices import RepositoryFactory

# Automatically loads from environment variables and .env file
factory = RepositoryFactory.from_settings()

Using .env File

Create a .env file in your project root:

INVOICES_BACKEND=sqlite
INVOICES_DATABASE_URL=sqlite:///invoices.db
INVOICES_DATABASE_ECHO=false
# INVOICES_TEMPLATE_DIR=templates
INVOICES_OUTPUT_DIR=output
from py_invoices import RepositoryFactory

# Loads configuration from .env file automatically
factory = RepositoryFactory.from_settings()

Programmatic Configuration

from py_invoices import InvoiceSettings, RepositoryFactory

settings = InvoiceSettings(
    backend="sqlite",
    database_url="sqlite:///invoices.db",
    database_echo=False
)

factory = RepositoryFactory.from_settings(settings)

Available Settings

Setting Environment Variable Default Description
backend INVOICES_BACKEND memory Backend type: memory, files, sqlite, postgres, mysql
database_url INVOICES_DATABASE_URL None Database connection URL
database_echo INVOICES_DATABASE_ECHO false Enable SQL query logging
file_format INVOICES_FILE_FORMAT md Format for files backend: json, yaml, yml, or md
storage_path INVOICES_STORAGE_PATH ./data Root directory for files backend
template_dir INVOICES_TEMPLATE_DIR None Directory for invoice templates (defaults to included)
output_dir INVOICES_OUTPUT_DIR output Directory for generated files
api_key INVOICES_API_KEY None Key every API request must send in X-API-Key; the API refuses requests until it is set
cors_origins INVOICES_CORS_ORIGINS [] JSON list of extra browser origins allowed to call the API

Core Services

PDF Generation

Generate professional PDF/HTML invoices using Jinja2 templates:

from py_invoices.core import PDFService

# Company details for the invoice header
company = {
    "name": "My Company",
    "address": "123 HQ Blvd, Tech City",
    "tax_id": "US-99999"
}

pdf_service = PDFService(output_dir="output")

# Generate to file
pdf_path = pdf_service.generate_pdf(invoice=invoice, company=company)

# Generate to memory (bytes)
pdf_bytes = pdf_service.generate_pdf_bytes(invoice=invoice, company=company)

HTML Generation

For scenarios where you only need HTML (e.g. emails) or don't want the heavy WeasyPrint dependency:

from py_invoices.core import HTMLService

# Uses only Jinja2 (no WeasyPrint)
html_service = HTMLService(output_dir="output")

# Generate HTML string
html_content = html_service.generate_html(invoice=invoice, company=company)

# Save to HTML file
html_path = html_service.save_html(invoice=invoice, company=company)

Industry Standard (Factur-X / UBL)

Generate enterprise-ready invoices compliant with European standards:

Factur-X (PDF/A-3 + XML): A PDF/A-3b document embedding the invoice as UN/CEFACT CII XML (factur-x.xml), Factur-X 1.0 profile EN 16931 (compatible with ZUGFeRD 2 COMFORT). Credit notes use document type 381.

# Requires py-invoices[pdf]
facturx_path = pdf_service.generate_facturx(invoice=invoice, company=company)

EN 16931 requires data that plain PDFs do not:

  • company: name, country_code (or country) as an ISO 3166-1 alpha-2 code, and tax_id; when tax_id is not a VAT number (e.g. DE123456789), also registration_number
  • the invoice: client_name_snapshot and client_country_snapshot, filled from the client's country when the invoice is created (clients create --country DE, invoices create --client-country DE)

Missing data raises FacturXDataError listing every problem. The delivery date defaults to the issue date (delivery_date= overrides it); pass original_invoice_number= for a credit note. The CII XML alone is available from CIIService().generate_cii(invoice, company).

UBL (XML Only): Universal Business Language Version 2.1 XML invoices.

from py_invoices.core import UBLService

ubl_service = UBLService(output_dir="output")
ubl_path = ubl_service.save_ubl(invoice, company)

In-Memory Generation (Bytes)

Generate files directly to memory without saving to disk:

# Generate Factur-X bytes
pdf_bytes = pdf_service.generate_facturx_bytes(invoice, company)

# Generate UBL XML bytes
xml_bytes = ubl_service.generate_ubl_bytes(invoice, company)

Audit Logging

Track every change in the invoice lifecycle:

from py_invoices.core import AuditService

audit_service = AuditService(audit_repo=factory.create_audit_repository())

# Log event
audit_service.log_invoice_created(invoice, user_id="system_admin")
audit_service.log_payment_added(invoice, payment, user_id="cashier_01")

# Retrieve logs
logs = audit_service.get_logs(invoice_id=invoice.id)

Invoice Numbering

Generate sequential, formatted invoice numbers:

from py_invoices.core import NumberingService

# Default format: INV-{year}-{sequence:04d}
numbering = NumberingService(invoice_repo=invoice_repo)
next_number = numbering.generate_number()

Storage Backends

Memory (Testing)

Perfect for development and testing:

factory = RepositoryFactory(backend="memory")

SQLite (Production-Ready)

For single-file database storage:

factory = RepositoryFactory(
    backend="sqlite",
    database_url="sqlite:///invoices.db"
)

PostgreSQL (Enterprise)

For production deployments:

factory = RepositoryFactory(
    backend="postgres",
    database_url="postgresql://user:pass@localhost/invoices"
)

MySQL

For common web server setups:

factory = RepositoryFactory(
    backend="mysql",
    database_url="mysql+pymysql://user:pass@localhost/invoices"
)

Files (Local Storage)

Stores data as individual files (JSON, YAML, XML, or Markdown) in a local directory. Markdown format is recommended for human readability and direct editing.

factory = RepositoryFactory(
    backend="files",
    root_dir="./data",
    file_format="yaml"  # Defaults to "md" for *writing* new files
)

Note: The backend automatically detects and reads files in any supported format (json, yaml, xml, md) regardless of the file_format setting. This allows you to mix formats or manually edit files in your preferred format. It also supports friendly filenames for better organization (e.g., 1.Customer Name.json instead of just 1.json).

Architecture

py-invoices (business logic)
    ↓ depends on
pydantic-invoices (schemas)

Development

# Clone repository
git clone https://github.com/Romamo/py-invoices
cd py-invoices

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Type checking
mypy py_invoices

# Linting
ruff check py_invoices

License

MIT License - see LICENSE file for details.

Metadata

Release files for py-invoices 5.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for py-invoices 5.0.1
File Size Uploaded Provenance
py_invoices-5.0.1.tar.gz 169.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for py-invoices 5.0.1
File Interpreter ABI Platform
py_invoices-5.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 295.0 kB

Release files / py_invoices-5.0.1.tar.gz

Download URL py_invoices-5.0.1.tar.gz
Size 169.7 kB
Tags Source
SHA-256 checksum
How to use checksums
ae5f971648de312d7701c04d320535cc79f080976f367db2aadf9b2bb298223a
BLAKE2b-256 checksum
How to use checksums
e42989f4459599d3065634733dbaecd8c05b9c043c33c8635e9b0c5bc81cd8ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / py_invoices-5.0.1-py3-none-any.whl

Download URL py_invoices-5.0.1-py3-none-any.whl
Size 125.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c624719477456c2029057d83e176e219838a9f74705ef69aacf0737b72ace8d
BLAKE2b-256 checksum
How to use checksums
f4a47bd413afc25e939f68e41b7a1ff187cc01c19abcb908726f6c8855ad229a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.5 {"installer":{"name":"uv","version":"0.13.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for py-invoices 5.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page