• #Chrome拡張機能
  • #クラウド会計
  • #自動化
  • #バグ修正
  • #UIデザイン
  • #Claude Code
開発tax-assistant

Chrome拡張で会計ソフトの次年度繰越を一括自動化した話

毎年3月決算後、会計ソフトAで「次年度繰り越し」ボタンを何度もクリックして年度を進める作業がある。顧問先が増えるほど、同じ画面を開いて同じボタンを押す回数が積み上がる。Chrome拡張にボタンを1つ追加して、複数年度分の繰越を一発で走らせる機能を実装した。完成までに無限ループバグを踏み、UIを3回作り直し、Codexレビューを3回受けた。


Chrome DevTools MCPでフォーム構造を解析

まず会計ソフトAの繰越画面をChrome DevTools MCPで開き、フォームのDOM構造を調べた。実際にボタンをクリックしてNetworkタブを眺めると、内部APIのエンドポイントが見えてくる。フォームのhidden fieldにCTI(事業者ID)とTID(年度ID)が埋まっており、POSTリクエストで繰越処理が走る仕組みだった。

APIの構造が掴めたので、bridge.jsにAPI呼び出し関数を追加する方針を決めた。


3ファイル構成の実装

実装は3つのファイルに分かれた。

  • bridge.js: 繰越API呼び出し関数を追加。CSRFトークン取得、POSTリクエスト送信、レスポンスのパースまでを1関数に閉じ込めた
  • import.js: オーケストレーション層。現在の年度を取得→繰越API呼び出し→次年度の確認→ループ、という流れを制御する
  • content.js: UIボタンの描画とクリックイベントのハンドリング

bridge.jsで薄いAPI関数を作り、import.jsがそれを呼んでループを回し、content.jsがUIを被せる。関心の分離がはっきりしている構成にした。


Codexレビュー3回の反映

実装のたびにCodexにレビューを投げた。3回で指摘された内容はそれぞれ異なる。

第1回: CTI/TID整合性

繰越後に返ってくるレスポンスのCTIとTIDが、リクエスト時の値と一致しているか検証していなかった。別の事業者のデータを誤って繰り越すリスクがある。レスポンスのCTI/TIDをリクエスト時の値と突き合わせるバリデーションを追加。

第2回: 成否判定の厳密化

繰越APIのレスポンスがHTTP 200を返しても、body内のステータスフィールドが"error"になっているケースがある。HTTPステータスだけでなく、bodyの中身まで見て成否を判定するよう修正した。

第3回: 安全弁の上限到達時エラー表示

無限ループ防止のために繰越回数に上限(10回)を設けていたが、上限に達したとき黙って止まるだけだった。ユーザーには「なぜ止まったのか」が伝わらない。上限到達時にエラーメッセージをUIに表示するよう修正した。


致命的バグ: 繰越フォームが常に表示される罠

症状: 2033年度まで繰越が走った

テスト実行したら、繰越処理が止まらない。ログを眺めていると年度が2027、2028...と進んでいき、2033年度まで事業年度が作成されてしまった。

原因の特定

当初の終了条件は「繰越フォームが画面上に存在しなくなったら停止」だった。会計ソフトAの画面には「次年度へ繰り越す」ボタンが表示されており、最新年度まで進めばフォームが消えると想定していた。

しかし実際には、会計ソフトAは最新年度であっても繰越フォームを常に表示する。まだ存在しない未来の年度へも繰越できてしまう仕様だった。フォームの有無をチェックするnoForm条件だけでは永遠に終わらない。

修正: 実世界の年を基準にする

終了条件を「現在年度の終了年 >= 実世界の年(2026)」に変更した。事業年度の終了日が2026年以降であれば、それ以上先に進める必要はない。

// Before: フォームの存在チェック(無限ループの原因)
if (!document.querySelector('.carryover-form')) break;

// After: 実世界の年で打ち止め
const currentYear = new Date().getFullYear();
if (fiscalYearEnd >= currentYear) break;

2033年度まで作られてしまったテストデータは手動で削除した。この修正でCodexの第3回レビュー(安全弁)の指摘とも噛み合った。上限回数の安全弁と、年度ベースの終了条件の二重チェックで無限ループを防止する。


UIデザインの変遷: v1 → v2 → v3

繰越ボタンの配置とデザインを3回作り直した。

v1: 設定タブ下部に独立セクション

最初は設定タブの下に「繰越処理」セクションを独立して配置した。動作はするが、事業者との関連が視覚的に切れている。どの事業者の繰越なのかが一目でわからない。

v2: 事業者カード右側にボタン配置

「現在」バッジが付いている事業者カードの右側にボタンを置いた。事業者との紐付きが明確になったが、ボタンが「現在」バッジと近すぎて視線が散る。

v3: 最終形

ボタン → 事業者番号 → 「現在」バッジの順に左から並べた。ボタンのテキストは「繰越処理を実行」、色はオレンジ(#e65100)。注意を引く色で、かつ「削除」の赤とは区別がつく。この配置でボタンの目的と対象が一目で伝わるようになった。


Chrome拡張リロード手順の学び

開発中、コードを修正してもブラウザ上の動作が変わらず、何度か首を傾げた。Chrome拡張は拡張管理画面でのリロード対象ページのリロードの両方が必要になる。片方だけでは古いコードが残る。

  1. chrome://extensions/ で拡張の「更新」ボタンをクリック
  2. 対象の会計ソフトAのページをリロード

この2ステップを忘れると、修正したはずのコードが反映されず原因調査の時間を浪費する。


テスト項目のマークダウン化と検証

実装完了後、テスト項目をマークダウンでリスト化した。

  • 繰越が1年度分だけ正しく実行されること
  • 複数年度(例: 2023→2024→2025→2026)を一括で繰越できること
  • 現在年度(2026)に到達したら自動停止すること
  • 上限回数(10回)に達したらエラーメッセージが表示されること
  • CTI/TIDの不整合時にエラーで停止すること
  • HTTP 200だがbodyがエラーの場合に検知できること

各項目をChrome DevTools MCPで実際の画面を操作しながら検証し、すべてパスした。


振り返り

一番手が止まったのは無限ループバグの原因特定だった。「フォームが消えたら終了」という前提が崩れた瞬間、ログに流れる2027、2028...の数字を見て背筋が凍った。外部サービスのUI仕様を前提条件にすると、仕様が想定と異なるだけで暴走する。終了条件は外部UIの状態ではなく、自分でコントロールできる値(実世界の年)に基づかせるべきだった。

UIデザインを3回やり直したのも収穫がある。v1で「動くけど使いにくい」を体感し、v2で「近すぎる」を体感し、v3でようやく視線の流れが揃った。画面に要素を並べてみないと気づけないことがある。