backbone_pos/infrastructure/persistence/pos_table_repository_custom.rs
1//! Hand-written PosTable reads (user-owned, never regenerated) — the custom methods on
2//! [`PosTableRepository`]. Sibling to the generated newtype file, per the module's custom-code
3//! convention. Holds the SQL per the 4-layer rule: services orchestrate, repositories hold SQL.
4//!
5//! The dining table is a UI-affordance record (geometry, seats); the write path cares about its
6//! IDENTITY only — that a ticket seating itself at a table names a table that exists in the
7//! caller's scope. Geometry is deliberately not read here.
8
9use sqlx::Row;
10use uuid::Uuid;
11
12use crate::infrastructure::persistence::PosTableRepository;
13
14/// A dining table as the ticket write path validates it: identity + the floor it belongs to (so a
15/// caller can scope a register's seating by floor if it chooses).
16pub struct TableRow {
17 pub id: Uuid,
18 pub pos_floor_plan_id: Uuid,
19 pub name: Option<String>,
20}
21
22impl PosTableRepository {
23 /// Read one dining table by id, on the CALLER'S scope-relayed transaction (the seating guard
24 /// runs inside the ring/sync unit of work). `Ok(None)` = no such table visible to the caller's
25 /// scope — the fence is the whole guard (ADR-0029).
26 pub async fn fetch_table(
27 &self,
28 conn: &mut sqlx::PgConnection,
29 pos_table_id: Uuid,
30 ) -> Result<Option<TableRow>, sqlx::Error> {
31 let row = sqlx::query(
32 r#"SELECT id, pos_floor_plan_id, name
33 FROM pos.pos_tables
34 WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
35 )
36 .bind(pos_table_id)
37 .fetch_optional(conn)
38 .await?;
39 Ok(row.map(|r| TableRow {
40 id: r.get("id"),
41 pos_floor_plan_id: r.get("pos_floor_plan_id"),
42 name: r.get("name"),
43 }))
44 }
45}