diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..9274b9e --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,25 @@ +name: Lint +on: + push: + tags: + - v* + branches: + - main + pull_request: +jobs: + lint: + name: golangci-lint + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v3 + + - uses: actions/setup-go@v4 + with: + go-version-file: "go.mod" + + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + version: v1.55 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9d7a9b6 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,30 @@ +# +# Automatically tag a merge with main. +# + +name: Release + +on: + push: + branches: + - main + paths-ignore: + - "README.md" + +jobs: + release: + name: Tag + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: "0" # make sure we get all commits! + + - name: Bump version and push tag + id: bump + uses: anothrNick/github-tag-action@1.52.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_BRANCHES: release + WITH_V: true diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..8c72412 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,24 @@ +name: Test Go +on: [push] +jobs: + test: + name: Test + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version-file: "go.mod" + id: go + + - name: Install Dependencies + env: + GOPROXY: https://proxy.golang.org,direct + run: go mod download + + - name: Test + run: go test -tags unit -race ./... diff --git a/.gitignore b/.gitignore index 3915f0f..26b61a3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ # vendor/ *.hide.json +*.hide.html # Go workspace file go.work diff --git a/README.md b/README.md index 4f0dd9a..9a73d2c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Released under the Apache 2.0 [LICENSE](https://github.com/invopop/gobl/blob/mai ### Generate Templates -GOBL HTML uses [templ](https://templ.guide/) to define a set of components in Go. To genera the templates, run: +GOBL HTML uses [templ](https://templ.guide/) to define a set of components in Go. To generate the templates, run: ```bash templ generate @@ -19,3 +19,11 @@ During development, it can help massive to have hot reload to be able to make ch ```bash templ generate --watch --cmd="go run ./cmd/gobl.html serve --pdf prince" ``` + +### Testing + +Tests are currently pretty limited. To ensure the basics are covered, the contents of the `examples` directory are converted to HTML, pretty printed, and output to the `examples/out` directory. The tests will ensure the output is as expected. To update the output test data run: + +```bash +go test ./... --update +``` diff --git a/cmd/gobl.html/main.go b/cmd/gobl.html/main.go index e77c508..2287ec8 100644 --- a/cmd/gobl.html/main.go +++ b/cmd/gobl.html/main.go @@ -6,9 +6,6 @@ import ( "os" "os/signal" "syscall" - - "github.com/invopop/ctxi18n" - "github.com/invopop/gobl.html/locales" ) // build data provided by goreleaser and mage setup @@ -28,10 +25,6 @@ func run() error { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer cancel() - if err := ctxi18n.LoadWithDefault(locales.Content, "en"); err != nil { - return fmt.Errorf("loading locales: %w", err) - } - return root().cmd().ExecuteContext(ctx) } diff --git a/components/bill/invoice/invoice.templ b/components/bill/invoice/invoice.templ index e8ba2e2..d6a9a16 100644 --- a/components/bill/invoice/invoice.templ +++ b/components/bill/invoice/invoice.templ @@ -8,17 +8,15 @@ import ( "github.com/invopop/gobl/org" "github.com/invopop/gobl/tax" "github.com/invopop/gobl.html/components/t" - "github.com/invopop/gobl.html/components/regimes/es" "github.com/invopop/gobl.html/components/regimes/co" "github.com/invopop/gobl.html/components/regimes/mx" + "github.com/invopop/gobl.html/internal" ) // Invoice renders a complete GOBL bill.Invoice object. templ Invoice(env *gobl.Envelope, inv *bill.Invoice) { + + +
+ + + +
+
+
+
+
+
+ Provide One S.L. +
+
+

+ Credit Note +

+

+ FR-012 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
  • + + Previous Invoice + + + SAMPLE-085 + + + 2022-01-10 + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Provide One S.L. +
+
+ + San Frantzisko 42, Bilbo, Bizkaia, 48003 (Spain) + +
+
+ Email: billing@example.com +
+
+ NIF: (ES) B98602642 +
+
+
+
+

+ Customer +

+
+
+ Sample Customer +
+
+ + Calle del Barro 13, Alcañiz, Teruel, 44600 (Spain) + +
+
+ Email: customer@example.com +
+
+ NIF: (ES) 54387763P +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Unit + + Price + + VAT + + Disc. + + Total +
+ 1 + + + Development services + + + 20 + + h + + €90,00 + + 21,0% + + 10% + + €1.620,00 +
+ 2 + + + Financial service + + + 1 + + + + €10,00 + + 0,0% + + €10,00 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + +
+ Sum + + €1.630,00 +
+ Tax + + €340,20 +
+ Total + + €1.970,20 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €1.620,00 + + 21,0% + + €340,20 +
+ €10,00 + + 0,0% + + €0,00 +
+
+
+
+

+ Notes +

+
+

+ Some random description +

+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/full-invoice.html b/examples/out/full-invoice.html new file mode 100644 index 0000000..fcafc9f --- /dev/null +++ b/examples/out/full-invoice.html @@ -0,0 +1,865 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+ Invopop +
+

+ Invoice +

+

+ FAKE20220001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
  • + + Order + + + 111-9244735-1237858 + +
  • +
  • + + Service period + + + 2022-02-01 to 2022-02-28 + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Biz España S.L. +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ Email: billing@bizspain.es +
+
+ NIF: (ES) B28774008 +
+
+ LEI: 1010101010 +
+
+ Identitiy code: ABC1234 +
+
+
+
+

