Real Output / 実物プルーフ

現状理解ダイジェスト(実例)

SpecLift が、実在の銀行基幹システムを実際に解析した出力です。作り物ではありません。「要レビュー」と書いた箇所まで、隠さず公開しています。

  • Apache Fineract(公開OSS・develop)
  • commit 2a4a6cce…(管理対象 7,677 ファイル規模)
  • 利息計算・返済スケジュール・手数料(最も複雑な金融計算)
  • 読了 約10分

Apache Fineract は、世界中の金融機関が使う実在のオープンソース銀行システムです。その中でも特に複雑な「利息・返済予定・手数料」の現行ロジックを、SpecLift がどこまで正確に読み解けたか——その実際の出力を、根拠と限界つきでそのまま掲載します。

00

30秒で分かる要点

  • 対象は、世界中の金融機関が使うOSS銀行システム「Apache Fineract」。中でも一番複雑な「利息・返済予定・手数料」の計算を解析した。

  • 肝は、金融ロジックが「計算式」だけで終わらないこと。計算結果がどのDB項目に保存され、どんな条件で再計算が止まるかまで含めて読む必要がある。

  • 利息:商品設定から年利を導き出し、専用のDB項目に保存。日割り計算もあるが、一部は自動抽出で取りこぼし → 正直に「要レビュー」。

  • 返済予定:「利息再計算が有効」「ローンが有効」などの条件を満たすときだけ、将来の予定を作り直して返済予定テーブルへ反映。

  • 手数料:支払い・免除・償却を差し引いて未収額を算出。割合手数料は「金額 ×(率 ÷ 100)」。

  • 正直な精度:データの保存先と計算式の特定は満点〜96.8%。業務ルールの抽出は正解率63.6%・網羅率70%と、できない所も数字で開示している。

一言で:お金のロジックは、計算式だけでなく「どのDB項目に保存され、どんな条件で再計算が止まるか」まで含めて読む必要がある。

01

この記事の用語(先に3つだけ)

derived column(派生カラム)
直接入力ではなく、支払・免除・利息などから計算して保持するDB列。例:未収額の列。
返済スケジュール
返済予定表。各回の期日・元本・利息・手数料・延滞金を持つ。
年利(APR)
年換算の利率。Fineract では計算機が商品設定から導き出す。
waiver(免除)
手数料や利息などの免除。未収額を減らす。
NPA
延滞などで正常でない状態のローン(不良債権)。
書き込み経路(write route)
その列に値が入るコード上の道筋。どこで更新されるか。
02

お金の流れ(入口 → 処理 → 保存先)

刷新や改修では、まず保存先のDB列(derived column)の契約を固定するのが肝です。ここがずれると、計算結果だけでなくAPI応答・残高・会計連携の前提までずれます。

利息

POST /v1/loans(ローン作成・返済予定計算)

商品設定(周期・利率・返済回数・年日数)から年利と日割り可否を計算

m_product_loan:年利・部分期間利息の可否

返済予定

POST /v1/loans/{id}/schedule(返済予定の再計算)

状態ガード(有効/NPAでない/償却でない/利息再計算ON)を満たせば、将来予定を再構成

m_loan_repayment_schedule:期日・元本・利息

手数料

POST /v1/loans/{id}/charges(手数料作成・支払)

支払・免除・償却を反映し、未収額を算出

m_loan_charge:支払済・免除済・未収(derived)

03

代表機能の肝(利息・返済・手数料)

01

利息計算

ローン商品の設定(周期・名目利率・返済回数・年日数)から年換算利率を計算し、商品テーブルに保存します。日次計算の場合は、部分期間利息が自動的に無効になります。

年利の計算式

周期・名目利率・返済回数などから、年換算利率を導出する。

根拠:LoanProductRelatedDetailUpdateUtil.java:372-378(round2 GT: interest-de-001)

保存先

m_product_loan.annual_nominal_interest_rate(精度 DECIMAL(19,6))に保存。

根拠:LoanProductRelatedDetail.java:73-74

関連ルール

日次利息計算のときは、部分期間利息を強制的に無効(false)にする。

