• #eurekapu
  • #キャッシュフロー計算書
  • #Python
  • #リファクタリング
  • #Codex
  • #Excel
  • #デバッグ
開発eurekapu-nuxt4メモ

CFWS NGフィックス Phase 3 - OK 5件から32件まで引き上げた一日

朝6時、前日のタスク引き継ぎで積んでおいたNG 25件の一覧をターミナルに並べた。OK 8件 / NG 25件 / SKIP 2件。ここから一日かけて OK 32件 / NG 3件まで削り込んだ記録。


Phase 3-A: 繰越利益剰余金の集約と配当配賦

indent=3 明細キーの集約

繰越利益剰余金の配下に indent=3 の明細科目が複数ぶら下がっていた。年次推移表の生成時にこれらが個別の行として出力され、CF参考書の模範解答と列構成が一致しない。

修正方針は「indent=3 の明細キーを親の繰越利益剰余金に集約する」こと。accounts_master.json のインデント情報を使い、集約対象の科目を自動判定するロジックを追加した。

Q3-7 配当金の配賦ロジック

Q3-7(配当金支払い)は繰越利益剰余金から現金への振替だが、CFラベルが「財務活動」に分岐する。既存ロジックでは繰越利益剰余金の増減としてしか処理されず、「配当金の支払額」列が欠落していた。

配当仕訳を検出して財務CF側に配賦するロジックを追加。これでPhase 3-AだけでOKが5件から19件に跳ねた。


Phase 3-B: _classify_cf_label のSoT化

CFラベル分類を一箇所に集約

CFラベルの判定ロジックが _allocate_cfws_gen_annual_table の2箇所に分散していた。同じ科目でも判定結果が食い違うケースが出始めたため、_classify_cf_label を唯一の判定関数(Single Source of Truth)に昇格させた。

これで以下の5論点が一気にOK化した。

  • Q5-10: 投資有価証券売却
  • Q5-11: 関係会社株式取得
  • Q5-19: 社債発行差金
  • Q5-25: 新株予約権
  • Q5-26: ストックオプション

判定ロジックの重複を潰しただけで5件通ったのは、問題の根っこが「同じことを2箇所で書いていた」設計負債だったことを物語っている。


Phase 3-C: 残りのNG論点を個別撃破

Q3-5 為替差損益

為替差損益がPL項目としてしか処理されず、CF調整項目として拾われていなかった。_classify_cf_label に為替差損益の判定を追加。

Q3-9 有形固定資産のエイリアス衝突

qa_journal_parser._ALIASES に登録した有形固定資産の別名が、accounts_master.json の独立キーと衝突していた。パーサーが別名で引いた結果、本来の科目とは違うCFラベルに振り分けられる。

エイリアスを削除し、accounts_master.json 側のキーだけで引くように統一して解消。

Q3-10 / Q3-12 / Q5-3 純資産 indent=2 の上書き問題

純資産セクションの indent=2 科目(資本金、資本剰余金など)が、集約ロジックの副作用で親科目の値を上書きしていた。indent判定の優先度を修正し、indent=2 は集約対象外として明示的にスキップ。

Q5-6 資産除去債務

資産除去債務がBS負債として認識されず、CF調整項目から漏れていた。accounts_master.json に資産除去債務のCFマッピングを追加。

Q5-12 / Q5-13 LLP(有限責任事業組合)

LLP出資金が投資活動CFに分類されるべきところ、営業CFの「その他」に混入していた。LLP関連科目のCFラベルを投資活動に再マッピング。

最終結果: OK 32件 / NG 3件


文字化けの一括修復

テスト途中でNGプラン生成スクリプト _gen_ng_plan.py の出力に U+FFFD(置換文字)が大量に混入しているのに気付いた。

原因を追うと、Pythonスクリプトがログファイルを open(path, 'r', encoding='utf-8') で読んでいたが、元ログはExcel COM経由で出力されたCP932エンコーディングだった。合計U+FFFD U+FFFD に化けて、比較ロジックが壊れていた。

encoding='cp932' に修正した上で、既に生成済みのNGプランファイル111箇所の U+FFFD を一括置換で修復した。


Q5-20 自己株式取得信託

accounts_master.json を開くと、「預け金」が負債合計セクション内に配置されていた。本来は資産科目なのに、JSONの構造上「負債の部」のブロックに紛れ込んでいる。

この誤配置のせいで符号が逆転し、CFの増減額が正負反転していた。accounts_master.json 内の預け金を資産セクションに移動し、符号ロジックが正しく動くことを確認。


Q5-21 ESOP(日本版ESOP)

ESOPは5つの科目が同時に動く。信託口の現預金、投資有価証券、長期借入金、自己株式、その他資本剰余金。これらを正しいCF区分に配賦する必要がある。

Codexレビュー3回で計画を固める