+ Customer +

+
+
+ Customer Spain S.L. +
+
+ + Office:  + + + Calle Mayor 10, Madrid, Madrid, 28003 (Spain) + +
+
+ Email: sam@customer.com +
+
+ NIF: (ES) B33105842 +
+
+ Código de Cliente: 123456789ABC +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + IRPF + + Disc. + + Total +
+ 1 + + + Development Day Rate + +
+ + Development services for the month of February 2022 + +
+ 20 + + €200,00 + + 21,0% + + + + 5% + + €3.800,00 +
+ 2 + + + Something random + + + 10 + + €100,00 + + 21,0% + + + + €1.000,00 +
+ 3 + + + Additional random services + + + 5 + + €34,50 + + 10,0% + + 15% + + €172,50 +
+ C1 + + Impuesto de Turismo + + + + + + + + + + + + €15,00 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Sum + + €4.972,50 +
+ Charge + + €15,00 +
+ Total + + €4.987,50 +
+ Tax + + €999,38 +
+ Total with tax + + €5.986,88 +
+ Outlay 1: Something paid for by us + + €200,00 +
+ Total to pay + + €6.186,88 +
+ Advance: 2022-01-15 Prepayment with Paypal (PAY1234) + + €1.796,06 +
+ Total due + + €4.390,81 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €4.800,00 + + 21,0% + + €1.008,00 +
+ €172,50 + + 10,0% + + €17,25 +
+ IRPF + + €172,50 + + -15% + + €-25,88 +
+
+
+
+

+ Payment +

+
    +
  • + + Method of payment + + + Bank transfer + +
  • +
  • + + Bank transfer instructions + + + + + Bank: RANDOM + + + IBAN: ES31 0240 4036 4573 9340 5564 + + + +
  • +
+
+
+

+ Notes +

+
+

+ Thank you for you business! +

+
+
+

+ Please pay within 30 days according to some random law in the country we sell from that you should comply with because we want to be paid sooner. +

+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice-es-simplified.html b/examples/out/invoice-es-simplified.html new file mode 100644 index 0000000..a864b6a --- /dev/null +++ b/examples/out/invoice-es-simplified.html @@ -0,0 +1,633 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ Provide One S.L. +
+
+

+ Simplified Invoice +

+

+ SAMPLE-001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Provide One S.L. +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ Email: billing@example.com +
+
+ NIF: (ES) B98602642 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + Disc. + + Total +
+ 1 + + + Main product + + + 20 + + €90,00 + + 21,0% + + 10% + + €1.620,00 +
+ 2 + + + Something else + + + 1 + + €10,00 + + 21,0% + + €10,00 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + +
+ Sum + + €1.630,00 +
+ Tax + + €342,30 +
+ Total to pay + + €1.972,30 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €1.630,00 + + 21,0% + + €342,30 +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice-es-ticketbai.html b/examples/out/invoice-es-ticketbai.html new file mode 100644 index 0000000..8150622 --- /dev/null +++ b/examples/out/invoice-es-ticketbai.html @@ -0,0 +1,685 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ Provide One S.L. +
+
+

+ Invoice +

+

+ SAMPLE-001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Provide One S.L. +
+
+ + Calle Pradillo 42, Bilbao, Vizcaya, 48001 (Spain) + +
+
+ Email: billing@example.com +
+
+ NIF: (ES) B98602642 +
+
+
+
+

+ Customer +

+
+
+ Sample Consumer +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ NIF: (ES) 54387763P +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Unit + + Price + + VAT + + Total +
+ 1 + + + Development services + + + 20 + + h + + €90,00 + + 21,0% + + €1.800,00 +
+ 2 + + + Out of hours support + + + 1 + + + + €120,00 + + 21,0% + + €120,00 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + +
+ Sum + + €1.920,00 +
+ Tax + + €403,20 +
+ Total to pay + + €2.323,20 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €1.920,00 + + 21,0% + + €403,20 +
+
+
+
+ +
+
+
+ TBAI-B98602642-010222-LrNMp23wKPLjx-112 +
+
+ + + +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice-limited-company.html b/examples/out/invoice-limited-company.html new file mode 100644 index 0000000..f46076d --- /dev/null +++ b/examples/out/invoice-limited-company.html @@ -0,0 +1,745 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ Biz España S.L. +
+
+

+ Invoice +

+

+ FAKE20220001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Biz España S.L. +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ Email: billing@bizspain.es +
+
+ NIF: (ES) B28774008 +
+
+
+
+

+ Customer +

+
+
+ Customer Spain S.L. +
+
+ + Calle Mayor 10, Madrid, Madrid, 28003 (Spain) + +
+
+ Email: sam@customer.com +
+
+ NIF: (ES) B33105842 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + Disc. + + Total +
+ 1 + + + Development Day Rate + + + 20 + + €200,00 + + 21,0% + + 5% + + €3.800,00 +
+ 2 + + + Something random + + + 10 + + €100,00 + + 21,0% + + €1.000,00 +
+ 3 + + + Additional random services + + + 5 + + €34,50 + + 10,0% + + €172,50 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + +
+ Sum + + €4.972,50 +
+ Tax + + €1.025,25 +
+ Total to pay + + €5.997,75 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €4.800,00 + + 21,0% + + €1.008,00 +
+ €172,50 + + 10,0% + + €17,25 +
+
+
+
+

