Skip to content

Commit 114af29

Browse files
committed
first commit
0 parents  commit 114af29

File tree

10 files changed

+466
-0
lines changed

10 files changed

+466
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/.DS_Store

LICENSE

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) Mickaël Canouil
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.

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Invoice Format Template
2+
3+
This is a Quarto template that assists you in creating PDF invoices via Typst.
4+
5+
## Creating a New Invoice
6+
7+
You can use this as a template to create an invoice.
8+
To do this, use the following command:
9+
10+
```bash
11+
quarto use template mcanouil/quarto-typst-invoice
12+
```
13+
14+
This will install the extension and create an example qmd file that you can use as a starting place for your invoice.
15+
16+
## Installation For Existing Document
17+
18+
You may also use this format with an existing Quarto project or document.
19+
From the Quarto project or document directory, run the following command to install this format:
20+
21+
```bash
22+
quarto add extension mcanouil/quarto-invoice
23+
```
24+
25+
## Usage
26+
27+
To use the format, you can use the format name `invoice-typst`.
28+
For example:
29+
30+
```bash
31+
quarto render template.qmd --to invoice-typst
32+
```
33+
34+
or in your document yaml
35+
36+
```yaml
37+
format:
38+
invoice-typst:
39+
lang: en_UK
40+
papersize: a4
41+
margin:
42+
x: 2.5cm
43+
y: 2.5cm
44+
mainfont: "Alegreya Sans"
45+
fontsize: 12pt
46+
```
47+
48+
You can view a preview of the rendered template below:
49+
50+
![Invoice Template Screenshot](./template.png)

_extensions/invoice/LICENSE

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) Mickaël Canouil
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.

_extensions/invoice/_extension.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
title: Invoice (Typst)
2+
author: Mickaël Canouil
3+
version: 1.0.0
4+
quarto-required: ">=1.4.0"
5+
contributes:
6+
formats:
7+
typst:
8+
template: template.typ
9+
template-partials:
10+
- typst-template.typ
11+
- typst-show.typ

_extensions/invoice/template.typ

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
$typst-template.typ()$
2+
3+
$typst-show.typ()$
4+
5+
$body$