根拠:LoanProductRelatedDetailUpdateUtil.java:211-212(GT interest-br-003 は自動抽出では取りこぼし)

日割りの根拠

日割りは「利息 ÷ 期間日数 × 日数」。ソースでは確認済みだが、自動抽出では取りこぼし。

根拠:LoanBalanceService.java:374-378(要レビュー扱い)

API入口

POST /v1/loans(200)が、利息関連パラメータを受ける入口。

根拠:LoansApiResource.java:550-563

なぜ重要か

利息は「元本 × 利率」の一行ではなく、商品の周期設定と年日数を計算機に渡して導出されます。年利を単純な入力値として扱うと、設定変更時の再計算とAPI応答がずれます。

要レビュー

「元本 × 利率 × 日数 ÷ 365」型の単利式は、この代表8件では直接の正解として採用していません。確認できたのは年利の導出と部分期間の日割りです。日割り式はソースで確認済みですが、自動抽出(網羅率)では取りこぼしており、今後の補強対象です。

02

返済スケジュール

返済予定の作り直しは、すべてのローンで走るわけではありません。「利息再計算が有効」「ローンが有効」「延滞(NPA)でない」「償却済でない」などの条件を満たすときだけ、将来の返済予定を組み直します。

実行ガード

条件を満たさなければ、返済予定を変更せずに終了する。

根拠:LoanScheduleCalculationPlatformServiceImpl.java:91-93(GT repayment-br-001)

多重実行ガード

複数回実行のローンでは、残元本が0以下なら終了する。

根拠:同ファイル :96-101(GT repayment-br-002)

新しい返済行

元本・残高・利息・手数料・延滞金を渡して、将来予定の行を作る。

根拠:同ファイル :116-142(GT repayment-de-001 / 002 / 003)

保存先

m_loan_repayment_schedule の duedate(期日)・principal_amount(元本)・interest_amount(利息)。

根拠:LoanRepaymentScheduleInstallment.java:66-79

API入口

POST /v1/loans/{loanId}/schedule(200)。参照は GET /v1/loans/{loanId}(associations=repaymentSchedule)。

根拠:LoanScheduleApiResource.java:52-75

なぜ重要か

返済予定は単なる表示用の配列ではなく、将来の残元本・利息・手数料の再計算結果です。状態ガードを外すと、延滞や償却済のローンまで作り直し、残高や会計の前提を壊す恐れがあります。

要レビュー

「返済予定の種類 × 利息計算方式」で計算機を選ぶルールはソースで確認できますが、自動抽出では取りこぼしています(DefaultLoanScheduleGeneratorFactory.java:34-45)。

03

手数料

手数料は「元の金額」から「支払済・免除済・償却済」を引いて未収額を作ります。支払いや免除が起きるたびに、保存用の項目(derived column)が更新されます。

未収額の式

未収 = 金額 −(支払済 + 免除済 + 償却済)。

根拠:LoanCharge.java:282-303(GT fees-br-002)

割合手数料

金額 ×(率 ÷ 100)で算出。元の値が0以下なら0のまま。

根拠:LoanCharge.java:310-318(GT fees-br-003)

免除ルール

免除すると、免除済 += 免除額、未収 −= 免除額。

根拠:LoanCharge.java:199-208(GT fees-br-001)

支払時の更新

支払額が未収以上なら未収を0に。未満なら差し引いて未収を再計算する。

根拠:LoanCharge.java:436-468(GT fees-de-001 / 002)

保存先

m_loan_charge の amount_paid_derived(支払済)・amount_waived_derived(免除済)・amount_outstanding_derived(未収)。

根拠:LoanCharge.java:99-109(精度 DECIMAL(19,6))

API入口

POST /v1/loans/{loanId}/charges(command=pay で支払)。一覧は GET /v1/loans/{loanId}/charges。

根拠:LoanChargesApiResource.java:94-100, 194-205

なぜ重要か

手数料の正しさは、金額計算だけでなく保存項目の整合で決まります。未収額が「元額 − 支払 − 免除 − 償却」に保たれないと、残高表示・支払可否・免除後の状態が全部ずれます。

