他の項目から計算する
金額=単価×数量のような、自動で埋まる項目。
他の項目から自動で決まる値は computed に書く。入力が変わると再計算される。
fields:
- { field: qty, label: 数量, type: number, required: true }
- { field: price, label: 単価, type: number, required: true }
- { field: amount, label: 金額, computed: { op: product, fields: [qty, price] } }op が計算の種類、fields が使う項目。上の例は「金額 = 数量 × 単価」。
同じレコードの項目をまとめる op
| op | 計算 | 例 |
|---|---|---|
sum | 足す | 小計 + 消費税 |
subtract | 引く | 売価 − 原価 |
product | 掛ける | 数量 × 単価 |
concat | 文字をつなぐ | 姓 + 名 |
concat のときだけ separator で区切り文字を指定できる。
- { field: fullName, label: 氏名,
computed: { op: concat, fields: [lastName, firstName], separator: " " } }計算項目は入力欄にならない
computed を付けた項目は読み取り表示になる。type を書く必要はなく、利用者が直接触ることもできない。だから「金額を手で上書きしたい」という要求には合わない(それは readOnly でもない普通の項目にして、計算はしない)。
明細の1行の中でも使える
subTable の fields に書けば、行ごとに計算される。受注明細の「金額」はこれで済む。
- field: lines
label: 明細
type: subTable
columns:
- { field: qty, label: 数量, type: number }
- { field: price, label: 単価, type: number }
- { field: amount, label: 金額, type: number, format: currency }
fields:
- { field: qty, label: 数量, type: number, required: true }
- { field: price, label: 単価, type: number, required: true }
- { field: amount, label: 金額, computed: { op: product, fields: [qty, price] } }明細の合計(縦計)
fields は同じレコードの項目を指すので、行をまたいだ集計はできない。行をまとめるときは field(明細の項目名)と of(行の項目名)を書く。
- title: 金額
fields:
- { field: subtotal, label: 小計, format: currency,
computed: { op: sum, field: lines, of: amount } }
- { field: lineCount, label: 明細行数,
computed: { op: count, field: lines } }行を1行足す・数量を直すと、その場で変わる。保存する内容にも入る。
数のまとめ方は5つ。count / sum / avg / min / max(文字にする join は後述)。ダッシュボードのカードや compare の検証と同じ語彙なので、同じ書き方をすれば同じ数が出る。
| 書き方 | 何が出るか |
|---|---|
{ op: sum, field: lines, of: amount } | 金額の合計 |
{ op: count, field: lines } | 行数(of は要らない) |
{ op: avg, field: lines, of: price } | 単価の平均 |
{ op: max, field: lines, of: amount } | 一番大きい行の金額 |
取消した行を外す(where)
業務の合計は「全部の行」ではないことが多い。畳む前に行を絞るなら where を書く。
- { field: subtotal, label: 小計, format: currency,
computed: { op: sum, field: lines, of: amount,
where: { field: cancelled, operator: notEquals, value: true } } }条件の書き方は visibleWhen と同じ(all / any / not も使える)。条件の書き方を 2つ覚える必要はない。ただし判定するのは行1件なので、{ mode: create }(新規のとき) はここでは意味を持たない=常に当たらない(validate が言う)。
絞った結果が 0 件になったときの値は「行が無いとき」と同じ。
品名を1行にまとめる(join)
数ではなく文字をまとめるときは join。伝票の「品名」欄や確認画面の要約で要る。
- { field: itemNames, label: 品名, type: textarea, readOnly: true,
computed: { op: join, field: lines, of: item, separator: "、" } }区切りの既定は ", "。concat の既定が空なのは姓と名を詰めるためで、行を並べるときに 詰めると読めないから、既定が違う。空の値は飛ばす(区切りだけが並ばないように)。行が 1件も無ければ空文字(0 ではない)。
決まりごと
ofはcount以外で必須。無いと空欄になる(validateが先に言う)- 行が1件も無いとき
sumとcountは 0、avg/min/maxは空(「平均 0 円」と 出ると読み違えるので、値が定まらないときは空にする)、joinは空文字 - 数として読めない値の行は飛ばす(文字で来た数は読む)
- 計算は書いた順に1回。小計を使って消費税を出すなら、消費税は小計より後ろに書く (逆に書くと空のまま計算される。画面では空欄に見えるだけなので
validateが言う) - 畳めるのは親と一緒に保存する明細だけ。
sourceを書いた明細はページ送りで別に 取るので、画面に出ている行を足しても業務の合計にはならない(これもvalidateが言う)
紙に出す小計(グループごとの小計・総計)は帳票の totals の担当で、これとは別。
上位だけ並べる・上位だけ畳む
要約の欄は幅が決まっているので、明細が 30 行あるものを全部並べると溢れる。金額の大きい順に3件だけは定義に書ける。
- { field: itemNames, label: 主な品名, type: textarea, readOnly: true,
computed: { op: join, field: lines, of: item, separator: "、",
sort: { field: amount, ascending: false }, limit: 3 } }出るのは「ペン、鉛筆、ノート、ほか 2 件」。黙って切らないのが大事なところで、3件だけ出して終わると読む人は「明細は3行」と読む。
| キー | 意味 |
|---|---|
sort | 畳む前に並べる。{ field, ascending }=ダッシュボードのカードと帳票と同じ書き方(同じことを2つの言い方で持たない) |
limit | where → sort のあと、先頭だけ採る |
overflow | 切ったぶんを言う文(join のとき)。{count} は隠れた行数。既定は「ほか {count} 件」、overflow: "" で何も足さない |
並ぶ順は where(絞る)→ sort(並べる)→ limit(採る)で固定。順番が違えば「上位3件」が別の3件になる。
- 比べ方は
compareと同じ(両方が数として読めれば数、でなければ文字) - 値が無い行は、向きに関わらず後ろ。「金額の大きい順」で金額の無い行が上に来ると、読む人は「これが上位」と読み違える
- 同じ値なら元の順(並べ替えの実装は言語ごとに違うが、答えは3版で同じになる)
limitは数を畳むときにも効く(op: sumで「上位3件の合計」)overflow: ""は「黙って切る」と決めたということ。書いてあるので読み返せる(npx -p @hatake-fw/api hatake explainにも「切ったぶんは出さない」と出る)
依存は絵にできる
順番の警告(computed-order)が出たとき、どこを動かせばいいかは依存を辿らないと分からない。定義から読めるので、絵にできる。
npx -p @hatake-fw/api hatake diagram order_entry.yaml --computedMermaid で出る(PR の本文にそのまま貼れる。--format dot なら Graphviz)。明細の行から親への線も、畳む前の絞り込みが見ている行の項目も出る。順番が逆の線は赤なので、動かす所が色で分かる。
確認画面の表示にも使える
ステップ入力の最後で「入力内容の確認」を見せたいとき、concat でまとめて1項目にすると手軽。
- { field: summary, label: 登録内容,
computed: { op: concat, fields: [code, name], separator: " / " } }足りない計算は足す
op も開いた文字列。税込金額の計算や社内固有の按分ルールなどは、名前を決めて登録すれば op: withTax のように書ける。消費税や年度・元号のような日本の業務でよく要る計算は、Framework 側に道具として用意されている(定義からではなくコードから呼ぶ)。
書けるキー
| キー | 書く場所 | 型 | 必須 | 既定値 | 有効なページ種別 | 説明 |
|---|---|---|---|---|---|---|
computed | field | object → field.computed | 任意 | — | crud dashboard detail form master report search wizard | Derive the field value from the record. Two modes: fields folds values of the same record (concat / sum / subtract / product), while field folds the rows of a subTable (count / sum / avg / min / max — the same aggregate vocabulary as dashboards and compare — plus join, which lists the rows as one string). Row-folding accepts where to keep only some rows, sort to order them and limit to fold just the first ones (in that order). Derived once, in declaration order. Extensible via ComputedRegistry. |
op | field.computed | string (concat / sum / subtract / product / count / avg / min / max / join ほか) | 必須 | — | crud dashboard detail form master report search wizard | Same-record ops: concat, sum, subtract, product (with fields). Row-folding ops: count, sum, avg, min, max, join (with field). Open string — register your own on ComputedRegistry. |
separator | field.computed | string | 任意 | — | crud dashboard detail form master report search wizard | What goes between the joined values. Default: empty for concat (it packs values of one record, like a name), ", " for join (it lists rows, which are unreadable packed together). |
of | field.computed | string (amount ほか) | 任意 | — | crud dashboard detail form master report search wizard | Which value of each row to fold. Required except for count. Same meaning as of on the compare validator. |
where | field.computed | object → condition | 任意 | — | crud dashboard detail form master report search wizard | Row-folding mode only: fold just the rows matching this condition, evaluated against one row (the same condition language as visibleWhen). { mode: ... } is never true here — a row has no form mode. |
sort | dashboardItem | object → dashboardItem.sort | 任意 | — | dashboard | Sort passed to the repository. |
sort | field.computed | object → field.computed.sort | 任意 | — | crud dashboard detail form master report search wizard | Row-folding mode only: order the rows before folding, so limit takes the top ones (the three largest amounts). Same shape as the sort of a dashboard card and a report. Values compare as numbers when both read as numbers and as text otherwise (the rule compare uses); rows with no value go last either way, and equal values keep their original order. |
sort | report | object → report.sort | 任意 | — | report | Print order, passed to the repository. Groups are control breaks, so the rows must arrive in this order. |
limit | dashboardItem | integer | 任意 | 100 | dashboard | Rows to fetch (the query's pageSize). |
limit | field.computed | integer (3 ほか) | 任意 | — | crud dashboard detail form master report search wizard | Row-folding mode only: fold just the first rows, after where and sort. A summary field has a fixed width, so listing every row overflows it. join says how many rows it left out (see overflow). |
limit | optionsSource | integer | 任意 | 200 | crud dashboard detail form master report search wizard | Rows to fetch (a select is not a list screen). |
limit | report | integer | 任意 | 1000 | report | Rows read for one run (a report is printed, not paged). |
overflow | field.computed | string (他 {count} 品目 ほか) | 任意 | — | crud dashboard detail form master report search wizard | join with a limit only: what to append when rows were left out. {count} is the number of hidden rows, not the total. Defaults to the framework's wording (Japanese, via MessageResolver); an empty string appends nothing, which reads as a decision to truncate silently. |
この表は spec/reference.json から生成している(JSON Schema が正)。手元では npx -p @hatake-fw/api hatake reference <キー名> で同じものが引ける。
近い例
例は丸ごと写して直すのが一番速い。以下は CI で検証済み(そのまま動く形)。
| ファイル | 種別 | 画面 | どういうときに使うか |
|---|---|---|---|
customer_wizard.yaml | wizard | 顧客登録 | 項目が多いので入力をステップに分けて、1ステップずつ検証したい |
order_entry.yaml | form | 受注入力 | ヘッダと明細行を1画面で入力して、まとめて保存したい |
sales_dashboard.yaml | dashboard | 売上ダッシュボード | 件数・金額・グラフのカードを並べて、まず数字を見せたい |
sales_report.yaml | report | 売上明細表 | 一覧の印刷版が欲しい。顧客ごとに小計を出して、CSV も落としたい、紙にも刷りたい |
実物を見る
デモアプリの「受注入力」がこれを使っている。 デモを開く