_extensions/invoice/typst-show.typ

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#show: invoice.with(
2+
$if(title)$
3+
title: "$title$",
4+
$if(description)$
5+
description: "$description$",
6+
$endif$
7+
$endif$
8+
$if(logo)$
9+
logo: "$logo$",
10+
$endif$
11+
$if(sender)$
12+
sender: (
13+
name: "$sender.name$",
14+
address: (
15+
street: "$sender.address.street$",
16+
zip: "$sender.address.zip$",
17+
city: "$sender.address.city$",
18+
state: "$sender.address.state$",
19+
country: "$sender.address.country$"
20+
),
21+
email: "$sender.email$",
22+
registration: "$sender.registration$",
23+
vat: "$sender.vat$",
24+
exempted: "$sender.exempted$"
25+
),
26+
$endif$
27+
$if(recipient)$
28+
recipient: (
29+
name: "$recipient.name$",
30+
address: (
31+
street: "$recipient.address.street$",
32+
zip: "$recipient.address.zip$",
33+
city: "$recipient.address.city$",
34+
state: "$recipient.address.state$",
35+
country: "$recipient.address.country$"
36+
)
37+
),
38+
$endif$
39+
$if(invoice)$
40+
invoice: (
41+
number: "$invoice.number$",
42+
issued: "$invoice.issued$",
43+
due: "$invoice.due$",
44+
reference: "$invoice.reference$",
45+
fee: "$invoice.fee$",
46+
penalty: "$invoice.penalty$"
47+
),
48+
$endif$
49+
$if(bank)$
50+
bank: (
51+
iban: "$bank.iban$",
52+
bic: "$bank.bic$"
53+
),
54+
$endif$
55+
$if(lang)$
56+
lang: "$lang$",
57+
$endif$
58+
$if(margin)$
59+
margin: ($for(margin/pairs)$$margin.key$: $margin.value$,$endfor$),
60+
$endif$
61+
$if(papersize)$
62+
paper: "$papersize$",
63+
$endif$
64+
$if(mainfont)$
65+
font: ("$mainfont$",),
66+
$endif$
67+
$if(fontsize)$
68+
fontsize: $fontsize$,
69+
$endif$
70+
)
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#let parse-date(date) = {
2+
let date = date.replace("\\", "")
3+
let date = str(date).split("-").map(int)
4+
datetime(year: date.at(0), month: date.at(1), day: date.at(2))
5+
}
6+
7+
#let format-date(date) = {
8+
let day = date.day()
9+
let ord = super(if 10 < day and day < 20 {
10+
"th"
11+
} else if calc.rem(day, 10) == 1 {
12+
"st"
13+
} else if calc.rem(day, 10) == 2 {
14+
"nd"
15+
} else if calc.rem(day, 10) == 3 {
16+
"rd"
17+
} else {
18+
"th"
19+
})
20+
[the #day#ord of #date.display("[month repr:long]"), #date.year()]
21+
}
22+
23+
#let count-days(x, y) = {
24+
let duration = y - x
25+
str(duration.days())
26+
}
27+
28+
#let invoice(
29+
logo: none,
30+
title: none,
31+
description: none,
32+
sender: none,
33+
recipient: none,
34+
invoice: none,
35+
bank: none,
36+
fee: 2.28,
37+
penalty: "€40",
38+
paper: "a4",
39+
margin: (x: 2.5cm, y: 2.5cm),
40+
lang: "en_UK",
41+
font: ("Alegreya Sans", "Alegreya Sans SC", "Alegreya Sans", "Alegreya Sans SC"),
42+
fontsize: 12pt,
43+
body
44+
) = {
45+
set page(
46+
paper: paper,
47+
margin: margin,
48+
)
49+
set par(justify: true)
50+
set text(
51+
lang: lang,
52+
font: font,
53+
size: fontsize,
54+
)
55+
56+
grid(
57+
columns: (50%, 50%),
58+
align(left, {
59+
heading(level: 2, sender.at("name").replace("\\", ""))
60+
61+
if "address" in sender and sender != none {
62+
v(fontsize * 0.5)
63+
emph(sender.at("address").at("street").replace("\\", ""))
64+
linebreak()
65+
sender.at("address").at("zip").replace("\\", "") + " " + sender.at("address").at("city").replace("\\", "")
66+
if "state" in sender.at("address") and not sender.at("address").at("state") in (none, "") {
67+
", " + sender.at("address").at("state").replace("\\", "")
68+
} else {
69+
""
70+
}
71+
linebreak()
72+
sender.at("address").at("country").replace("\\", "")
73+
}
74+
75+
v(fontsize * 0.1)
76+
77+
if "email" in sender and sender != none {
78+
link("mailto:" + sender.at("email").replace("\\", ""))
79+
} else {
80+
hide("a")
81+
}
82+
}),
83+
align(right, {
84+
heading(level: 2, recipient.at("name").replace("\\", ""))
85+
86+
if "address" in recipient and recipient != none {
87+
v(fontsize * 0.5)
88+
emph(recipient.at("address").at("street").replace("\\", ""))
89+
linebreak()
90+
recipient.at("address").at("zip").replace("\\", "") + " " + recipient.at("address").at("city").replace("\\", "")
91+
if "state" in recipient.at("address") and not recipient.at("address").at("state") in (none, "") {
92+
", " + recipient.at("address").at("state").replace("\\", "")
93+
} else {
94+
""
95+
}
96+
linebreak()
97+
recipient.at("address").at("country").replace("\\", "")
98+
}
99+
})
100+
)
101+
102+
v(fontsize * 1)
103+
104+
grid(
105+
columns: (50%, 50%),
106+
align(left, {
107+
if "registration" in sender and sender != none and sender.at("registration") != "" {
108+
"Registration number: " + sender.at("registration").replace("\\", "")
109+
linebreak()
110+
} else {
111+
hide("a")
112+
}
113+
114+
if "vat" in sender and sender != none and sender.at("vat") != "" {
115+
"VAT number: " + sender.at("vat").replace("\\", "")
116+
} else {
117+
hide("a")
118+
}
119+
120+
v(fontsize * 1)
121+
122+
if "number" in invoice and invoice != none and invoice.at("number") != "" {
123+
"Invoice number: " + invoice.at("number").replace("\\", "")
124+
linebreak()
125+
} else {
126+
hide("a")
127+
}
128+
129+
if "issued" in invoice and invoice != none {
130+
"Issued on: " + invoice.at("issued").replace("\\", "")
131+
linebreak()
132+
} else {
133+
hide("a")
134+
}
135+
136+
if "due" in invoice and invoice != none {
137+
"Payment due date: " + invoice.at("due").replace("\\", "")
138+
} else {
139+
hide("a")
140+
}
141+
}),
142+
align(center, {
143+
if logo != "none" and logo != none {
144+
image(logo, width: 3cm)
145+
} else {
146+
hide("a")
147+
}
148+
})
149+
)
150+
151+
align(horizon, {
152+
if title != none {
153+
heading(level: 1, title.replace("\\", ""))
154+
if description != none {
155+
emph(description.replace("\\", ""))
156+
}
157+
}
158+
159+
body
160+
161+
align(right, if "exempted" in sender and sender != none and sender.exempted != "none" and sender.exempted != none {
162+
text(luma(100), emph(sender.at("exempted").replace("\\", "")))
163+
} else {
164+
hide("a")
165+
})
166+
})
167+
168+
align(bottom, {
169+
if "bic" in bank and "iban" in bank and bank != none {
170+
heading(level: 3, "Payment information")
171+
v(fontsize * 0.5)
172+
"BIC: " + bank.at("bic").replace("\\", "")
173+
linebreak()
174+
"IBAN: " + bank.at("iban").replace("\\", "")
175+
linebreak()
176+
"Reference: " + strong(invoice.at("reference").replace("\\", ""))
177+
linebreak()
178+
text(luma(100), emph("To use as label on your bank transfer to identify the transaction."))
179+
linebreak()
180+
} else {
181+
hide("a")
182+
}
183+
184+
v(fontsize * 2)
185+
186+
let issued = parse-date(invoice.at("issued"))
187+
188+
text(luma(100),
189+
emph(
190+
sender.at("name").replace("\\", "")
191+
+ " sent you this invoice on "
192+
+ format-date(issued)
193+
+ ". The invoice must be paid under "
194+
+ count-days(issued, parse-date(invoice.at("due")))
195+
+ " day(s), otherwise you will have to pay a late fee of "
196+
+ invoice.at("fee", default:"2.28")
197+
+ " % and a "
198+
+ invoice.at("penalty", default:"€40")
199+
+ " penalty for recovery costs. "
200+
+ "No discount will be granted for early settlement."
201+
)
202+
)
203+
})
204+
}

template.png

209 KB
Loading

0 commit comments

Comments
 (0)