• #CFWS
  • #Vue
  • #TypeScript
  • #リファクタリング
  • #Codexレビュー
  • #Pinia
  • #Vitest
開発eurekapu-nuxt4

キャッシュフロー精算表v2: 順方向ロジックへの全面書き換え

別リポジトリ eurekapu-nuxt4 で1日かけて、CFWS(キャッシュフロー精算表)の構造を逆向きから順方向に組み直した。既存v1は「CFWSの完成形を先に決め、そこから逆算する仕訳生成スクリプト」を回していたため、ローンの金利を2%から3%に変えても画面が動かない。v2では「取引モジュール → 仕訳 → 年次推移表 → CFWS」の順に各層が前段の出力だけを参照する構造に組み直し、UIの数字を変えるとCFまで一発で連動するようにした。

動機: 履歴2%可変が回らないと気づいた瞬間

v1ページの実装を読み直すと、CFWSのセル位置を決め打ちしてからスクリプト側で仕訳を逆算していた。「ローン金利を可変にする」という当初の設計思想に到達できないため、見た目は同じまま裏のロジックを順方向に差し替える v2 を新設する判断をした。

v1は資産として残し、ミラーカラムレイアウトの大項目に「資金調達_v1」「資金調達_v2」を並列配置。トグルではなくMiller Columnsの第1カラムで切り替える形にしたので、ユーザーは両方を同じURL体系で行き来できる。

Phase構成

実装は8フェーズに分けて一気通貫で通した。

  • Phase 1: 骨組み(ルーティング、ストア、ページ分岐)
  • Phase 2: ローン仕訳(借入実行・元金返済・利息支払・期首仕訳)
  • Phase 4: 年次推移表
  • Phase 5: CFWS構築 + 厳密チェック
  • Phase 6: ページ繋ぎ込み + ブラウザ実機確認
  • Phase 7: パラメータ編集UI(ローン/固定資産/設立イベント/期間/localStorage永続化)
  • Phase 8: CF計算書専用シート + 年次推移スタイル化 + IBフォーマット

各フェーズ末尾でVitestを回し、最終的に79件→86件まで増やして全てpass。Codex GPT-5.4のレビューも4往復挟んで「OK」獲得。

cfTag設計: 列単位検算を成立させる肝

v1互換のCFWSを作るには、各仕訳行に「CF計算書のどの項目に流すか」を表す cfTag を付ける必要がある。ここを軽く扱ったらPhase 5で破綻した。

type CfTag =
  | 'cash'                // 現金振替(小計内に出さない)
  | '長期借入金による収入'
  | '長期借入金の返済による支出'
  | '利息の支払額'
  | '有形固定資産の取得による支出'
  | '株式の発行による収入'
  // ...

借入実行・元金返済の現金側は cfTag='cash' に戻し、利息側だけ「利息の支払額」を残す。最初は現金側もCF項目に振っていたためダブルカウントが発生した。

詰まりと修正の経緯

「最小縦串先行」がユーザー指摘で破綻

最初は「CFWS表示まで先に通す」方針で進めていたら、ユーザーから「チェック甘いまま OK 出すな」と指摘が入った。期末バランスが揃っていないのに ✓ 表示する状態だった。

修正したチェック項目:

  • BS精算表に「現金及び預金」行が抜けていた → 追加
  • BS精算表のチェック行右端の貸借成立検算セルが空 → worksheetCheckTotal=0 検証を追加
  • cashTransferDiff のロジックがv1と違う → v1と同じ「現金行のcashTransfer + 期首現金 = 期末現金」に合わせ直し

普通預金の補助科目漏れで残高マイナス

固定資産取得仕訳の貸方(普通預金)に subAccount を設定していなかった。-24,600,000円が銀行ごとに振り分けられず、みずほ銀行の年次推移表残高がマイナスに。buildAcquisitionEntrysubAccount 設定漏れを直した。

