はじめに
連結精算表の計算ロジックには、追加取得・段階取得・持分法など複数のパターンがある。当初、各パターンごとにデータファイルを作成していたが、計算ロジック(特に buildAccountRows 関数)が5ファイルにコピペされている状態だった。
今回、この5パターンの差分を分析し、共通部分を抽出して buildWorksheetCore 関数として統一した。
5パターンの差分分析
対象パターン
| パターン | 持分比率 | 処理タイプ | 特徴 |
|---|---|---|---|
| 1 子会社株式の追加取得 | 70% → 80% | 連結 | 開始仕訳あり、単純合算あり |
| 2 段階取得(その他→連結) | 10% → 80% | 連結 | 開始仕訳なし、段階取得差益 |
| 3 段階取得(持分法→連結) | 30% → 80% | 連結 | 持分法修正 + 段階取得差益 |
| 4 関連会社の追加取得 | 30% → 40% | 持分法 | 単純合算なし |
| 5 段階取得→持分法 | 10% → 30% | 持分法 | 最もシンプル |
共通構造の発見
5パターンを並べて比較すると、計算フローには共通の構造があった。
個別F/S → [合算?] → [修正?] → [開始仕訳?] → 当期仕訳(1〜N本) → 連結精算表
パターンごとの違いは以下の4点に集約される。
- 合算するか否か — 連結は P社 + S社 を合算、持分法は P社のみ
- S社修正仕訳の金額 — 土地の時価評価額など
- 開始仕訳の有無と金額 — 2年目以降の連結で必要
- 当期仕訳の種類・本数・金額 — パターンごとに異なる
仕訳の「意味」(のれん償却なのか、持分法投資利益なのか)は計算結果に影響しない。影響するのは勘定科目コードと金額だけ。
buildCodeValues 関数の設計
引数: ColumnPipelineConfig
interface ColumnPipelineConfig {
companies: CompanyValues[] // 会社別の個別F/S数値
simpleAggregation: boolean // 単純合算を行うか
subAdjustments?: Record<string, number> // S社修正仕訳
openingAdjustments?: Record<string, number> // 開始仕訳
currentAdjustments: AdjustmentColumn[] // 当期仕訳(順序付き)
}
戻り値: CodeValuesResult
interface CodeValuesResult {
codeValues: Map<string, Record<string, number>> // 科目code → 列ID → 金額
colKeys: string[] // 列IDの配列(順序保持)
}
内部実装のポイント
実装で重要なのは runningTotal 変数。精算表の各列を左から右へ処理しながら、累積値を追跡する。
// 連結の場合: sub-total が累積の起点
let runningTotal = subTotal // = simpleTotal + subAdj
// 持分法の場合: P社の値がそのまま起点
let runningTotal = companies[0].values[code] ?? 0
開始仕訳があれば runningTotal を更新。
if (openingAdjustments) {
const ajeOpen = openingAdjustments[code] ?? 0
row['aje-open-adjusted'] = runningTotal + ajeOpen
runningTotal = runningTotal + ajeOpen
}
当期仕訳は runningTotal に加算せず、別途 currentTotal として集計。
let currentTotal = 0
for (const adj of currentAdjustments) {
const val = adj.values[code] ?? 0
row[adj.columnId] = val
currentTotal += val
}
row['aje-current-total'] = currentTotal
row['aje-total'] = runningTotal + currentTotal
createHelpers 関数
buildCodeValues が返した codeValues と colKeys を受け取り、精算表の行データ構築に必要なヘルパー関数群を生成する。
function createHelpers(
codeValues: Map<string, Record<string, number>>,
colKeys: string[],
): WorksheetHelpers
ヘルパー関数一覧
| 関数 | 用途 |
|---|---|
cv(code) | 指定codeの全列の値を取得 |
sumCols(codes) | 複数codeを列ごとに合算 |
linearCols(specs) | 加重和を列ごとに計算 |
addFs(vals, section) | F/S列を付加 |
negateCols(vals) | 全列の符号を反転 |
detail(section, code, name) | 明細行を生成 |
summary(section, name, vals) | 合計行を生成 |
subtotal(section, name, vals) | 小計行を生成 |
addRecords(...records) | 複数Recordを列ごとに加算 |
zeroVals | 全列が0のRecord |
t(vals) | vals['aje-total'] を取得 |
これらは5つの既存ファイルで100%同一のコードがコピペされていたもの。1箇所に集約できた。
列パイプラインの可視化
パターンによって生成される列が異なる。
パターン1(追加取得・開始仕訳あり)
P社 → S社 → 単純合算 → 合算 → S社修正 → 修正後 → 修正後 → 開始仕訳 → 開始計 → 調整後 → NCI損益 → のれん償却 → 追加取得 → 当期計 → 連結精算表
パターン5(持分法・最もシンプル)
P社 → 修正後 → 持分法適用 → 当期計 → 連結精算表
テストコードの作成
35テストを作成し、以下を検証。
各パターンのテスト項目
- 全codeのaje-totalが既存実装と一致 — 既存データセットの
accountRowsから値を抽出して比較 - 列キーの順序が正しい — 期待される列の配列と完全一致
- 全列でB/Sバランスが成立 — 資産合計 = 負債純資産合計
- 中間列の値が正しい — 土地の単純合算、S社修正後などスポットチェック
- のれん計算が正しい — 開始仕訳 + 償却の累積
横断テスト
const configs = [
{ name: 'パターン1', config: pattern1Config, dataset: additionalAcqDataset, ... },
{ name: 'パターン2', config: pattern2Config, dataset: stepAcquisitionDataset, ... },
// ...
]
it.each(configs)('$name: 全codeの全列が既存実装と完全一致', ({ config, dataset }) => {
const { codeValues, colKeys } = buildCodeValues(config)
// 検証ロジック
})
テスト結果
✓ パターン1 子会社株式の追加取得(70%→80%)
✓ 全codeのaje-totalが既存実装と一致
✓ 列キーの順序が正しい
✓ 全列でB/Sバランスが成立
✓ 中間列の値が既存実装と一致
✓ のれん計算が正しい
// ... 全35テストパス
インタラクティブなテストページ
/consolidated-worksheet/function-test にテストページを作成した。
機能
- 左サイドバーで5パターンを切り替え
- パターン概要(持分比率、処理タイプ、時価評価の有無など)を表示
- 入力:
currentAdjustments(当期仕訳)を借方・貸方に分解して表示 - 出力:
codeValues(精算表の全セル)をテーブル表示 - 検証: 全列のB/Sバランスチェック結果
- 列パイプラインの可視化
実装のポイント
<script setup lang="ts">
import { buildCodeValues, createHelpers } from '~/components/consolidated-worksheet/data/build-worksheet-core'
const result = computed(() => {
const preset = currentPreset.value
const { codeValues, colKeys } = buildCodeValues(preset.config)
const helpers = createHelpers(codeValues, colKeys)
// ...
})
</script>
その他の改善
個別財務諸表ヘッダーに持分割合行を追加
持分計算表で「持分割合が何%か」がわかるよう、ヘッダー行を追加した。
持分計算表ヘッダーの3行分割
従来は1行にすべての情報を詰め込んでいたが、以下の3行に分割。
- 時点ラベル(X1年3月31日など)
- イベントラベル(支配獲得、NI按分など)
- 持分割合(80%など)
まとめ
5パターンにコピペされていた計算ロジックを1つの関数に統一できた。
- buildCodeValues: 列パイプラインの値を計算
- createHelpers: 行データ構築用のヘルパー関数群
パターンごとの違いは ColumnPipelineConfig で吸収。仕訳の「意味」ではなく「勘定科目コードと金額」だけを見て計算するため、新しいパターンが追加されても同じ関数で対応できる。
ファイル構成
apps/web/app/components/consolidated-worksheet/data/
build-worksheet-core.ts # 共通関数
apps/web/tests/
build-worksheet-core.test.ts # 35テスト
apps/web/app/pages/consolidated-worksheet/
function-test.vue # インタラクティブテストページ
.claude/plans/
2026-02-03-build-worksheet-core-explained.md # 設計ドキュメント