HeaderLines
Summary
HeaderLines is a Card with an attached editable grid — used for documents like Sales Orders and Purchase Orders. The header contains form fields; the grid contains line items.
What the client does with HeaderLines
The client renders the card normally. The user presses Ctrl+L to open the editable grid as an overlay on the card's lower portion (Esc closes it). The user edits both header fields and grid cells. Ctrl+S saves everything together.
┌─ Sales Order ─────────────────────────────┐
│ No............... [SO-1001 ] │
│ Customer......... [The Cannon Group ] │
│ Posting Date..... [10-03-2026 ] │
├───────────────────────────────────────────┤
│ Type No. Description Qty Amount│
│ ─────── ─────── ──────────── ──── ────────│
│ Item 1000 Bicycle 2 2990.00│
│ Item 1100 Chain 4 162.00│
│ │
└───────────────────────────────────────────┘Building a HeaderLines screen
Route
GET /screen/sales_order/{id} → ScreenContractResponse structure
json
{
"layout": "HeaderLines",
"title": "Sales Order - SO-1001",
"lines_overlay_pct": 65,
"sections": [
{
"id": "header",
"label": "Sales Order",
"fields": [
{
"id": "no",
"label": "No.",
"type": "Text",
"value": "SO-1001",
"editable": false
},
{
"id": "sell_to_name",
"label": "Customer",
"type": "Text",
"value": "The Cannon Group"
},
{
"id": "posting_date",
"label": "Posting Date",
"type": "Date",
"value": "10-03-2026"
}
]
}
],
"lines": {
"columns": [
{
"id": "type",
"label": "Type",
"type": "Option",
"width": 10,
"editable": true,
"options": [
"",
"Item",
"Resource",
"G/L Account",
"Text"
]
},
{
"id": "no",
"label": "No.",
"type": "Text",
"width": 10,
"editable": true,
"lookup": {
"endpoint": "/lookup/no",
"display_field": "no",
"validate": "/validate/no",
"display": "modal"
}
},
{
"id": "description",
"label": "Description",
"type": "Text",
"width": "fill",
"editable": true
},
{
"id": "unit_of_measure",
"label": "UoM",
"type": "Text",
"width": 6,
"editable": false
},
{
"id": "quantity",
"label": "Quantity",
"type": "Decimal",
"width": 8,
"align": "right",
"editable": true
},
{
"id": "unit_price",
"label": "Unit Price",
"type": "Decimal",
"width": 12,
"align": "right",
"editable": true
},
{
"id": "discount_pct",
"label": "Disc. %",
"type": "Decimal",
"width": 8,
"align": "right",
"editable": true
},
{
"id": "line_amount",
"label": "Amount",
"type": "Decimal",
"width": 12,
"align": "right",
"editable": false
}
],
"rows": [
{
"index": 0,
"values": [
"Item",
"1000",
"Bicycle",
"PCS",
"2",
"1,495.00",
"0.00",
"2,990.00"
]
},
{
"index": 1,
"values": [
"Item",
"1100",
"Chain",
"PCS",
"4",
"45.00",
"10.00",
"162.00"
]
},
{
"index": 2,
"values": [
"",
"",
"",
"",
"",
"",
"",
""
]
}
],
"editable": true,
"row_count": 3
},
"actions": {
"save": "/screen/sales_order/SO-1001/save",
"delete": "/screen/sales_order/SO-1001/delete"
}
}lines_overlay_pct
Controls how much of the screen the grid overlay covers when opened with Ctrl+L:
| Value | Result |
|---|---|
0 | Not used (for Card/List layouts that have no lines) |
0–100 | Overlay covers this percentage of the body area |
| Default | 50 |
Set lines_open: true to open the overlay automatically when the screen loads. By default the overlay starts closed and the user opens it with Ctrl+L.
For full-screen editable grids without a card header (journals, batch entry), use the Grid layout instead.
Editable grid columns
Grid columns use the same structure as list columns, but with editable: true on columns the user can modify:
json
{
"id": "quantity",
"label": "Quantity",
"type": "Decimal",
"width": 8,
"align": "right",
"editable": true
}Each column supports:
type— determines editing behavior (Text allows free typing, Option cycles, Decimal filters input)options— for Option-type columns, the available choiceslookup— for columns with relational lookups (e.g., Item No.)validation— client-side validation rules for cellsquick_entry— when false, Enter skips this column (fast data entry). See Quick Entry on grid columns.formula— arithmetic expression for live calculated columns. See Calculated columns.
Context-dependent lookups
Grid columns can depend on other columns for lookups. For example, the "No." column might look up Items, Resources, or G/L Accounts depending on the "Type" column. Define a context array on the column's lookup to enable this.
See Context-dependent lookups for the full mechanism.
Empty trailing rows
Include at least one empty row at the end of the grid. This gives the user a place to start adding new lines. The row values should be empty strings:
json
{
"index": 2,
"values": [
"",
"",
"",
"",
"",
"",
"",
""
]
}How saves work with HeaderLines
When the user presses Ctrl+S, the client sends both header changes and the full grid data:
json
{
"screen_id": "sales_order",
"record_id": "SO-1001",
"changes": {
"sell_to_name": "Acme Corporation"
},
"lines": [
[
"Item",
"1000",
"Bicycle",
"PCS",
"2",
"1,495.00",
"0.00",
"2,990.00"
],
[
"Item",
"1100",
"Chain",
"PCS",
"4",
"45.00",
"10.00",
"162.00"
],
[
"",
"",
"",
"",
"",
"",
"",
""
]
]
}Column order is the contract. The lines array uses the same column ordering as your columns array. Parse by position, not by name. Empty trailing rows are included — your server should ignore them.
See Saving for the full save protocol.