+ Payment +

+
    +
  • + + Method of payment + + + Bank transfer + +
  • +
  • + + Bank transfer instructions + + + + + Bank: RANDOM + + + IBAN: ES31 0240 4036 4573 9340 5564 + + + +
  • +
+
+
+

+ Notes +

+
+

+ Thank you for your custom! If you require assistance with this invoice please get in touch. +

+
+
+ + Inscribed in: + + + Volume 27590 + + + Sheet 129 + + + Page M-497191 + + + Entry 1 + +
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice-mx.html b/examples/out/invoice-mx.html new file mode 100644 index 0000000..220efe6 --- /dev/null +++ b/examples/out/invoice-mx.html @@ -0,0 +1,868 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ ESCUELA KEMPER URGATE +
+
+

+ Invoice +

+

+ LMC-0010 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2024-04-25 + +
  • +
  • + + Currency + + + Mexican Peso (MXN) + +
  • +
  • + + Place of Issue + + + 26015 + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ ESCUELA KEMPER URGATE +
+
+ RFC: (MX) EKU9003173C9 +
+
+ Régimen Fiscal: 601 +
+
+
+
+

+ Customer +

+
+
+ UNIVERSIDAD ROBOTICA ESPAÑOLA +
+
+ RFC: (MX) URE180429TM6 +
+
+ Régimen Fiscal: 601 +
+
+ Lugar: 86991 +
+
+ Uso CFDI: G01 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Unit + + Price + + VAT + + Retained VAT + + ISR + + Disc. + + Total +
+ 1 + + + Cigarros + + + + Clave SAT: + + + 50211502 + + + + 2 + + piece + + $200.2020 + + 16.0% + + 10.6667% + + 10% + + 25.0% + + $200.2020 +
+ 2 + + + Cerveza + + + + Clave SAT: + + + 50211502 + + + + 1 + + piece + + $10.50 + + — + + + + + + $10.50 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + + + + + + + + + +
+ Sum + + $210.70 +
+ Tax + + $-9.34 +
+ Total to pay + + $201.36 +
+ Advance: Top-up payment + + $201.36 +
+ Total due + + $0.00 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + $200.20 + + 16.0% + + $32.03 +
+ $10.50 + + — + + $0.00 +
+ Retained VAT + + $200.20 + + -10.6667% + + $-21.35 +
+ ISR + + $200.20 + + -10% + + $-20.02 +
+
+
+
+

+ Payment +

+
    +
  • + + Payment Terms Notes + + + Pago a 30 días. + +
  • +
+
+
+ +
+
+ + + +
+
+
+ + Folio Fiscal: + + + 187501fc-3e99-4034-bcb1-7429ee841bb1 + +
+
+ + Fecha y hora de certificación: + + + 2024-04-25T09:19:41 + +
+
+ + Sello digital del CFDI + + + Serie: + + 30001000000500003416 + + + + eMreWl4lUV+D3r1bQnQf4oOAlpJIDBHf5f4vwE/JsrE8orTIeJnaMWmh8nGWRHRXMnsrGpbPmYWpUL2yym2YEWz8by+BWt2B5wKkKWuwt3mhdxeyhUPY22x7AtIF9GFmfvRX5rpQkmhKCxojjkxsv+3JoW1ppN2Db88ZgJIV1UxcHguObSQ01mw9zhaRx4Uoxhri9wPHxRvbQX7jSuFhSs6uiOyH4z5L+MB9WgrilJQOInHXFIIisnRQ99U5O+UMZoJrXWS16iFgo2ccOCteS61whEOZIgNAZYJ/CkKQeDLuZDcCFihMX9tPYL/3nLiaqDFSS9fvrbjhLdp1+hv+oQ== + +
+
+ + Sello digital del SAT + + + Serie: + + 30001000000500003456 + + + + YrFWG5lHOTvdrPjlL2AG6SDu0CSkd3lst3NR6ROCsRqTX3j6jVr7hBOyJXev5JkeWbEuLgGmVv+pNuHRoeaoQaQsfFaYM39Zfar5ow/iGIcLfOrUei60l7aicsK2Yx87IWY8CNr5ZZmlQ8Nr/HWp6I7CThWx+6H14+qnSX7PYIK/gkSWr5ZoOVUZvkukZoOgSzaah0DFNa4w6s6RjXjwCwkWavdf4QFg/VSZzs6RquJkFgkVBo1hDyXIlKRpGa03674RujuKtTAsoSZBPDERDj0Lc1hqh11cK0Hlm2TVLHA63P4zwKDvA3XLRUEXr6upbwDv3CxiDL0JXRurmkowEg== + +
+
+ + Cadena Original del complemento de certificación digital del SAT + + + ||1.1|187501fc-3e99-4034-bcb1-7429ee841bb1|2024-04-25T09:19:41|SPR190613I52|eMreWl4lUV+D3r1bQnQf4oOAlpJIDBHf5f4vwE/JsrE8orTIeJnaMWmh8nGWRHRXMnsrGpbPmYWpUL2yym2YEWz8by+BWt2B5wKkKWuwt3mhdxeyhUPY22x7AtIF9GFmfvRX5rpQkmhKCxojjkxsv+3JoW1ppN2Db88ZgJIV1UxcHguObSQ01mw9zhaRx4Uoxhri9wPHxRvbQX7jSuFhSs6uiOyH4z5L+MB9WgrilJQOInHXFIIisnRQ99U5O+UMZoJrXWS16iFgo2ccOCteS61whEOZIgNAZYJ/CkKQeDLuZDcCFihMX9tPYL/3nLiaqDFSS9fvrbjhLdp1+hv+oQ==|30001000000500003456|| + +
+
+ Este documento es una represetnación impresa de un CFDI. +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice-tax-included.html b/examples/out/invoice-tax-included.html new file mode 100644 index 0000000..651d9d1 --- /dev/null +++ b/examples/out/invoice-tax-included.html @@ -0,0 +1,753 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ Biz España S.L. +
+
+

