連結会計シミュレーターの計算バグをテスト先行で直し、参考書の設例7つと照合した

開発mdx-playground

連結会計シミュレーターの計算バグをテスト先行で直し、参考書の設例7つと照合した

昨日の記事は、「経路別の実装をこのまま正とするか、状態モデルへ寄せ直すか。明日はここから始める」で終わっていた。 直し残しも2件あった。 円未満の端数がずれる F15 と、まだテストで確かめていない F18 である。

もう一つ残っていたのが、参考書との照合である。 テストは全部通っている。 ただ、紙面と照合済みの設例は、6月10日に済ませた設例4-1 の1つだけだった。

朝いちばんに決めた「上にかぶせる」

Claude Code に前日の計画書と実装の実物を突き合わせさせると、進捗の記録と実装は一致していた。 計画書どおり、最初にやるのは進め方の判断を1件下すことだった。

自分が選んだのは「上にかぶせる」案である。 前日に直した経路別の関数は計算の部品として残し、その上に「期首状態 → 取引 → 期末状態」を返す入口(executeCapitalTransition)を1つ作る。 仕訳、中間計算、持分計算表、精算表は、すべてこの入口につなぐ。 部品の中身を書き直すのは、状態を取り出せない箇所だけにした。 期待値を固定した既存のテストは実装の形に依存しないので、そのまま受入条件に使える。

F18 は落ちるテストから書いた

F18 は、80→10 のように連結から外れても投資が一部残るケースの誤りである。 投資の修正額を、全額まとめて売却益から控除していた。 資本連結の実務指針の設例に合わせると、売却益から控除するのは売った持分に対応する分だけで、残る持分の分は P/L を通さずに利益剰余金から取り崩す。

売却益から控除 = 4,000 × 70/80 = 3,500
残存持分の分   = 4,000 − 3,500 = 500(利益剰余金から直接取り崩す)

Claude Code には、直す前にテストを書かせた。 確かめたかったのは、テストが意図した理由で落ちることである。 3,500 を期待したところに 3,000、10,500 を期待したところに 12,000 が返り、テストは狙いどおりに落ちた。

修正では、投資の修正額の計算と按分を1つの関数に切り出し、仕訳と持分計算表の両方がそれを使う形にした。 持分計算表の連結除外損益は 3,500 になり、残存投資の取崩し △500 は別の行で示す。 期末の残高は変わらず、当期純利益が 57,000 から 57,500 になった。 全部売る 80→0 は按分せず、全額を売却分として扱う。

コードのコミットは1回では通らず、やり直しになった。 その途中で修正を一度外し、テストがバグを捕まえることを確かめてから、修正を戻してコミットした。

F15 の端数はどこでずれていたか

F15 は、前日から it.fails(落ちることを期待するテスト)のまま追跡していた不具合である。 まず、小数の単価でどの仕訳が何円ずれるかを、一時的な確認用テストで Claude Code に洗い出させた。 このファイルは、確認が済んだあとで消している。

調べてみると、ずれは小数の単価に限らなかった。 のれんの償却年数7年や、端数のあるS社の資本金のような整数の入力でも、連結から持分法へ移る一部売却などで、仕訳と精算表の貸借が 0.5〜2 円ずれていた。 原因は、同じ金額を経路ごとに別の式で丸めていたことにある。

直し方は、円に丸めるのを一度だけにして、片方を丸めたらもう片方は差額で出す形にそろえた。

  • 持分 × 単価(取得原価、売却対価、時価)は、一度だけ円にする
  • 売った分の原価は「元の原価 − 残る分の原価」で求める
  • S社純資産の按分は非支配株主持分を丸め、親会社持分は差額で出す
  • のれんは売却分を丸め、残存分は差額で出す

追加取得、支配が続く一部売却、連結からその他への経路に残っていた計算も、同じ関数へ寄せた。 先に仕訳を直してテストを回し、そのあとで持分法の表と精算表のP社個別残高に進む順にした。 仕訳、持分計算表、中間計算値、精算表のP社個別残高が同じ関数から金額を取るようになり、it.fails は0件になった。

