この日やったこと
CF精算表(CFWS)Excel自動生成の2つの論点 -- 固定資産応用(CF_202)と株主資本応用(CF_205) -- で、数式が合わない問題を一つずつ潰した。verify_checksを厳格化したら18件の不一致が噴き出し、圧縮積立金の二重計上という根本的な設計ミスに行き着いた。午後はCodex(GPT-5.4)とレビューサイクルを5回まわして株主資本のパイプラインを組み上げ、夜は3600行のスクリプトを論点別モジュールに分割する計画書を書き上げた。
CF_202 固定資産応用: SUMIFSの完全一致が複合イベントを取りこぼす
症状
CFWS中間テーブルのSUMIFS数式が、期待した金額を返さない。Python側のverify_checksはcheck=0なのに、Excel側のcheck行にズレが出る。
原因の深掘り
SUMIFSの条件列にイベント名を入れていて、完全一致で検索していた。ところが固定資産応用では「減価償却+売却」のように1行に複合イベントが入るケースがある。SUMIFSは部分一致を標準でサポートしないから、この行をまるごと取りこぼす。
解決: イベント分離
複合イベントを単一イベントに分離してから中間テーブルに流す方式に変更した。「減価償却+売却」→「減価償却」と「売却」の2行に展開する。SUMIFSの完全一致がそのまま使えるようになり、ワイルドカードのような曖昧な仕組みを入れずに済んだ。
残存年数計算バグ
イベント分離の過程で、残存年数の計算にもバグが見つかった。取得年度からの経過年数を引くべきところで、期首の帳簿価額から逆算するロジックになっていた。直接修正。
手付金がSUMIFSで資産取得支出に混入
建設仮勘定の手付金イベントが、SUMIFSで「資産取得支出」の集計に巻き込まれていた。手付金は建設仮勘定に計上するだけで、固定資産の取得とは別物。除外フィルタを追加して切り離した。
符号規則の整理 -- row_defsとcomputed_valuesの二重実装
露呈した問題
CFWSの符号を決めるロジックが2箇所に散らばっていた。
- row_defs: CFWS行定義で「この行は正負どちらで表示するか」を指定
- computed_values: Pythonの計算時に
*-1で符号を反転
この二重実装のせいで、片方を直すともう片方と矛盾する。建設仮勘定の振替額で*-1が二重適用されていて、符号が逆転していた。
対処
computed_values側の*-1を削除し、符号はrow_defsに一元化する方針を決めた。ただし全面的な統一は3600行スクリプトのリファクタリングで対応する範囲なので、今回は建設仮勘定周りだけ修正した。
verify_checksの厳格化 -- 18件の不一致が噴出
何をしたか
これまでverify_checksは「BS増減 = 調整列合計」の行チェックだけだった。ここに列単位チェック(各調整列のBS配分 + CF計上 = 0)を追加し、さらに浮動小数点の丸め許容を厳しくした。
結果
18件の不一致がいきなり検出された。大半は「圧縮積立金」と「繰越利益剰余金_圧縮積立金」の二重計上だった。年次推移表のBS行に両方が出てしまい、CFWSに持ち込んだときに数値が倍になっていた。
これはイベント分離や符号修正で個別対処できるレベルではなく、勘定科目のデータモデルそのものの問題だった。
圧縮積立金の二重計上 -- 根本的解決方針の決定
ユーザーの指摘で方向が定まる
自分で3時間ほどパッチ的な修正を試したが、直すたびに別の場所が壊れた。ユーザーが「アンダースコア区切りの勘定科目名は仕訳側で直接使うべきでは」と指摘して、目の前が開けた。
「繰越利益剰余金_圧縮積立金」のような合成科目名を中間テーブルで生成するのではなく、仕訳データの段階で正しい科目名をつける。中間テーブルは仕訳をそのまま集約するだけにして、科目の合成・分解をしない。
Codex 3回レビューで計画書化
この方針をCodex(GPT-5.4)で3回レビューした。1回目で「仕訳側の科目名変更が既存テストに波及する範囲を確認せよ」と指摘され、characterization testを先に作る計画に修正。2回目で「goldenデータの固定方法が不明確」と指摘され、JSON保存方式を明記。3回目でOKが出た。
CF_205 株主資本応用: Codex 5回レビューで計画→実装
Step a: 取引モジュール
配当金支払額の逆算ロジックが核心だった。繰越利益剰余金の期首残高・当期純利益・利益準備金積立額から配当原資を計算し、retained_dividendとして算出する。
当期純利益に根拠を持たせるため、運転資本取引(売上・仕入・減価償却)を組み込んだ。PLが立たないと配当のCFWS配分が宙に浮くので、ここは省略できなかった。
Step b: 年次推移表
16科目+利益準備金で正常生成。繰越利益剰余金のhidden行を追加して、圧縮積立金との内訳関係を保持するようにした。
Step c: CFWSパイプライン
利益準備金のCFWS配分が最後まで手こずった。利益処分の調整列で「配当支出(CF)」と「利益準備金積立(非資金BS振替)」を分離する必要があり、row_defsに2行追加して対応した。
サマリーCFWSの集計範囲バグ
サマリーCFWSシートの営業CF合計が合わない。セルの数式を見たらSUM(C8:C8)になっていて、1行分しか集計していなかった。正しくはSUM(C7:C8)。損益調整項目の開始行が1行ずれていた。修正して全年度のcheckが0に揃った。
3600行リファクタリング計画
動機
gen_cfws_multi_year.pyが3600行に膨れていて、1つの論点を直すと別の論点が壊れるサイクルに入っていた。符号規則の二重実装も、このスクリプトが肥大化した結果起きた問題。
方針: Codex 4回レビューで確定
- 論点別モジュール分割: 社債・固定資産基礎・固定資産応用・株主資本をそれぞれ独立モジュールに切り出す
- registry化: 各モジュールを登録して、メインスクリプトはregistryからモジュールを引くだけにする
- テストファースト: 先にcharacterization testでgoldenデータを固定し、リファクタ後も同一出力を保証する
- goldenデータはJSON保存: Excel出力のセル値をJSONにダンプし、テスト時に1:1比較する
Codexの1回目で「registry化とモジュール分割を同時にやると壊れやすい。先にテストを固めてからregistryを入れるべき」と指摘されて順序を入れ替えた。4回目でOK。
CFドキュメント統合
5つの重複するスキル/ワークフロー文書を3ファイルに統合した。-504行/+403行の差分で、約100行削減。v13フォーマットへの完全移管も完了。ドキュメントが散らばっていると、どのファイルが最新か分からなくなっていた問題がこれで解消した。
IBフォーマット: グレーグラデーション定義
xlsx_helpersにグレーグラデーション8段階(GL1-GL8)を定義した。投資銀行モデルのExcel出力で、ヘッダーの階層をグレーの濃淡で表現する。GL1が最も薄く(#F5F5F5)、GL8が最も濃い(#404040)。スキルとコマンドの両方に反映し、テストExcel出力で視認確認した。
characterization test作成
リファクタリングに先立ち、現在の出力値をgoldenデータとして固定した。computed_valuesの全セル値をJSON形式で保存し、6テストケース(社債2本+固定資産4論点)を作成して全パスを確認した。今後のリファクタでは、このgoldenデータとの差分が出たら即座に検知できる。
verify_cfws_excel.py拡張
サマリーCFWSのCheck行検証を追加した。これまでは個別CFWSシートのcheckのみだったが、サマリーシートの営業CF・投資CF・財務CFの合計がそれぞれ正しいかを検証するようにした。CF_205の集計範囲バグはこの検証で発見できた。
学びメモ
- SUMIFSの完全一致は複合データに脆い: ワイルドカードで逃げるより、入力データ側を正規化するほうが後で壊れない。イベント分離はSUMIFSだけでなく、データパイプライン全体の見通しを良くした。
- verify_checksを厳しくしたら壊れる箇所が増えた、のではない。元から壊れていた: テストの閾値を緩くしておくと「通っているから大丈夫」と思い込む。18件の不一致を見たときは手が止まったが、厳格化しなければ圧縮積立金の二重計上に気づけなかった。
- Codexレビューは順序の指摘が鋭い: 「何をやるか」より「どの順番でやるか」の指摘が一番効いた。テスト→registry→モジュール分割の順序は、自分だけでは逆にしていた。
- 3600行を前にして足がすくんだが、goldenデータを固定した瞬間に踏み出せた: リファクタの恐怖は「壊したことに気づけない」恐怖。検知手段があれば、あとは分割するだけ。