仕訳の修正機能

登録済みの仕訳を直接書き換えるのではなく、「改訂」として扱う修正機能を追加した。

削除機能の時と同じ考え方で、過去のデータを消してしまうと後から追えなくなるので、旧仕訳は無効化して残し、修正後の内容は新しい仕訳として登録する。新旧はrevised_from_transaction_idで繋げる。

見た目は1件の修正でも、内部的には「旧版の無効化 + 新版の作成」になる。

修正できるのは、今のところ金額と勘定科目(実装上はsub_account_id)だけ。日付・摘要・取引先などはひとまず対象外で、旧取引の値をそのまま新版に引き継ぐ。

修正の単位もJournalEntry1行ではなくTransaction全体にした。借方だけ・貸方だけを部分更新できるようにすると、貸借一致や税区分の整合が簡単に壊れるため。

入口はTransactionRegistrarとは別にTransactionRevisorを新設。TransactionRegistrarに更新の責務まで混ぜたくなかったので、期首仕訳の時(OpeningEntryRegistrar)と同じ方針で専用サービスに切り出す。

Revisorがやること

  • 修正対象が修正可能な状態か判定
  • 入力のバリデーション
  • 旧取引をロックして再取得し、二重修正を防ぐ
  • TransactionRegistrarを使って改訂版を新規作成
  • 旧取引をdeactivate()
  • 全部を1つのDBトランザクションにまとめる

修正対象外にした取引

  • 期首仕訳
  • 予定取引
  • 決算整理仕訳
  • 定期取引計画由来の取引
  • クレジットカード取込由来の取引
  • 減価償却仕訳が紐づいている取引
  • 決算済み年度に属する取引

これらは発生源ごとに業務ルールが強いので、通常仕訳の修正と一緒くたにしない。必要になったら別APIで対応する。

並行修正のガードは二段構え。DBトランザクション内でlockForUpdate()して無効化済みなら弾くのに加えて、revised_from_transaction_idにunique制約を張って、万一すり抜けても2件目の改訂作成をDB側で失敗させる。

監査のために、旧版にはdeactivated_by(誰が)とdeactivation_reason(修正による改訂、固定文言)、新版にはcreated_by と revision_reason(ユーザー入力の修正理由)を記録する。

伝票番号(entry_number)は改訂版で新しく採番される。旧版の番号が引き継がれるわけではないので、修正のタイミング次第で日付順と番号順がずれることがあるが、採番ロジックに特例を作るコストの方が高いと判断して、初期実装ではこのまま。

削除機能と修正機能、どちらも「状態遷移+履歴保持」という同じ流儀で統一できた。