+ Invoice +

+

+ FAKE20220001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Biz España S.L. +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ Email: billing@bizspain.es +
+
+ NIF: (ES) B28774008 +
+
+
+
+

+ Customer +

+
+
+ Customer Spain S.L. +
+
+ + Calle Mayor 10, Madrid, Madrid, 28003 (Spain) + +
+
+ Email: sam@customer.com +
+
+ NIF: (ES) B33105842 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + Disc. + + Total +
+ 1 + + + Development Day Rate + + + 20 + + €200,00 + + 21,0% + + 5% + + €3.800,00 +
+ 2 + + + Something random + + + 10 + + €100,00 + + 21,0% + + €1.000,00 +
+ 3 + + + Additional random services + + + 5 + + €34,50 + + 10,0% + + €172,50 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + + + + + +
+ Sum + + €4.972,50 +
+ Included tax + + €848,74 +
+ Total + + €4.123,76 +
+ Total to pay + + €4.972,50 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €3.966,94 + + 21,0% + + €833,06 +
+ €156,82 + + 10,0% + + €15,68 +
+
+
+
+

+ Payment +

+
    +
  • + + Method of payment + + + Bank transfer + +
  • +
  • + + Bank transfer instructions + + + + + Bank: RANDOM + + + IBAN: ES31 0240 4036 4573 9340 5564 + + + +
  • +
+
+
+

+ Notes +

+
+

+ Thank you for your custom! If you require assistance with this invoice please get in touch. +

+
+
+ + Inscribed in: + + + Volume 27590 + + + Sheet 129 + + + Page M-497191 + + + Entry 1 + +
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/invoice.env.html b/examples/out/invoice.env.html new file mode 100644 index 0000000..f0901e1 --- /dev/null +++ b/examples/out/invoice.env.html @@ -0,0 +1,617 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ Provide One S.L. +
+
+

+ Invoice +

+

+ SAMPLE-001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2022-02-01 + +
  • +
  • + + Currency + + + Euro (EUR) + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ Provide One S.L. +
+
+ + Calle Pradillo 42, Madrid, Madrid, 28002 (Spain) + +
+
+ Email: billing@example.com +
+
+ NIF: (ES) B98602642 +
+
+
+
+

+ Customer +

+
+
+ Sample Consumer +
+
+ NIF: (ES) 54387763P +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + Total +
+ 1 + + + Item being purchased + + + 10 + + €100,00 + + 21,0% + + €1.000,00 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + +
+ Sum + + €1.000,00 +
+ Tax + + €210,00 +
+ Total to pay + + €1.210,00 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + €1.000,00 + + 21,0% + + €210,00 +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/mx-food-voucher.html b/examples/out/mx-food-voucher.html new file mode 100644 index 0000000..a8f34b1 --- /dev/null +++ b/examples/out/mx-food-voucher.html @@ -0,0 +1,898 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ ESCUELA KEMPER URGATE +
+
+

+ Invoice +

+

+ IN2024-0001 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2024-04-29 + +
  • +
  • + + Currency + + + Mexican Peso (MXN) + +
  • +
  • + + Place of Issue + + + 26015 + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ ESCUELA KEMPER URGATE +
+
+ RFC: (MX) EKU9003173C9 +
+
+ Régimen Fiscal: 601 +
+
+ Lugar: 26015 +
+
+
+
+

+ Customer +

+
+
+ UNIVERSIDAD ROBOTICA ESPAÑOLA +
+
+ RFC: (MX) URE180429TM6 +
+
+ Régimen Fiscal: 601 +
+
+ Lugar: 86991 +
+
+ Uso CFDI: G01 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Unit + + Price + + VAT + + Disc. + + Total +
+ 1 + + + Dispersión de Despensa + + + + Clave SAT: + + + 84141602 + + + + 1 + + E48 + + $0.02 + + — + + $-0.01 + + $0.01 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + + + + + + + + + +
+ Sum + + $0.01 +
+ Tax + + $0.00 +
+ Total to pay + + $0.01 +
+ Advance: Transferencia electrónica de fondos + + $0.00 +
+ Total due + + $0.01 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + $0.01 + + — + + $0.00 +
+
+
+
+ +
+

