backbone_pos/infrastructure/persistence/
pos_invoice_item_repository.rs1use anyhow::Result;
15use rust_decimal::Decimal;
16use sqlx::{PgPool, Row};
17use uuid::Uuid;
18
19use backbone_orm::org_scope::fetch_all_rows_scoped;
24
25use crate::domain::entity::PosInvoiceItem;
26
27pub const TABLE_NAME: &str = "pos.pos_invoice_items";
29
30pub struct PosInvoiceItemRepository(
35 backbone_orm::GenericCrudRepository<PosInvoiceItem, backbone_orm::SoftDelete>,
36);
37
38impl std::ops::Deref for PosInvoiceItemRepository {
39 type Target = backbone_orm::GenericCrudRepository<PosInvoiceItem, backbone_orm::SoftDelete>;
40 fn deref(&self) -> &Self::Target { &self.0 }
41}
42
43impl PosInvoiceItemRepository {
44 pub fn new(pool: PgPool) -> Self {
46 Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
47 }
48}
49
50pub struct NewInvoiceItemRow<'a> {
53 pub id: Uuid,
54 pub pos_invoice_id: Uuid,
55 pub client_uuid: Option<Uuid>,
57 pub item_id: Uuid,
58 pub description: Option<&'a str>,
59 pub course: Option<i32>,
62 pub quantity: Decimal,
63 pub unit_price: Decimal,
64 pub discount_amount: Decimal,
65 pub net_amount: Decimal,
66 pub revenue_account_id: Option<Uuid>,
67}
68
69pub struct RevenueLineRow {
71 pub item_id: Uuid,
72 pub net_amount: Decimal,
73 pub revenue_account_id: Option<Uuid>,
74}
75
76pub struct QuantityLineRow {
78 pub item_id: Uuid,
79 pub quantity: Decimal,
80}
81
82pub struct ReceiptLineRow {
84 pub description: Option<String>,
85 pub item_id: Uuid,
86 pub course: Option<i32>,
88 pub quantity: Decimal,
89 pub unit_price: Decimal,
90 pub discount_amount: Decimal,
91 pub net_amount: Decimal,
92}
93
94impl PosInvoiceItemRepository {
97 pub async fn insert_line(
103 &self,
104 conn: &mut sqlx::PgConnection,
105 l: &NewInvoiceItemRow<'_>,
106 ) -> Result<(), sqlx::Error> {
107 sqlx::query(
108 r#"INSERT INTO pos.pos_invoice_items
109 (id, pos_invoice_id, client_uuid, item_id, description, course, quantity, unit_price, discount_amount, net_amount, revenue_account_id)
110 VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)"#,
111 )
112 .bind(l.id).bind(l.pos_invoice_id).bind(l.client_uuid).bind(l.item_id).bind(l.description).bind(l.course).bind(l.quantity)
113 .bind(l.unit_price).bind(l.discount_amount).bind(l.net_amount).bind(l.revenue_account_id)
114 .execute(conn)
115 .await?;
116 Ok(())
117 }
118
119 pub async fn fetch_revenue_lines(
127 &self,
128 pool: &PgPool,
129 pos_invoice_id: Uuid,
130 ) -> Result<Vec<RevenueLineRow>, sqlx::Error> {
131 let rows = fetch_all_rows_scoped(
132 pool,
133 sqlx::query(
134 "SELECT item_id, net_amount, revenue_account_id FROM pos.pos_invoice_items WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL",
135 )
136 .bind(pos_invoice_id),
137 )
138 .await?;
139 Ok(rows.iter().map(|r| RevenueLineRow {
140 item_id: r.get("item_id"), net_amount: r.get("net_amount"),
141 revenue_account_id: r.get("revenue_account_id"),
142 }).collect())
143 }
144
145 pub async fn fetch_quantity_lines(
148 &self,
149 pool: &PgPool,
150 pos_invoice_id: Uuid,
151 ) -> Result<Vec<QuantityLineRow>, sqlx::Error> {
152 let rows = fetch_all_rows_scoped(
153 pool,
154 sqlx::query(
155 "SELECT item_id, quantity FROM pos.pos_invoice_items WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL",
156 )
157 .bind(pos_invoice_id),
158 )
159 .await?;
160 Ok(rows.iter().map(|r| QuantityLineRow {
161 item_id: r.get("item_id"), quantity: r.get("quantity"),
162 }).collect())
163 }
164
165 pub async fn soft_delete_lines_for_ticket(
171 &self,
172 conn: &mut sqlx::PgConnection,
173 pos_invoice_id: Uuid,
174 now: chrono::DateTime<chrono::Utc>,
175 ) -> Result<(), sqlx::Error> {
176 sqlx::query(
177 r#"UPDATE pos.pos_invoice_items
178 SET metadata = jsonb_set(COALESCE(metadata, '{}'::jsonb), '{deleted_at}', to_jsonb($2::timestamptz))
179 WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL"#,
180 )
181 .bind(pos_invoice_id)
182 .bind(now)
183 .execute(conn)
184 .await?;
185 Ok(())
186 }
187
188 pub async fn fetch_receipt_lines(
191 &self,
192 pool: &PgPool,
193 pos_invoice_id: Uuid,
194 ) -> Result<Vec<ReceiptLineRow>, sqlx::Error> {
195 let rows = fetch_all_rows_scoped(
196 pool,
197 sqlx::query(
198 "SELECT description, item_id, course, quantity, unit_price, discount_amount, net_amount
199 FROM pos.pos_invoice_items WHERE pos_invoice_id=$1 AND (metadata->>'deleted_at') IS NULL
200 ORDER BY (metadata->>'created_at')",
201 )
202 .bind(pos_invoice_id),
203 )
204 .await?;
205 Ok(rows.iter().map(|r| ReceiptLineRow {
206 description: r.get("description"), item_id: r.get("item_id"), course: r.get("course"),
207 quantity: r.get("quantity"),
208 unit_price: r.get("unit_price"), discount_amount: r.get("discount_amount"),
209 net_amount: r.get("net_amount"),
210 }).collect())
211 }
212}
213
214backbone_core::impl_crud_repository!(PosInvoiceItemRepository, PosInvoiceItem, soft_delete);