backbone-inventory 0.17.2

Inventory: stock ledger of record (SLE) + moving-average valuation; emits COGS/asset AccountingPost (Delivery/Receipt -> GL)
Documentation
//! 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);