• #Chrome拡張機能
  • #クラウド会計
  • #Google Sheets API
  • #UIリファクタリング
  • #HTMLスクレイピング
開発misc-devメモ

Chrome拡張 会計サービス連携 - 帳表×年度マトリクスUIリファクタリング

前日から持ち越していた残高試算表エクスポートを朝一で片付けた後、ポップアップのUI構造を根本から組み替えた。3タブ構成を捨てて帳表×年度のマトリクスに統合し、推移表エクスポートを新規追加し、スプレッドシートの自動作成まで入れた。夜はautoResizeDimensionsのAPIバグと格闘して、Codexに相談して、3フェーズ分離を試して失敗して、最終的にデータ計算方式で落ち着いた。


残高試算表エクスポートの完了(前日からの積み残し)

前日に骨格を作っていたHTMLスクレイピング方式の残高試算表エクスポートを仕上げた。BS/PLテーブルのパースとインデント再現、合計行の背景色設定まで。これで仕訳帳・連携明細・残高試算表の3帳表がエクスポート可能になり、UI再構成に着手できる状態が整った。


3タブUIから事業者単位の統合ビューへ

旧構造の問題

連携明細・仕訳帳・残高試算表がそれぞれ独立タブになっていた。事業者Aの仕訳帳をエクスポートした後、連携明細タブに切り替えて、また事業者Aを探して選択する。同じ事業者の操作がタブをまたぐたびに途切れる。

新構造: 事業者カード内にマトリクス

事業者を主軸にし、各事業者カード内に帳表×年度のマトリクスを配置した。

事業者A
┌──────────┬──────┬──────┬──────┐
│          │ 2023 │ 2024 │ 2025 │
├──────────┼──────┼──────┼──────┤
│ 仕訳帳    │  ☑  │  ☑  │  ☑  │
│ 残高試算表 │  ☑  │  ☑  │  ☑  │
│ 連携明細   │      │      │      │
│  ├ 銀行A  │  ☑  │  ☑  │  ☑  │
│  └ カードB │  ☑  │  ☑  │  ☑  │
└──────────┴──────┴──────┴──────┘

1列目が帳表名、列が年度、各セルがチェックボックス。連携明細はサービス(銀行口座やカード)ごとのサブ行としてインデント付きで表示する。仕訳帳と残高試算表は事業者に対して1行ずつ。

列ヘッダーのクリックで年度単位の全選択/解除、行ヘッダーのクリックで帳表単位の全選択/解除ができる。


推移表エクスポートの新規追加

HTMLスクレイピングでBS/PL年次推移を取得

会計サービスの推移表ページは、1回のHTMLレスポンスに全年度のBS/PL推移データが含まれている。年度ごとにfetchを分ける必要がない代わりに、テーブル構造が残高試算表より複雑で、列ヘッダーが年度、行が勘定科目のクロス集計テーブルになっている。

formatTransitionRows で各行を { 科目名, 2021年, 2022年, ..., 2025年 } のフラット構造に変換し、スプレッドシートに書き込む。BS推移とPL推移は別シートに分離。

重複実行防止

推移表は1回のfetchで全年度データが取れるため、年度ごとに実行すると同一データを何度もエクスポートしてしまう。事業者×推移表タイプ(BS/PL)の組み合わせで実行済みフラグを持ち、2回目以降はスキップするロジックを追加した。

マトリクスから独立したセクション

推移表は年度選択と無関係(常に全年度を含む)なので、マトリクスの中には入れず独立セクションとして配置した。「補助科目あり」「補助科目なし」の2バリアントをチェックボックスで選択する形。


スプレッドシート自動作成

URL未設定時の自動生成

これまではスプレッドシートURLを手動で設定画面に貼る必要があった。URLが未設定の帳表をエクスポートしようとしたとき、Google Sheets APIで新規スプレッドシートを自動作成し、URLをストレージに保存する機能を追加した。

drive.file スコープの追加が必要で、manifest.jsonの oauth2.scopes に追記。このスコープは「アプリが作成したファイルのみ操作可能」という制限付きなので、ユーザーの既存Driveファイルには触れない。

新規SS作成時の「シート1」削除

