「rex0220 kSQL アクションプラグイン」の実践編です。
⑧ 請求データ編 で、受注から請求データを作り、何度でも作り直せるようにしました。今回はその締め作業の画面を作ります。kSQL Dashboard Pro と組み合わせて、押す前に見る画面を 1 つにまとめます。
シリーズ: 概要 / ① 詳細画面 / ② 一覧 / ③ 集計 / ④ 運用 / ⑤ 事例 / ⑥ イベント / ⑦ オーサリング / ⑧ 請求データ / ⑨ 締めの画面(この記事) / ⑩ どこに出すか / ⑪ 多言語 / ⑫ オーサリング改善
夜間バッチが作れないなら、押すしかない
⑤でこう書きました。
kSQL アクションは押した人のブラウザーで動きます。夜間バッチはできません。定期実行が要るなら kSQL Flow(CLI)や kSQL ジョブ実行プラグインの領分です。
ここを引き直します。情報システム部門が無い会社では、その領分に手が届きません。 サーバーを 1 台立てて、スケジューラーを組んで、落ちたら誰が気づくのか — 外に出せば回せますが、その話を始めた時点で導入のハードルは別物になります。
だから誰かが押す。それがこのプラグインの前提です。
そして押す運用でいちばん怖いのは、処理が失敗することではありません。押し忘れです。失敗はエラーで分かりますが、押し忘れは何も起きません。受注を直した人は請求が古いことを知らず、締め担当は直されたことを知りません。
押す人が迷わないために、押す前に見る画面が要る。
ダッシュボードは飾りではなく、押す運用の片割れです。
作る画面
⑧で作った 3 つのアプリ(受注・請求(未確定)・請求確定)を、1 画面に集めます。
見出しの ⌘ でフル画面にすると kintone の枠もボタンも消えてペインだけになります。隣の Aa で表示サイズも変えられます(その端末のこのアプリだけに残るので、他の人の画面は変わりません)。
通常の画面では、この上にアクションのボタンが並びます。見て、押して、その場で確かめるまでが 1 画面で閉じます。
確かめる 4 枚を上の列に、明細と手順を下に並べています。縦に長くするとスクロールが要るので、締め担当の目が横に流れるだけで済む形にしました。
ペインは 6 枚です。
| ペイン | 役割 |
|---|---|
| 突合(分母つき) | 受注と請求の合計が合っているか。分母を並べる |
| 締めの状態 | 差(合計)と ずれている顧客(顧客別)を 2 つのタイルで。どちらも 0 以外なら赤 |
| 顧客別のずれ | その明細。片方にしか無い顧客も出る |
| 請求(未確定)の明細 | 顧客ごとの件数・金額・集計日時 |
| 請求対象月が未設定の受注 | 人が直すほうの未請求 |
| 締めの手順 | マークダウン。順番と「未請求は 2 種類」の表 |
分母を並べる
いちばん伝えたいのがここです。
「未請求 0 件」には 2 つの意味があります。 全部請求できている状態と、ペインの条件を間違えて何も拾っていない状態が、まったく同じ見え方になります。
分母を並べると、一目で区別できます。
| 区分 | 受注件数 | 金額 |
|---|---|---|
| 1 受注(請求対象) | 200 | 34,897,000 |
| 2 受注(対象外) | 1 | 98,000 |
| 3 請求(未確定) | 200 | 34,897,000 |
| 4 請求確定 | 0 | 0 |
1 と 3 が揃っていれば、受注と請求の全体はずれていません。2 の対象外は請求に入らないのが正しい姿です。そして分母だけ増えて分子が動いていなければ、受注を足したのに誰も押していないと分かります。
差 0 は「合計が合っている」であって「全部合っている」ではありません
ここは正直に書いておきます。この突合が見ているのは合計です。顧客別のずれは相殺されると見えません。
| 顧客 | 正しい請求 | 今の請求 |
|---|---|---|
| C001 | 100,000 | 120,000 |
| C002 | 200,000 | 180,000 |
| 合計 | 300,000 | 300,000 |
差は 0 ですが、2 社とも間違っています。③で扱った「顧客コードを付け替えた受注」がまさにこれです。付け替えても合計は変わらないので、合計だけ見ていると気づけません。
だから顧客別も出します(しかもタダです)
合計で見えないなら、顧客別に突き合わせればいい。そしてこれは、読む量が増えません。
金額の突合はもう当月の受注を全部読んでいます。顧客別の集計は同じ押し下げ条件(請求対象月 = @m AND 請求対象 in ('対象'))なので、取得が共有されます。つまりペインを足しても kintone を読む回数は変わりません。
書き方は、差の KPI と同じ符号を反転して足す手です。
CREATE TEMP TABLE #d AS
SELECT 顧客コード, MAX(顧客名) AS 顧客名, COUNT(*) AS 件数, SUM(金額) AS 金額
FROM LAPP_受注 WHERE 請求対象月 = @m AND 請求対象 in ('対象') GROUP BY 顧客コード
UNION ALL
SELECT 顧客コード, 顧客名, 件数 * -1, 金額 * -1
FROM LAPP_請求 WHERE 請求月 = @m;
SELECT 顧客コード, MAX(顧客名) AS 顧客名, SUM(件数) AS 件数差, SUM(金額) AS 金額差
FROM #d GROUP BY 顧客コード HAVING SUM(件数) <> 0 OR SUM(金額) <> 0;
JOIN ではなく UNION ALL で足すのが肝です。 JOIN だと「片方にしかない顧客」が落ちますが、この書き方なら片側だけの行もそのまま残り、合計が 0 にならないので拾えます。拾えるのは次の 4 通りです。
| 状態 | 出かた |
|---|---|
| 顧客コードを付け替えた | 付け替え元が負、先が正 |
| 受注にあるのに請求が無い(新しい顧客) | 正 |
| 請求にあるのに受注が無い(取り消された) | 負 |
| 金額だけ違う | その顧客が 1 行 |
4 通りとも実機で確かめました。どれも[請求を全体再計算]を 1 回押せば消えます(受注だけの顧客は追加され、請求だけの顧客は⑧の 0 更新方式で削除されます)。
一番怖いのは最初の「付け替え」です。受注を 1 件だけ別の顧客に付け替えてみました。合計は 200 件・34,897,000 円でぴたりと一致、差も緑の 0。それでも「ずれている顧客 2」が赤く出ます。
C017 が −1 件 −512,000 円、C018 が +1 件 +512,000 円。付け替えた 1 件が、どこからどこへ移ったかまで名指しで出ています。
空の表は「0 件」と同じ問題を起こすので、KPI カードで件数も出しておきます。表が空のときに「全部そろっている」のか「条件を間違えた」のかが、カードの 0 で決まります。この記事で最初に書いた「分母を並べる」と同じ理屈です。
この画面の役割は、締める前に「全体再計算を押すべきか」を判断することです。顧客別まで見られるようになっても、確定したあとの請求を監査する画面ではありません。
件数だけで突合すると、毎月まちがったアラートが出ます
最初、受注と請求の COUNT(*) を並べました。受注 10 件に対して請求 5 行。ずれています。
正常です。 請求は顧客ごとに 1 行だからです。10 件の受注が 5 社から来ていれば、請求は 5 行になります。
突合するなら、請求側は行数ではなく件数の合計です。
SELECT '1 受注(請求対象)' AS 区分, COUNT(*) AS 受注件数, SUM(金額) AS 金額
FROM LAPP_受注 WHERE 請求対象月 = @m AND 請求対象 in ('対象')
UNION ALL
SELECT '2 受注(対象外)', COUNT(*), SUM(金額)
FROM LAPP_受注 WHERE 請求対象月 = @m AND 請求対象 in ('対象外')
UNION ALL
SELECT '3 請求(未確定)', SUM(件数), SUM(金額)
FROM LAPP_請求 WHERE 請求月 = @m
UNION ALL
SELECT '4 請求確定', SUM(件数), SUM(金額)
FROM LAPP_請求確定 WHERE 請求月 = @m
3 行目だけ SUM(件数) です。ここを COUNT(*) にすると、何も壊れていないのに毎月赤くなるダッシュボードができあがります。
「未請求」は 2 種類あります
分けて出してください。直し方が違うからです。
| 種類 | 原因 | 直し方 |
|---|---|---|
| 請求対象月が未設定の受注 | 入れ忘れ。押しても拾われない | 人が直す |
| 請求対象月はあるのに請求行が無い | 押し忘れ | ボタンで直る |
前者をボタンで直せると思われると困ります。同じ「未請求」の枠にまとめず、行を分けて出します。
そして前者の表には $id を SELECT しておきます。
SELECT $id, 受注日, 顧客コード, 顧客名, 金額
FROM LAPP_受注 WHERE 請求対象月 = '' KORDER BY 受注日 DESC LIMIT 100
$id を選ぶと、表の行の左端に「レコードを開く」アイコンが出ます。気づいた画面から、その受注をそのまま開いて直せます($id 列は columns の hidden で隠しても、アイコンは残ります)。
API の話
⑤でこう書きました。
件数より先に効くのは API。ダッシュボードは開くたびに集計するので、読む件数 × 見る人数 × 開く回数だけ kintone の API を使う。
締めのコックピットは、見る人は少ないものの、正本(受注)を読みます。ここは見積もっておく必要があります。
結論から言うと、思ったより安く作れます。
COUNT(*) は取得上限の影響を受けません
上の突合 SQL の実行計画です。
fetch summary: COUNT_ONLY
[union:1]
mode: COUNT_TOTAL_COUNT
kintone query: 請求対象月 = "2026-12" and 請求対象 in ("対象") limit 1
fetch: COUNT_ONLY (limit 1)
fetch API: GET records.json (totalCount=true)
REST execution: single GET
record limit: maxRecords/onLimitReached not applied
limit 1 で投げて totalCount だけ読んでいます。レコードを 1 件も走査しません。 枝をいくつ並べても 1 枝 = 1 リクエストで、取得上限にも掛かりません。
注意が 1 つ。同じ計画に fallback: full record scan when totalCount is missing or invalid と出ます。totalCount が取れないときは全件走査に落ちます。
SUM を 1 つ足すと全件走査になります
同じ条件に SUM(金額) を足しただけで、計画が変わります。
| SQL | 計画 |
|---|---|
COUNT(*) だけ |
COUNT_ONLY / single GET / 上限が適用されない |
COUNT(*), SUM(金額) |
FULL_SCAN / complete input: required |
合計を出すにはレコードが要るので当然ですが、単発 GET のつもりが全件走査になっていたというのは起きがちです。件数と金額で桁違いにコストが変わることは、頭に入れておいてください。
ORDER BY を KORDER BY にすると単発 GET になります
明細の表に LIMIT 100 を付けても、ORDER BY が付いていると効きません。
mode: FULL_SCAN
order plan: CANONICAL_LOCAL
order reason: ORDER_KEY_NOT_REST_EQUIVALENT
complete input: required
kSQL の ORDER BY は kintone の並べ替えと意味が違うので、全件取ってから JavaScript 側で並べ替えます。だから LIMIT が効きません。
KORDER BY に変えると、kintone の order by としてそのまま渡ります。
mode: SIMPLE
order plan: KORDER_NATIVE
REST execution: single GET
kintone query: 請求対象月 = "" order by 受注日 desc limit 100
1 文字の違いで全件走査が単発 GET になります。 並び順が kintone の規則になる(order semantics: kintone native)ので、細かい並びにこだわる場面では使えませんが、明細の表はこれで十分です。
分母をキャッシュしてはいけません
「毎回受注を読むのが重いなら、再計算のときに受注側の件数と金額を請求レコードに書いておけばいい」と考えたくなります。
それでは意味がありません。 その数字は「最後に押したときの受注」であって、押したあとに受注が変わったことを検知できません。押し忘れを見つけるのが目的なのですから、分母は毎回正本から読みます。
幸い、上のとおり件数はタダ同然です。金額の分母だけが読みます。
全ペインの WHERE を同じ書き方で揃える
Dashboard Pro は、押し下げ後のクエリが同じペインの取得を 1 回にまとめます。今回のペインはすべて 請求対象月 = @m / 請求月 = @m なので、コックピット全体が少ない取得で回ります。
ここで気をつけるのは、同じ「今月」を別の書き方で表さないことです。結果が同じでもキーが別になり、押し下がらない書き方をすれば全件取得になります。
押し忘れ → 気づく → 押す → 戻る
さきほどの状態(差は緑の 0、ずれている顧客だけが赤い)から続けます。
色は KPI カードの条件付きカラーです。SQL でアラートを組む必要はありません。閾値のルールを書くだけで、2 つのタイルそれぞれに色が付きます(差が 0 でないときも同じルールで赤くなります)。
"rules": [
{ "target": "main", "op": "=", "value": 0, "tone": "good", "apply": "text" },
{ "target": "main", "op": "!=", "value": 0, "tone": "crit", "apply": "background" }
]
[請求を全体再計算]を押して 2026-12 を入れると、更新 42 件・2 秒で終わりました。
ボタン 1 回で直ります。画面は「作る画面」で見たのと同じ、差もずれている顧客も緑の 0 に戻ります。集計日時も更新されています。
押したのに赤いまま、を直す
ここで 1 つつまずきました。再計算が終わっても、ダッシュボードは古い数字のままです。
アクションの「画面の更新」が 出さない になっていたためです。出す に変えると、完了のダイアログに[画面を更新]が出ます。
締め担当がいちばん混乱するのは「押したのに赤いまま」です。 ダッシュボードと組み合わせるアクションでは、ここを必ず見直してください。
現場の画面と、締めの画面を分ける
受注の一覧は、日々レコードを入力する作業画面です。そこに締め用のダッシュボードが常に出ていると邪魔になります。
締め専用の一覧を 1 つ作って、そこにだけ出します。
分ける前は、日々の作業画面である受注一覧にもダッシュボードが出ていました。
Dashboard Pro の設定は一覧ごとに有効・無効を選べるので、設定ファイルではこう書きます。
"common": { "view": { /* ダッシュボード本体 */ } },
"views": {
"16551406": { "viewName": "受注一覧", "enabled": false, "layout": [], "panes": [] },
"16551407": { "viewName": "未処理", "enabled": false, "layout": [], "panes": [] }
}
ダッシュボード本体は共通に置き、出したくない一覧だけ enabled: false にします。
はじめは逆に、views の下に直接ダッシュボードを書こうとしました。これは個別ダッシュボードになりません。 取り込むと中身は共通のほうに入り、その一覧の「個別」は空のままです。共通に置いて、出さない一覧を指定する — こちらが正しい形です。
アクションの置き場所
⑧のアクションは受注アプリに付いています。締めの画面を別アプリ(請求)に作ると、同じアクションをもう 1 組定義することになり、設定が二重管理になります。
受注アプリに締め用の一覧を足すほうが、アクションはそのまま使えます。アプリを増やすより、一覧を増やす。
設定ファイルは論理アプリ名で書く
ペインの SQL でもアプリを LAPP_受注 のような論理アプリ名で書けます。番号との対応は設定の logicalApps に 1 か所だけ持ちます。
"logicalApps": { "受注": 4387, "請求": 4400, "請求確定": 4401 }
⑧のアクションの SQL と見た目がそろい、環境を移すときも対応表の差し替えだけで済みます。取り込みのときに「論理アプリの割り当て」が出て、論理名・番号・アプリ名の 3 つが並ぶので、取り違えにも気づけます。
1 つだけ落とし穴があります。LAPP_請求 は LAPP_請求確定 の接頭辞です。 設定ファイルを自分のスクリプトで機械的に置換するときは、長い名前から順に置き換えてください。素直に上から置換すると APP4400確定 のような壊れた名前ができます(実際にやりました)。
締めの手順
画面の右下にマークダウンのペインを 1 枚置いて、手順を書いておきます。
1. 差と「請求対象月が未設定の受注」を見る
2. 差が 0 でも[請求を全体再計算]を押す
3. 差が 0 なのを確かめて[請求を確定する]
2 が「0 でなければ」ではなく「0 でも」なのが肝です。 上に書いたとおり、差 0 は合計が合っているだけで、顧客別のずれは見えません。締める前に 1 回押しておけば、見えていないずれも直ります。
これができるのは、⑧の全体再計算が何度押しても同じ結果になる作りだからです。押して損がないなら、押す。ダッシュボードは「押すかどうか」ではなく、押す前に人が直すものが残っていないか(請求対象月の入れ忘れ)と、押したあと本当に揃ったかを見るために使います。
確定した月は、⑧の ASSERT で再計算も作成・更新も止まります。確定のあとは差が合わなくなっても、画面は赤くなりません(請求(未確定)が空になるため)。赤を見るのは確定の前までです。
つまりこの画面は、確定の前に「押すべきか」を判断するためのものです。確定したあとの請求内容を監査する画面ではありません。役割をここで切っておくと、現場が画面に期待しすぎずに済みます。
請求書の PDF を出すところまで進めるなら、rex0220 印刷屋プラグイン を同じ一覧に入れて、確定した請求から一括出力する形になります。
まとめ
- 情報システム部門が無いと夜間バッチは手が届きません。 誰かが押す運用が回る形で、押す前に見る画面がその片割れです
- 押す運用で怖いのは失敗ではなく押し忘れ。分母を並べると「0 件」の意味が決まり、押し忘れも見えます
-
差 0 は合計の一致です。顧客別のずれを見るには、
JOINではなく符号を反転してUNION ALLで足します。片方にしか無い顧客も拾えます - そのペインは読む量を増やしません。金額の突合がすでに当月の受注を読んでおり、押し下げ条件が同じなら取得が共有されます
- それでも締める前は 1 回押します(⑧の再計算は何度押しても同じ結果です)
-
件数だけで突合すると誤報が出ます。 請求は顧客ごとに 1 行なので、請求側は
SUM(件数)で比べます -
COUNT(*)だけならCOUNT_ONLYになり、totalCountを読むので取得上限の影響を受けません。SUMを 1 つ足すと全件走査になります -
ORDER BYをKORDER BYにすると、order byとlimitが kintone にそのまま渡って単発 GET になります - 分母はキャッシュしない。 押したあとの変化を検知できなくなります
- 「未請求」は 2 種類。人が直すほうは
$idを選んで、画面から開いて直せるようにします - 締め専用の一覧を作り、共通に置いて出さない一覧を指定します。アプリを増やすより一覧を増やす
- アクションの「画面の更新」を出すに。「押したのに赤いまま」を作らない
関連記事
- rex0220 kSQL アクションプラグイン
- ① 詳細画面編(ボタンとイベント)
- ② 一覧編(選択したレコードをまとめて処理する)
- ③ 集計編(顧客別集計とテーブルへの書き戻し)
- ④ 運用編(ログアプリ・二重実行の防止・権限)
- ⑤ 事例編(10 万件を集計して Dashboard Pro で見る)
- ⑥ イベント編(保存・ステータス変更・削除で自動実行する)
- ⑦ オーサリング編(設定を AI に作ってもらう)
- ⑧ 請求データ編(全体再計算で整合性を保つ)
- ⑨ 締めの画面編(押し忘れに気づくダッシュボード)(この記事)
- ⑩ 画面のどこに出すか(ボタン・メニュー・パネルの使い分け)
- ⑪ 日本語・英語・中国語で使う(共通の文と多言語)
- ⑫ オーサリング改善編(AI に使わせて見つけた穴)
- 事例集(やりたいことから探す)


![完了のダイアログに[画面を更新]が出る](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F100572%2F7ee6e7f4-7edb-40d9-9ef2-70f4f8cc33f7.jpeg?ixlib=rb-4.1.1&auto=format&gif-q=60&q=75&s=9e6b1483965e4055fbbe1cab4226d5b4)
