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拡張は拡張管理画面でのリロードと対象ページのリロードの両方が必要になる。片方だけでは古いコードが残る。
chrome://extensions/で拡張の「更新」ボタンをクリック- 対象の会計ソフト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でようやく視線の流れが揃った。画面に要素を並べてみないと気づけないことがある。