その後ユーザーから「勘定科目レベルでは現金及び預金でいい。銀行名は補助科目に統一しろ」と指摘。借入仕訳・固定資産取得仕訳の両方を 普通預金現金及び預金 に変更し、同時にv2→v1依存(defaultAccountConfigをv1から借りていた件)も解消した。

設立シナリオへの切り替え

ユーザーから「期種残高があるのが意味分からない。設立シナリオにしろ」と指摘。ストア初期値を「資本金/資本準備金/利益剰余金期首残高なし、設立イベントで現金注入」に変更した。

期初B/S計算で利益剰余金の擬似行(pseudoRetained)を入れる必要があった点も、Codexレビューで指摘された。利剰のBS直打ち変動を擬似利剰行のrefsに統合し、29/29 pass で「OK」獲得。

バックエンドとフロントエンド両方のチェック

「テストコードが有効になっていないのではないか」というユーザー指摘を受け、バックエンドVitestで実際のストア初期値(設立シナリオ)を使った統合テストを追加。フロントエンドの CfWorksheetTable.vue でも貸借成立検算セルを表示させた。

// CFWS最終チェック行の判定
// v1と同じ式: 現金行のcashTransfer + 期首現金 == 期末現金
const isOk =
  cashTransferDiff.value === cashEndBalance.value &&
  worksheetCheckTotal.value === 0

表記統一

「FY202603 こんな感じで表記を統一してください」とのユーザー指示を受け、period.label を「FY{YYYY}{MM}」形式に統一。テストの periods も同形式に。

Phase 7: パラメータ編集UIで順方向ロジックを証明

ローン契約・固定資産・設立イベント・期間を編集するUIを V2AssumptionsEditor.vue に追加し、Pinia の $subscribe で localStorage に永続化。順方向ロジックの動作証明テストとして「金利を 2.0% → 3.0% に変更すると、CFWS の支払利息列が増える」を書いた。

長期借入金 公庫の1期末残高がブラウザ上でも 16,400,498 → 32,800,997(principal倍)と動き、リロード後も 5% が復元されるところまで agent-browser で確認した。

Phase 8: CF計算書を年次推移表スタイルに

最初は年度タブ形式で作ったが、ユーザーから「年次推移表みたいに横にFY並べてくれ」と要望。縦=項目、横=年度のレイアウトに書き換え、IBフォーマット(フォント Meiryo UI、リンク=緑、計算=黒、入力=青、数値書式 #,##0;"▲"#,##0;"-")を適用。

CSS specificity の罠で .cf-table td { color: #000000 }.navigable { color: #008000 } に勝ってしまい、デフォルトで navigable 項目が黒のまま。マウスオーバー時だけ緑になる挙動を、specificity を上げて修正した。

セル単位のオレンジハイライト + scrollIntoView も実装。建物クリックで「建物」行のセルだけがハイライトされ、合計行や備品行には影響しない。複合キー('建物:減価償却' 等)で行と列を特定する設計にした。

学び

  • v1依存を残すと「順方向で完結」が崩れる。defaultAccountConfig のような共通定数も含めて完全分離した方が後で楽
  • 「最小縦串で先に通す」は生産性が高いが、チェックロジックを甘くしたら ✓ の意味が消える。チェックは最初から本物にする
  • Codexレビューは4往復挟んで「致命点のみ指摘」と縛ると、瑣末な指摘に消耗せずに本質的な構造の歪みだけ拾える
  • Pinia + storeToRefs + computed の連鎖は、actions を足して <input> バインドするだけでリアクティブが回る。Phase 7 の編集UI追加は1日で済んだ

残タスク

Phase 7 の actions テストは入ったが、PL 段階損益(売上総利益→営業利益→経常利益→税引前→当期純利益)の年次推移表表示や、特別損益の項目枠(中身は空でも)の追加は次セッションで継続予定。

別セッション引き継ぎ用のプロンプトは計画書末尾「14. 新セッション引き継ぎプロンプト」に記録した。