257 lines
7.4 KiB
Typst
257 lines
7.4 KiB
Typst
#let format_invoice_num(num, pre:4) = {
|
|
let a = calc.max(0,pre - str(num).len())
|
|
return a*"0" + str(num)
|
|
}
|
|
|
|
#let format_num(num, decimal: ".", thousands: "'") = {
|
|
let parts = str(num).split(".")
|
|
let decimal_part = if parts.len() == 2 { parts.at(1) }
|
|
let integer_part = parts.at(0).rev().clusters().enumerate()
|
|
.map((item) => {
|
|
let (index, value) = item
|
|
return value + if calc.rem(index, 3) == 0 and index != 0 {
|
|
thousands
|
|
}
|
|
}).rev().join("")
|
|
// TODO
|
|
return integer_part + if decimal_part != none { decimal + decimal_part }
|
|
}
|
|
|
|
|
|
#let translations = (
|
|
français: (
|
|
sender: "ÉMETTEUR",
|
|
recipient: "DESTINATAIRE",
|
|
invoice: "FACTURE",
|
|
invoice_number: "Facture N° :",
|
|
date: "Date :",
|
|
due_date: "Échéance :",
|
|
description: "Description",
|
|
amount: "Montant",
|
|
total: "Total :",
|
|
vat_notice: "Association non soumise à la TVA selon l'Art. 10 LTVA",
|
|
payment_terms: "Cette facture est payable dans les 30 jours suivant sa réception.",
|
|
banking_info: "COORDONNÉES BANCAIRES",
|
|
bank: "Banque :",
|
|
account_holder: "Titulaire :",
|
|
account_notice: "Compte au nom d'un membre de l'association",
|
|
association_notice: "Association à but non lucratif non inscrite au registre du commerce",
|
|
),
|
|
deutsch: (
|
|
sender: "ABSENDER",
|
|
recipient: "EMPFÄNGER",
|
|
invoice: "RECHNUNG",
|
|
invoice_number: "Rechnungs-Nr.:",
|
|
date: "Datum:",
|
|
due_date: "Fälligkeitsdatum:",
|
|
description: "Beschreibung",
|
|
amount: "Betrag",
|
|
total: "Gesamtbetrag:",
|
|
vat_notice: "Verein nicht mehrwertsteuerpflichtig gemäß Art. 10 MWSTG",
|
|
payment_terms: "Diese Rechnung ist innerhalb von 30 Tagen nach Erhalt zu bezahlen.",
|
|
banking_info: "BANKVERBINDUNG",
|
|
bank: "Bank:",
|
|
account_holder: "Kontoinhaber:",
|
|
account_notice: "Konto auf den Namen eines Vereinsmitglieds",
|
|
association_notice: "Gemeinnütziger Verein, nicht im Handelsregister eingetragen",
|
|
),
|
|
english: (
|
|
sender: "FROM",
|
|
recipient: "TO",
|
|
invoice: "INVOICE",
|
|
invoice_number: "Invoice No.:",
|
|
date: "Date:",
|
|
due_date: "Due date:",
|
|
description: "Description",
|
|
amount: "Amount",
|
|
total: "Total:",
|
|
vat_notice: "Association not subject to VAT according to Art. 10 VAT Act",
|
|
payment_terms: "This invoice is payable within 30 days of receipt.",
|
|
banking_info: "BANKING DETAILS",
|
|
bank: "Bank:",
|
|
account_holder: "Account holder:",
|
|
account_notice: "Account in the name of an association member",
|
|
association_notice: "Non-profit association not registered in the commercial register",
|
|
),
|
|
)
|
|
|
|
#let bank_wise_eur = [
|
|
#let t = translations.at("english", default: translations.français)
|
|
#grid(
|
|
columns:(auto, auto),
|
|
align:(right,left),
|
|
column-gutter: 1em,
|
|
row-gutter: .5em,
|
|
[#t.bank],[ Wise, Rue du Trône 100, 3rd floor, Brussels, 1050, Belgium],
|
|
[IBAN:],[*BE22905094540247*],
|
|
[BIC/SWIFT:],[ TRWIBEB1XXX],
|
|
[#t.account_holder],[ Robin Szymczak],
|
|
[],[#t.account_notice],
|
|
)
|
|
// #t.bank Wise, Rue du Trône 100, 3rd floor, Brussels, 1050, Belgium\
|
|
// IBAN: BE22905094540247\
|
|
// BIC/SWIFT: TRWIBEB1XXX\
|
|
// #t.account_holder Robin Szymczak\
|
|
]
|
|
|
|
#let bank_wise_chf = [
|
|
#let t = translations.at("english", default: translations.français)
|
|
#grid(
|
|
columns:(auto, auto),
|
|
align:(right,left),
|
|
column-gutter: 1em,
|
|
row-gutter: .5em,
|
|
[#t.bank],[ Wise Payments Limited, 1st Floor, Worship Square, 65 Clifton Street, London, EC2A 4JE, United Kingdom ],
|
|
[IBAN:],[*GB51TRWI23080140876698*],
|
|
[BIC/SWIFT:],[ TRWIGB2LXXX ],
|
|
[#t.account_holder],[ Robin Szymczak ],
|
|
[],[#t.account_notice],
|
|
)
|
|
]
|
|
|
|
// on stock en centimes, et on arrondit
|
|
#let chf(value) = (CHF:calc.round(value*100)/100)
|
|
#let eur(value) = (EUR:calc.round(value*100)/100)
|
|
#let usd(value) = (USD:calc.round(value*100)/100)
|
|
// #let money(value,currency) = (value:decimal(value),currency:currency)
|
|
// #let money_add(m1,m2) = if m1.currency != m2.currency : fail
|
|
#let money_render(m) = {
|
|
|
|
let cs = m.keys().sorted().map(currency => {
|
|
let value = m.at(currency)
|
|
let a = int(value)
|
|
let b1 = int(100*(value - a))
|
|
let b0 = if b1 < 10 [0] else []
|
|
let b = [#b0#b1]
|
|
let c = currency
|
|
return [#format_num(a)\.#b #c]
|
|
})
|
|
cs.join([ \+ ])
|
|
}
|
|
|
|
#let invoice_thing(price,description) = (price:price,description:description)
|
|
#let invoice_enumerate(start:0, invoice_data) = invoice_data.enumerate(start:start).map(e => {
|
|
e.at(1).invoice_number=format_invoice_num(e.at(0))
|
|
e.at(1)
|
|
})
|
|
|
|
#let invoice_render(
|
|
invoice,
|
|
language: "english", // Options: "français", "deutsch", "english"
|
|
) = [
|
|
#set page(
|
|
footer: align(center)[
|
|
#line(length: 100%, stroke: 0.5pt + gray)
|
|
#text(size:9pt)[Cheap Motion Pictures - 1 rue de l'Ale - 1003 Lausanne - contact\@cheapmo.ch]
|
|
],
|
|
)
|
|
#set text(font: "Arial", size: 11pt)
|
|
|
|
// Sélection du dictionnaire en fonction de la langue
|
|
#let t = translations.at(language, default: translations.français)
|
|
|
|
// Titre et informations de la facture
|
|
#grid(
|
|
columns: (auto,1fr, 1fr),
|
|
gutter:10pt,
|
|
image("cheapmo-logo.png",width:60pt),
|
|
align(left)[
|
|
#text(weight: "bold", size: 14pt)[Cheap Motion Pictures ]\
|
|
#text(style: "italic")[#t.association_notice]
|
|
],
|
|
align(right)[
|
|
#t.invoice_number *2025-#(invoice.invoice_number)*\
|
|
#t.date #(invoice.date ).display("[day]/[month]/[year]")\
|
|
#t.due_date *#(invoice.date + duration(days: 30)).display("[day]/[month]/[year]")*\
|
|
],
|
|
)
|
|
|
|
#v(15pt)
|
|
|
|
#grid(
|
|
columns: (1fr, 1fr),
|
|
|
|
// Informations de l'émetteur
|
|
align(left)[
|
|
#par(text(weight: "bold", size: 14pt)[#t.sender])
|
|
Cheap Motion Pictures\
|
|
Rue de l'Ale 1\
|
|
1003 Lausanne, Suisse\
|
|
Tél: +41 77 471 11 34\
|
|
Email: contact\@cheapmo.ch
|
|
],
|
|
|
|
// Informations du destinataire
|
|
align(right)[
|
|
#par(text(weight: "bold", size: 14pt)[#t.recipient])
|
|
#invoice.recipient.name\
|
|
#invoice.recipient.adress\
|
|
#invoice.recipient.postal_code #invoice.recipient.town, #invoice.recipient.country
|
|
#if invoice.recipient.vat_number != none [VAT: #invoice.recipient.vat_number] else []
|
|
],
|
|
)
|
|
#let things = invoice.invoice.fold((),(l,e)=>(..l,e.description,money_render(e.price)))
|
|
#let total = money_render(invoice.invoice.fold((:),(l,e)=> {
|
|
for currency in e.price.keys() {
|
|
l.insert(currency, l.at(currency,default:0) + e.price.at(currency))
|
|
}
|
|
return l
|
|
}))
|
|
|
|
#v(15pt)
|
|
|
|
#table(
|
|
columns: (1fr, auto),
|
|
inset: (x:10pt,y:8pt),
|
|
align: (left, right),
|
|
table.header(
|
|
table.cell(fill:rgb(245, 245, 245))[*#t.description*],
|
|
table.cell(fill:rgb(245, 245, 245))[*#t.amount* ],
|
|
),
|
|
..things,
|
|
table.cell(fill:silver,align:right)[TOTAL],
|
|
table.cell(fill:silver,align:right)[*#total*]
|
|
)
|
|
|
|
#v(15pt)
|
|
|
|
// Informations complémentaires
|
|
#t.vat_notice \
|
|
#t.payment_terms
|
|
|
|
// Coordonnées bancaires avec notification spéciale
|
|
= #t.banking_info
|
|
#invoice.bank
|
|
|
|
#if invoice.recu {
|
|
place(horizon+center,rotate(-60deg,text(fill:red.transparentize(50%),size:300pt,[PAID]),reflow:true))
|
|
}
|
|
]
|
|
|
|
#let invoice_render_bilan(invoices) = {
|
|
let content = ()
|
|
for invoice in invoices {
|
|
let total = money_render(invoice.invoice.fold((:),(l,e)=> {
|
|
for currency in e.price.keys() {
|
|
l.insert(currency, l.at(currency,default:0) + e.price.at(currency))
|
|
}
|
|
return l
|
|
}))
|
|
|
|
content.push(invoice.recipient.name)
|
|
content.push([2025-#invoice.invoice_number])
|
|
content.push([#total])
|
|
content.push([])
|
|
// #table(
|
|
// [#i.recipient.name],
|
|
// [#i.invoice],
|
|
// )
|
|
}
|
|
table(
|
|
columns: (1fr, auto, auto, auto),
|
|
align: (auto, auto, right, right),
|
|
[*name*],[*invoice*],[*amount*],[*received*],
|
|
..content
|
|
)
|
|
}
|