Skip to main content

backbone_pos/infrastructure/persistence/
pos_closing_entry_repository.rs

1//! Repository for PosClosingEntry entities
2//!
3//! Originally generated by metaphor-schema; now **user-owned** — this exact path is declared under
4//! `user_owned` in `metaphor.codegen.yaml`, so the generator skips it wholesale. The custom methods
5//! below hold the hand-written drawer-close SQL (4-layer rule: services orchestrate, repos hold SQL).
6//!
7//! Thin newtype over `backbone_orm::GenericCrudRepository<PosClosingEntry, backbone_orm::SoftDelete>`.
8//! All standard CRUD methods are available via `Deref`.
9
10use anyhow::Result;
11use rust_decimal::Decimal;
12use sqlx::{PgPool, Row};
13use uuid::Uuid;
14
15use crate::domain::entity::PosClosingEntry;
16
17/// Table name for PosClosingEntry entities
18pub const TABLE_NAME: &str = "pos.pos_closing_entries";
19
20/// Repository for PosClosingEntry entities.
21///
22/// All standard CRUD, soft-delete, pagination, and bulk methods are
23/// provided automatically via `Deref` to `backbone_orm::GenericCrudRepository`.
24pub struct PosClosingEntryRepository(
25    backbone_orm::GenericCrudRepository<PosClosingEntry, backbone_orm::SoftDelete>,
26);
27
28impl std::ops::Deref for PosClosingEntryRepository {
29    type Target = backbone_orm::GenericCrudRepository<PosClosingEntry, backbone_orm::SoftDelete>;
30    fn deref(&self) -> &Self::Target { &self.0 }
31}
32
33impl PosClosingEntryRepository {
34    /// Create a new repository instance.
35    pub fn new(pool: PgPool) -> Self {
36        Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
37    }
38}
39
40/// The exact row a drawer close (Z-report) writes. Mirrors the raw column shape rather than the
41/// `PosClosingEntry` entity: `totals_by_method` is the already-serialised per-method reconciliation and
42/// `invoice_count` is the `i32` the column holds. `status` is fixed to `'submitted'` by the SQL.
43pub struct NewClosingEntryRow {
44    pub id: Uuid,
45    pub pos_profile_id: Uuid,
46    pub opening_entry_id: Uuid,
47    pub closed_at: chrono::NaiveDateTime,
48    pub cashier_party_id: Uuid,
49    pub totals_by_method: serde_json::Value,
50    pub grand_total: Decimal,
51    pub invoice_count: i32,
52    pub difference_total: Decimal,
53}
54
55/// Hand-written drawer-close SQL. Lives here (not in the write service) per the module's 4-layer rule:
56/// services orchestrate and own the unit of work, repositories hold the SQL.
57impl PosClosingEntryRepository {
58    /// Write the closing entry (the Z-report).
59    ///
60    /// Takes the CALLER'S connection so the closing entry and the session's flip to `closed` commit as
61    /// one unit — a drawer is never counted without the session closing. The caller has already
62    /// relayed the ambient org scope onto it (ADR-0029) — don't re-bind here.
63    pub async fn insert_closing_entry(
64        &self,
65        conn: &mut sqlx::PgConnection,
66        c: &NewClosingEntryRow,
67    ) -> Result<(), sqlx::Error> {
68        sqlx::query(
69            r#"INSERT INTO pos.pos_closing_entries
70                (id, pos_profile_id, opening_entry_id, closed_at, cashier_party_id,
71                 totals_by_method, grand_total, invoice_count, difference_total, status)
72               VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,'submitted'::pos_closing_status)"#,
73        )
74        .bind(c.id).bind(c.pos_profile_id).bind(c.opening_entry_id).bind(c.closed_at)
75        .bind(c.cashier_party_id).bind(sqlx::types::Json(&c.totals_by_method)).bind(c.grand_total)
76        .bind(c.invoice_count).bind(c.difference_total)
77        .execute(conn)
78        .await?;
79        Ok(())
80    }
81}
82
83backbone_core::impl_crud_repository!(PosClosingEntryRepository, PosClosingEntry, soft_delete);