1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

rex0220 kSQL アクションプラグイン - ⑨ 締めの画面を作る(押し忘れに気づくダッシュボード)

1
Last updated at Posted at 2026-11-03

「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」が赤く出ます。

合計は一致して差 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 を選んで、画面から開いて直せるようにします
  • 締め専用の一覧を作り、共通に置いて出さない一覧を指定します。アプリを増やすより一覧を増やす
  • アクションの「画面の更新」を出すに。「押したのに赤いまま」を作らない

関連記事

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?