1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Repository for StockItem 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 StockItem registration SQL
//! (4-layer rule: services orchestrate, repositories hold the SQL).
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<StockItem, 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 backbone_orm::org_scope;
use crate::domain::entity::StockItem;
/// Table name for StockItem entities
pub const TABLE_NAME: &str = "inventory.stock_items";
/// Repository for StockItem entities.
///
/// All standard CRUD, soft-delete, pagination, and bulk methods are
/// provided automatically via `Deref` to `backbone_orm::GenericCrudRepository`.
pub struct StockItemRepository(
backbone_orm::GenericCrudRepository<StockItem, backbone_orm::SoftDelete>,
);
impl std::ops::Deref for StockItemRepository {
type Target = backbone_orm::GenericCrudRepository<StockItem, backbone_orm::SoftDelete>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl StockItemRepository {
/// Create a new repository instance.
pub fn new(pool: PgPool) -> Self {
Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
}
}
/// The exact row a stock-item registration writes.
///
/// Mirrors the raw column shape rather than the `StockItem` entity: `valuation_method` is cast at
/// the DB (`$5::valuation_method`), and `is_stock_item`/`has_batch` are literals in the statement.
pub struct NewStockItemRow<'a> {
pub id: Uuid,
pub item_id: Uuid,
pub stock_uom: &'a str,
pub valuation_method: &'a str,
pub reorder_level: Decimal,
}
/// Hand-written StockItem SQL. Lives here per the module's 4-layer rule.
impl StockItemRepository {
/// Register an item as stock-tracked.
///
/// A write outside any transaction: `org_scope::execute_scoped` on the pool — the write
/// rides the ambient org scope's request-dedicated connection (the composing service sets
/// it per request) so it passes the decorator's WITH CHECK fence (ADR-0029). Undecorated
/// (module tests) the insert runs plain.
///
/// Returns the raw `sqlx::Error` deliberately: the caller inspects it for a unique violation
/// (the composing decorator's org-scoped item arbiter) to turn an already-registered item
/// into a domain error.
pub async fn insert_stock_item(
&self,
pool: &PgPool,
s: &NewStockItemRow<'_>,
) -> Result<(), sqlx::Error> {
org_scope::execute_scoped(
pool,
sqlx::query(
r#"INSERT INTO inventory.stock_items
(id, item_id, stock_uom, is_stock_item, has_batch, valuation_method, reorder_level)
VALUES ($1,$2,$3,TRUE,FALSE,$4::valuation_method,$5)"#,
)
.bind(s.id).bind(s.item_id).bind(s.stock_uom)
.bind(s.valuation_method).bind(s.reorder_level),
)
.await?;
Ok(())
}
}
backbone_core::impl_crud_repository!(StockItemRepository, StockItem, soft_delete);