Files
komp_ac/invoice-print-setup.md
2026-07-21 20:33:50 +02:00

14 KiB

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

PROFILE="fakturacia_2026"
GRPC_ADDR="[::1]:50051"

Log in. Keep -d and its JSON argument on the same logical command line:

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:

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:

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.

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:

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

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

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

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

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:

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.

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
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
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:

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
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
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.

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
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:

DODAVATEL_ID="1"
ODBERATEL_ID="1"

Insert the invoice. Scripted totals are initially zero because it has no items yet.

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:

FAKTURA_ROW_ID="PASTE_INVOICE_ID"

Insert invoice items. Because the item columns are scripted, include their calculated values in each request.

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:

~/.config/komp-ac/komp_ac_client/print_templates/

Place the supplied files there with matching stems:

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:

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

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.