• #Vue.js
  • #キャッシュフロー計算書
  • #財務諸表
  • #Vitest
  • #リファクタリング
開発未分類アクティブ

キャッシュフロー精算表ビューアをVue.jsで実装した開発記録

ExcelのキャッシュフローCF精算表をWebアプリケーションとして再現するプロジェクトの開発記録。仕訳データからCF精算表を自動生成し、計算チェックまで行う仕組みを構築した。

背景と目的

Excelで管理していたキャッシュフロー精算表をWebアプリに移植することで、以下を実現したかった。

  • 仕訳データの編集がリアルタイムで精算表に反映される
  • 計算チェック(各行の合計=0)の自動検証
  • 複数の事例を切り替えて表示できるUI

使用したExcelファイル

2つのExcelファイルを使い分けた。

ファイル用途
キャッシュフロー仕訳のコンテンツ案_仮説検証用_20240418_1200.xlsxコンテンツ用の仕訳データソース
GS連結CS_202309-4Q_20240325_0900.xlsx計算ロジック・Excel数式の参照用

コンテンツ用のExcelにはQ3-2などの事例別シートがあり、各シートに仕訳データが格納されている。計算ロジック参照用のExcelからは、現金及び預金の振替列やバランスチェックの数式を参考にした。

実装した機能

1. キャッシュフロー精算表の表示

精算表の列構成は以下の通り。

内容
勘定科目B/S科目名
期首残高前期末の残高
期末残高当期末の残高
増減期末 - 期首
CF調整項目税引前当期純利益、棚卸資産の増減、仕入債務の増減など
現金及び預金の振替現預金の増減を相殺する調整
チェック各行の合計が0になるかの検証

2. 期首残高・期末残高・増減の計算

Q3-2の事例では期首残高を全てゼロとし、前提仕訳「現金及び預金 10,000 / 資本金 10,000」を当期の取引として扱った。

// 仕訳から各勘定科目の増減を計算
const accountBalances = {};
journals.forEach(journal => {
  journal.entries.forEach(entry => {
    const account = entry.account;
    const amount = entry.debit || -entry.credit;
    accountBalances[account] = (accountBalances[account] || 0) + amount;
  });
});

3. 現金及び預金の振替ロジック

Excelの計算式を参考に、現金及び預金の行だけに振替額を入れ、他のB/S科目の振替は0とした。

// 現金及び預金の振替 = 他のB/S科目の振替合計の逆符号
const cashTransfer = -otherBSTransferTotal;
cashRow.cashTransfer = cashTransfer;
cashRow.rowTotal = cashRow.change + cashRow.cfAdjustment + cashTransfer;

4. 計算チェックの実装

各行の合計(増減 + CF調整 + 現金振替)が0になるかをチェックする。

// 行ごとの計算チェック
const rowTotal = change + cfAdjustment + cashTransfer;
const isValid = Math.abs(rowTotal) < 0.01; // 浮動小数点誤差を考慮

チェック結果の表示は以下のようにした。

  • 合計が0の場合: 緑色のチェックマーク
  • 合計が0でない場合: 赤色で差額を表示

5. CF計算書への転記

Excelの数式 =IF(AG$3=$D96,AG$86*-1,0) を参考に、精算表の列合計を-1倍してCF計算書に転記するロジックを実装した。

// CF計算書用にcfColumnTotalsを-1倍
const cfStatementItems = cfColumns.map(col => ({
  name: col,
  total: -cfColumnTotals[col] // 精算表の合計を-1倍
}));

6. 貸借一致チェック

ヘッダー行の「期首残高」「期末残高」「増減」に貸借一致のチェックマークを表示。

// 貸借一致チェック
const beginningDiff = assetsBegin - (liabilitiesBegin + equityBegin);
const endingDiff = assetsEnd - (liabilitiesEnd + equityEnd);
const changeDiff = assetsChange - (liabilitiesChange + equityChange);

