backbone-banking 0.7.1

Cash & bank — bank/account master, statement import, reconciliation, and clearing postings (5th GL producer)
Documentation
//! Repository for BankReconciliation 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 BankReconciliation SQL (4-layer rule: services orchestrate, repos hold
//! SQL).
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<BankReconciliation, backbone_orm::SoftDelete>`.
//! All standard CRUD methods are available via `Deref`.

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

use backbone_orm::org_scope;

use crate::domain::entity::BankReconciliation;

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

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

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

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

/// The exact row a reconciliation session writes.
///
/// Mirrors the raw column shape rather than the `BankReconciliation` entity: `status` binds as `&str`
/// and is cast at the DB (`$10::recon_status`), so a bad value fails as a DB error rather than a
/// deserialize panic. `unreconciled_count` is the EXCEPTION SNAPSHOT at sign-off — persisted so the
/// close decision stays audit-reconstructable.
pub struct NewReconciliationRow<'a> {
    pub id: Uuid,
    pub bank_account_id: Uuid,
    pub from_date: chrono::NaiveDate,
    pub to_date: chrono::NaiveDate,
    pub statement_closing_balance: Decimal,
    pub ledger_balance: Decimal,
    pub computed_difference: Decimal,
    pub unreconciled_count: i32,
    /// `open` | `balanced` | `closed`.
    pub status: &'a str,
}

/// Hand-written BankReconciliation SQL. Lives here (not in the write service) per the module's
/// 4-layer rule. The close DECISION (open / balanced / closed) stays in the service.
impl BankReconciliationRepository {
    /// Record a reconciliation session in its decided state.
    ///
    /// A write outside any transaction: takes the pool and runs `org_scope::execute_scoped` so the
    /// statement rides the request-dedicated connection carrying the composing service's org scope
    /// — the same ambient-scope discipline as the exception count that decided `status`
    /// (ADR-0029). Undecorated deployments execute plainly on the pool.
    pub async fn insert_reconciliation(
        &self,
        pool: &PgPool,
        r: &NewReconciliationRow<'_>,
    ) -> Result<(), sqlx::Error> {
        org_scope::execute_scoped(
            pool,
            sqlx::query(
                r#"INSERT INTO banking.bank_reconciliations
                    (id, bank_account_id, from_date, to_date, statement_closing_balance,
                     ledger_balance, computed_difference, unreconciled_count, status)
                   VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::recon_status)"#,
            )
            .bind(r.id)
            .bind(r.bank_account_id)
            .bind(r.from_date)
            .bind(r.to_date)
            .bind(r.statement_closing_balance)
            .bind(r.ledger_balance)
            .bind(r.computed_difference)
            .bind(r.unreconciled_count)
            .bind(r.status),
        )
        .await?;
        Ok(())
    }
}

backbone_core::impl_crud_repository!(
    BankReconciliationRepository,
    BankReconciliation,
    soft_delete
);