• #Chrome拡張機能
  • #クラウド会計
  • #自動仕訳ルール
  • #エクスポート
  • #インポート
  • #同期
  • #内部API
開発claude-code-toolsメモ

自動仕訳ルールのエクスポート・インポート・同期機能を実装した記録

Chrome DevToolsで会計ソフトAの内部APIを3本掘り当て、自動仕訳ルールのエクスポート・インポート・同期機能を6ファイルにわたって組み上げた。UIを2回作り直し、CTIクロスコンタミネーションというバグを踏み抜き、Codexレビューを3回回して着地させるまでの一日。


Chrome DevToolsでの内部API調査

前日の調査で公開APIに自動仕訳ルールのエンドポイントが存在しないことは確認済みだった。今日はChrome DevToolsのNetworkタブに張り付いて、実際に画面を操作しながら内部APIを捕まえにいった。

掘り当てた3つのエンドポイント

  • journal_rules: ルール一覧の取得。ページネーション付きでルールID・条件・仕訳内容がJSONで返る
  • attribute_options: 勘定科目・補助科目・税区分などのマスタデータ。ルールが参照するIDを名前に変換するために必要だった
  • Search API: POST /api/v1/office_journal_rules/search で条件付き検索。後の同期機能でルールのマッチングに使うことになる

Search APIへのPOSTで最初に422エラーが返ってきた。リクエストヘッダを見比べて、X-CSRF-Tokenヘッダが必須だと突き止めた。CSRFトークンはHTMLのmetaタグから取得する方式に落ち着いた。


エクスポート機能の実装

6ファイルの変更

エクスポート機能は以下の6ファイルに手を入れた。

ファイル役割
lib.jsAPI呼び出し・データ変換のユーティリティ
bridge.jsChrome拡張のバックグラウンドとコンテンツスクリプトの通信橋渡し
export.jsスプレッドシートへの書き出しロジック
import.jsスプレッドシートからの読み込みロジック
content.jsDOM操作・UI制御
manifest.jsonパーミッション追加

UIの試行錯誤:独立タブからチェックボックスへ

最初は「ルール管理」という独立タブを設けて、エクスポート・インポートのボタンを並べた。動くものはできたが、実際に触ってみると操作の流れが途切れる。仕訳データをエクスポートするついでにルールも一緒に出したい場面がほとんどだった。

ユーザーフィードバックを受けて方針を変えた。既存のエクスポート画面に「自動仕訳ルールも含める」チェックボックスを1つ追加する形に統合した。コード量は減り、操作ステップも1つ減った。

スプレッドシートの書式設計

エクスポート先のスプレッドシートは、視認性を上げるために3つの工夫を入れた。

  1. 3エリアの縦ボーダー区切り: ルール条件 / 仕訳設定 / 借方貸方の3ブロックを縦ボーダーで区切り、どの列がどのブロックに属するかを一目で判別できるようにした
  2. Material Designカラー: ヘッダー行にMaterial Designのカラーパレットを適用。ルール条件は青系、仕訳は緑系、貸借は紫系
  3. 金融機関ごとの色分けと濃淡: 銀行・クレジットカード・電子マネーなど金融機関の種別で背景色を変え、さらに同じ金融機関内でも口座ごとに濃淡を付けた。100行を超えるルール一覧でも、目的の口座のルールがどこにあるか色で追える

シート名は ルール_エクスポート とした。後述のインポート用シート ルール_インポート とヘッダー書式を共通化し、両シート間でコピー&ペーストしても列がずれない設計にした。


インポート機能の実装

インポートシートの自動作成

エクスポートを実行すると、同じスプレッドシートに ルール_インポート シートが自動で作成される。ヘッダー行はエクスポートシートと同一書式で、データ行は空。ユーザーはエクスポートシートから必要な行をコピーし、編集してからインポートを実行する。

インポートシートのURLは設定画面に自動保存される。次回以降はワンクリックでシートに飛べる。

事業者ごとの個別インポートボタン

当初は全事業者を一括でインポートするボタンを置いていたが、事業者Aのルールだけ更新したい場面のほうが多かった。一括ボタンを廃止し、各事業者の横に個別のインポートボタンを配置した。

ローカル保存ログ

