• #マネーフォワード
  • #CSV
  • #仕訳
  • #Python
  • #エクスポート
  • #税区分
開発tax-assistantメモ

マネーフォワード仕訳CSVエクスポート機能の実装

仕訳ビューアの支出タブを開いて「これ全部MFに流し込みたい」と思った瞬間から、この機能の実装が始まった。クレカ明細、レシート単独、仮払金明細 -- 3種類のデータソースをマネーフォワードが受け付けるCSV形式に揃えて吐き出す。朝から取り掛かって、夕方にはボタン一発でCSVが落ちてくるところまで持っていった記録。

expense_builder.py の新規作成

支出データをMF仕訳形式に変換する expense_builder.py を新規作成した。

対象データは3種類:

  • 確定済みクレカ明細: receipt_confirmed / rule_confirmed / manual_confirmed の3ステータス
  • レシート単独: クレカ明細に紐付かないレシートデータ
  • 仮払金明細: 仮払金として計上済みの支出

各データソースごとにMFの仕訳フォーマット(日付、借方勘定科目、借方金額、貸方勘定科目、貸方金額、摘要、税区分など)に変換するロジックを組んだ。

receipt_confirmed で借方勘定科目が空になるバグ

最初にCSVを吐いてみたら、receipt_confirmed のクレカ明細で借方勘定科目が空欄になっていた。原因を追いかけると、matched_rules カラムがNULLになっている行があった。

receipt_confirmed はレシートとのマッチングで確定したデータなので、仕訳ルールのマッチングを経由していない。つまり matched_rules がNULLなのは正常な挙動だった。勘定科目はレシート側が持っている。

修正はレシートテーブルへのJOIN追加。レシートの勘定科目情報を引っ張ってきて、matched_rules がNULLの場合はレシート側の勘定科目をフォールバックさせた。

日付フォーマットの不一致でJOINが空振り

レシートJOINを追加しても、結合結果が0件になるケースが残った。ログを眺めていて気づいた -- クレカ明細側は 2026-03-03(ハイフン区切り)、レシート側は 2026/03/03(スラッシュ区切り)で格納されていた。

文字列としてのJOINなので、フォーマットが違えば一致しない。日付カラムを正規化してからJOINするように修正した。

APIエンドポイントとフロントエンド

バックエンドにEXPENSE DocType用のAPIエンドポイントを追加し、フロントエンドの仕訳ビューア支出タブに「MFエクスポート」ボタンを配置した。ボタンを押すとAPIを叩いてCSVをダウンロードする。

税区分の表記修正

MFにインポートしてみたら、税区分のバリデーションで弾かれた。原因は2つ。

全角括弧と半角括弧

税区分文字列に全角括弧 () が混入していた。MFが受け付けるのは半角 () のみ。文字列置換で全角を半角に統一した。

ハイフン入り税区分

一部の税区分にハイフンが含まれるパターンがあり、MFのマスタと表記が合っていなかった。MFのインポート仕様に合わせて表記を修正した。

貸方補助科目の修正

貸方補助科目に「未処理事項」がハードコードされていた箇所を、document_types テーブルのデフォルト値(三井住友カードなど、実際のカード名称)を参照するように修正した。

画像ファイルのサブフォルダ出力対応

CSVエクスポートに付随して、レシート画像ファイルをサブフォルダに振り分けて出力する機能も追加した。

/simplify レビューによるリファクタリング

一通り動くようになった後、/simplify でコードレビューを実施した。指摘に沿ってexpense_builder.py内の重複処理を関数に切り出し、条件分岐を整理した。

振り返り

朝の時点では「CSVを吐くだけ」と思っていたが、実際に手を動かすと日付フォーマット、税区分の全角半角、JOINの結合条件と、データの端っこに潜む不整合が次々と顔を出した。CSVを1行ずつMFに食わせてエラーメッセージを読み、修正してまた食わせる -- このサイクルを5回ほど回して、ようやく全行がインポートを通った。

「動くコード」と「外部システムが受け入れるコード」の間には、仕様書に書かれていない溝がある。今日はその溝を一つずつ埋めた一日だった。