組織ごと・月別の課金情報(契約プランに基づく自動計算値と、手動上書き値)を 履歴レコードとして残し、システム管理画面から確認・再計算・編集できるようにする機能。
課金履歴は、組織1つ・年月1つにつき1レコードを持つ課金記録である。これまで「定義」のみだった 契約プランと、生成数の SoT である 生成ログを突き合わせ、 実際の請求額を算出・保存する最初の機能である。
各レコードは、計算時点のプラン内容のスナップショット(プラン名・月額・上限・超過課金)と、 計算対象となった画像生成数を保持する。プランが後から変更・削除されても、レコードは凍結された値を持ち続ける(履歴性)。 さらに、初月の日割りなど例外的な調整に備え、すべての計算量に手動入力用フィールドを持ち、 値が入っていれば自動計算値より優先される(NULL のときは自動計算値を使用)。
後払い(超過分は前月実績)モデル
(Y年 M月) のレコードは、
M月分の基本月額と、M-1月の生成実績に対する超過課金を合算する。
したがって「計算対象画像生成数」は前月(M-1)の生成数を指す。
詳しくは 用語: 課金履歴。
BillingRecord(prisma/schema/billing-record.prisma / テーブル billing_records)。
論理削除(deletedAt)運用のため DB のユニーク制約は設けない(削除済み行とのタプル衝突を避けるため)。組織×年月の一意性はアプリ側で担保する。
| フィールド | 型 | 区分 | 説明 |
|---|---|---|---|
id | String | — | cuid(2) |
organizationId | String | — | 組織 |
contractPlanId | String? | スナップショット | 計算時のプランID(未割当なら null) |
contractPlanName | String? | スナップショット | プラン名 |
year | Int | — | 対象年(基本月額の月) |
month | Int | — | 対象月 1-12(基本月額の月) |
amount | Int | 永続化 | 課金合計額(実効値ベース。生成・再計算・編集の各経路で更新) |
monthlyCharge | Int | 自動(プラン) | 課金額/月 |
monthlyGenerationQuota1〜3 | Int | 自動(プラン) | 月間生成数上限(区分1〜3) |
extraGenerationCharge1〜3 | Int | 自動(プラン) | 超過生成分課金額(区分1〜3) |
targetGenerationCount1〜3 | Int | 自動(実測) | 計算対象画像生成数(前月実績・区分1〜3) |
manualMonthlyCharge | Int? | 手動 | 課金額/月(NULL=自動値) |
manualMonthlyGenerationQuota1〜3 | Int? | 手動 | 月間生成数上限(NULL=自動値) |
manualExtraGenerationCharge1〜3 | Int? | 手動 | 超過生成分課金額(NULL=自動値) |
manualTargetGenerationCount1〜3 | Int? | 手動 | 計算対象画像生成数(NULL=実測値) |
note | String? | — | 備考(手動入力で更新する場合は必須) |
createdAt / updatedAt / deletedAt | DateTime | — | 既存規約に倣う(deletedAt でソフトデリート) |
凡例:緑=自動計算値
橙=手動上書き(NULL のとき自動値を使用)。
実効値はすべて 手動 ?? 自動 で解決する。
レコード (Y年 M月) の課金合計額:
計算対象画像生成数k = 区分k の生成ログ件数(createdAt が 前月 M-1 に入るもの)
超過生成数k = max(0, 実効(計算対象生成数k) − 実効(上限k))
区分k 超過課金額 = 超過生成数k × 実効(超過課金k)
課金合計額 = 実効(月額) + Σ(区分1〜3 超過課金額) (k = 1, 2, 3)
reduce の catch-all(区分2/3 判定の else)で区分1に計上する。DateTime を使用)。
現状、集計・amount 算出のロジックは一括生成 API(POST /api/billing-records/generate)と再計算アクションに重複実装されている(課題: 重複の共有関数化)。/billing-records — 一覧画面
/billing-records/[billingRecordId] — 詳細画面
contractPlanId から自動値を取り直し、実測生成数を再集計。
手動入力フィールドはすべて破棄(NULL リセット)される(実行前に確認ダイアログ)。/billing-records/[billingRecordId]/edit — 編集画面
手動入力用フィールド(課金額・上限1〜3・超過課金1〜3・計算対象生成数1〜3)を編集。空欄=自動値を使用。
| 項目 | 本機能(初回セッション) |
|---|---|
| DBスキーマ(BillingRecord) | ✅ |
| 一覧/詳細/編集画面 | ✅ |
| 課金計算ロジック(共有関数)・再計算アクション | ✅ |
レコードの一括生成 API Route(POST /api/billing-records/generate) | ✅ 追加(対象月の未生成組織にレコードを一括作成) |
| 登録カードへの実際の課金(請求)実行 | ❌ 未実装(決済機能参照) |
※ 一括生成 API Route(POST /api/billing-records/generate):リクエストボディで year/month(省略時は現在の Asia/Tokyo の年月)・organizationIds(省略時は対象組織全件)を受け取り、
対象月のレコードが未生成の組織に対してのみレコードを作成する。プラン未割当の組織はスキップ。外部スケジューラからの定期実行を想定。
対象組織の条件(organizationIds 省略時):
① activatedAt < 対象月初日(対象月開始前に有効化済み)、かつ
② deactivatedAt IS NULL または deactivatedAt > 対象月初日(対象月初日時点で解約済みでない)。
対象月中に解約した組織(deactivatedAt が対象月内)は含まれる。