インポート実行時、CSVデータをローカルにも保存するようにした。ファイル名は {SSタイトル}_rules_import_{YYYYMMDD_HHmmss}.csv の形式。スプレッドシートのデータを誤って消しても、ローカルにCSVが残っているので復元できる。


エラー判定バグの修正

インポート結果の成功/失敗判定で、会計ソフトAの画面構造に起因するバグを踏んだ。

会計ソフトAのインポート結果画面には alert-successalert-warningalert-danger の3つのdivが常に存在していて、hiddenクラスの付け外しで表示を切り替える仕様だった。最初の実装では「alertを含むdivを正規表現で探し、可視のものを判定する」というロジックにしていたが、3つ全てにマッチしてしまい、常に最初の alert-success を拾って成功と判定していた。

修正後は alert-successalert-warningalert-danger それぞれを個別にセレクタで取得し、hiddenクラスが付いていないものだけを結果として採用するようにした。


設定画面の改善

設定画面に保存されたスプレッドシートURLが、プレーンテキストのまま表示されていた。URLをコピーしてブラウザのアドレスバーに貼り付ける手間が毎回発生していた。

URLをアンカータグに変換し、クリックで直接スプレッドシートに遷移できるようにした。target="_blank" で新しいタブに開く。


ルール同期機能

設計:CSV + Search APIハイブリッド

エクスポート・インポートは手動操作だが、「会計ソフトA上のルールを最新のスプレッドシートと一致させたい」という要望があった。同期機能の設計は以下の方針で組んだ。

  1. スプレッドシートからルール一覧をCSVとして読み込む
  2. 会計ソフトAのSearch APIで既存ルールを取得する
  3. ルールIDでマッチングして、スプレッドシートにあって会計ソフトAにないルールは追加、会計ソフトAにあってスプレッドシートにないルールは削除候補としてリストアップ
  4. 差分削除は確認ダイアログを挟む(誤削除防止)

フォールバックルールの仕様

調査中に「他のルールにマッチしない場合」に適用されるフォールバックルールの存在を確認した。このルールにはルールIDが振られず、Search APIのレスポンスにも含まれない。同期処理ではフォールバックルールを除外するロジックを入れ、仕様をドキュメントとして残した。


CTIクロスコンタミネーションバグ

同期機能のテスト中に、事業者Aのルールが事業者Bのデータとして保存される現象が起きた。原因を追うと extractCti() 関数にたどり着いた。

CTI(Company/Tenant Identifier)はURLのパスから抽出する。しかし、同期処理が非同期で走る間にユーザーが別の事業者のページに遷移すると、extractCti()現在のURLからCTIを取得してしまう。結果として、事業者Aのデータが事業者BのCTIに紐付いて保存される——これがクロスコンタミネーションの正体だった。

修正は、同期処理の開始時にCTIをキャプチャして変数に保持し、以降の処理ではキャプチャ済みの値を使う方式に変えた。URLから毎回取得するのをやめたことで、ページ遷移の影響を受けなくなった。


Codexレビュー3回

実装の区切りごとにCodexレビューを回した。3回のレビューで受けた致命的指摘と対応は以下の通り。

指摘内容対応
1回目CSRFトークンの取得タイミングがリクエストと離れすぎており、トークン失効のリスクがあるトークン取得をリクエスト直前に移動
2回目CTI取得が非同期処理中にURLから再取得される設計になっている上述のCTIキャプチャ方式に修正
3回目差分削除で確認なしに削除が走る可能性がある確認ダイアログの追加と、削除対象のプレビュー表示を実装

2回目の指摘でCTIバグに気づけたのは大きかった。手動テストでは再現条件が限られるため、レビューなしでは本番で踏むまで気づかなかった可能性が高い。


振り返り

朝のAPI調査から始めて、エクスポート → インポート → 同期と段階的に機能を積み上げた。UIを2回作り直す回り道はあったが、結果的にチェックボックス1つに統合した現在のUIのほうが操作が自然に流れる。

CTIクロスコンタミネーションは、非同期処理で「今のURL」を都度参照する設計の危うさを身をもって体験した。状態は開始時にキャプチャして閉じ込める——関数型の原則がそのまま当てはまるバグだった。