backbone-banking 0.7.1

Cash & bank — bank/account master, statement import, reconciliation, and clearing postings (5th GL producer)
Documentation
//! Repository for BankStatementImport 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 BankStatementImport SQL (4-layer rule: services orchestrate, repos
//! hold SQL).
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<BankStatementImport, 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 crate::domain::entity::BankStatementImport;

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

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

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

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

/// The exact row a statement-import header writes.
///
/// Mirrors the raw column shape rather than the `BankStatementImport` entity: `source_format` binds
/// as `&str` and is cast at the DB (`$4::source_format`), so a bad value fails as a DB error rather
/// than a deserialize panic. `status` is not a field — the INSERT hard-codes `'imported'`. The
/// balances carry the SERVER-rounded values (2dp half-up) the continuity check validated.
pub struct NewStatementImportRow<'a> {
    pub id: Uuid,
    pub bank_account_id: Uuid,
    pub source_format: &'a str,
    pub statement_period_start: chrono::NaiveDate,
    pub statement_period_end: chrono::NaiveDate,
    pub opening_balance: Decimal,
    pub closing_balance: Decimal,
    pub file_ref: Option<&'a str>,
    pub row_count: i32,
}

/// Hand-written BankStatementImport SQL. Lives here (not in the write service) per the module's
/// 4-layer rule.
impl BankStatementImportRepository {
    /// Insert a statement-import header as `imported`.
    ///
    /// Takes the CALLER'S connection so the header and every statement line commit as one unit.
    /// The caller has already relayed the ambient org scope onto it (`org_scope::bind_org_scope_on`)
    /// — don't re-bind here (ADR-0029).
    pub async fn insert_import(
        &self,
        conn: &mut sqlx::PgConnection,
        i: &NewStatementImportRow<'_>,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            r#"INSERT INTO banking.bank_statement_imports
                (id, bank_account_id, source_format, statement_period_start, statement_period_end,
                 opening_balance, closing_balance, file_ref, status, row_count)
               VALUES ($1,$2,$3::source_format,$4,$5,$6,$7,$8,'imported'::import_status,$9)"#,
        )
        .bind(i.id).bind(i.bank_account_id).bind(i.source_format)
        .bind(i.statement_period_start).bind(i.statement_period_end)
        .bind(i.opening_balance).bind(i.closing_balance).bind(i.file_ref).bind(i.row_count)
        .execute(conn)
        .await?;
        Ok(())
    }
}

backbone_core::impl_crud_repository!(
    BankStatementImportRepository,
    BankStatementImport,
    soft_delete
);