Google Sheets APIで新規作成すると「シート1」というデフォルトシートが付いてくる。エクスポート時に帳表ごとのシートを作成するので、この空シートが残ると邪魔になる。新規作成直後に限り、デフォルトシートを削除する処理を入れた。既存SSに対しては削除しない(ユーザーが自分で作ったシートを消してしまう危険がある)。

設定UIの自動リフレッシュ

スプレッドシートを自動作成した後、設定画面のURL欄が空のままだと混乱する。エクスポート完了後に renderEntityTable() を再呼び出しして、新しく生成されたURLを設定画面に反映するようにした。


URL設定済み事業者の優先ソート

事業者一覧で、スプレッドシートURL設定済みの事業者を上に表示するようソートを追加。URL未設定の事業者はグレーアウトして下部に配置。実際に使う事業者がリスト上部に集まるので、毎回スクロールして探す手間が消えた。


autoResizeDimensionsのAPIバグとの格闘

症状

Google Sheets APIの autoResizeDimensions リクエストで列幅を自動調整すると、太字テキストや背景色付きセルの幅が実際のレンダリング幅より狭く計算される。文字が途中で切れる列が頻出した。

Codexに相談 → 3フェーズ分離を提案される

Codexに状況を伝えたところ、「データ書き込み → 書式設定 → autoResize」の3フェーズに分離する案を提示された。書式適用後にautoResizeを走らせれば、太字分の幅も計算に含まれるはず、という理屈。

3フェーズに分離して、各フェーズ間にAPIリクエストを分けて順次実行した。結果は変わらず。太字セルの列幅がやはり足りない。Google Sheets APIのautoResizeは、サーバーサイドで幅を計算するため、クライアント側のレンダリングエンジンとフォントメトリクスが一致しない既知の問題だった。

calcColumnWidthsによるデータ計算方式

autoResizeを諦めて、エクスポートデータから列幅を自前で計算する方式に切り替えた。各列の最大文字数を走査し、文字数×係数でピクセル幅を算出する calcColumnWidths 関数を実装。日本語(全角)と英数字(半角)で係数を分け、太字行にはさらに1.1倍の補正を入れた。

APIの自動調整より若干大きめの幅になるが、文字切れは完全に解消した。


Codexレビューで致命的な指摘2件

エクスポート機能がひととおり動いたところでCodexにコードレビューを依頼。致命的な指摘が2件出た。

推移表のみ選択時にタスク0件

マトリクスのチェックボックスを何も選ばず、推移表セクションだけチェックした場合、buildEntityExportTasks がタスク0件を返す。推移表はマトリクス外の独立セクションなのに、タスク生成ロジックがマトリクスのチェック状態だけを見ていた。推移表の選択状態を別途チェックしてタスクに含めるように修正。

列ヘッダークリック時のsyncAllColCb未呼出

年度列ヘッダーをクリックして全選択/解除した後、syncAllColCb(全選択チェックボックスの状態を同期するコールバック)が呼ばれていなかった。列の全チェックボックスをONにしても、ヘッダー行の「全選択」チェックボックスが未チェックのまま残る。個別チェックボックスの変更イベントハンドラには syncAllColCb が入っていたが、列ヘッダーからの一括変更パスでは呼び忘れていた。


積み残し

  • 残高試算表の整合性チェック(合計行の検証ロジック)は翌日に持ち越し

振り返り

一日で3タブ構成を解体してマトリクスUIに組み替え、推移表エクスポートを一から追加し、スプレッドシート自動作成まで入れた。午前中は構造変更でDOM操作のコードを大量に書き直し、午後は推移表のHTMLパーサーを組み、夜はautoResizeのバグで手が止まった。

autoResizeDimensionsの件は、APIドキュメントに「サーバーサイドで計算」と明記されていたのを見落としていた。3フェーズ分離を試す前にドキュメントを読み直していれば、1時間は節約できた。「動かないとき、まずドキュメントに戻る」という基本を身体が忘れていた。

Codexレビューの2件はどちらも「別パスからの操作で同じ結果が得られるか」のテスト漏れ。推移表のみ選択、列ヘッダーからの一括操作。メインの操作パスが動くと安心してしまい、代替パスの検証を飛ばす癖が出ている。