+ Complemento de Vales de Despensa +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + ID + + Fecha + + RFC + + CURP + + Nombre + + Seg. Social + + Importe +
+ 1 + + ABC1234 + + 2022-07-19 10:20 + + JUFA7608212V6 + + JUFA760821MDFRRR00 + + Adriana Juarez Fernández + + 12345678901 + + $100.00 +
+ 2 + + BCD4321 + + 2022-08-20 11:20 + + KAHO641101B39 + + KAHO641101HDFRRR00 + + Oscar Kala Haak + + 12345678905 + + $100.00 +
+ Total: + + $200.00 +
+
+ +
+
+ + + +
+
+
+ + Folio Fiscal: + + + b7ed8978-f0d7-48c5-b547-daf9ac54aa4a + +
+
+ + Fecha y hora de certificación: + + + 2024-04-29T07:16:10 + +
+
+ + Sello digital del CFDI + + + Serie: + + 30001000000500003416 + + + + Qk+hftgbpSYMqcc7BoBzGOctcNbDypP4IiHfzgf4mJuraxGEFbo8JEfS6BOzfqXojVLbRxPkXE83fqzWdnowOhOmi0Mvk3CdlTZNZ9i2i7hJv6cwfHXMTPpeOxfptuv2AzhFX7O14gS2szeai5yJIcT+d8/W6H0zN6b/2S12GkMdik0tyF8XRtQQat4Q1jxQzslOp46brROc0Xuobc7/jogq2ib7oaJEu7qDKhjYAkH7BHMaLMJgeC6eVeHUkkrr7RVTkH75H4wteKZNuP85E/F12nCkbMSQq7uplzRuLyO6YEkyIWxpT+0Gqtcb6pziYk8wqwP1pVsVZZ4Q0PPOsw== + +
+
+ + Sello digital del SAT + + + Serie: + + 30001000000500003456 + + + + MQWzFEvDEWag8sM8OWjlCS1DmrZWe+/1VtGsxDz8ycSH1yh8aoUGV3lKezQy190TnB2mncusGvFoUpbDhrk0tlRpTGT2+68/43yb0RZ+yWTVCrd09sCzRk/Dct+oyUKWr2AYYxA0Jt7ECyR4DQfPQYbPF4shsmcerMWxsbMUKqR43shvsb5QTxk/yA6CiWx3z/+uSxvJitUoUihAtFeBBdiNnKVBcG7ifWNJaoaBL03UOm1Ro77rGvUfYuxqqI02Fav98iv9eS//YmGzUTpz0Uvc9Lut+MRqkBvs3G/PBiJvO5Yi6I2PhzNo0O7z9SD6P9WnmufmVI9JFCbHVk+PNg== + +
+
+ + Cadena Original del complemento de certificación digital del SAT + + + ||1.1|b7ed8978-f0d7-48c5-b547-daf9ac54aa4a|2024-04-29T07:16:10|SPR190613I52|Qk+hftgbpSYMqcc7BoBzGOctcNbDypP4IiHfzgf4mJuraxGEFbo8JEfS6BOzfqXojVLbRxPkXE83fqzWdnowOhOmi0Mvk3CdlTZNZ9i2i7hJv6cwfHXMTPpeOxfptuv2AzhFX7O14gS2szeai5yJIcT+d8/W6H0zN6b/2S12GkMdik0tyF8XRtQQat4Q1jxQzslOp46brROc0Xuobc7/jogq2ib7oaJEu7qDKhjYAkH7BHMaLMJgeC6eVeHUkkrr7RVTkH75H4wteKZNuP85E/F12nCkbMSQq7uplzRuLyO6YEkyIWxpT+0Gqtcb6pziYk8wqwP1pVsVZZ4Q0PPOsw==|30001000000500003456|| + +
+
+ Este documento es una represetnación impresa de un CFDI. +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples/out/mx-fuel-balance.html b/examples/out/mx-fuel-balance.html new file mode 100644 index 0000000..3982692 --- /dev/null +++ b/examples/out/mx-fuel-balance.html @@ -0,0 +1,931 @@ + + + + GOBL HTML Generator + + + + + + + + + +
+ + + +
+
+
+
+
+
+ ESCUELA KEMPER URGATE +
+
+

+ Invoice +

+

+ IN2024-0003 +

+
+
+

+ Summary +

+
    +
  • + + Issue Date + + + 2024-04-29 + +
  • +
  • + + Currency + + + Mexican Peso (MXN) + +
  • +
  • + + Place of Issue + + + 26015 + +
  • +
+
+
+
+
+

+ Supplier +

+
+
+ ESCUELA KEMPER URGATE +
+
+ RFC: (MX) EKU9003173C9 +
+
+ Régimen Fiscal: 601 +
+
+ Lugar: 26015 +
+
+
+
+

+ Customer +

+
+
+ UNIVERSIDAD ROBOTICA ESPAÑOLA +
+
+ RFC: (MX) URE180429TM6 +
+
+ Régimen Fiscal: 601 +
+
+ Lugar: 86991 +
+
+ Uso CFDI: G01 +
+
+
+
+
+
+

+ Lines +

+ + + + + + + + + + + + + + + + + + + + + +
+ # + + Description + + Qty. + + Price + + VAT + + Total +
+ 1 + + + Dispersión de Despensa + + + + Clave SAT: + + + 80141628 + + + + 1 + + $0.01 + + 16.0% + + $0.01 +
+
+
+
+

+ Totals +

+ + + + + + + + + + + + + + + + + + + + + + + +
+ Sum + + $0.01 +
+ Tax + + $0.00 +
+ Total to pay + + $0.01 +
+ Advance: Transferencia electrónica de fondos + + $0.00 +
+ Total due + + $0.01 +
+
+
+

+ Taxes +

