polishing of the backend

This commit is contained in:
Priec
2026-07-21 20:33:50 +02:00
parent 91a3575064
commit c9c93cc4a7
2 changed files with 451 additions and 1 deletions

450
invoice-print-setup.md Normal file
View File

@@ -0,0 +1,450 @@
# 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.

2
server

Submodule server updated: 79afc75211...33db98ce94