Skip to main content

backbone_payroll/infrastructure/persistence/
salary_structure_repository.rs

1//! Repository for SalaryStructure 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 SalaryStructure SQL (4-layer rule: services orchestrate, repos hold SQL).
6//!
7//! Thin newtype over `backbone_orm::GenericCrudRepository<SalaryStructure, backbone_orm::SoftDelete>`.
8//! All standard CRUD methods are available via `Deref`.
9
10use anyhow::Result;
11use sqlx::PgPool;
12use uuid::Uuid;
13
14use crate::domain::entity::SalaryStructure;
15
16/// Table name for SalaryStructure entities
17pub const TABLE_NAME: &str = "payroll.salary_structures";
18
19/// Repository for SalaryStructure entities.
20///
21/// All standard CRUD, soft-delete, pagination, and bulk methods are
22/// provided automatically via `Deref` to `backbone_orm::GenericCrudRepository`.
23pub struct SalaryStructureRepository(
24    backbone_orm::GenericCrudRepository<SalaryStructure, backbone_orm::SoftDelete>,
25);
26
27impl std::ops::Deref for SalaryStructureRepository {
28    type Target = backbone_orm::GenericCrudRepository<SalaryStructure, backbone_orm::SoftDelete>;
29    fn deref(&self) -> &Self::Target { &self.0 }
30}
31
32impl SalaryStructureRepository {
33    /// Create a new repository instance.
34    pub fn new(pool: PgPool) -> Self {
35        Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
36    }
37}
38
39/// The exact row a structure definition writes. Mirrors the raw column shape, not the
40/// `SalaryStructure` entity: the insert hard-codes `status = 'active'`, so that column is not a
41/// parameter.
42pub struct NewStructureRow<'a> {
43    pub id: Uuid,
44    pub name: &'a str,
45}
46
47/// Hand-written SalaryStructure SQL. Lives here (not in the write service) per the module's 4-layer
48/// rule: services orchestrate and own the unit of work, repositories hold the SQL.
49impl SalaryStructureRepository {
50    /// Define a salary structure, active from the start.
51    ///
52    /// Takes the CALLER'S connection so the structure and its components commit as ONE unit. The
53    /// caller has already relayed the ambient org request scope onto it (`bind_org_scope_on`,
54    /// ADR-0029) so this passes the tenancy RLS fence — don't re-bind here.
55    pub async fn insert_structure(
56        &self,
57        conn: &mut sqlx::PgConnection,
58        s: &NewStructureRow<'_>,
59    ) -> Result<(), sqlx::Error> {
60        sqlx::query(
61            r#"INSERT INTO payroll.salary_structures (id, name, status)
62               VALUES ($1,$2,'active')"#,
63        )
64        .bind(s.id).bind(s.name)
65        .execute(conn)
66        .await?;
67        Ok(())
68    }
69}
70
71backbone_core::impl_crud_repository!(SalaryStructureRepository, SalaryStructure, soft_delete);