Skip to content

Commit 0f67823

Browse files
authored
Merge pull request #94 from invopop/tax-notes-in-notes
GOBL tax notes in PDF Invoice notes
2 parents dbce317 + 1b46780 commit 0f67823

13 files changed

Lines changed: 578 additions & 16 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ localazy.json
3131
/gobl.html
3232

3333
/tmp
34+
.claude

components/bill/invoice.templ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ templ Invoice(env *gobl.Envelope, inv *bill.Invoice) {
4141
@taxes(inv.RegimeDef(), inv.Totals.Taxes)
4242
</div>
4343
@paymentDetails(inv)
44-
@notes(inv.Notes)
44+
@notes(invoiceNotes(ctx, inv))
4545
<div class="extensions">
4646
// Region specific information
4747
@ar.ARCAQR(env)

components/bill/invoice_templ.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/bill/notes.templ

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"fmt"
77

88
"github.com/invopop/gobl.html/components/t"
9+
"github.com/invopop/gobl/bill"
910
"github.com/invopop/gobl/org"
11+
"github.com/invopop/gobl/tax"
1012
"github.com/yuin/goldmark"
1113
"github.com/yuin/goldmark/extension"
1214
"github.com/yuin/goldmark/renderer/html"
@@ -58,6 +60,40 @@ templ note(note *org.Note) {
5860
</div>
5961
}
6062

63+
// invoiceNotes converts tax notes to org notes (with bold category prefix)
64+
// and prepends them to the invoice's regular notes.
65+
func invoiceNotes(ctx context.Context, inv *bill.Invoice) []*org.Note {
66+
var result []*org.Note
67+
if inv.Tax != nil {
68+
r := inv.RegimeDef()
69+
for _, tn := range inv.Tax.Notes {
70+
result = append(result, &org.Note{
71+
Text: renderTaxNoteText(ctx, r, tn),
72+
})
73+
}
74+
}
75+
result = append(result, inv.Notes...)
76+
return result
77+
}
78+
79+
// renderTaxNoteText returns formatted markdown text for a tax note,
80+
// with a bold category prefix when available.
81+
func renderTaxNoteText(ctx context.Context, r *tax.RegimeDef, n *tax.Note) string {
82+
txt := n.Text
83+
prefix := ""
84+
if n.Category != "" {
85+
if cd := r.CategoryDef(n.Category); cd != nil {
86+
prefix = cd.Name.In(t.Lang(ctx))
87+
} else {
88+
prefix = n.Category.String()
89+
}
90+
}
91+
if prefix != "" {
92+
txt = fmt.Sprintf("%s &middot; %s", prefix, txt)
93+
}
94+
return txt
95+
}
96+
6197
func renderNote(note *org.Note) string {
6298
txt := note.Text
6399
if note.Code != "" {

components/bill/notes_templ.go

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/regimes/pl/ksef.templ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func ksefQR(env *gobl.Envelope) string {
6868
func ksefID(env *gobl.Envelope) string {
6969
for _, stamp := range env.Head.Stamps {
7070
switch stamp.Provider {
71-
case favat.StampKSEFNumber:
71+
case favat.StampKSeFNumber:
7272
return stamp.Value
7373
}
7474
}

components/regimes/pl/ksef_templ.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"$schema": "https://gobl.org/draft-0/envelope",
3+
"head": {
4+
"uuid": "8a51fd30-2a27-11ee-be56-0242ac120002",
5+
"dig": {
6+
"alg": "sha256",
7+
"val": "81208a0321b0aefc6448ffdc0e9ebc682c5eff102ce1d0cd5c92506751505604"
8+
}
9+
},
10+
"doc": {
11+
"$schema": "https://gobl.org/draft-0/bill/invoice",
12+
"$regime": "ES",
13+
"$tags": [
14+
"reverse-charge"
15+
],
16+
"uuid": "3aea7b56-59d8-4beb-90bd-f8f280d852a0",
17+
"type": "standard",
18+
"code": "SAMPLE-X-002",
19+
"issue_date": "2022-02-01",
20+
"currency": "EUR",
21+
"tax": {
22+
"notes": [
23+
{
24+
"cat": "VAT",
25+
"key": "reverse-charge",
26+
"text": "Reverse Charge / Inversión del sujeto pasivo."
27+
}
28+
]
29+
},
30+
"supplier": {
31+
"name": "Provide One S.L.",
32+
"tax_id": {
33+
"country": "ES",
34+
"code": "B98602642"
35+
},
36+
"addresses": [
37+
{
38+
"num": "42",
39+
"street": "Calle Pradillo",
40+
"locality": "Madrid",
41+
"region": "Madrid",
42+
"code": "28002",
43+
"country": "ES"
44+
}
45+
],
46+
"emails": [
47+
{
48+
"addr": "billing@example.com"
49+
}
50+
]
51+
},
52+
"customer": {
53+
"name": "Sample Consumer",
54+
"tax_id": {
55+
"country": "NL",
56+
"code": "000099995B57"
57+
}
58+
},
59+
"lines": [
60+
{
61+
"i": 1,
62+
"quantity": "10",
63+
"item": {
64+
"name": "Services exported",
65+
"price": "20.00",
66+
"unit": "day"
67+
},
68+
"sum": "200.00",
69+
"taxes": [
70+
{
71+
"cat": "VAT",
72+
"key": "outside-scope"
73+
}
74+
],
75+
"total": "200.00"
76+
},
77+
{
78+
"i": 2,
79+
"quantity": "50",
80+
"item": {
81+
"name": "Branded Mugs",
82+
"price": "7.50"
83+
},
84+
"sum": "375.00",
85+
"taxes": [
86+
{
87+
"cat": "VAT",
88+
"key": "export"
89+
}
90+
],
91+
"total": "375.00"
92+
}
93+
],
94+
"totals": {
95+
"sum": "575.00",
96+
"total": "575.00",
97+
"taxes": {
98+
"categories": [
99+
{
100+
"code": "VAT",
101+
"rates": [
102+
{
103+
"key": "outside-scope",
104+
"base": "575.00",
105+
"amount": "0.00"
106+
}
107+
],
108+
"amount": "0.00"
109+
}
110+
],
111+
"sum": "0.00"
112+
},
113+
"tax": "0.00",
114+
"total_with_tax": "575.00",
115+
"payable": "575.00"
116+
},
117+
"notes": [
118+
{
119+
"text": "Thank you for your business!"
120+
},
121+
{
122+
"text": "Powered by Invopop"
123+
}
124+
]
125+
}
126+
}

examples/mx-acuentaterceros.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"uuid": "c82905b0-061b-11ef-9fc5-12007b149b55",
55
"dig": {
66
"alg": "sha256",
7-
"val": "35d95e0080b99b4c0f05d0667a017f89d6404cf8039ebd5d1cc25d4062af636a"
7+
"val": "89c905a760c34209cc0654f2202318d3d4b99ba46c7d4399f6a0c9864f5d140e"
88
}
99
},
1010
"doc": {
@@ -17,7 +17,7 @@
1717
"type": "standard",
1818
"series": "LMC",
1919
"code": "002",
20-
"issue_date": "2026-03-20",
20+
"issue_date": "2026-03-24",
2121
"issue_time": "01:42:18",
2222
"currency": "MXN",
2323
"tax": {
@@ -74,14 +74,14 @@
7474
],
7575
"total": "200.00",
7676
"seller": {
77-
"name": "OZONO POLARIS",
77+
"name": "XENON INDUSTRIAL ARTICLES",
7878
"tax_id": {
7979
"country": "MX",
80-
"code": "OPO870812NC7"
80+
"code": "XIA190128J61"
8181
},
8282
"addresses": [
8383
{
84-
"code": "72190"
84+
"code": "76343"
8585
}
8686
],
8787
"ext": {

0 commit comments

Comments
 (0)