Purchase order
Build a purchase order whose supplier and delivery terms live in fixed cells. You learn hand-built grid tables, merged cells, per-cell padding and alignment, and how to hide a whole row when a value is missing.
このレシピで身につくこと
エディタで作る
- Add a table and choose the hand-built mode at the top of the table properties. Set it to 6 rows and 4 columns.
- The two left columns are label columns, so make them narrow. Drag a column boundary to resize; the widths always keep adding up to the table width.
- Click a cell on the canvas to select it. Type its text, and turn the label cells into header cells so the header background applies to them.
- A long value such as an address needs more room. Select that cell and press merge with the cell on the right to make one wider cell.
- Put bindings such as {{ data.supplier }} into the value cells. For a date, {{ format(data.due, "date", "long", "en-US") }} renders it the way people read it.
- If a cell feels cramped, give that cell more padding. Selecting a cell hides the whole-table padding and shows only this cell — the screen tells you what you are editing.
- For a row that only appears sometimes, put {{ data.note }} in the "show when" box of the object that holds it. With no value the row is left out entirely, leaving no gap.
- Add a line above the signature area. A shape only needs a colour and a thickness; there is no reason to build it as a table.
スキーマと API で作る
- A grid table leaves props.bind empty. Instead rows gives the row count and cells lists the cells.
- Only list the cells you use — a missing position is simply an empty cell, so you never have to write all 24.
- span merges to the right and rowspan merges downward. Do not also list a cell for a position that has been merged over.
- Setting head to true makes it a header cell, and the table fill colour is painted only on those.
- A node show is an expression. When the value is missing or empty, that node and everything inside it is left out of the output.
出来上がったドキュメント
下の JSON は、2 つの道筋が生むドキュメントそのものです。テストがこれを検証し、警告なしでレンダリングされることも確認しているので、そのまま貼り付けてお使いいただけます。
{
"v": 1,
"unit": "px",
"page": {
"size": "a4",
"orientation": "portrait",
"margin": {
"t": 56,
"r": 48,
"b": 56,
"l": 48
},
"header": "",
"footer": ""
},
"fonts": [],
"styles": [
{
"id": "s_h1",
"fontFamily": "Liberation Sans",
"fontSize": 24,
"fontWeight": 700,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_tbl",
"fontFamily": "Liberation Sans",
"fontSize": 12,
"fontWeight": 400,
"italic": false,
"color": "#111111",
"bg": "transparent",
"align": "left",
"valign": "middle",
"lineHeight": 1.4,
"border": "none",
"radius": 0,
"opacity": 1
},
{
"id": "s_note",
"fontFamily": "Liberation Sans",
"fontSize": 11,
"fontWeight": 400,
"italic": false,
"color": "#555555",
"bg": "transparent",
"align": "left",
"valign": "top",
"lineHeight": 1.5,
"border": "none",
"radius": 0,
"opacity": 1
}
],
"nodes": [
{
"id": "n_title",
"parent": "",
"order": 0,
"type": "label",
"x": 0,
"y": 0,
"w": 697,
"h": 32,
"show": "",
"styleId": "s_h1",
"props": {
"text": "Purchase order",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Title"
},
{
"id": "n_head",
"parent": "",
"order": 1,
"type": "table",
"x": 0,
"y": 52,
"w": 697,
"h": 120,
"show": "",
"styleId": "s_tbl",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#c9ced6",
"fill": "#f3f5f7",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 7,
"padX": 9,
"align": "",
"valign": "middle",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 30,
"max": 0,
"columns": [
{
"header": "",
"cell": "",
"width": 110,
"align": "left"
},
{
"header": "",
"cell": "",
"width": 238,
"align": "left"
},
{
"header": "",
"cell": "",
"width": 110,
"align": "left"
},
{
"header": "",
"cell": "",
"width": 239,
"align": "left"
}
],
"rows": 4,
"cells": [
{
"r": 0,
"c": 0,
"span": 1,
"rowspan": 1,
"text": "Supplier",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 0,
"c": 1,
"span": 1,
"rowspan": 1,
"text": "{{ data.supplier }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
},
{
"r": 0,
"c": 2,
"span": 1,
"rowspan": 1,
"text": "PO number",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 0,
"c": 3,
"span": 1,
"rowspan": 1,
"text": "{{ data.no }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
},
{
"r": 1,
"c": 0,
"span": 1,
"rowspan": 1,
"text": "Ship to",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 1,
"c": 1,
"span": 3,
"rowspan": 1,
"text": "{{ data.shipTo }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": 12,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
},
{
"r": 2,
"c": 0,
"span": 1,
"rowspan": 1,
"text": "Due",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 2,
"c": 1,
"span": 1,
"rowspan": 1,
"text": "{{ format(data.due, \"date\", \"long\", \"en-US\") }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
},
{
"r": 2,
"c": 2,
"span": 1,
"rowspan": 1,
"text": "Terms",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 2,
"c": 3,
"span": 1,
"rowspan": 1,
"text": "{{ data.terms }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
},
{
"r": 3,
"c": 0,
"span": 1,
"rowspan": 1,
"text": "Contact",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": true,
"border": ""
},
{
"r": 3,
"c": 1,
"span": 3,
"rowspan": 1,
"text": "{{ data.contact }}",
"align": "left",
"valign": "",
"wrap": "",
"pad": -1,
"padY": -1,
"padX": -1,
"src": "",
"fit": "",
"bg": "",
"head": false,
"border": ""
}
],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Order details"
},
{
"id": "n_note",
"parent": "",
"order": 2,
"type": "label",
"x": 0,
"y": 188,
"w": 697,
"h": 20,
"show": "{{ data.note }}",
"styleId": "s_note",
"props": {
"text": "Note: {{ data.note }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Note"
},
{
"id": "n_rule",
"parent": "",
"order": 3,
"type": "line",
"x": 397,
"y": 256,
"w": 300,
"h": 2,
"show": "",
"styleId": "s_note",
"props": {
"text": "",
"src": "",
"fit": "",
"thickness": 1,
"stroke": "#111111",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Signature rule"
},
{
"id": "n_sign",
"parent": "",
"order": 4,
"type": "label",
"x": 397,
"y": 262,
"w": 300,
"h": 18,
"show": "",
"styleId": "s_note",
"props": {
"text": "{{ data.approver ? data.approver : \"(awaiting approval)\" }}",
"src": "",
"fit": "",
"thickness": 0,
"stroke": "",
"fill": "",
"repeat": "",
"as": "",
"break": "",
"keep": "",
"padding": 0,
"padY": 0,
"padX": 0,
"align": "",
"valign": "",
"wrap": "",
"bind": "",
"headerHeight": 0,
"rowHeight": 0,
"max": 0,
"columns": [],
"rows": 0,
"cells": [],
"chartKind": "",
"codeKind": "",
"ecc": "",
"fieldKind": "",
"fieldName": "",
"options": [],
"required": false,
"readonly": false,
"html": ""
},
"name": "Approver"
}
]
}リクエストの本文です。上のドキュメントを template に、下のデータを data に入れてください。
{
"template": "(the document above)",
"data": {
"supplier": "Hanbit Trading Ltd.",
"no": "PO-2026-0117",
"shipTo": "118 Seongsui-ro, Seongdong-gu, Seoul — 3F warehouse",
"due": "2026-09-15",
"terms": "Net 30 after delivery",
"contact": "Do-hyun Kim · +82 10 0000 0000",
"note": "Fragile — please load by pallet.",
"approver": ""
}
}