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);