Skip to content

Commit

Permalink
feat: add non-cash adjustments to statement (#1426)
Browse files Browse the repository at this point in the history
* feat: add non-cash adjustments to statement

* chore: rename facility activation template

* chore: add non-cash income adjustment accounts in chart

* chore: add non-cash income adjustment accounts in credit facility ledger

* chore: add non-cash income adjustment accounts to template

* chore: add deposits to cash flow statement

* feat: add 'deposit_adjustments' non-cash account

* chore: add deposit adjustment entries to template

* fix: separate doubly-accounted cash-flow control account

* chore: mirror changes to core/credit
  • Loading branch information
vindard authored Feb 26, 2025
1 parent 8cb2d0c commit 13eead4
Show file tree
Hide file tree
Showing 10 changed files with 359 additions and 31 deletions.
4 changes: 2 additions & 2 deletions bats/customer.bats
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ wait_for_approval() {
'{
input: {
depositAccountId: $depositAccountId,
amount: 150000,
amount: 120000,
reference: ("withdrawal-ref-" + $date)
}
}'
Expand All @@ -185,7 +185,7 @@ wait_for_approval() {
# settled_usd_balance=$(graphql_output '.data.withdrawalInitiate.withdrawal.customer.balance.checking.settled')
# [[ "$settled_usd_balance" == "0" ]] || exit 1
# pending_usd_balance=$(graphql_output '.data.withdrawalInitiate.withdrawal.customer.balance.checking.pending')
# [[ "$pending_usd_balance" == "150000" ]] || exit 1
# [[ "$pending_usd_balance" == "120000" ]] || exit 1

assert_accounts_balanced

Expand Down
3 changes: 3 additions & 0 deletions core/credit/src/ledger/credit_facility_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ impl CreditFacilityAccountIds {
pub struct CreditFacilityOmnibusAccountIds {
pub bank_collateral: LedgerAccountId,
pub facility: LedgerAccountId,
pub fee_income_adjustment: LedgerAccountId,
pub debit_account_adjustment: LedgerAccountId,
pub non_cash_offset: LedgerAccountId,
}

#[derive(Clone)]
Expand Down
19 changes: 16 additions & 3 deletions core/credit/src/ledger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ pub struct CreditLedger {
journal_id: JournalId,
credit_omnibus_account_id: AccountId,
bank_collateral_account_id: AccountId,
fee_income_adjustment_omnibus_account_id: AccountId,
debit_account_adjustment_omnibus_account_id: AccountId,
non_cash_offset_omnibus_account_id: AccountId,
credit_facility_control_id: VelocityControlId,
account_factories: CreditFacilityAccountFactories,
usd: Currency,
Expand All @@ -44,7 +47,7 @@ impl CreditLedger {
omnibus_ids: CreditFacilityOmnibusAccountIds,
) -> Result<Self, CreditLedgerError> {
templates::AddCollateral::init(cala).await?;
templates::ApproveCreditFacility::init(cala).await?;
templates::ActivateCreditFacility::init(cala).await?;
templates::RemoveCollateral::init(cala).await?;
templates::RecordPayment::init(cala).await?;
templates::CreditFacilityIncurInterest::init(cala).await?;
Expand Down Expand Up @@ -72,6 +75,9 @@ impl CreditLedger {
journal_id,
bank_collateral_account_id: omnibus_ids.bank_collateral,
credit_omnibus_account_id: omnibus_ids.facility,
fee_income_adjustment_omnibus_account_id: omnibus_ids.fee_income_adjustment,
debit_account_adjustment_omnibus_account_id: omnibus_ids.debit_account_adjustment,
non_cash_offset_omnibus_account_id: omnibus_ids.non_cash_offset,
credit_facility_control_id,
account_factories,
usd: "USD".parse().expect("Could not parse 'USD'"),
Expand Down Expand Up @@ -270,14 +276,17 @@ impl CreditLedger {
.post_transaction_in_op(
&mut op,
tx_id,
templates::APPROVE_CREDIT_FACILITY_CODE,
templates::ApproveCreditFacilityParams {
templates::ACTIVATE_CREDIT_FACILITY_CODE,
templates::ActivateCreditFacilityParams {
journal_id: self.journal_id,
credit_omnibus_account: self.credit_omnibus_account_id,
credit_facility_account: credit_facility_account_ids.facility_account_id,
facility_disbursed_receivable_account: credit_facility_account_ids
.disbursed_receivable_account_id,
facility_fee_income_account: credit_facility_account_ids.fee_income_account_id,
fee_income_adjustment_omnibus_account: self
.fee_income_adjustment_omnibus_account_id,
non_cash_offset_omnibus_account: self.non_cash_offset_omnibus_account_id,
debit_account_id,
facility_amount: facility_amount.to_usd(),
structuring_fee_amount: structuring_fee_amount.to_usd(),
Expand All @@ -289,6 +298,7 @@ impl CreditLedger {
op.commit().await?;
Ok(())
}

pub async fn record_interest_incurrence(
&self,
op: es_entity::DbOp<'_>,
Expand Down Expand Up @@ -422,6 +432,9 @@ impl CreditLedger {
facility_disbursed_receivable_account: credit_facility_account_ids
.disbursed_receivable_account_id,
debit_account_id,
debit_account_adjustment_omnibus_account: self
.debit_account_adjustment_omnibus_account_id,
non_cash_offset_omnibus_account: self.non_cash_offset_omnibus_account_id,
disbursed_amount: amount.to_usd(),
external_id: tx_ref,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,25 @@ use cala_ledger::{

use crate::ledger::error::*;

pub const APPROVE_CREDIT_FACILITY_CODE: &str = "APPROVE_CREDIT_FACILITY";
pub const ACTIVATE_CREDIT_FACILITY_CODE: &str = "ACTIVATE_CREDIT_FACILITY";

#[derive(Debug)]
pub struct ApproveCreditFacilityParams {
pub struct ActivateCreditFacilityParams {
pub journal_id: JournalId,
pub credit_omnibus_account: AccountId,
pub credit_facility_account: AccountId,
pub facility_disbursed_receivable_account: AccountId,
pub facility_fee_income_account: AccountId,
pub fee_income_adjustment_omnibus_account: AccountId,
pub non_cash_offset_omnibus_account: AccountId,
pub debit_account_id: AccountId,
pub facility_amount: Decimal,
pub structuring_fee_amount: Decimal,
pub currency: Currency,
pub external_id: String,
}

impl ApproveCreditFacilityParams {
impl ActivateCreditFacilityParams {
pub fn defs() -> Vec<NewParamDefinition> {
vec![
NewParamDefinition::builder()
Expand Down Expand Up @@ -52,6 +54,16 @@ impl ApproveCreditFacilityParams {
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("fee_income_adjustment_omnibus_account")
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("non_cash_offset_omnibus_account")
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("debit_account_id")
.r#type(ParamDataType::Uuid)
Expand Down Expand Up @@ -86,20 +98,22 @@ impl ApproveCreditFacilityParams {
}
}

impl From<ApproveCreditFacilityParams> for Params {
impl From<ActivateCreditFacilityParams> for Params {
fn from(
ApproveCreditFacilityParams {
ActivateCreditFacilityParams {
journal_id,
credit_omnibus_account,
credit_facility_account,
facility_disbursed_receivable_account,
facility_fee_income_account,
fee_income_adjustment_omnibus_account,
non_cash_offset_omnibus_account,
debit_account_id,
facility_amount,
structuring_fee_amount,
currency,
external_id,
}: ApproveCreditFacilityParams,
}: ActivateCreditFacilityParams,
) -> Self {
let mut params = Self::default();
params.insert("journal_id", journal_id);
Expand All @@ -110,6 +124,14 @@ impl From<ApproveCreditFacilityParams> for Params {
facility_disbursed_receivable_account,
);
params.insert("facility_fee_income_account", facility_fee_income_account);
params.insert(
"fee_income_adjustment_omnibus_account",
fee_income_adjustment_omnibus_account,
);
params.insert(
"non_cash_offset_omnibus_account",
non_cash_offset_omnibus_account,
);
params.insert("debit_account_id", debit_account_id);
params.insert("facility_amount", facility_amount);
params.insert("structuring_fee_amount", structuring_fee_amount);
Expand All @@ -120,16 +142,16 @@ impl From<ApproveCreditFacilityParams> for Params {
}
}

pub struct ApproveCreditFacility;
pub struct ActivateCreditFacility;

impl ApproveCreditFacility {
#[instrument(name = "ledger.approve_credit_facility.init", skip_all)]
impl ActivateCreditFacility {
#[instrument(name = "ledger.activate_credit_facility.init", skip_all)]
pub async fn init(ledger: &CalaLedger) -> Result<(), CreditLedgerError> {
let tx_input = NewTxTemplateTransaction::builder()
.journal_id("params.journal_id")
.effective("params.effective")
.external_id("params.external_id")
.description("'Approve credit facility'")
.description("'Activate credit facility'")
.build()
.expect("Couldn't build TxInput");

Expand All @@ -138,7 +160,7 @@ impl ApproveCreditFacility {
.account_id("params.credit_omnibus_account")
.units("params.facility_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_DR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
Expand All @@ -147,7 +169,7 @@ impl ApproveCreditFacility {
.account_id("params.credit_facility_account")
.units("params.facility_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_CR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
Expand All @@ -156,7 +178,7 @@ impl ApproveCreditFacility {
.account_id("params.credit_facility_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_DISBURSEMENT_DRAWDOWN_DR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_DISBURSEMENT_DRAWDOWN_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
Expand All @@ -165,7 +187,7 @@ impl ApproveCreditFacility {
.account_id("params.credit_omnibus_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_DISBURSEMENT_DRAWDOWN_CR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_DISBURSEMENT_DRAWDOWN_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
Expand All @@ -174,7 +196,7 @@ impl ApproveCreditFacility {
.account_id("params.facility_disbursed_receivable_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_DISBURSEMENT_DR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_DISBURSEMENT_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
Expand All @@ -183,7 +205,7 @@ impl ApproveCreditFacility {
.account_id("params.debit_account_id")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_DISBURSEMENT_CR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_DISBURSEMENT_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
Expand All @@ -192,7 +214,7 @@ impl ApproveCreditFacility {
.account_id("params.debit_account_id")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_STRUCTURING_FEE_DR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_STRUCTURING_FEE_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
Expand All @@ -201,16 +223,34 @@ impl ApproveCreditFacility {
.account_id("params.facility_fee_income_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'APPROVE_CREDIT_FACILITY_STRUCTURING_FEE_CR'")
.entry_type("'ACTIVATE_CREDIT_FACILITY_STRUCTURING_FEE_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
NewTxTemplateEntry::builder()
.account_id("params.fee_income_adjustment_omnibus_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'ACTIVATE_CREDIT_FACILITY_STRUCTURING_FEE_NON_CASH_ADJ_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
NewTxTemplateEntry::builder()
.account_id("params.non_cash_offset_omnibus_account")
.units("params.structuring_fee_amount")
.currency("params.currency")
.entry_type("'ACTIVATE_CREDIT_FACILITY_STRUCTURING_FEE_NON_CASH_ADJ_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
];
let params = ApproveCreditFacilityParams::defs();
let params = ActivateCreditFacilityParams::defs();
let template = NewTxTemplate::builder()
.id(TxTemplateId::new())
.code(APPROVE_CREDIT_FACILITY_CODE)
.code(ACTIVATE_CREDIT_FACILITY_CODE)
.transaction(tx_input)
.entries(entries)
.params(params)
Expand Down
4 changes: 2 additions & 2 deletions core/credit/src/ledger/templates/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mod accrue_interest;
mod activate_credit_facility;
mod add_collateral;
mod approve_credit_facility;
mod cancel_disbursal;
mod incur_interest;
mod initiate_disbursal;
Expand All @@ -9,8 +9,8 @@ mod remove_collateral;
mod settle_disbursal;

pub use accrue_interest::*;
pub use activate_credit_facility::*;
pub use add_collateral::*;
pub use approve_credit_facility::*;
pub use cancel_disbursal::*;
pub use incur_interest::*;
pub use initiate_disbursal::*;
Expand Down
40 changes: 40 additions & 0 deletions core/credit/src/ledger/templates/settle_disbursal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub struct SettleDisbursalParams {
pub credit_facility_account: AccountId,
pub facility_disbursed_receivable_account: AccountId,
pub debit_account_id: AccountId,
pub debit_account_adjustment_omnibus_account: AccountId,
pub non_cash_offset_omnibus_account: AccountId,
pub disbursed_amount: Decimal,
pub external_id: String,
}
Expand Down Expand Up @@ -48,6 +50,16 @@ impl SettleDisbursalParams {
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("debit_account_adjustment_omnibus_account")
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("non_cash_offset_omnibus_account")
.r#type(ParamDataType::Uuid)
.build()
.unwrap(),
NewParamDefinition::builder()
.name("disbursed_amount")
.r#type(ParamDataType::Decimal)
Expand Down Expand Up @@ -75,6 +87,8 @@ impl From<SettleDisbursalParams> for Params {
credit_facility_account,
facility_disbursed_receivable_account,
debit_account_id,
debit_account_adjustment_omnibus_account,
non_cash_offset_omnibus_account,
disbursed_amount,
external_id,
}: SettleDisbursalParams,
Expand All @@ -88,6 +102,14 @@ impl From<SettleDisbursalParams> for Params {
facility_disbursed_receivable_account,
);
params.insert("debit_account_id", debit_account_id);
params.insert(
"debit_account_adjustment_omnibus_account",
debit_account_adjustment_omnibus_account,
);
params.insert(
"non_cash_offset_omnibus_account",
non_cash_offset_omnibus_account,
);
params.insert("disbursed_amount", disbursed_amount);
params.insert("external_id", external_id);
params.insert("effective", chrono::Utc::now().date_naive());
Expand Down Expand Up @@ -146,6 +168,24 @@ impl SettleDisbursal {
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
NewTxTemplateEntry::builder()
.account_id("params.debit_account_adjustment_omnibus_account")
.units("params.disbursed_amount")
.currency("'USD'")
.entry_type("'SETTLE_DISBURSAL_NON_CASH_ADJ_DR'")
.direction("DEBIT")
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
NewTxTemplateEntry::builder()
.account_id("params.non_cash_offset_omnibus_account")
.units("params.disbursed_amount")
.currency("'USD'")
.entry_type("'SETTLE_DISBURSAL_NON_CASH_ADJ_CR'")
.direction("CREDIT")
.layer("SETTLED")
.build()
.expect("Couldn't build entry"),
];

let params = SettleDisbursalParams::defs();
Expand Down
Loading

0 comments on commit 13eead4

Please sign in to comment.