451 lines
14 KiB
Markdown
451 lines
14 KiB
Markdown
# Invoice print setup
|
|
|
|
This guide creates the invoice tables and Steel calculations through the gRPC
|
|
API, installs the Typst template used by the print page, inserts example data,
|
|
and compiles the exported JSON into a PDF.
|
|
|
|
Run the commands from the repository root. The server address used by the
|
|
client is `[::1]:50051`.
|
|
|
|
## 1. Configure grpcurl
|
|
|
|
```bash
|
|
PROFILE="fakturacia_2026"
|
|
GRPC_ADDR="[::1]:50051"
|
|
```
|
|
|
|
Log in. Keep `-d` and its JSON argument on the same logical command line:
|
|
|
|
```bash
|
|
grpcurl -plaintext \
|
|
-import-path common/proto \
|
|
-proto auth.proto \
|
|
-d '{"identifier":"YOUR_USERNAME","password":"YOUR_PASSWORD"}' \
|
|
"$GRPC_ADDR" \
|
|
komp_ac.auth.AuthService/Login
|
|
```
|
|
|
|
Copy the returned `access_token`:
|
|
|
|
```bash
|
|
TOKEN="PASTE_ACCESS_TOKEN_HERE"
|
|
AUTH_HEADER="authorization: Bearer $TOKEN"
|
|
```
|
|
|
|
The account must have the administrator role for table and script creation.
|
|
|
|
Table definitions and Steel scripts require an `admin` token. Row data writes
|
|
require an `accountant` token. Keep the two tokens in separate shell variables,
|
|
for example:
|
|
|
|
```bash
|
|
ADMIN_TOKEN="PASTE_ADMIN_ACCESS_TOKEN_HERE"
|
|
ACCOUNTANT_TOKEN="PASTE_ACCOUNTANT_ACCESS_TOKEN_HERE"
|
|
ADMIN_AUTH_HEADER="authorization: Bearer $ADMIN_TOKEN"
|
|
ACCOUNTANT_AUTH_HEADER="authorization: Bearer $ACCOUNTANT_TOKEN"
|
|
```
|
|
|
|
Use `ADMIN_AUTH_HEADER` for the table-definition and table-script calls. Use
|
|
`ACCOUNTANT_AUTH_HEADER` for the `TablesData/PostTableData` calls below.
|
|
|
|
If you do not know an existing password, create a new administrator account
|
|
on a trusted local development server. The registration endpoint currently
|
|
accepts the requested role, so do not expose this unauthenticated endpoint to
|
|
an untrusted network.
|
|
|
|
```bash
|
|
grpcurl -plaintext \
|
|
-import-path common/proto \
|
|
-proto auth.proto \
|
|
-d '{
|
|
"username":"invoice_admin",
|
|
"email":"invoice_admin@example.test",
|
|
"password":"CHANGE_THIS_PASSWORD",
|
|
"password_confirmation":"CHANGE_THIS_PASSWORD",
|
|
"role":"admin",
|
|
"timezone":"Europe/Bratislava",
|
|
"phone_country":"SK"
|
|
}' \
|
|
"$GRPC_ADDR" \
|
|
komp_ac.auth.AuthService/Register
|
|
```
|
|
|
|
Then log in with the same credentials and copy the returned token:
|
|
|
|
```bash
|
|
grpcurl -plaintext \
|
|
-import-path common/proto \
|
|
-proto auth.proto \
|
|
-d '{"identifier":"invoice_admin","password":"CHANGE_THIS_PASSWORD"}' \
|
|
"$GRPC_ADDR" \
|
|
komp_ac.auth.AuthService/Login
|
|
```
|
|
|
|
The `Invalid credentials` response means the username/email or password is
|
|
wrong. Sending only `{"identifier":"admin"}` cannot work because the login
|
|
request still requires a valid password. A temporary `connection refused`
|
|
means the server was not listening at that moment; retry after starting the
|
|
server.
|
|
|
|
## 2. Create the tables
|
|
|
|
Create referenced tables first.
|
|
|
|
### Supplier
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_definition.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"dodavatel",
|
|
"row_display_column":"nazov",
|
|
"columns":[
|
|
{"name":"nazov","field_type":"TEXT"},
|
|
{"name":"ulica","field_type":"TEXT"},
|
|
{"name":"mesto","field_type":"TEXT"},
|
|
{"name":"ico","field_type":"TEXT"},
|
|
{"name":"dic","field_type":"TEXT"},
|
|
{"name":"icdph","field_type":"TEXT"},
|
|
{"name":"iban","field_type":"TEXT"},
|
|
{"name":"banka","field_type":"TEXT"},
|
|
{"name":"telefon","field_type":"TEXT"},
|
|
{"name":"email","field_type":"TEXT"}
|
|
]
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.table_definition.TableDefinition/PostTableDefinition
|
|
```
|
|
|
|
### Customer
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_definition.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"odberatel",
|
|
"row_display_column":"nazov",
|
|
"columns":[
|
|
{"name":"nazov","field_type":"TEXT"},
|
|
{"name":"ulica","field_type":"TEXT"},
|
|
{"name":"mesto","field_type":"TEXT"},
|
|
{"name":"ico","field_type":"TEXT"},
|
|
{"name":"dic","field_type":"TEXT"},
|
|
{"name":"icdph","field_type":"TEXT"}
|
|
]
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.table_definition.TableDefinition/PostTableDefinition
|
|
```
|
|
|
|
### Invoice
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_definition.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"faktura",
|
|
"row_display_column":"cislo",
|
|
"base_currency":"EUR",
|
|
"links":[
|
|
{"linked_table_name":"dodavatel","required":true},
|
|
{"linked_table_name":"odberatel","required":true}
|
|
],
|
|
"columns":[
|
|
{"name":"cislo","field_type":"TEXT"},
|
|
{"name":"datum_vystavenia","field_type":"DATE"},
|
|
{"name":"datum_dodania","field_type":"DATE"},
|
|
{"name":"datum_splatnosti","field_type":"DATE"},
|
|
{"name":"variabilny_symbol","field_type":"TEXT"},
|
|
{"name":"sposob_uhrady","field_type":"TEXT"},
|
|
{"name":"zaklad_dane","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true},
|
|
{"name":"dph_celkom","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true},
|
|
{"name":"celkom_spolu","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true},
|
|
{"name":"poznamka","field_type":"TEXT"}
|
|
]
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.table_definition.TableDefinition/PostTableDefinition
|
|
```
|
|
|
|
### Invoice item
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_definition.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"polozka",
|
|
"row_display_column":"nazov",
|
|
"base_currency":"EUR",
|
|
"links":[{"linked_table_name":"faktura","required":true}],
|
|
"columns":[
|
|
{"name":"nazov","field_type":"TEXT"},
|
|
{"name":"mnozstvo","field_type":"DECIMAL(12,3)"},
|
|
{"name":"mj","field_type":"TEXT"},
|
|
{"name":"cena","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP"},
|
|
{"name":"dph","field_type":"DECIMAL(5,2)"},
|
|
{"name":"bez","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true},
|
|
{"name":"dph_suma","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true},
|
|
{"name":"spolu","field_type":"MONEY","rounding":"MONEY_ROUNDING_HALF_UP","recompute_on_dependency_change":true}
|
|
]
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.table_definition.TableDefinition/PostTableDefinition
|
|
```
|
|
|
|
## 3. Get table IDs
|
|
|
|
Use `table_definition.proto`, not `common.proto`, for this service call:
|
|
|
|
```bash
|
|
TREE=$(grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_definition.proto \
|
|
-d '{}' "$GRPC_ADDR" \
|
|
komp_ac.table_definition.TableDefinition/GetProfileTree)
|
|
|
|
echo "$TREE" | jq .
|
|
|
|
FAKTURA_ID=$(echo "$TREE" | jq -r \
|
|
'.profiles[] | select(.name=="fakturacia_2026") | .tables[] | select(.name=="faktura") | .id')
|
|
|
|
POLOZKA_ID=$(echo "$TREE" | jq -r \
|
|
'.profiles[] | select(.name=="fakturacia_2026") | .tables[] | select(.name=="polozka") | .id')
|
|
```
|
|
|
|
## 4. Add Steel calculations
|
|
|
|
The script API requires expressions beginning with `(`. Money values must be
|
|
combined using the money functions; do not use ordinary decimal `+` on MONEY.
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$POLOZKA_ID,
|
|
\"target_column\":\"bez\",
|
|
\"script\":\"(money-mul (steel_get_column \\\"polozka\\\" \\\"cena\\\") (steel_get_column \\\"polozka\\\" \\\"mnozstvo\\\"))\",
|
|
\"description\":\"Quantity multiplied by unit price\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$POLOZKA_ID,
|
|
\"target_column\":\"dph_suma\",
|
|
\"script\":\"(money-div (money-mul (steel_get_column \\\"polozka\\\" \\\"bez\\\") (steel_get_column \\\"polozka\\\" \\\"dph\\\")) \\\"100\\\")\",
|
|
\"description\":\"VAT amount\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$POLOZKA_ID,
|
|
\"target_column\":\"spolu\",
|
|
\"script\":\"(money-add (steel_get_column \\\"polozka\\\" \\\"bez\\\") (steel_get_column \\\"polozka\\\" \\\"dph_suma\\\"))\",
|
|
\"description\":\"Total including VAT\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
Invoice aggregate scripts:
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$FAKTURA_ID,
|
|
\"target_column\":\"zaklad_dane\",
|
|
\"script\":\"(money-add (steel_related_aggregate \\\"sum\\\" \\\"polozka\\\" \\\"bez\\\" \\\"faktura\\\") (money-new \\\"EUR\\\" \\\"0\\\"))\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$FAKTURA_ID,
|
|
\"target_column\":\"dph_celkom\",
|
|
\"script\":\"(money-add (steel_related_aggregate \\\"sum\\\" \\\"polozka\\\" \\\"dph_suma\\\" \\\"faktura\\\") (money-new \\\"EUR\\\" \\\"0\\\"))\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto table_script.proto \
|
|
-d "{
|
|
\"table_definition_id\":$FAKTURA_ID,
|
|
\"target_column\":\"celkom_spolu\",
|
|
\"script\":\"(money-add (steel_related_aggregate \\\"sum\\\" \\\"polozka\\\" \\\"spolu\\\" \\\"faktura\\\") (money-new \\\"EUR\\\" \\\"0\\\"))\"
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.table_script.TableScript/PostTableScript
|
|
```
|
|
|
|
## 5. Insert data
|
|
|
|
Insert supplier and customer rows first. The API returns their IDs.
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto tables_data.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"dodavatel",
|
|
"data":{
|
|
"nazov":"Kreatív Studio s.r.o.",
|
|
"ulica":"Štúrova 8",
|
|
"mesto":"040 01 Košice",
|
|
"ico":"45612378",
|
|
"dic":"2022998877",
|
|
"icdph":"SK2022998877",
|
|
"iban":"SK89 1100 0000 0026 1234 5678",
|
|
"banka":"Tatra banka, a.s.",
|
|
"telefon":"+421 911 222 333",
|
|
"email":"fakturacia@kreativstudio.sk"
|
|
}
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.tables_data.TablesData/PostTableData
|
|
```
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto tables_data.proto \
|
|
-d '{
|
|
"profile_name":"fakturacia_2026",
|
|
"table_name":"odberatel",
|
|
"data":{
|
|
"nazov":"Nová Kaviareň s.r.o.",
|
|
"ulica":"Námestie SNP 12",
|
|
"mesto":"974 01 Banská Bystrica",
|
|
"ico":"51239876",
|
|
"dic":"2120654321",
|
|
"icdph":"SK2120654321"
|
|
}
|
|
}' \
|
|
"$GRPC_ADDR" komp_ac.tables_data.TablesData/PostTableData
|
|
```
|
|
|
|
Set the returned IDs:
|
|
|
|
```bash
|
|
DODAVATEL_ID="1"
|
|
ODBERATEL_ID="1"
|
|
```
|
|
|
|
Insert the invoice. Scripted totals are initially zero because it has no
|
|
items yet.
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto tables_data.proto \
|
|
-d "{
|
|
\"profile_name\":\"fakturacia_2026\",
|
|
\"table_name\":\"faktura\",
|
|
\"data\":{
|
|
\"dodavatel_id\":$DODAVATEL_ID,
|
|
\"odberatel_id\":$ODBERATEL_ID,
|
|
\"cislo\":\"2026042\",
|
|
\"datum_vystavenia\":\"2026-06-15\",
|
|
\"datum_dodania\":\"2026-06-12\",
|
|
\"datum_splatnosti\":\"2026-06-29\",
|
|
\"variabilny_symbol\":\"2026042\",
|
|
\"sposob_uhrady\":\"Prevodný príkaz\",
|
|
\"zaklad_dane\":\"0\",
|
|
\"dph_celkom\":\"0\",
|
|
\"celkom_spolu\":\"0\",
|
|
\"poznamka\":\"Ďakujeme za Vašu dôveru.\"
|
|
}
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.tables_data.TablesData/PostTableData
|
|
```
|
|
|
|
Set the returned invoice ID:
|
|
|
|
```bash
|
|
FAKTURA_ROW_ID="PASTE_INVOICE_ID"
|
|
```
|
|
|
|
Insert invoice items. Because the item columns are scripted, include their
|
|
calculated values in each request.
|
|
|
|
```bash
|
|
grpcurl -plaintext -H "$AUTH_HEADER" \
|
|
-import-path common/proto -proto tables_data.proto \
|
|
-d "{
|
|
\"profile_name\":\"fakturacia_2026\",
|
|
\"table_name\":\"polozka\",
|
|
\"data\":{
|
|
\"faktura_id\":$FAKTURA_ROW_ID,
|
|
\"nazov\":\"Návrh loga a vizuálnej identity\",
|
|
\"mnozstvo\":\"1\",
|
|
\"mj\":\"ks\",
|
|
\"cena\":\"850.00\",
|
|
\"dph\":\"20\",
|
|
\"bez\":\"850.00\",
|
|
\"dph_suma\":\"170.00\",
|
|
\"spolu\":\"1020.00\"
|
|
}
|
|
}" \
|
|
"$GRPC_ADDR" komp_ac.tables_data.TablesData/PostTableData
|
|
```
|
|
|
|
Repeat the same request for additional items, changing the values. For the
|
|
sample invoice, the calculated values are:
|
|
|
|
| Item | `bez` | `dph_suma` | `spolu` |
|
|
|---|---:|---:|---:|
|
|
| Logo | 850.00 | 170.00 | 1020.00 |
|
|
| Menu, quantity 2 | 240.00 | 48.00 | 288.00 |
|
|
| Printed menus | 255.00 | 25.50 | 280.50 |
|
|
| Interior photography | 260.00 | 52.00 | 312.00 |
|
|
|
|
The resulting invoice totals are `zaklad_dane = 1605.00`,
|
|
`dph_celkom = 295.50`, and `celkom_spolu = 1900.50`.
|
|
|
|
## 6. Install the Typst template
|
|
|
|
The Linux client discovers templates in:
|
|
|
|
```text
|
|
~/.config/komp-ac/komp_ac_client/print_templates/
|
|
```
|
|
|
|
Place the supplied files there with matching stems:
|
|
|
|
```bash
|
|
mkdir -p ~/.config/komp-ac/komp_ac_client/print_templates
|
|
cp inv2_generic.typ ~/.config/komp-ac/komp_ac_client/print_templates/faktura.typ
|
|
cp inv2_generic.json ~/.config/komp-ac/komp_ac_client/print_templates/faktura.json
|
|
```
|
|
|
|
The JSON must contain the target paths used by the print mapper:
|
|
|
|
```text
|
|
faktura.*
|
|
dodavatel.*
|
|
odberatel.*
|
|
polozky[].*
|
|
```
|
|
|
|
Open the invoice row in the client, open the Print page, select `faktura.typ`,
|
|
select the related `polozka` rows, and export. The client writes the generated
|
|
JSON as `faktura.export.json` beside the template.
|
|
|
|
## 7. Compile the PDF
|
|
|
|
```bash
|
|
typst compile \
|
|
--input data="$HOME/.config/komp-ac/komp_ac_client/print_templates/faktura.export.json" \
|
|
"$HOME/.config/komp-ac/komp_ac_client/print_templates/faktura.typ" \
|
|
faktura.pdf
|
|
```
|
|
|
|
The client currently exports the API-backed JSON and saves the document
|
|
snapshot, but it does not invoke the Typst compiler itself.
|