backbone_inventory/infrastructure/persistence/
stock_move_line_repository.rs1use rust_decimal::Decimal;
15use sqlx::{PgPool, Row};
16use uuid::Uuid;
17
18use crate::domain::entity::StockMoveLine;
19
20pub const TABLE_NAME: &str = "inventory.stock_move_lines";
22
23pub struct StockMoveLineRepository(
28 backbone_orm::GenericCrudRepository<StockMoveLine, backbone_orm::SoftDelete>,
29);
30
31impl std::ops::Deref for StockMoveLineRepository {
32 type Target = backbone_orm::GenericCrudRepository<StockMoveLine, backbone_orm::SoftDelete>;
33 fn deref(&self) -> &Self::Target { &self.0 }
34}
35
36impl StockMoveLineRepository {
37 pub fn new(pool: PgPool) -> Self {
39 Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
40 }
41}
42
43pub struct NewMoveLineRow<'a> {
45 pub id: Uuid,
46 pub quantity: Decimal,
47 pub lot_id: Option<Uuid>,
48 pub package_id: Option<Uuid>,
49 pub result_package_id: Option<Uuid>,
50 pub owner_id: Option<Uuid>,
51 pub move_id: Uuid,
52 pub picking_id: Option<Uuid>,
53 pub location_id: Uuid,
54 pub location_dest_id: Uuid,
55 pub item_id: Uuid,
56 pub state: &'a str,
58}
59
60pub struct MoveLineRow {
62 pub id: Uuid,
63 pub quantity: Decimal,
64 pub lot_id: Option<Uuid>,
65 pub package_id: Option<Uuid>,
66 pub result_package_id: Option<Uuid>,
67 pub owner_id: Option<Uuid>,
68 pub state: String,
69 pub move_id: Uuid,
70 pub location_id: Uuid,
71 pub location_dest_id: Uuid,
72 pub item_id: Uuid,
73}
74
75impl StockMoveLineRepository {
76 pub async fn insert_line(
78 &self,
79 conn: &mut sqlx::PgConnection,
80 l: &NewMoveLineRow<'_>,
81 ) -> Result<(), sqlx::Error> {
82 sqlx::query(
83 r#"INSERT INTO inventory.stock_move_lines
84 (id, quantity, lot_id, package_id, result_package_id, owner_id, state, move_id,
85 picking_id, location_id, location_dest_id, item_id)
86 VALUES ($1,$2,$3,$4,$5,$6,$7::move_state,$8,$9,$10,$11,$12)"#,
87 )
88 .bind(l.id).bind(l.quantity).bind(l.lot_id).bind(l.package_id).bind(l.result_package_id)
89 .bind(l.owner_id).bind(l.state).bind(l.move_id).bind(l.picking_id)
90 .bind(l.location_id).bind(l.location_dest_id).bind(l.item_id)
91 .execute(conn)
92 .await?;
93 Ok(())
94 }
95
96 pub async fn retarget_picking(
101 &self,
102 conn: &mut sqlx::PgConnection,
103 move_id: Uuid,
104 picking_id: Uuid,
105 ) -> Result<(), sqlx::Error> {
106 sqlx::query(
107 r#"UPDATE inventory.stock_move_lines
108 SET picking_id = $2
109 WHERE move_id = $1 AND (metadata->>'deleted_at') IS NULL"#,
110 )
111 .bind(move_id)
112 .bind(picking_id)
113 .execute(&mut *conn)
114 .await?;
115 Ok(())
116 }
117
118 pub async fn fetch_lines_for_move(
120 &self,
121 conn: &mut sqlx::PgConnection,
122 move_id: Uuid,
123 ) -> Result<Vec<MoveLineRow>, sqlx::Error> {
124 let rows = sqlx::query(
125 r#"SELECT id, quantity, lot_id, package_id, result_package_id, owner_id,
126 state::text AS state, move_id, location_id, location_dest_id, item_id
127 FROM inventory.stock_move_lines
128 WHERE move_id=$1 AND (metadata->>'deleted_at') IS NULL
129 ORDER BY id"#,
130 )
131 .bind(move_id)
132 .fetch_all(&mut *conn)
133 .await?;
134 Ok(rows.into_iter().map(line_row_of).collect())
135 }
136
137 pub async fn fetch_line(
141 &self,
142 conn: &mut sqlx::PgConnection,
143 move_line_id: Uuid,
144 ) -> Result<Option<MoveLineRow>, sqlx::Error> {
145 let row = sqlx::query(
146 r#"SELECT id, quantity, lot_id, package_id, result_package_id, owner_id,
147 state::text AS state, move_id, location_id, location_dest_id, item_id
148 FROM inventory.stock_move_lines
149 WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
150 )
151 .bind(move_line_id)
152 .fetch_optional(&mut *conn)
153 .await?;
154 Ok(row.map(line_row_of))
155 }
156
157 pub async fn sum_mirror_qty(
160 &self,
161 conn: &mut sqlx::PgConnection,
162 move_id: Uuid,
163 ) -> Result<Decimal, sqlx::Error> {
164 let qty: Option<Decimal> = sqlx::query_scalar(
165 r#"SELECT SUM(quantity) FROM inventory.stock_move_lines
166 WHERE move_id=$1 AND (metadata->>'deleted_at') IS NULL"#,
167 )
168 .bind(move_id)
169 .fetch_one(&mut *conn)
170 .await?;
171 Ok(qty.unwrap_or(Decimal::ZERO))
172 }
173
174 pub async fn set_quantity(
176 &self,
177 conn: &mut sqlx::PgConnection,
178 line_id: Uuid,
179 quantity: Decimal,
180 ) -> Result<(), sqlx::Error> {
181 sqlx::query(
182 r#"UPDATE inventory.stock_move_lines SET quantity=$2
183 WHERE id=$1 AND (metadata->>'deleted_at') IS NULL"#,
184 )
185 .bind(line_id).bind(quantity)
186 .execute(conn)
187 .await?;
188 Ok(())
189 }
190
191 pub async fn mirror_state(
194 &self,
195 conn: &mut sqlx::PgConnection,
196 move_id: Uuid,
197 state: &str,
198 ) -> Result<(), sqlx::Error> {
199 sqlx::query(
200 r#"UPDATE inventory.stock_move_lines SET state=$2::move_state
201 WHERE move_id=$1 AND (metadata->>'deleted_at') IS NULL"#,
202 )
203 .bind(move_id).bind(state)
204 .execute(conn)
205 .await?;
206 Ok(())
207 }
208}
209
210fn line_row_of(r: sqlx::postgres::PgRow) -> MoveLineRow {
211 MoveLineRow {
212 id: r.get("id"),
213 quantity: r.get("quantity"),
214 lot_id: r.get("lot_id"),
215 package_id: r.get("package_id"),
216 result_package_id: r.get("result_package_id"),
217 owner_id: r.get("owner_id"),
218 state: r.get("state"),
219 move_id: r.get("move_id"),
220 location_id: r.get("location_id"),
221 location_dest_id: r.get("location_dest_id"),
222 item_id: r.get("item_id"),
223 }
224}
225
226backbone_core::impl_crud_repository!(StockMoveLineRepository, StockMoveLine, soft_delete);