家計簿アプリを MoneyForward ME(以下MF。家計簿の老舗アプリ)から Zaim(こちらも国産家計簿アプリ)に乗り換え中。MFには何年分もの取引履歴が溜まっているけど、これを引っ越さないとZaim側では「過去の支出傾向」が一切見えなくなる。家計簿の価値は積み上げの長さだから、ここで歴史を切ったら乗り換える意味が半減する。
放置すると次の問題が起きる:
なので Suica(交通系IC)の自動取込やクレカの自動連携を Zaim 側で本格稼働させる前に、まず歴史データの引っ越しを片付ける。健人 msg 5791「Suica前にMF移行」を踏襲。今回は設計と調査だけ、実装は別ジョブで起こす。
結論を3つに分けると:
1MF側の書き出し
MFの管理画面から月別CSV(カンマ区切りの表データ)ファイルを取得する。プレミアム会員(月額500円)の公式機能を使うのが本命。無料会員ならTampermonkey(ブラウザ拡張で画面の文字をJavaScriptで吸い出す道具)で代替可能。これがないと過去データの引っ越し元が確保できない。
2Zaim側の取込
既存の自作サービス claude-code-zaim(CloudflareのWorkers=小さなサーバ実行環境上で動くZaim連携)の /api/zaim/import-csv(CSVをまとめて受け取り内部で1行ずつZaimに登録するAPI)を流用する。重複防止・カテゴリ自動解決・エラー再開まで実装済みなので、ここを使えば新規開発を最小化できる。
3変換だけ新規実装
MFは10列のCSVを吐き、Zaim取込APIは13列のCSVを期待しているので、間に変換層を1枚挟む。具体的には mf-to-zaim-csv.ts(10列を13列に組み替えるスクリプト、新規)と mfFallbackMap.ts(MFカテゴリ名 → Zaimカテゴリ名の対応表、新規)の2ファイルを claude-code-zaim リポジトリに追加する。
| 案 | 仕組み | 会員要件 | 判定 |
|---|---|---|---|
| A. 公式CSVダウンロード | Web版「家計簿 → 入出金 → ダウンロード」から月別/年別CSVを取得 | プレミアム必須 | 採用(基本) |
| B. URL直叩きで一括DL | moneyforward.com/cf/csv?year=Y&month=M に月数だけ自動アクセスして連続取得 | プレミアム必須 | 採用(自動化版) |
| C. Tampermonkeyスクリプト | ブラウザ拡張で画面の表示内容をJavaScriptで読み取り、ローカル保存 | 無料会員でも可 | 代替案 |
| D. MF公式API | OAuth(外部サービスへの認可方式)でAPIに直接アクセス | — | 却下 |
MFには個人ユーザー向けの公開APIが事実上存在しない(D却下)。Aが本命だけど、MFは無料会員だと過去1年分しか閲覧できず、CSVダウンロード機能自体もプレミアム限定。1ヶ月だけプレミアム加入(500円)して全期間DL → 解約、が最安ルート。それも嫌なら C(Tampermonkey)で逃げる。Bは何年分もある場合に手で月切替するのが面倒なときの自動化版(健人が「12ヶ月分なら手でいいや」なら不要)。
| 案 | 仕組み | 判定 |
|---|---|---|
| α. Zaim公式CSVインポート | Web版「ファイル入出力 → 入力」で13列CSVをアップロード | 却下(2019/8/8でサポート終了) |
| β. Zaim API直叩き | /v2/home/money/payment(Zaimの支出登録窓口)に1件ずつOAuth署名付きで送信 | 既存実装あり(γの土台) |
γ. claude-code-zaim の /api/zaim/import-csv 流用 | 13列CSVをまとめて投げると、内部でβを1行ずつ実行+重複弾き+カテゴリ自動解決 | 採用 |
α は Zaim が2019年8月8日で公式サポートを打ち切ってる(機能は残っているけど絵文字混入で失敗、無保証)。β は1件ずつ手で送るには手数が多すぎる。γ は Suica の月次自動取込ですでに本番運用されているパスを丸ごと再利用できるから、新規開発は最小限で済む。
MF側10列CSVのヘッダ:
計算対象, 日付, 内容, 金額(円), 保有金融機関, 大項目, 中項目, メモ, 振替, ID
Zaim側13列CSV(既存 importCsvHandler.ts=取込APIの本体ファイル が期待する形式)のヘッダ:
日付, 方法, カテゴリ, カテゴリの内訳, 支払元, 入金先, 品目, メモ, お店, 通貨, 収入, 支出, 振替
変換ロジックの要点:
| Zaim カラム | 変換ルール |
|---|---|
| 日付 | MF 日付 をそのまま(YYYY-MM-DD へ正規化) |
| 方法 | MF 振替=1 なら「振替」、金額 が正なら「収入」、負なら「支出」 |
| カテゴリ | MF 大項目 を mfFallbackMap.ts でZaimカテゴリ名に変換 |
| カテゴリの内訳 | MF 中項目 を同マップでZaimジャンル名(カテゴリの下位区分)に変換 |
| 支払元 / 入金先 | 方法に応じてMF 保有金融機関 を片方だけ埋める |
| 品目 | MF 内容 をそのまま |
| メモ / お店 | メモはそのまま、お店はMF側に対応カラムがないので空 |
| 通貨 | 固定で JPY |
| 収入 / 支出 / 振替 | 方法に応じて1列だけ abs(金額) を入れる |
カテゴリ名は MF と Zaim で表記が微妙に違う(MF「交通費」⇔ Zaim「交通」、MF「水道光熱費」⇔ Zaim「水道・光熱」など)。なので既存の suicaFallbackMap.ts(Suica取込で使われている対応表ファイル)と同じ構造で mfFallbackMap.ts を新規追加し、HTTPヘッダ X-Import-Source: mf|suica でどちらの対応表を使うか切り替える設計にする。
マップに無いカテゴリは Zaim 側「その他」に自動フォールバック(既存ロジック)。警告ログとして残るので、後で見直して対応表に追加できる。
家計簿移行で一番怖いのが「同じ取引が二重に登録されること」。3段構えで守る。
カットオーバー(旧→新システムの切替日)を1つ決めて、時間軸で完全分離する:
D をスクショで記録D + 1日 に固定D 以前のみD + 1日 以降のみこれで日付が重なる行は構造的にゼロになる。日付が決まらないとレイヤー2/3 も意味が薄まるので、ここが本丸。
既存 importCsvHandler は、各取引の [日付, 方法, 金額, 品目, メモ, お店] から SHA-256(同じ入力から同じ短い文字列を作るハッシュ関数。指紋のようなもの)で識別キーを生成し、Cloudflareの簡易データベース KV(キー=値の高速ストア)に1年保存する。同じCSVを2度POSTしても、2度目は「すでに登録済み」として自動的にスキップされる。
レイヤー1で運用ミスがあって時間軸が重なっても、ここで救える保険。
MFには「集計から外す」フラグがあり、計算対象 列が 0 の行は MF 内部でも合計に入っていない。変換時にこれらをCSVから落とす。MFで意図的に外していたものをZaimに入れたら、移行で家計の数字がズレる。
D を確定するための準備)D。Zaim自動連携の開始日を確定するため必須scripts/mf-to-zaim-csv.ts(10列を13列に組み替えるNodeスクリプト、新規)を claude-code-zaim に追加。ローカルで実行できる変換層を作る目的src/lib/mfFallbackMap.ts(MF→Zaim カテゴリ対応表、新規)を追加。表記揺れを吸収する目的importCsvHandler.ts(取込APIの本体)を拡張: ヘッダ X-Import-Source: mf|suica で使う対応表を切り替える。Suica既存パスを壊さずMF対応を載せるためimported:<source>:<hash> 形式に拡張(Suica と MF で名前空間を分けるか統合するかは健人に確認したい論点、後述)importCsvHandler.test.ts(テスト本体)にMF由来CSVのテストケースを追加。本番投入前の品質保証curl(コマンドラインからHTTP通信を送る道具)で /api/zaim/import-csv に POSTimported(取込成功)/skipped(重複でスキップ)/errors(失敗)を確認D+1日 以降で起動健人に確認したい4点(実装ジョブ起票前にここを潰したい):
1. 健人はMFプレミアム会員?(未加入なら課金 / Tampermonkey 判断)
2. 移行対象期間は何年分?(5年想定だが実データの古さで件数が変わる)
3. 振替(口座間の資金移動)は移行対象に含める? 現状の importCsvHandler は振替を一律スキップする設計。MF時代の振替履歴を Zaim にも残したいなら処理拡張が必要。
4. 重複検知キーの名前空間を Suica と MF で分ける(誤検知ゼロだが二重登録リスクあり)か統合する(同じ取引が両系統で登録されたら自動弾きされるが、たまたま属性が完全一致する別取引も誤って弾く可能性)か。
普通なら「データ移行ツールを一から書く」となるところを、Suica取込のために作った importCsvHandler が以下の機能をすでに持っているから流用できる:
振替行の自動スキップつまり 新規実装は変換層1枚 + カテゴリ対応表1枚 で済む。差分の小ささが安全性の裏付けになる。
| 工程 | 時間 | 担当 |
|---|---|---|
| 事前準備 | 15分 | 健人 |
| 変換スクリプト実装+テスト | 2〜3時間 | 実装ジョブ(チバ) |
| カテゴリ対応表 初期作成 | 30分〜1時間 | 実装ジョブ + 健人レビュー |
| MF全期間DL(5年想定) | 20分 | 健人 or 自動化 |
| 変換 + Zaim投入 | 30分〜1時間 | 健人 + チバ |
| カテゴリ未知の手直し | 1〜2時間 | 健人 + チバ |
| # | 項目 | 状態 |
|---|---|---|
| 1 | MFデータ最終日時確認 | 健人手作業 — 移行直前に実施。手順は本ページに記載 |
| 2 | MF過去データエクスポート4案比較 | 完了 — 採用A、保険C |
| 3 | Zaimインポート方式調査 | 完了 — 採用γ |
| 4 | 重複防止戦略 | 完了 — 3層 |
| 5 | 移行手順整理 | 完了 |
| 6 | plan-viewer出力 | 本ページ |
| 7 | verification_summary | 完了(別途記録) |
次のアクション: 健人がこの設計をレビュー → OK判定 → 実装ジョブを別建てで起票(mf-to-zaim-csv.ts + mfFallbackMap.ts + importCsvHandler 拡張 + テスト追加)。