開発tax-assistantアクティブ
クレカ明細への自動仕訳ルールマッチング機能の設計
クレジットカード明細に対して、マネーフォワードからエクスポートした仕訳ルールを自動適用し、勘定科目を提案する機能の設計。
概要
やりたいこと
- クレカ明細の「利用先」を仕訳ルールの「明細一致条件」と照合
- マッチしたルールの勘定科目を候補として表示
- ユーザーが確定すれば、その勘定科目で仕訳を生成
マッチタイプ
| タイプ | 説明 | 用途 |
|---|---|---|
| 完全一致 | 文字列が完全に一致 | 固定名称の店舗 |
| 部分一致 | パターンが含まれる | 店舗名の一部 |
| レーベンシュタイン | 編集距離で類似度判定 | OCRの誤認識対応 |
| 類似度 | 単語の重複率 | 語順が入れ替わるケース |
データフロー
[仕訳ルールCSV] → /import-shiwake-rules → [shiwake_rules テーブル]
↓
[クレカ明細] → 突き合わせチェック → [マッチング処理]
↓
[matched_rules JSON保存]
↓
[UI: 候補表示 → ユーザー確定]
↓
[仕訳生成・エクスポート]
DBスキーマ
creditcard_transactions(追加カラム)
ALTER TABLE creditcard_transactions ADD COLUMN matched_rules TEXT;
ALTER TABLE creditcard_transactions ADD COLUMN selected_rule_hash TEXT;
ALTER TABLE creditcard_transactions ADD COLUMN selected_rule_row INTEGER;
matched_rules JSON構造
[
{
"row_number": 15,
"rule_hash": "a1b2c3d4e5f6...",
"pattern": "スターバックス",
"match_type": "partial",
"similarity": 100,
"account": "会議費",
"sub_account": "",
"tax_type": "課税仕入10%",
"credit_account": "未払金",
"summary": "打合せ"
}
]
check_status の拡張
| ステータス | 説明 | 色 |
|---|---|---|
unchecked | 未チェック | グレー |
matched | レシート一致 | 緑 |
unmatched | レシート不一致 | 赤 |
rule_matched | ルールで候補あり | 青 |
rule_confirmed | ルール確定済み | 緑 |
private | 私的利用 | 紫 |
refund | 返金 | オレンジ |
マッチングロジック
def match_shiwake_rules(description: str, rules: list[dict]) -> list[dict]:
"""クレカ明細の説明文に対して仕訳ルールをマッチング"""
description_normalized = normalize_text(description)
matches = []
for rule in rules:
pattern = rule['pattern']
pattern_normalized = normalize_text(pattern)
match_type = rule.get('match_type', 'partial')
threshold = rule.get('threshold', 80)
# Phase 1 では正規表現は無効
if rule.get('regex_enabled') == '1':
continue
similarity = 0
if match_type == 'exact':
if description_normalized == pattern_normalized:
similarity = 100
elif match_type == 'partial':
if pattern_normalized in description_normalized:
similarity = 100
elif match_type == 'levenshtein':
similarity = calc_levenshtein_similarity(
description_normalized, pattern_normalized
)
elif match_type == 'token':
similarity = calc_token_similarity(
description_normalized, pattern_normalized
)
if similarity >= threshold:
matches.append({
'row_number': rule['row_number'],
'rule_hash': calc_rule_hash(rule),
'pattern': pattern,
'match_type': match_type,
'similarity': similarity,
'account': rule['account'],
'sub_account': rule.get('sub_account', ''),
'tax_type': rule.get('tax_type', ''),
'credit_account': rule.get('credit_account', ''),
'summary': rule.get('summary', '')
})
# 類似度の高い順にソート、重複排除
matches = sorted(matches, key=lambda x: -x['similarity'])
seen_hashes = set()
unique_matches = []
for m in matches:
if m['rule_hash'] not in seen_hashes:
seen_hashes.add(m['rule_hash'])
unique_matches.append(m)
return unique_matches
API設計
ルールマッチング実行
POST /api/creditcard/match-rules
Request: { client_id: string }
Response: {
total: number,
rule_matched: number,
unchanged: number
}
ルール確定
POST /api/creditcard/{id}/confirm-rule
Request: {
rule_hash: string,
rule_row: number
}
Response: { success: boolean }
UI設計
クレカ明細タブ
check_statusがrule_matchedの行は青背景- 「勘定科目」列に候補を表示
- 複数候補がある場合は「会議費 他2件」のように表示
- クリックでドロップダウンから選択
ルール確定フロー
- 候補をクリック → ドロップダウン表示
- ルールを選択 →
selected_rule_hash/selected_rule_rowを保存 check_statusをrule_confirmedに更新
Codexレビューで固めたポイント
- rule_hash: CSV更新時の整合性検証に使用(row_numberだけでは行がずれる)
- 正規表現はPhase 2: Phase 1では完全/部分/類似度マッチのみ
- ReDoS対策: 正規表現を使う場合はタイムアウト(100ms)を設定
- 履歴ポリシー: 再マッチ時は
selected_rule_*をクリアして再判定
TODO
-
/import-shiwake-rulesスラッシュコマンドの実装 - マッチングロジックの実装
- APIエンドポイントの追加
- フロントエンドUIの実装
- 正規表現対応(Phase 2)