backbone-banking 0.7.1

Cash & bank — bank/account master, statement import, reconciliation, and clearing postings (5th GL producer)
Documentation
//! Repository for BankAccount entities
//!
//! Originally generated by metaphor-schema; now **user-owned** — this exact path is declared under
//! `user_owned` in `metaphor.codegen.yaml`, so the generator skips it wholesale. The custom methods
//! below hold the hand-written BankAccount SQL (4-layer rule: services orchestrate, repos hold SQL).
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<BankAccount, backbone_orm::SoftDelete>`.
//! All standard CRUD methods are available via `Deref`.

use anyhow::Result;
use sqlx::PgPool;
use uuid::Uuid;

use backbone_orm::org_scope;

use crate::domain::entity::BankAccount;

/// Table name for BankAccount entities
pub const TABLE_NAME: &str = "banking.bank_accounts";

/// Repository for BankAccount entities.
///
/// All standard CRUD, soft-delete, pagination, and bulk methods are
/// provided automatically via `Deref` to `backbone_orm::GenericCrudRepository`.
pub struct BankAccountRepository(
    backbone_orm::GenericCrudRepository<BankAccount, backbone_orm::SoftDelete>,
);

impl std::ops::Deref for BankAccountRepository {
    type Target = backbone_orm::GenericCrudRepository<BankAccount, backbone_orm::SoftDelete>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl BankAccountRepository {
    /// Create a new repository instance.
    pub fn new(pool: PgPool) -> Self {
        Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
    }
}

/// The exact row a bank-account registration writes.
///
/// Mirrors the raw column shape rather than the `BankAccount` entity: `account_type` binds as `&str`
/// and is cast at the DB (`$10::bank_account_type`), so a bad value fails as a DB error rather than a
/// deserialize panic. `is_default`/`status` are not fields — the INSERT hard-codes `false`/`'active'`.
/// `currency`/`account_type` are already defaulted by the caller, so they are not optional here.
pub struct NewBankAccountRow<'a> {
    pub id: Uuid,
    pub branch_id: Option<Uuid>,
    pub bank_id: Uuid,
    pub account_name: &'a str,
    pub account_number: &'a str,
    /// The GL account the bank balance itself lives in.
    pub gl_account_id: Uuid,
    /// The account a settlement debits/credits until the statement confirms it — the account bank
    /// clearing nets to zero.
    pub clearing_account_id: Uuid,
    pub currency: &'a str,
    pub account_type: &'a str,
}

/// Hand-written BankAccount SQL. Lives here (not in the write service) per the module's 4-layer rule.
impl BankAccountRepository {
    /// Register a bank account.
    ///
    /// A write outside any transaction: takes the pool and runs `org_scope::execute_scoped` — the
    /// same ambient-scope discipline as [`super::BankRepository::insert_bank`] (ADR-0029).
    pub async fn insert_bank_account(
        &self,
        pool: &PgPool,
        a: &NewBankAccountRow<'_>,
    ) -> Result<(), sqlx::Error> {
        org_scope::execute_scoped(
            pool,
            sqlx::query(
                r#"INSERT INTO banking.bank_accounts
                    (id, branch_id, bank_id, account_name, account_number, gl_account_id,
                     clearing_account_id, currency, account_type, is_default, status)
                   VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::bank_account_type,false,'active')"#,
            )
            .bind(a.id).bind(a.branch_id).bind(a.bank_id).bind(a.account_name)
            .bind(a.account_number).bind(a.gl_account_id).bind(a.clearing_account_id)
            .bind(a.currency).bind(a.account_type),
        )
        .await?;
        Ok(())
    }
}

backbone_core::impl_crud_repository!(BankAccountRepository, BankAccount, soft_delete);