Skip to content

Commit 854df48

Browse files
invoice-pro:0.1.0 (#3649)
Co-authored-by: Jakob Ziechmann <[email protected]>
1 parent bdd85ff commit 854df48

File tree

10 files changed

+923
-0
lines changed

10 files changed

+923
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Leonie Ziechmann
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# invoice-pro
2+
Modern Invoice Template for Typst
3+
4+
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.
5+
6+
![Example Invoice](thumbnail.png)
7+
## Features
8+
9+
* **DIN 5008 Compliant:** Supports both Form A and Form B layouts.
10+
* **Automatic Calculations:** Handles line items, sub-totals, and VAT (MwSt) automatically.
11+
* **EPC QR-Code (GiroCode):** Generates a scannable banking QR code for easy payment apps using `rustycure`.
12+
* **Flexible Tax Settings:** * Supports standard VAT (Brutto/Netto modes).
13+
* **Kleinunternehmerregelung:** Built-in support for small business exemption (§ 19 UStG).
14+
* **Customizable:** Easy configuration of sender, recipient, and bank details.
15+
16+
## Getting Started
17+
18+
### Installation
19+
20+
Import the package at the top of your Typst file:
21+
22+
```typ
23+
#import "@preview/invoice-pro:0.1.0": *
24+
````
25+
26+
### Basic Usage
27+
28+
Here is a minimal example of how to create an invoice:
29+
30+
```typ
31+
#import "@preview/invoice-pro:0.1.0": *
32+
33+
// Set language to German for correct date/number formatting
34+
#set text(lang: "de")
35+
36+
#show: invoice.with(
37+
format: "DIN-5008-A", // or "DIN-5008-B"
38+
39+
sender: (
40+
name: "Deine Firma / Name",
41+
address: "Musterstraße 1",
42+
city: "12345 Musterstadt",
43+
extra: (
44+
"Tel": [+49 123 4567890],
45+
"Web": [#link("https://www.example.com")[www.example.com]],
46+
)
47+
),
48+
49+
recipient: (
50+
name: "Kunden Name",
51+
address: "Kundenstraße 5",
52+
city: "98765 Kundenstadt"
53+
),
54+
55+
invoice-nr: "2024-001",
56+
date: datetime.today(),
57+
tax-nr: "123/456/789",
58+
)
59+
60+
// Add Invoice Items
61+
#invoice-line-items(
62+
item([Consulting Service], quantity: 4, unit: [hrs], price: 80),
63+
item([Software License], price: 150),
64+
)
65+
66+
// Payment Terms
67+
#payment-goal(days: 14)
68+
69+
// Bank Details with QR Code
70+
#bank-details(
71+
bank: "Musterbank",
72+
iban: "DE07100202005821158846",
73+
bic: "MUSTERBIC",
74+
)
75+
76+
#signature(signature: block[Your Signature])
77+
```
78+
79+
## Configuration
80+
81+
### `invoice` arguments
82+
83+
| Argument | Type | Description |
84+
| :--- | :--- | :--- |
85+
| `format` | string | "DIN-5008-A" or "DIN-5008-B" (Default: B). |
86+
| `sender` | dict | Sender details (`name`, `address`, `city`, `extra`). |
87+
| `recipient` | dict | Recipient details. |
88+
| `vat` | float | Default VAT rate (e.g., `0.19` for 19%). |
89+
| `vat-exempt-small-biz` | bool | If `true`, enables "Kleinunternehmer" mode (no VAT). |
90+
| `show-gross-prices` | bool | If `true`, calculates B2C gross prices. Default is `false` (B2B/Net). |
91+
92+
### `invoice-line-items` function
93+
94+
| Argument | Type | Description |
95+
| :--- | :--- | :--- |
96+
| `vat-exemption` | `bool` \| `auto` | Overrides the global setting. `auto` inherits from `invoice`. |
97+
| `show-quantity` | `bool` \| `auto` | Controls the quantity column. `auto` hides it if all quantities are 1. |
98+
| `show-vat-per-item` | `bool` \| `auto` | Controls the VAT column. `auto` shows it only if VAT rates differ between items. |
99+
| `currency` | `content` | The currency symbol to display (Default: `[€]`). |
100+
| `show-gross-prices` | `bool` | Calculation mode: `false` (Default/B2B) for net prices, `true` (B2C) for gross prices (prevents rounding errors). |
101+
| `..items` | `arguments` | A list of `item()` calls. |
102+
103+
104+
### `item` function
105+
106+
| Argument | Type | Description |
107+
| :--- | :--- | :--- |
108+
| `description` | content | Description of the service/product. |
109+
| `price` | float | Price per unit. |
110+
| `quantity` | float | Amount (Default: 1). |
111+
| `vat` | float | Specific VAT rate for this item (overrides default). |
112+
113+
114+
```typst
115+
#invoice-line-items(
116+
vat-exemption: true,
117+
item([Consulting Service], quantity: 4, unit: [hrs], price: 80),
118+
item([Software License], price: 150),
119+
)
120+
```
121+
![Line Items with Vat Exemption](images/items-1.png)
122+
123+
```typst
124+
#invoice-line-items(
125+
show-gross-prices: true,
126+
item([Fresh Mango], quantity: 4, unit: [pc.], vat: 0.07, price: 3.50),
127+
item([Döner Kebap to Go], unit: [pc.], price: 8),
128+
)
129+
```
130+
![Line Items B2C relation](images/items-2.png)
131+
132+
```typst
133+
#invoice-line-items(
134+
item([Ergonomic Office Chair "Modell Air"], quantity: 10, price: 250.00, gross-price: false, unit: [pc.]),
135+
item([Monitor Mount VESA 100], quantity: 20, unit: [pc.], gross-price: false, price: 45.50),
136+
item([Shipment], quantity: 1, gross-price: false, price: 89.90),
137+
)
138+
```
139+
![Line Items B2B relation](images/items-3.png)
140+
141+
## Dependencies
142+
143+
This template relies on these amazing packages:
144+
145+
* `letter-pro` for the DIN layout.
146+
* `rustycure` for QR-Code generation.
147+
* `ibanator` for IBAN formatting.
148+
149+
**Acknowledgements:**
150+
* 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.
151+
152+
## License
153+
154+
MIT
73.7 KB
Loading
84 KB
Loading
96.3 KB
Loading
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#let to-string(it) = {
2+
if it == none { return ""; }
3+
if type(it) == str { return it; }
4+
if type(it) != content { return str(it); }
5+
if it.has("text") { return it.text; }
6+
if it.has("children") {
7+
return it.children.map(to-string).join()
8+
}
9+
if it.has("body") { return to-string(it.body) }
10+
if it == [ ] { return " " }
11+
return ""
12+
}
13+
14+
#let format-currency(number, locale: "de") = {
15+
let precision = 2
16+
assert(precision > 0)
17+
let s = str(calc.round(number, digits: precision))
18+
let after_dot = s.find(regex("\..*"))
19+
if after_dot == none {
20+
s = s + "."
21+
after_dot = "."
22+
}
23+
for i in range(precision - after_dot.len() + 1){
24+
s = s + "0"
25+
}
26+
// fake de locale
27+
if locale == "de" {
28+
s.replace(".", ",")
29+
} else {
30+
s
31+
}
32+
}
33+
34+
#let extract-city-name(zip-city-string) = {
35+
let pattern = regex("^\\s*\\d*")
36+
to-string(zip-city-string).trim(pattern)
37+
}

0 commit comments

Comments
 (0)