From c9c93cc4a77fe3bf1cc23b2a518a6c6781bb0a70 Mon Sep 17 00:00:00 2001 From: Priec Date: Tue, 21 Jul 2026 20:33:50 +0200 Subject: [PATCH] polishing of the backend --- invoice-print-setup.md | 450 +++++++++++++++++++++++++++++++++++++++++ server | 2 +- 2 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 invoice-print-setup.md diff --git a/invoice-print-setup.md b/invoice-print-setup.md new file mode 100644 index 0000000..98813f5 --- /dev/null +++ b/invoice-print-setup.md @@ -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. diff --git a/server b/server index 79afc75..33db98c 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 79afc752119e41a8b1ed509c16732e083ef4863a +Subproject commit 33db98ce94e45c5c1b5ef8051e96420f3e033a6a