backbone-pos 0.7.1

Point of sale — cashier session, ticket + multi-tender; orchestrates billing (revenue) + payment (settlement), posts no GL directly
Documentation
//! Repository for PosClosingEntry 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 drawer-close SQL (4-layer rule: services orchestrate, repos hold SQL).
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<PosClosingEntry, backbone_orm::SoftDelete>`.
//! All standard CRUD methods are available via `Deref`.

use anyhow::Result;
use rust_decimal::Decimal;
use sqlx::{PgPool, Row};
use uuid::Uuid;

use crate::domain::entity::PosClosingEntry;

/// Table name for PosClosingEntry entities
pub const TABLE_NAME: &str = "pos.pos_closing_entries";

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

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

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

/// The exact row a drawer close (Z-report) writes. Mirrors the raw column shape rather than the
/// `PosClosingEntry` entity: `totals_by_method` is the already-serialised per-method reconciliation and
/// `invoice_count` is the `i32` the column holds. `status` is fixed to `'submitted'` by the SQL.
pub struct NewClosingEntryRow {
    pub id: Uuid,
    pub pos_profile_id: Uuid,
    pub opening_entry_id: Uuid,
    pub closed_at: chrono::NaiveDateTime,
    pub cashier_party_id: Uuid,
    pub totals_by_method: serde_json::Value,
    pub grand_total: Decimal,
    pub invoice_count: i32,
    pub difference_total: Decimal,
}

/// Hand-written drawer-close SQL. Lives here (not in the write service) per the module's 4-layer rule:
/// services orchestrate and own the unit of work, repositories hold the SQL.
impl PosClosingEntryRepository {
    /// Write the closing entry (the Z-report).
    ///
    /// Takes the CALLER'S connection so the closing entry and the session's flip to `closed` commit as
    /// one unit — a drawer is never counted without the session closing. The caller has already
    /// relayed the ambient org scope onto it (ADR-0029) — don't re-bind here.
    pub async fn insert_closing_entry(
        &self,
        conn: &mut sqlx::PgConnection,
        c: &NewClosingEntryRow,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            r#"INSERT INTO pos.pos_closing_entries
                (id, pos_profile_id, opening_entry_id, closed_at, cashier_party_id,
                 totals_by_method, grand_total, invoice_count, difference_total, status)
               VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'submitted'::pos_closing_status)"#,
        )
        .bind(c.id).bind(c.pos_profile_id).bind(c.opening_entry_id).bind(c.closed_at)
        .bind(c.cashier_party_id).bind(sqlx::types::Json(&c.totals_by_method)).bind(c.grand_total)
        .bind(c.invoice_count).bind(c.difference_total)
        .execute(conn)
        .await?;
        Ok(())
    }
}

backbone_core::impl_crud_repository!(PosClosingEntryRepository, PosClosingEntry, soft_delete);