状態モデルの入口と、拒否される入力

続いて、朝に決めた入口 executeCapitalTransition を作らせた。 のれんの補助関数は持分ベース(position-based)に切り替え、一部売却の経路と投資の修正額の関数にも手を入れた。 コミットの前に、拒否されるべき入力(償却年数の 0 と空欄)を入力欄に入れさせ、画面が壊れないことを確かめた。 対象の5ファイルと Vitest の全件も回した。

画面には「期首の状態 → 期末の取引 → 期末の状態」という処理の流れを出し、代表的なシナリオで E2E のテストも書かせた。 Claude Code が前に書いたテストには、タイトルに 40,000 と書いてあるのに本文は正しく 32,000 を期待しているものがあり、タイトルも直させた。

最後に、取引の二重計上を防ぐ不変条件のテストを足した。 取得と売却はP社の個別の試算表にだけ載り、連結修正の仕訳に現金預金は出ない。 個別上の簿価は「期首 ± 取引」で一度だけ動き、精算表のP社の列と一致する。 この2点を、5%刻みの全441組で確かめる。 画面の日付欄には、表示用で計算には使わないこと、取引はつねに期末として計算することを書き添えた。 ここまでを ec256edd でコミットした。

計画書の書き戻しで拾われた古い文

Phase 2〜4 の結果を計画書へ書き戻すコミットでは、Codex のコミットレビューが指摘を2件出した。 どちらも、更新した文のすぐ隣に古い文が残っていたものである。 §1 の段落とカバレッジの行を直して、3ddeda60 でコミットした。 残っていた medium の指摘(§0 の F12 の行が、nextState への引継ぎを未了のまま書いていた)も正しかったので、これも直した。

受入条件の6項目と、注記を外すまで

この時点の進捗は、計画書のチェック項目で47項目中39、約83%だった。 実装は済んでいて、残りは受入条件の確認とデプロイである。

続きを頼み、まず前日のセッションが作った issue ファイルをコミットさせた(3de463ba)。 ほかのセッションの未コミットのファイルには手を付けていない。 そのあと §8 の受入条件6項目に入った。 1項目めは、精算表が知らない勘定をどう扱うかの確認である。

§8 の確認の中で修正が1件入った。 精算表の非支配株主持分と資本剰余金を期首残高と当期変動に分け、転記できない勘定を黙って捨てないようにした。 画面には、期首 40,000 → 当期純利益 2,000 → その他変動 21,000 → 期末 63,000 と並んだ。

コード上では、公開ページの「数値を見直しています」の注記も外した。 本番は、この時点では注記つきで、修正前の数字のままである。 チェック項目は55項目中52まで進み、残りは本番デプロイ、書籍の設例の照合、計画書の Codex 再レビューの3つになった。

続きを頼み、push と計画書の Codex 再レビューを済ませさせた(再レビューは通ったが、実行中のデプロイに関わる指摘が1件出た)。 書籍の照合は、次のセッションに回すことになった。 引き継ぎプロンプトは、ダウンロードディレクトリにテキストで保存させた。

設例のページはスキャンの何番か

09:08 のセッションでは、引き継ぎプロンプトと計画書を Claude Code に読ませ、書籍スキャンの目次から設例4-4と4-5 のページを探させた。 目次のスキャンから、書籍のページ番号に18を足すとスキャンの通し番号になると分かった。

スキャンは Dropbox のフォルダにあり、ローカルではオンライン専用になっていて開けなかった。 そこで Chrome に接続させ、自分が開いているタブには触らずに、新しいタブで Dropbox の Web 版の該当フォルダを開かせた。 ページ画像はプレビューから取って読ませた。

081番のスキャンを開くと、出てきたのは6月10日に照合済みの設例4-1 の持分計算表の末尾と、次の節の導入だった。 設例4-2 は次のページから始まるので、082番に進んだ。

設例4-2から4-5 の照合結果