最初の計画書をCodexに投げたら「信託口の現預金は連結範囲の現金に含めるべきか除外すべきか、方針が曖昧」と返ってきた。CF参考書の該当ページを読み直し、「信託口は連結範囲内、ただしCF上は財務活動に分類」と方針を明記して再投入。2回目は「自己株式の取得と処分で符号が逆になるケースの扱い」を指摘され、テストケースを追加。3回目でようやくクリア。

5科目配賦の実装

信託口の現預金 → 財務CF(信託設定時の支出)
投資有価証券  → 投資CF(株式取得)
長期借入金    → 財務CF(借入・返済)
自己株式     → 財務CF(自己株式の取得)
その他資本剰余金 → 財務CF(自己株式の処分差額)

各科目のCFラベルを accounts_master.json に定義し、_classify_cf_label で判定を通すだけでアロケータが正しく配賦した。SoT化の恩恵がここで効いた。


年次推移表の1列化リファクタリング

背景: 期中の各期列が冗長

従来の年次推移表は期ごとに列を持ち、各期の増減を個別に表示していた。しかしCF精算表で必要なのは期末残高のみ。冗長な列構造がSUMIFS数式の複雑化を招いていた。

OPEN列の追加で解決

構造を「期末残高のみの1列」に変更した。ただしそれだけだと期首残高がどこにも残らない。OPEN列(期首残高)を先頭に追加し、期末 = OPEN + 増減 で検算できるようにした。

この変更はサブエージェントに投げて大規模書き換えを実行。書き換え後に全Q回帰テストを走らせたら32件FAIL。原因を調べると、累積SUMIFSの数式が $B$1 を固定参照していて、列構造の変更に追随できていなかった。OPEN列を挿入した分だけ参照がずれていた。

SUMIFS数式の参照をOPEN列基準に修正し、回帰テスト全件パス。


会計ソフトA形式の仕訳シート改善

1行にまとめる

借方/貸方が1行ずつの単純仕訳は、従来2行に分かれて出力されていた。これを1行にまとめるフォーマット変更を実施。見た目がすっきりし、会計ソフトAへのインポート時の視認性が上がった。

開始仕訳の取引ナンバー統一

開始仕訳(期首残高の設定)の取引ナンバーがバラバラに振られていたのを、統一した連番に修正。


試行錯誤の記録

税引前NI直接参照 → Q3-6リグレッション

Phase 3-Bの途中で「税引前当期純利益をPLから直接参照すれば、調整項目の積み上げが不要になる」と思いついて実装した。テストを回すとQ3-6がNGに転落。

原因を掘ると、法人税等の差額3,500が直接参照では拾えていなかった。税引前NIと税引後NIの差分から法人税等を逆算する既存ロジックとの整合が崩れていた。PL検証ロジックを見直し、直接参照と差額計算を併用する形で対応。

_ALIASESとaccounts_master.jsonの衝突

Q3-9の修正中、qa_journal_parser._ALIASES に「建物」→「有形固定資産」のエイリアスを入れたら、今度はQ5-6の資産除去債務で「有形固定資産」が accounts_master.json の独立キーとして引かれ、CFラベルが変わってしまった。

教訓: エイリアスは便利だが、マスタデータのキーと名前空間を共有していると衝突する。マスタのキーが正とし、エイリアスは必要最小限にとどめる。

年次推移表1列化 → 全Q回帰32件FAIL

1列化の変更を入れた直後に --all テストを走らせたら32件FAIL。SUMIFSの $B$1 参照がOPEN列挿入で1列ずれたのが原因。数式の基準列をOPEN列に固定し直して全件復旧。

「構造変更は回帰テストの前にやらないと、何が壊れたか特定できない」という当然のことを再確認した。

Excel COM競合で --all テスト中断

--all フラグで全論点を一括テストしようとしたら、Excel COMのプロセスが競合してスクリプトがハングした。個別実行(論点を1つずつ指定)に切り替えて回避。COM操作は排他制御が効かないので、並列実行は諦めて逐次処理にするのが安全。


学びメモ

  • SoT化は遠回りに見えて近道。Phase 3-Bで _classify_cf_label を一箇所に集約した瞬間、5論点が何の追加修正もなくOKに変わった。重複ロジックを潰す作業は地味だが、そのあとの修正速度が目に見えて上がる
  • エンコーディングは疑え。CP932 vs UTF-8の文字化けは目視では気付きにくい。ログ出力元の文字コードを常に確認する習慣が要る
  • accounts_master.json は金庫。科目の配置ミスが符号逆転を引き起こすように、マスタデータの構造エラーは下流の全ロジックに波及する。マスタを変更するときはdiffを目視確認してからテストを回す
  • Codexレビューは「曖昧な方針」を炙り出す。ESOPの計画で3回投げ直したが、毎回「ここの方針が不明確」と具体的に返ってきた。自分では気付けない前提の抜けをレビュアーに拾わせるフローが機能している