+ + + + + + + + + + + + + + + + + +
+ Tax + + Base + + Rate + + Amount +
+ VAT + + $0.01 + + 16.0% + + $0.00 +
+
+
+
+ + + +
+
+ + + +
+
+
+ + Folio Fiscal: + + + e212d624-4812-4987-9499-0d552d5fbeee + +
+
+ + Fecha y hora de certificación: + + + 2024-04-29T15:47:56 + +
+
+ + Sello digital del CFDI + + + Serie: + + 30001000000500003416 + + + + e64nyfKdZUH0VQW6cmj8X8RVJyo8REDJ/IX78NSY4gFEgvgVbTerC8qyewNFz2uhPQCPRRqQg5I18dw0iFeghmE6eqyp/r8ap3Gl3xLz+zbhJyuHEh6tYrJbGF82lmcaIC8iXkBWeAc6hYREG3dZP3f1CeHx8HrUlDxTi5qYzfNONLha9sVvw6GaxpPJw/Fg8pschNNB9qzi/1MKniNaE+nXa1TdRblg/XGptARySkL+L7cLwitfsWjysUc1j3fayaeHaqenndRi9RzuSrVZzyq3FN3Cmtp8j1AGUsEUfKUNZPCLV1xBKZgWs2i0Fb5IF5kcTCbA/TrGK9biyx2PLw== + +
+
+ + Sello digital del SAT + + + Serie: + + 30001000000500003456 + + + + W3R199ZxrwmgSMPnecd0KzO/M8oY12OQX6j1c9G2jhQPLAf1jZoq3A4fqFpflSzpE+b8taUfsDBzprpb5Vo/cZvvOB0pxk5q7zVXWOoicafc5GkBw38emLftCej2VowLIHRd956x++xcEcx+GJNCTaOXi5gd5fC76KnHGKrCbOBfUtFJXSo8bTQRQjJf4zkK6wWl76ua4oZbeUcTtCFA/J+6lJzYPmP7ggpXM+fEcawK1oa2f+2KAwkxTfTLxKSDSAfnyrhlLL2215hWDG4K3W4Tf5xLvzKprQ0NTbxDpXxnXbkpWUaLDuu+7MX6BfWRQk9RbX/L7Vue5Aq9ydJcVQ== + +
+
+ + Cadena Original del complemento de certificación digital del SAT + + + ||1.1|e212d624-4812-4987-9499-0d552d5fbeee|2024-04-29T15:47:56|SPR190613I52|e64nyfKdZUH0VQW6cmj8X8RVJyo8REDJ/IX78NSY4gFEgvgVbTerC8qyewNFz2uhPQCPRRqQg5I18dw0iFeghmE6eqyp/r8ap3Gl3xLz+zbhJyuHEh6tYrJbGF82lmcaIC8iXkBWeAc6hYREG3dZP3f1CeHx8HrUlDxTi5qYzfNONLha9sVvw6GaxpPJw/Fg8pschNNB9qzi/1MKniNaE+nXa1TdRblg/XGptARySkL+L7cLwitfsWjysUc1j3fayaeHaqenndRi9RzuSrVZzyq3FN3Cmtp8j1AGUsEUfKUNZPCLV1xBKZgWs2i0Fb5IF5kcTCbA/TrGK9biyx2PLw==|30001000000500003456|| + +
+
+ Este documento es una represetnación impresa de un CFDI. +
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/examples_test.go b/examples_test.go new file mode 100644 index 0000000..8f2d47b --- /dev/null +++ b/examples_test.go @@ -0,0 +1,107 @@ +package goblhtml_test + +import ( + "bytes" + "context" + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/invopop/gobl" + goblhtml "github.com/invopop/gobl.html" + "github.com/pmezard/go-difflib/difflib" + "github.com/stretchr/testify/require" + "github.com/yosssi/gohtml" +) + +const ( + examplesPath = "./examples" + examplesOutPath = "./examples/out" +) + +var updateOut = flag.Bool("update", false, "Update the HTML files in the examples/out directory") + +func TestGOBLRenderExamples(t *testing.T) { + examples, err := findExamples() + require.NoError(t, err) + + for _, example := range examples { + name := fmt.Sprintf("should convert %s example file successfully", example) + + t.Run(name, func(t *testing.T) { + data, err := convertExample(example) + require.NoError(t, err) + + outPath := filepath.Join(examplesOutPath, strings.TrimSuffix(example, ".json")+".html") + + // Make the output pretty! + out := gohtml.Format(string(data)) + + if *updateOut { + err = os.WriteFile(outPath, []byte(out), 0644) + require.NoError(t, err) + return + } + + expected, err := os.ReadFile(outPath) + + require.False(t, os.IsNotExist(err), "output file %s missing, run tests with `--update` flag to create", filepath.Base(outPath)) + require.NoError(t, err) + if out != string(expected) { + diff := difflib.UnifiedDiff{ + A: difflib.SplitLines(string(expected)), + B: difflib.SplitLines(out), + FromFile: "Expected", + ToFile: "Got", + Context: 2, + } + text, _ := difflib.GetUnifiedDiffString(diff) + t.Errorf("output file %s does not match:\n%s", filepath.Base(outPath), text) + } + // assert.Equal(t, string(expected), out, "output file %s does not match, run tests with `--update` flag to update", filepath.Base(outPath)) + }) + } +} + +func findExamples() ([]string, error) { + examples, err := filepath.Glob(filepath.Join(examplesPath, "*.json")) + if err != nil { + return nil, err + } + + for i, example := range examples { + examples[i] = filepath.Base(example) + } + + return examples, nil +} + +func convertExample(name string) ([]byte, error) { + env, err := loadExample(name) + if err != nil { + return nil, err + } + + ctx := context.Background() + return goblhtml.Render(ctx, env) +} + +func loadExample(name string) (*gobl.Envelope, error) { + src, _ := os.Open(filepath.Join(examplesPath, name)) + + buf := new(bytes.Buffer) + if _, err := buf.ReadFrom(src); err != nil { + return nil, err + } + + env := new(gobl.Envelope) + if err := json.Unmarshal(buf.Bytes(), env); err != nil { + return nil, err + } + + return env, nil +} diff --git a/go.mod b/go.mod index 44be47a..d411523 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,13 @@ require ( github.com/a-h/templ v0.2.598 github.com/go-resty/resty/v2 v2.12.0 github.com/invopop/ctxi18n v0.6.0 - github.com/invopop/gobl v0.74.2-0.20240429205753-51d26c7ca2f8 + github.com/invopop/gobl v0.75.0 github.com/invopop/princepdf v0.0.0-20240408123340-585be3cab91a github.com/labstack/echo/v4 v4.11.4 github.com/piglig/go-qr v0.2.4 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 + github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 github.com/yuin/goldmark v1.4.13 gitlab.com/flimzy/testy v0.14.0 ) diff --git a/go.sum b/go.sum index d134d57..684e64d 100644 --- a/go.sum +++ b/go.sum @@ -24,14 +24,12 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/ctxi18n v0.4.0 h1:wj8dMqjevUtsXkUt/EmvUXoYWZQJN8uSbfCSblVMJLU= -github.com/invopop/ctxi18n v0.4.0/go.mod h1:1Osw+JGYA+anHt0Z4reF36r5FtGHYjGQ+m1X7keIhPc= -github.com/invopop/ctxi18n v0.5.0 h1:veT4ZBr8qXEu3USRyS2UvQshFVVtTjkQOYiq8FkATMc= -github.com/invopop/ctxi18n v0.5.0/go.mod h1:1Osw+JGYA+anHt0Z4reF36r5FtGHYjGQ+m1X7keIhPc= github.com/invopop/ctxi18n v0.6.0 h1:Qm3ZL/kK4EKvmLI3U2ETN2rWrtSTaxXrcA6ZUY9aVGE= github.com/invopop/ctxi18n v0.6.0/go.mod h1:1Osw+JGYA+anHt0Z4reF36r5FtGHYjGQ+m1X7keIhPc= github.com/invopop/gobl v0.74.2-0.20240429205753-51d26c7ca2f8 h1:1EPYjNWXRiW7n80XmBSLcO9MfOlilVxLUj3l+Iy/6Ng= github.com/invopop/gobl v0.74.2-0.20240429205753-51d26c7ca2f8/go.mod h1:3ixShxX1jlOKo5Rw22HVQh3jXnK9AZa7Twcw7L92qn0= +github.com/invopop/gobl v0.75.0 h1:QQpFTkraauZoGEGQlYqfHpJmBNayamtjT1+Onw8zVEA= +github.com/invopop/gobl v0.75.0/go.mod h1:3ixShxX1jlOKo5Rw22HVQh3jXnK9AZa7Twcw7L92qn0= github.com/invopop/jsonschema v0.12.0 h1:6ovsNSuvn9wEQVOyc72aycBMVQFKz7cPdMJn10CvzRI= github.com/invopop/jsonschema v0.12.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/invopop/princepdf v0.0.0-20240408123340-585be3cab91a h1:xt18LlIfizLkFgLi+vK/m2SWOsAbQwVwQgbkzxKY0eU= @@ -84,6 +82,8 @@ github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4 h1:0sw0nJM544SpsihWx1bkXdYLQDlzRflMgFJQ4Yih9ts= +github.com/yosssi/gohtml v0.0.0-20201013000340-ee4748c638f4/go.mod h1:+ccdNT0xMY1dtc5XBxumbYfOUhmduiGudqaDgD2rVRE= github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/flimzy/testy v0.14.0 h1:2nZV4Wa1OSJb3rOKHh0GJqvvhtE03zT+sKnPCI0owfQ= diff --git a/goblhtml.go b/goblhtml.go index 1402c3b..e9976bb 100644 --- a/goblhtml.go +++ b/goblhtml.go @@ -6,11 +6,11 @@ import ( "fmt" "time" - "github.com/invopop/ctxi18n" "github.com/invopop/ctxi18n/i18n" "github.com/invopop/gobl" "github.com/invopop/gobl.html/components" - "github.com/invopop/gobl.html/components/t" + "github.com/invopop/gobl.html/internal" + srclocales "github.com/invopop/gobl.html/locales" "github.com/invopop/gobl/bill" "github.com/invopop/gobl/currency" "github.com/invopop/gobl/num" @@ -18,46 +18,49 @@ import ( ) const ( - defaultLanguage i18n.Code = "en" + defaultLocale i18n.Code = "en" ) -// Option defines a configuration option to use for rendering. -type Option func(*options) - -type options struct { - locale i18n.Code - calFormatter *t.CalFormatter - numFormatter *num.Formatter - logo *org.Image - notes string +var locales *i18n.Locales + +func init() { + // Locales are loaded into a global variable for this package so that + // users can continue to use their own locales outside of this one. + locales = new(i18n.Locales) + if err := locales.LoadWithDefault(srclocales.Content, defaultLocale); err != nil { + panic(fmt.Errorf("loading locales: %w", err)) + } } +// Option defines a configuration option to use for rendering. +type Option func(*internal.Opts) + // WithLogo overrides whatever logo was defined in the original envelope, // if at all, using the provided logo according to the document type. func WithLogo(logo *org.Image) Option { - return func(o *options) { - o.logo = logo + return func(o *internal.Opts) { + o.Logo = logo } } // WithNotes adds the provided string to the envelope notes. func WithNotes(txt string) Option { - return func(o *options) { - o.notes = txt + return func(o *internal.Opts) { + o.Notes = txt } } // WithLocale sets the locale to use for rendering. func WithLocale(locale i18n.Code) Option { - return func(o *options) { - o.locale = locale + return func(o *internal.Opts) { + o.Locale = locale } } // WithCalFormatter prepares simple date and datetime formatting. func WithCalFormatter(date, dateTime string, loc *time.Location) Option { - return func(o *options) { - cf := t.CalFormatterISO + return func(o *internal.Opts) { + cf := internal.CalFormatterISO if date != "" { cf.Date = date } @@ -67,64 +70,52 @@ func WithCalFormatter(date, dateTime string, loc *time.Location) Option { if loc != nil { cf.Location = loc } - o.calFormatter = &cf + o.CalFormatter = &cf } } // WithNumFormatter defines a customer number formatter to use instead of // that provided by default for the currency. func WithNumFormatter(nf num.Formatter) Option { - return func(o *options) { - o.numFormatter = &nf + return func(o *internal.Opts) { + o.NumFormatter = &nf } } // Render takes the GOBL envelope and attempts to render an HTML document // from it. func Render(ctx context.Context, env *gobl.Envelope, opts ...Option) ([]byte, error) { - conf := new(options) + o := new(internal.Opts) for _, opt := range opts { - opt(conf) + opt(o) } // Prepare the Locale - if conf.locale == "" { - conf.locale = defaultLanguage + if o.Locale == "" { + o.Locale = defaultLocale } - ctx, err := ctxi18n.WithLocale(ctx, string(conf.locale)) - if err != nil { - return nil, fmt.Errorf("preparing locale: %w", err) - } - - // Is there a calendar formatter? - if conf.calFormatter != nil { - ctx = t.WithCalFormatter(ctx, *conf.calFormatter) + l := locales.Match(string(o.Locale)) + if l == nil { + l = locales.Get(defaultLocale) } + ctx = l.WithContext(ctx) // Extract the currency to use for formatting - if conf.numFormatter == nil { + if o.NumFormatter == nil { cur := currency.EUR switch doc := env.Extract().(type) { case *bill.Invoice: cur = doc.Currency } nf := cur.Def().Formatter() - conf.numFormatter = &nf + o.NumFormatter = &nf } - ctx = t.WithNumFormatter(ctx, *conf.numFormatter) - // Does a logo need to be replaced? - if conf.logo != nil { - switch doc := env.Extract().(type) { - case *bill.Invoice: - doc.Supplier.Logos = []*org.Image{conf.logo} - } + if o.CalFormatter == nil { + o.CalFormatter = &internal.CalFormatterISO } - // Any additional notes? - if conf.notes != "" { - env.Head.Notes = conf.notes - } + ctx = internal.WithOptions(ctx, o) out := components.Envelope(env) buf := new(bytes.Buffer) diff --git a/internal/options.go b/internal/options.go new file mode 100644 index 0000000..1b54ecc --- /dev/null +++ b/internal/options.go @@ -0,0 +1,62 @@ +package internal + +import ( + "context" + "time" + + "github.com/invopop/ctxi18n/i18n" + "github.com/invopop/gobl/num" + "github.com/invopop/gobl/org" +) + +type optionsKey string + +const ( + optsKey optionsKey = "opts" +) + +// Opts defines configuration options used internally with +// the current document. Putting this amount of information inside a +// context is normally an anti-pattern in Go, but saves a huge amount +// of effort when writing components. +type Opts struct { + // Locale is the language code to use for rendering. + Locale i18n.Code + // Logo used instead of the document's logo. + Logo *org.Image + // Notes to add to the document footer instead of the notes included + // in the envelope. + Notes string + // NumFormatter is used to format numbers. + NumFormatter *num.Formatter + // CalFormatter is used to format calendar dates and times. + CalFormatter *CalFormatter +} + +// WithOptions prepares the context with the options to use. +func WithOptions(ctx context.Context, opts *Opts) context.Context { + return context.WithValue(ctx, optsKey, opts) +} + +// Options provides the current options in the context. +func Options(ctx context.Context) *Opts { + if opts, ok := ctx.Value(optsKey).(*Opts); ok { + return opts + } + return nil +} + +// CalFormatter defines a simple date and datetime formatter +type CalFormatter struct { + Date string // Golang date format for dates, e.g. `02/01/2006` + Location *time.Location + DateTime string // Date-time format +} + +// CalFormatterISO is the default formatter for dates and times based on +// the recommended ISO 8601 formatting. +var CalFormatterISO = CalFormatter{ + Date: "2006-01-02", + DateTime: "2006-01-02 15:04", + Location: time.UTC, +}