照合の結果、どの設例も事例データと紙面の差異は0件だった。

  • 設例4-4(30→40):個別の残高と損益、評価差額、のれんと償却、持分計算表の全10行×5列まで一致
  • 設例4-5(10→30):原則法と簡便法の両方で一致
  • 設例4-2(10→80)と設例4-3(30→80):事例データと一致

エンジンについては、設例4-4と4-5 を書籍値の前提で直接動かし、仕訳、科目別の最終残高、持分計算表を紙面と比べた。 設例4-2と4-3 は、整合性テストが通っていることで確認した。 前日の F13 の修正が前提にした解釈(投資利益は追加取得前の比率で計上する、期末に取得した分ののれんは取得年度に償却しない、原価法の期間の利益は P/L を通さず利益剰余金に加える)も、紙面どおりだった。 事例データ、エンジン、テストは、どれも変えていない。

金額の誤りではない気づきは5件あり、計画書の §9.1 に書き残した。 たとえば、整合性テストの「連結精算表の最終残高」が比べているのは、資産合計、負債純資産合計、当期純利益の3行だけだった。 S社株式や利益剰余金は、科目単位では比べていない。

スキャンが開けなかった原因

前は開けていたスキャンが開けなくなった件も、原因を調べさせた。 最初の切り分けでは、Dropbox のアプリが、オンライン専用ファイルを取りに行く役として Windows に接続していない状態だと分かった。 なぜ接続しないのかは、外からの調査では特定できなかった。

最終的な原因は、Dropbox の「同期を一時停止」だった。 一時停止中はオンライン専用ファイルの取得依頼にも応じないので、「プロバイダーが実行されていません」になる。 再開した時点で、開けるようになった。 「全部落ちてきて C ドライブを圧迫しないか」と心配だったので、容量を実数で測らせ、圧迫していないことを確かめた。

設例5-1①、5-2、5-3 と、テストから漏れていた 80→10

ローカルからスキャンを読めるようになったので、残りの設例は直接読ませた。 設例5-1①(80→70)、5-2(80→20)、5-3(80→10)の3つとも、事例データと紙面の差異は0件だった。 書籍値の前提で動かしたエンジンの最終残高も紙面と一致し、計画書の対応表に載せた設例は、これですべて照合済みになった。

ただ、ここで抜けが1つ見つかった。 80→10 は、整合性テストの「連結精算表の最終残高」の対象に入っていなかった。 事例データの import も無かった。 先に手で照合を済ませ、そのうえで自分の指示でテストの対象へ加えた。 対象6ファイルのテストは 904件から 907件になり、全件通った(54c5990b)。 テスト、計画書、Issue は push まで済ませ、master は origin と同じ状態になった(7コミット)。

計画書で未チェックだった本番デプロイは、自分がすでに済ませていた。

利益剰余金の期首残高に入る 1,500 と 670

気づいた点のうち1件は、自分の指示で issue に切り出した。 10→30 の 1,500 は、精算表では利益剰余金の期首残高に入る。 紙面の説明は、連結株主資本等変動計算書に当期の増加高として載せる扱いである。 80→10 の「残存投資の帳簿価額への修正」670 も同じで、精算表では期首残高に入り、紙面は当期の変動として扱う。 期末残高は、どちらも一致する。 今回の計画の範囲では直さず、残してある。 この件は、同じ日の10時25分からの Excel 化の作業 で、期首残高から当期変動の行へ移す形に直させた(まだコミットしていない)。

学び

  • 同じ金額を経路ごとに別の式で丸めると、整数の入力でも貸借が 0.5〜2 円ずれた。円に丸めるのは一度だけにして、片方を丸めたらもう片方は差額で出す
  • テストが全部通っていても、紙面との照合は別の作業だった。整合性テストが比べていたのは3行だけで、80→10 は対象にも入っていなかった
  • 落ちるテストを先に置いたので、修正を一度外したときにもテストがバグを捕まえるかを、その場で試せた
#連結会計#Vitest#テスト駆動#Codex#持分法