VisiMark — examples
Table of Contents
# Invoice 2026/09/014

Invoice 2026/09/014

**Seller:** EPSI sp. z o.o., ul. Bura 21, 00-000 Burowce, NIP PL9998877666
**Buyer:** Northwind Logistics GmbH, Hafenstraße 8, 22222 Hamburg, USt-IdNr. DE881188118

Seller: EPSI sp. z o.o., ul. Bura 21, 00-000 Burowce, NIP PL9998877666 Buyer: Northwind Logistics GmbH, Hafenstraße 8, 22222 Hamburg, USt-IdNr. DE881188118

**Issued:** 2026-09-03    **Delivered:** 2026-08-31    **Payment due:** 2026-09-17

Issued: 2026-09-03    Delivered: 2026-08-31    Payment due: 2026-09-17

## Services rendered

Services rendered

```vmark #lines from example-invoice-csv-import.csv labelled Item, Unit, Qty, Rate [PLN], Net [PLN], VAT [PLN], Gross [PLN] at sha256:e6667fb8dda6801721d885c259e2456a9e6c92f1ec89af7f14330e0c54c6ac14
net_total   [PLN] = SUM(Net)
vat_total   [PLN] = SUM(VAT)
gross_total [PLN] = SUM(Gross)
```
net_total   [PLN] = SUM(Net)
vat_total   [PLN] = SUM(VAT)
gross_total [PLN] = SUM(Gross)
Net of tax the engagement comes to **23300.00**<!--vmark=lines.net_total--> PLN.
VAT at 23% adds **5359.00**<!--vmark=lines.vat_total--> PLN, giving a total due of
**28659.00**<!--vmark=lines.gross_total--> PLN.

Net of tax the engagement comes to 23300.00 PLN. VAT at 23% adds 5359.00 PLN, giving a total due of 28659.00 PLN.

---

## Appendix — how this differs from the inline version

Appendix — how this differs from the inline version

This is [example-invoice.md](example-invoice.md) with the `#lines` sheet's rows
moved out to [example-invoice-csv-import.csv](example-invoice-csv-import.csv)
and declared with a `from` clause instead of a preceding GFM table. Everything
else about the sheet is unchanged: `net_total`, `vat_total` and `gross_total`
are the same `SUM` reduces over the same column names, and the anchored
numbers below the block are the same values.

This is example-invoice.md with the #lines sheet's rows moved out to example-invoice-csv-import.csv and declared with a from clause instead of a preceding GFM table. Everything else about the sheet is unchanged: net_total, vat_total and gross_total are the same SUM reduces over the same column names, and the anchored numbers below the block are the same values.

Three things are different because the sheet is now imported rather than
inline:

Three things are different because the sheet is now imported rather than inline:

**No column rules.** The original document computes `Net`, `VAT` and `Gross`
per row inside the `vmark` block (`Net = Qty * Rate`, and so on). An imported
sheet has no cell for a column rule to write to — its columns are read-only
inputs taken straight from the CSV header row — so `Net`, `VAT` and `Gross`
are already computed values *in the CSV*, produced by whatever generated it,
not by VisiMark. Only the aggregate reduces (`SUM`) still run here.

No column rules. The original document computes Net, VAT and Gross per row inside the vmark block (Net = Qty * Rate, and so on). An imported sheet has no cell for a column rule to write to — its columns are read-only inputs taken straight from the CSV header row — so Net, VAT and Gross are already computed values in the CSV, produced by whatever generated it, not by VisiMark. Only the aggregate reduces (SUM) still run here.

**The `at sha256:…` stamp.** The clause on the fence line pins the CSV's exact
bytes. `visimark check` recomputes the digest on every run and fails with
`STALE` if the file has changed underneath the document, or `IMPORT` if the
clause is missing entirely — an inline table has no analogous guard, because
its data lives in the same text `check` is already reading. Editing
`example-invoice-csv-import.csv` and running `visimark fmt` rewrites only this
`at` clause; the CSV itself is never touched by any VisiMark command.

The at sha256:… stamp. The clause on the fence line pins the CSV's exact bytes. visimark check recomputes the digest on every run and fails with STALE if the file has changed underneath the document, or IMPORT if the clause is missing entirely — an inline table has no analogous guard, because its data lives in the same text check is already reading. Editing example-invoice-csv-import.csv and running visimark fmt rewrites only this at clause; the CSV itself is never touched by any VisiMark command.

**`labelled` asserts the header.** `labelled Item, Unit, Qty, Rate [PLN], Net
[PLN], VAT [PLN], Gross [PLN]` checks the CSV's header row matches this exact
name and order before anything is evaluated, so a column renamed or reordered
upstream is a loud `IMPORT` finding instead of a silent misread.

labelled asserts the header. labelled Item, Unit, Qty, Rate [PLN], Net [PLN], VAT [PLN], Gross [PLN] checks the CSV's header row matches this exact name and order before anything is evaluated, so a column renamed or reordered upstream is a loud IMPORT finding instead of a silent misread.

**Units come from the declaration.** The CSV header row stays `Item,Unit,Qty,
Rate,Net,VAT,Gross`, so its bytes and the `sha256` stamp are untouched; the
brackets in the `labelled` list attach `PLN` to the money columns. A CSV that
carried `Rate [PLN]` in its own header would work too, and a header and a
declaration that disagree is a `UNIT` finding. The three totals then declare
`[PLN]`, which `check` verifies against the unit the `SUM`s derive.

Units come from the declaration. The CSV header row stays Item,Unit,Qty, Rate,Net,VAT,Gross, so its bytes and the sha256 stamp are untouched; the brackets in the labelled list attach PLN to the money columns. A CSV that carried Rate [PLN] in its own header would work too, and a header and a declaration that disagree is a UNIT finding. The three totals then declare [PLN], which check verifies against the unit the SUMs derive.

See `docs/design/declared-local-data-imports-spec.md` for the full syntax and
semantics.

See docs/design/declared-local-data-imports-spec.md for the full syntax and semantics.

## Attachment: example-invoice-csv-import.csv

Attachment: example-invoice-csv-import.csv

```csv
Item,Unit,Qty,Rate,Net,VAT,Gross
Discovery workshop,day,2,1800.00,3600.00,828.00,4428.00
Backend implementation,hour,64,220.00,14080.00,3238.40,17318.40
Code review retainer,month,1,2500.00,2500.00,575.00,3075.00
On-call support,hour,12,260.00,3120.00,717.60,3837.60
```
Item,Unit,Qty,Rate,Net,VAT,Gross
Discovery workshop,day,2,1800.00,3600.00,828.00,4428.00
Backend implementation,hour,64,220.00,14080.00,3238.40,17318.40
Code review retainer,month,1,2500.00,2500.00,575.00,3075.00
On-call support,hour,12,260.00,3120.00,717.60,3837.60