要レビュー

手数料は網羅率100%ですが、正解率は42.9%(3/7)と低めです。金額式の主要3件は根拠付きで使えますが、追加で抽出されたルールにはノイズが残るため、刷新の入力に使う場合は根拠行を個別に確認してください。

04

API契約の入口一覧

利息計算

POST /v1/loans

ローン申請/返済予定計算。利息関連パラメータの入口。

根拠:LoansApiResource.java:550-563
返済予定

POST /v1/loans/{loanId}/schedule

返済予定の計算・更新。

根拠:LoanScheduleApiResource.java:52-75
返済予定

GET /v1/loans/{loanId}

返済予定を含めてローンを取得。

根拠:LoansApiResource.java:461-469
手数料

POST /v1/loans/{loanId}/charges

手数料作成、または command=pay で支払。

根拠:LoanChargesApiResource.java:194-205
手数料

GET /v1/loans/{loanId}/charges

ローン手数料の一覧。

根拠:LoanChargesApiResource.java:94-100
05

信頼できる範囲と、正直な限界

一部の綺麗な箇所だけでなく、代表機能の主要項目を、AIの出力とは独立に用意した正解と照合した実測値です。達成は緑、苦手な箇所はグレーで、隠さず示します。

100%8 / 8

データ項目の捕捉(保存先テーブル・列)

代表機能の保存先は、もれなく捕捉できた。

100%8 / 8

計算式の捕捉

代表機能の計算式も捕捉できた。

96.8%430 / 444

計算式・書き込み先の精度

独立チェックで、主張の大半は妥当だった。

100%5 / 5

API契約の一致

代表APIのメソッド・パス・ステータスは捕捉できた。

63.6%7 / 11

業務ルールの正解率

ノイズ抑制後も、誤検出が残る。

70.0%7 / 10

業務ルールの網羅率

重要ルールの一部は、取りこぼしている。

この数字の限界(正直に)

  • この数値は、代表機能(利息・返済・手数料)の主要項目に対するものです。Fineract 全体の全カラム・全計算式を網羅したものではありません。

  • 「元本 × 利率 × 日数 ÷ 365」型の単利式は、この代表8件では直接の正解にしていません。確認済みは年利の導出と部分期間の日割りです。

  • 業務ルールは金融ノイズを抑制済みですが、手数料は正解率42.9%(3/7)と低めです。本文では、根拠が確認できたものだけを採用しています。

  • データ項目の一部は、推定やgetterの戻り値式を含みます。刷新の入力にする場合は、列定義・マッピング・実際の書き込み経路の3点を併せて確認してください。

  • 本レポートは Fineract のソースを一切変更していません。DB移行や固有のハードコード、環境実行はいずれも行っていません。

06

このダイジェストで、何が追えるか

  • お金の流れ:API の入口から、利息・返済予定・手数料の計算を経て、最終的にどのテーブル・列へ保存されるかを、図と表で追える。

  • 肝の式:年利の導出、日割り利息、返済予定の元本/利息、手数料の未収額、割合手数料の式を、根拠(file:line)付きで確認できる。

  • 危ない分岐:有効/NPA/償却済などの状態ガード、日次利息での部分期間無効化、免除時の残高更新が明示されている。

ただし、業務ルールの網羅率は70.0%です。これは「Fineract 全体を任せ切れる完全な仕様書」ではありません。刷新や移植に使う場合は、このダイジェストを入口に、要レビュー項目と根拠行を追加確認する必要があります。

技術的な根拠(上級者向け)
検証結果(round2)
issue42_fineract_round2_result_20260623.json / .md
直接解析(round1)
issue42_fineract_direct_analysis_round1_20260623.json
正解データのハッシュ(凍結)
b9149ee0…e324b7b1
採点の独立性
独立 grep 裁定・AI出力不使用・自己採点なし
ソース
Apache Fineract develop / 2a4a6cce… / tracked files 7,677

これは Apache Fineract の例です。あなたのシステムでも、同じように数字を出します。

まずは1機能・1サブシステムから。読み取れなかった箇所も、こうして正直に開示します。