Skip to content

The field contract

The declaration must be named exactly komp_ac_fields and its value must be a parenthesized list of quoted strings:

#let komp_ac_fields = (
"invoice.number",
"invoice.issued_at",
"lines[].description",
"lines[].quantity",
)

Whitespace, trailing commas, // line comments, and /* ... */ block comments are accepted inside the parsed declaration. The declaration must occur exactly once.

The server rejects:

  • a missing or repeated declaration;
  • an empty field list;
  • duplicate full paths;
  • empty paths or empty path segments;
  • unquoted values;
  • malformed strings or list syntax;
  • malformed brackets;
  • a path ending at a collection, such as lines[], instead of a field inside it.

A field belongs to the deepest collection appearing in its path:

Path Scope
invoice.number Root
customer.name Root
lines[].quantity lines[]
lines[].taxes[].rate lines[].taxes[]

Every collection prefix creates a table, even when it has no direct scalar field. For example, packages[].items[].quantity creates both the packages[] and packages[].items[] scopes so the nested items have a relational parent.

The path prefix is not preserved as a local column name. A non-reference field always uses only its final segment. Therefore:

"seller.name"
"buyer.name"

both try to create a root column named name when neither path resolves to an existing table. The request is rejected because two invoice fields generate the same local alias.

Reference inference is based on the target profile’s current table catalog, not on names alone.

Given an existing table:

customer: id, name, vat_number

both paths below resolve to one LINK(customer) column named customer:

"customer.name"
"customer.vat_number"

No local name or vat_number column is created. The invoice row stores the customer row ID; the referenced fields remain in customer.

If customer does not exist, or the named field does not exist on it, the final segment instead becomes optional local TEXT. A small spelling mistake can therefore change the generated model rather than producing a missing-reference error.

If a generated reference alias collides with a local field of the same name, creation fails. A path set must also not produce two different local fields with the same final segment in one scope.

Assume the profile already contains:

customer: id, name
product: id, name

Use root table name sales_invoice and this contract:

#let komp_ac_fields = (
"invoice.number",
"invoice.issued_on",
"customer.name",
"items[].product.name",
"items[].description",
"items[].quantity",
"items[].unit_price",
"items[].allocations[].warehouse",
"items[].allocations[].quantity",
)

The generated structure is:

sales_invoice
├── number: TEXT, optional
├── issued_on: TEXT, optional
└── customer: LINK(customer), optional
sales_invoice_items
├── sales_invoice: LINK(sales_invoice), required
├── product: LINK(product), optional
├── description: TEXT, optional
├── quantity: TEXT, optional
└── unit_price: TEXT, optional
sales_invoice_items_allocations
├── sales_invoice_items: LINK(sales_invoice_items), required
├── warehouse: TEXT, optional
└── quantity: TEXT, optional

Here items[].product.name resolves against product.name; the extra items[] segment determines the generated child scope. The other item and allocation fields do not match existing table columns and therefore become local text.

The field contract is a concise storage-shape hint, not a complete schema language. Before creating tables, decide whether the generated defaults are acceptable:

  • dates remain text;
  • quantities remain text;
  • prices remain text and have no currency or rounding;
  • local fields are optional;
  • only links receive automatic indexes;
  • no validation or Steel scripts are created;
  • no accounting behavior is created.

If these limitations do not match the business model, define the tables explicitly. Typst rendering needs and durable accounting/data types are related concerns, but they are not the same schema.

Return to Invoice creation for transaction, permissions, and data-entry behavior.