残高の理由を後から説明できるように、複式の記録を残します。
重要なコードの抜粋へ ↓このページでわかること
- 仕訳と仕訳行
- 借方・貸方が釣り合う理由
- 追記専用の記録
一つの取引を二つの側から記録する
A口座からB口座へ1万円を送ると、A側は1万円減り、B側は1万円増えます。v1では顧客預金を銀行の負債として扱い、Aの預金負債を借方、Bの預金負債を貸方に同額記録します。
借方の合計と貸方の合計が一致することが、記録が片寄っていないか確かめる基本です。
仕訳が残高の根拠になる
ledger_accounts は勘定科目、journal_entries は一組の仕訳、journal_lines はその明細です。口座の表示用残高は便利ですが、帳簿から再計算できることが重要です。
負債の預金口座では貸方の合計から借方の合計を引きます。別の種類の勘定には別の計算が必要です。
過去を消さず、訂正を積む
確定した仕訳は書き換えず、誤りがあれば打ち消す仕訳を追加します。これなら、過去の操作と修正の両方がたどれます。銀行らしい仕組みは、残高の見た目よりも説明可能な履歴に現れます。
表示残高と帳簿を照合する
毎回全仕訳を集計すると重くなるため、画面には cached_balance を使えます。ただしキャッシュは根拠ではありません。帳簿から再計算でき、差があれば検知できることが条件です。
さらに、借方・貸方が一致しても、誤った相手の勘定に記録すれば業務上は間違いです。金額の釣り合いと、取引の意味の正しさを別々に確かめます。
仕訳の一組を追う
AからBへ1万円送るとき、Aの顧客預金負債に借方1万円、Bの顧客預金負債に貸方1万円を作ります。Aの残高は1万円減り、Bは1万円増えます。仕訳には取引と結びつくIDがあるので、履歴の一件から帳簿の二行をたどれます。
二行の額が同じでも、口座IDを取り違えれば正しい送金ではありません。金額の整合性と、相手先の正しさを両方検査します。
残高計算の符号を取り違えない
顧客預金は銀行から見れば負債なので、貸方の増加から借方の減少を計算します。System Fundingのように借方が通常側の勘定には、同じ式をそのまま使えません。勘定科目に「通常どちら側で増えるか」という属性が必要です。
この区別を誤ると、借方と貸方の合計が一致していても、表示残高の符号だけ逆になることがあります。
追記専用にも検証が要る
確定した仕訳を後から編集できないようにしても、最初に間違った仕訳を確定すれば問題は残ります。記帳前の権限確認、金額検証、仕訳の釣り合い、元取引との対応を同じ処理の中で確かめます。
間違いが分かった後は反対仕訳で直し、元の仕訳と訂正を両方残します。記録が消えないことが、原因の説明を可能にします。
照合は別の検査
一件の仕訳で借方と貸方が合うこと、全口座の表示用残高が帳簿と合うこと、完了した送金に仕訳があることは別々の条件です。一つのチェックだけで「全部正しい」とは言えません。
v1には整合性を確認する仕組みがあり、管理側で結果を見られます。帳簿を作ることと、帳簿を継続して信用できる状態に保つことを分けて考えます。
帳簿の保存と読み出し
仕訳を追記専用にすると、過去の残高を再現できます。その一方、期間や口座で大量の仕訳を読むときは検索しやすい索引や表示単位が必要です。正しさを守る保存方法と、利用者が履歴を読める速さの両方を設計します。
最初のお金にも相手がいる
学習用口座に最初の1万円を入れるときも、預金負債の貸方だけでは帳簿が釣り合いません。System Fundingなどの相手勘定を借方にして、同額を記録します。データを準備するための入金だからといって、残高だけを直接変えると、その後の照合で差が出ます。
この例は、帳簿の約束が「通常の送金だけ」に適用されるものではないと示します。
重要なコードを読む
v1.0.0 の実コードから、この章で押さえたい箇所だけを抜粋しました。前後の定義は省略しています。
借方と貸方の合計を一致させる
SQL / PLpgSQLselect coalesce(sum(debit_amount), 0), coalesce(sum(credit_amount), 0)
into v_debit_total, v_credit_total
from ledger.journal_lines where journal_entry_id = v_entry_id;
if v_debit_total <> v_credit_total then
raise exception 'journal_entry is not balanced (debit % <> credit %)', v_debit_total, v_credit_total
using errcode = 'LG002';
end if;まず理解する一つの仕訳に属する行を集計し、借方と貸方が同額でなければエラーにします。片側だけの記帳を成立させません。
一歩深く関数内のチェックに加え、v1にはコミット時に再検査する制約トリガーもあります。関数以外の書き込み経路が現れても、データベースの最終境界で釣り合いを守るためです。
出典:supabase/migrations/20260819155223_create_post_journal_entry_function.sql