入金と出金は、どこに相手を持つのか
顧客の残高だけを見ると単純な足し引き。でも銀行側には、必ずもう一つの記録が必要です。架空の入出金を仕訳まで追います。
本文の説明・図・掲載コードだけで読み進められます。出典リンクは、権限のある人向け(非公開GitHub)です。GitHubで閲覧権限がある方だけ開けます。
この記事でわかること
- 入金では顧客預金を貸方、出金では借方に記録する理由
- SYSTEM_FUNDING と顧客口座をつなぐ、対になる二つの仕訳
- 同じ依頼の再送、残高不足、不整合を別々に扱う方法
この記事の目次
3万円の入金、8千円の出金を分けて見る
架空の顧客・葵さんの普通預金が2万円から始まるとします。管理側で3万円の入金を記録すれば5万円、続けて8千円の出金を記録すれば4万2千円になります。この数字は読み方を説明する例で、実際に入出金を行った結果ではありません。
ここで問いたいのは「残高をいくらに変えるか」だけではなく、「何が増えて、何が減ったからその残高になるか」です。顧客の預金は、銀行から見ると将来返す義務があるお金です。したがって、預金の増加は銀行の利益が増えたことを意味しません。現金に相当する資産と、顧客に返す負債が、同じ金額だけ増えます。
MiniBank のこの経路では入出金の相手を SYSTEM_FUNDING という勘定で表します。後の銀行会計の対応表では、これを「現金」1000番へまとめています。ただし、名前に現金があるからといってATMや実際の現金保管庫へ接続しているわけではありません。教育用の帳簿の出来事として読むのが正確です。
入金は「現金の増加」と「返す義務の増加」
3万円の入金では、SYSTEM_FUNDING に借方3万円、葵さんの顧客預金勘定に貸方3万円を置きます。借方・貸方は、良い・悪い、入る・出るを単純に表す言葉ではありません。どの種類の勘定を見ているかで増減の意味が変わります。資産である現金は借方で増え、負債である預金は貸方で増える、という組み合わせです。
この一件には仕訳の見出しが一つ、その内訳が二行できます。さらに financial_operations には、入金という業務の種類、3万円という金額、その仕訳のID、操作した人、同じ依頼を見分けるキーが結び付けられます。業務の記録は「誰が何をしたか」、仕訳は「どの勘定がどう動いたか」を説明します。似て見えても、役割が違います。
顧客口座の表示用残高も更新されますが、単純に画面の2万円へ3万円を足して終わる設計ではありません。仕訳の貸方合計から借方合計を引いて残高を求め直します。履歴から5万円の根拠がたどれることに価値があります。なお、記帳前の残高に既存の食い違いがあれば、後述の共通処理が入金自体を止めます。
実装の読み方:入金の二行だけを見る
core.deposit の仕訳構成を日本語にした読み取り用の擬似コードです。実行手順ではありません。
入金 30,000円
SYSTEM_FUNDING 借方 30,000 / 貸方 0
葵さんの預金 借方 0 / 貸方 30,000
借方合計 = 貸方合計 = 30,000出金は、表示残高より奥の根拠を確かめる
8千円の出金では、二つの側が逆になります。葵さんの顧客預金に借方8千円、SYSTEM_FUNDING に貸方8千円です。銀行の返す義務と、現金に相当する資産が同時に減ります。預金の元帳では、これまでの貸方5万円から借方8千円を引いた4万2千円が残ります。
出金処理は、対象口座をロックした後、仕訳から残高を計算して資金が足りるかを検査します。もし5万円しかない時点で5万1円の出金が来れば、残高不足 WD010 で拒否する作りです。表示だけが誤って6万円になっていても、その数字を根拠に出金してよいとは判断しません。
ロックは、同じ口座への更新を一時的に順番待ちさせる仕組みです。出金の直前に定期預金の解約金が戻る、または別の支払いが走る場面で、古い数字を二つの処理が同時に使うのを防ぎます。待つぶんだけ応答時間は増え得ますが、残高判断と記帳を別々の時点にしないための負担です。
誰でも帳簿を書ける入口にはしない
入金できる関数がリポジトリに存在することと、顧客がそれを自由に呼べることは別です。管理者向けの入口 admin_execute_deposit と admin_execute_withdrawal は、ログインした利用者から実行者を求め、有効な管理担当者かをデータベース内で確かめます。この二つの入口で必要なのは有効な管理担当者であり、ここだけを読んで「管理責任者だけ」と言い換えるのも正確ではありません。
確認時には担当者自身の行もロックします。権限を確認した直後、順番待ちの間に担当者が無効化されたのに古い許可で処理を続ける、という時間差の問題を避けるためです。その内側で core.deposit や core.withdrawal が業務を担い、共通の仕訳作成処理へ渡します。
内部の ledger.post_journal_entry は、勘定や仕訳行を直接扱う強い部品です。共通化されていることは、読者が本番で呼ぶ便利なショートカットだという意味ではありません。API用のロールから直接使わせない権限設定と、正式な業務入口がセットで成り立つ設計です。
同じ3万円を二度足さないための目印
入金を送ったあと返事が来なかったので、同じ内容をもう一度送ったとします。これを別の入金と解釈すると、葵さんの残高は本来の5万円でなく8万円になってしまいます。そこで処理は、実行者・操作の種類・冪等キーという組み合わせから過去の業務記録を探します。冪等とは、同じ依頼を繰り返しても効果を増やさない性質です。
さらに、口座IDと金額をまとめた指紋のような値を照合します。同じキーと同じ内容なら既存の結果を返し、同じキーを使って金額を3万円から4万円へ変えれば、入金では DP009、出金では WD009 という衝突として拒否します。単にキーがあれば何でも同じ結果にしてよいわけではありません。
重複確認は口座ロックの前だけでなく、ロックを取った後にもあります。最初の確認では存在しなかった依頼が、順番待ちの間に先行処理によって完成することがあるからです。この二度目の確認が、「同時に来た同じ依頼」の穴をふさぎます。別のキーを発行すれば別件になり得るため、通信エラーを見て即座に新しい入出金を作る発想は危険です。
失敗しても、数字を合わせて済ませない
一つの仕訳では借方合計と貸方合計が一致し、正の金額である必要があります。共通処理は内訳を確認し、口座の表示用残高を同じデータベース処理の中で更新します。業務記録や監査まで含む処理が確定する前に失敗すれば、一部の金額だけを残さないことが重要になります。
特に見逃したくないのは、取引前から表示用残高と元帳が違っている場合です。たとえば元帳が2万円なのに表示用残高が2万1千円だったら、入金ついでに元帳から作り直すと、食い違いの発生を利用者が気付かないまま消してしまいます。現行の共通処理には、これを LG018 として止める検査があります。正しい入金を継続することより、原因不明の状態を広げないことを優先しています。
この章で確認したのは固定コミットの実装です。現金の授受、稼働中のデータベースの状態、管理画面からの実行結果を確認したものではありません。異常があれば、まずどの記録が食い違うかを照合し、証拠と権限のある修復手順へつなぎます。残高を直接書き換えたり、内部の仕訳関数で不足分を埋めたりする手順は、この読み物の範囲に含みません。
出典と、確認できた範囲
以下のリンクは非公開GitHubへの出典で、閲覧権限が必要です。本文はリンク先を開かなくても読める構成です。固定コード・設計資料を参照していますが、今回、この読み物のために銀行の取引やDB操作、本番試験は実行していません。実装や資料にある検査と、こちらで実施した検証を混同しないようにしています。
- 入出金と仕訳の原子性 ↗(非公開GitHub・要権限)
入金・出金の相手勘定、重複確認、残高再計算 - 管理操作の権限とロック ↗(非公開GitHub・要権限)
入出金の管理入口が有効な担当者を検査する - GLの科目対応 ↗(非公開GitHub・要権限)
SYSTEM_FUNDINGを現金、顧客勘定を普通預金へ集約 - 不一致の無断修復を防ぐ ↗(非公開GitHub・要権限)
新しい取引が既存の残高ずれを黙って直さない
MiniBankは学習・検証用の銀行アプリです。架空の金額や人物は、仕組みを説明するための例です。
つながる読み物
ここまで読んだ記録を、ひとつ。
自分のペースで。あとから取り消すこともできます。