Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/preview/invoice-pro/0.3.1/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Leonie Ziechmann

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
120 changes: 120 additions & 0 deletions packages/preview/invoice-pro/0.3.1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# invoice-pro

Modern Invoice Template for Typst

A professional, compliant, and automated invoice template for [Typst](https://typst.app). This package follows the German **DIN 5008** standard (Form A & B) and automates calculations, VAT handling, and payment details.

![Example Invoice](thumbnail.png)

## Features

- **Internationalization (i18n) (New in v0.3.0):** Built-in support for English and German out of the box, plus a highly flexible `locale` API to inject custom translations for any language.
- **DIN 5008 Compliant:** Supports both Form A and Form B layouts natively via the flexible Theming API.
- **Block-based API:** Clean, scoped, and declarative data structure using `#line-items`, `#item`, and `#bundle`—inspired by CeTZ, keeping your document clutter-free.
- **Automatic Calculations:** Effortlessly handles line items, nested bundles, sub-totals, and calculates taxes automatically.
- **EPC QR-Code (GiroCode):** Automatically generates a scannable banking QR code for quick and easy payments using banking apps.
- **Advanced Modifiers:** Apply specific discounts, surcharges, and custom tax rates at the item, bundle, or global level.
- **Highly Customizable:** Easy configuration of sender, recipient, payment goals, bank details, and visual themes to match your corporate identity.

## Documentation

For comprehensive guides, API references, theming instructions, and advanced examples, please visit our official documentation:

👉 **[Read the Full Documentation Here](https://leonieziechmann.github.io/invoice-pro/)**

## Getting Started

### Installation

Import the package at the top of your Typst file:

```typst
#import "@preview/invoice-pro:0.3.1": *
```

### Basic Usage

Here is an example of how to create an invoice using the new v0.3 API:

```typst
#import "@preview/invoice-pro:0.3.1": *

#show: invoice.with(
theme: themes.DIN-5008(form: "A"), // or form: "B"
locale: locale.en-de,
sender: (
name: "Your Company / Name",
address: "1 Example Street",
city: "12345 Example City",
),
recipient: (
name: "Customer Name",
address: "5 Customer Street",
city: "98765 Customer City",
),
invoice-nr: "2026-01",
tax-nr: "123/456/789",
)

// Add Invoice Items inside a scoped block
#line-items[
#item(
[Consulting & Concept],
price: 85.00,
quantity: 5,
unit: "hrs"
)

#item(
[Web Design Layout (Flat Rate)],
price: 1200.00,
)

#item(
[Stock Licenses (Images)],
price: 25.00,
quantity: 4,
)

#discount([Project Discount (Regular Customer)], amount: 10%)
]

// Payment Terms
#payment-goal(days: 14)

// Bank Details with QR Code
#bank-details(
bank: "Example Bank",
iban: "DE07100202005821158846",
bic: "EXAMPLEBIC",
)

#signature()
```

## API Stability

With the major refactoring introduced in version 0.2.0, the package structure is solidifying. Here is the current stability status of the various API components:

- **Invoice Header (`invoice` arguments):** **Mostly Stable**. The core invoice configuration is established. Future updates to the header will be non-breaking and will primarily consist of adding new optional fields.
- **Data Model (`#line-items`, `#bundle`, `#item`):** **Stable**. The new block-based data model is considered almost finished and safe to use.
- _Exception:_ The `unit` argument in `#item` and `#bundle` will change in a future release to strictly comply with the standardized unit formats and codes required for upcoming ZUGFeRD e-invoicing support.
- **Theming (`theme`):** **Under Construction**. The theming engine is still evolving and will most likely experience breaking changes in the next updates as we refine customization capabilities.
- **Localization (`locale`):** **Under Construction**. The localization and internationalization systems are actively being worked on and are subject to change.

## Dependencies

This template relies on these amazing packages:

- `letter-pro` for the DIN layout.
- `sepay` for EPC-QR-Code generation.
- `ibanator` for IBAN formatting.
- `loom` for reactive document rendering.

**Acknowledgements:**

- Special thanks to [classy-german-invoice](https://github.com/erictapen/typst-invoice) by Kerstin Humm, which served as inspiration and provided the logic for the EPC-QR-Code implementation.

## License

MIT
120 changes: 120 additions & 0 deletions packages/preview/invoice-pro/0.3.1/src/components/bank-details.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#import "../loom-wrapper.typ": loom, managed-motif
#import "../utils/types.typ"
#import "../utils/coercion.typ"

/// Defines and renders the bank account information for payments.
///
/// -> content
#let bank-details(
/// The name of the account holder. Defaults to the sender's name.
/// -> auto | none | string
name: auto,

/// The name of the banking institution.
/// -> none | string
bank: none,

/// The International Bank Account Number (IBAN).
/// -> none | string
iban: none,

/// The Bank Identifier Code (BIC/SWIFT).
/// -> none | string
bic: none,

/// The payment reference to be used by the customer.
/// -> auto | none | string
reference: auto,

/// The specific amount to be paid. If `auto`, it uses the document total.
/// -> auto | none | decimal | float | int
payment-amount: auto,

/// Whether to display the reference field in the output.
/// -> bool
show-reference: true,

/// Optional custom text to label the account holder field.
/// -> auto
account-holder-text: auto,

/// Configuration for a payment QR code (e.g., EPC-QR).
/// -> dictionary
qr-code: (:),
) = {
types.require(name, "bank-details::name", none, auto, str)
types.require(bank, "bank-details::bank", none, str)
types.require(iban, "bank-details::iban", none, str)
types.require(bic, "bank-details::bic", none, str)

types.require(reference, "bank-details::reference", none, auto, str)
types.require(
payment-amount,
"bank-details::payment-amount",
none,
auto,
types.decimal-like,
)

types.require(show-reference, "bank-details::show-reference", bool)

if name == none { name = "" }
if iban == none { iban = "" }
if bank == none { bank = "" }
if bic == none { bic = "" }
if payment-amount == none { payment-amount = 0 }
if payment-amount != auto {
payment-amount = coercion.to-decimal(payment-amount)
}

managed-motif(
"bank-details",
scope: ctx => loom.mutator.batch(ctx, {
import loom.mutator: *

derive("sender", "name", name, default: "")

put("reference", ctx.invoice-nr)
derive("reference", reference)

nest("theme", {
ensure("bank-details", (..) => panic(
"theme::bank-details is not provided",
))
})

nest("global", {
nest("total", {
ensure("gross", 0)
})
})
}),
measure: (ctx, _) => {
let data = (
sender: (
name: ctx.sender.name,
bank: bank,
iban: iban,
bic: bic,
),

qr-code: (
size: qr-code.at("size", default: 5em),
display: qr-code.at("display", default: true),
),

reference: ctx.reference,
show-reference: show-reference,
payment-amount: if payment-amount == auto {
ctx.global.total.gross
} else {
payment-amount
},
)

(none, data)
},
draw: (ctx, _, view, ..) => (ctx.theme.bank-details)(ctx, view),
none,
)
}
Loading
Loading