// 一致していればチェックマーク、不一致なら差額を表示

テストコードの作成

Vitestを使用してテストコードを作成した。

テスト仕様

// test/cashflow.test.js

describe('キャッシュフロー精算表', () => {
  test('各行の合計が0になること', () => {
    // 増減 + CF調整 + 現金振替 = 0
  });

  test('CF計算書の差異が0になること', () => {
    // 現金振替合計 - CF計算書の増減 = 0
  });

  test('期首・期末・増減の貸借が一致すること', () => {
    // 資産 = 負債 + 純資産
  });

  test('仕訳から残高を正しく計算できること', () => {
    // 借方合計 = 貸方合計
  });
});

テスト実行結果

$ pnpm test

 test 1: 資産科目の増減計算
 test 2: 負債科目の増減計算
 test 3: CF精算表の計算チェック
 test 4: CF計算書への転記
 test 5: 貸借一致チェック
 test 6: 期首・期末・増減の貸借一致
 test 7: 仕訳から残高を計算

全7テスト合格

ファイル分離リファクタリング

当初は1つのindex.htmlに全てのコードを記述していたが、保守性向上のためファイルを分離した。

分離前

cashflow-viewer/
├── index.html (HTML + CSS + JS 全て含む)
└── data.json

分離後

cashflow-viewer/
├── index.html (HTMLテンプレートのみ)
├── styles.css (スタイル定義)
├── app.js (Vueアプリケーションロジック)
├── data.json (仕訳データ)
└── test/
    └── cashflow.test.js

削除した未使用コード

リファクタリング時に以下の未使用コードを削除した。

  • beginningBS 関連のロジック(前期末B/Sセクションを削除したため)
  • groupedBeginningBScategoryLabel
  • .bs-table 関連のCSSクラス
  • 仕訳の参照番号表記(<*1> など)

仕訳編集とリアルタイム反映

仕訳の金額を編集すると、精算表が自動的に再計算されるようにした。

実装のポイント

  1. triggerRef を使用してVueのリアクティビティを手動でトリガー
  2. サマリーデータではなく仕訳データから直接残高を計算
  3. P/L科目の残高を利益剰余金として集計
// 仕訳編集時の処理
function finishEdit(entry, field) {
  // 値を更新
  entry[field] = newValue;

  // リアクティビティをトリガー
  triggerRef(data);
}

Chrome DevToolsでの動作確認

Chrome DevTools MCPを使用して、仕訳の金額を12,000に変更した際の動作を確認した。

  • 仕訳の金額が12,000に更新された
  • 精算表の現金及び預金が13,800に変更された
  • 貸借一致チェックが緑色のチェックマークで表示された

UIの改善

レスポンシブ対応

  • PC幅: 仕訳パネルと精算表パネルを横並び表示
  • 狭い画面(900px以下): タブ切り替えUIで「仕訳」と「精算表」を切り替え

財務諸表セクション

CF計算書の下に以下を追加した。

  • 損益計算書(P/L): 売上高から当期純利益までの段階損益
  • 貸借対照表(B/S): 借方(資産)と貸方(負債・純資産)の2列表示
  • 株主資本等変動計算書(S/S): 資本金と利益剰余金の変動

スタイリング

  • 数字のフォントをメイリオに統一
  • 合計行の背景を薄いグレーに設定
  • 外枠を削除してすっきりした見た目に
  • カンマ区切りの数字入力に対応

今後の予定

  1. 他の事例(Q6-9など)への対応
  2. 共通コンポーネントの切り出し
  3. 依存関係を減らすリファクタリング
  4. コンテンツ用Excelの全事例をWebに表示

学んだこと

  • Excelの計算式をそのままコードに落とし込むと理解しやすい
  • 計算チェックをテストコードにすることで、ロジック変更時の検証が楽になる
  • Vueのリアクティビティは深いネストで動作しないことがあるので triggerRef が必要
  • フロントエンド先行で作ると、不要なロジックを早期に発見できる