airlab_lib/model/
view_lot.rs

1use crate::ctx::Ctx;
2use crate::model::ModelManager;
3use crate::model::Result;
4use crate::model::clone::CloneFilter;
5use crate::model::lot::{Lot, LotBmc, LotFilter};
6use crate::model::provider::{Provider, ProviderBmc, ProviderFilter};
7use crate::model::validation::Validation;
8use crate::model::validation::{ValidationBmc, ValidationFilter};
9use crate::model::view_clone::{ViewClone, ViewCloneBmc};
10use modql::field::Fields;
11use modql::filter::ListOptions;
12use serde::{Deserialize, Serialize};
13use serde_json::json;
14use sqlx::FromRow;
15use std::collections::{HashMap, hash_map::Entry};
16
17#[derive(Serialize, Deserialize, Debug, Clone, Default)]
18pub struct ViewLot {
19    pub id: i32,
20    #[serde(rename = "createdBy")]
21    pub created_by: Option<i32>,
22    pub storage: String,
23    #[serde(rename = "cloneId")]
24    pub clone_id: i32,
25    #[serde(rename = "providerId")]
26    pub provider_id: Option<i32>,
27    pub name: String,
28    pub reference: Option<String>,
29    #[serde(rename = "requestedBy")]
30    pub requested_by: Option<i32>,
31    #[serde(rename = "approvedBy")]
32    pub approved_by: Option<i32>,
33    #[serde(rename = "orderedBy")]
34    pub ordered_by: Option<i32>,
35    #[serde(rename = "receivedBy")]
36    pub received_by: Option<i32>,
37    #[serde(rename = "finishedBy")]
38    pub finished_by: Option<i32>,
39    pub status: Option<i16>,
40    pub purpose: Option<String>,
41    pub number: Option<String>,
42    pub url: Option<String>,
43    pub price: Option<String>,
44    pub note: Option<String>,
45    #[serde(rename = "requestedAt")]
46    pub requested_at: Option<chrono::DateTime<chrono::Utc>>,
47    #[serde(rename = "orderedAt")]
48    pub ordered_at: Option<chrono::DateTime<chrono::Utc>>,
49    #[serde(rename = "receivedAt")]
50    pub received_at: Option<chrono::DateTime<chrono::Utc>>,
51    #[serde(rename = "finishedAt")]
52    pub finished_at: Option<chrono::DateTime<chrono::Utc>>,
53    pub clone: ViewClone,
54    pub validations: Vec<Validation>,
55    pub provider: Option<Provider>,
56}
57
58#[derive(Debug, Clone, Fields, FromRow, Serialize, Deserialize, Default)]
59pub struct ViewLotDetails {
60    pub id: i32,
61    #[serde(rename = "groupId")]
62    pub group_id: i32,
63    #[serde(rename = "createdBy")]
64    pub created_by: i32,
65    #[serde(rename = "cloneId")]
66    pub clone_id: i32,
67    #[serde(rename = "providerId")]
68    pub provider_id: Option<i32>,
69    pub name: Option<String>,
70    pub reference: Option<String>,
71    #[serde(rename = "requestedBy")]
72    pub requested_by: Option<i32>,
73    #[serde(rename = "approvedBy")]
74    pub approved_by: Option<i32>,
75    #[serde(rename = "orderedBy")]
76    pub ordered_by: Option<i32>,
77    #[serde(rename = "receivedBy")]
78    pub received_by: Option<i32>,
79    #[serde(rename = "finishedBy")]
80    pub finished_by: Option<i32>,
81    pub number: Option<String>,
82    pub status: Option<i16>,
83    pub purpose: Option<String>,
84    pub url: Option<String>,
85    pub price: Option<String>,
86    pub note: Option<String>,
87    #[serde(rename = "requestedAt")]
88    pub requested_at: Option<chrono::DateTime<chrono::Utc>>,
89    #[serde(rename = "orderedAt")]
90    pub ordered_at: Option<chrono::DateTime<chrono::Utc>>,
91    #[serde(rename = "receivedAt")]
92    pub received_at: Option<chrono::DateTime<chrono::Utc>>,
93    #[serde(rename = "finishedAt")]
94    pub finished_at: Option<chrono::DateTime<chrono::Utc>>,
95    pub meta: Option<serde_json::Value>,
96    pub clone: Option<serde_json::Value>,
97    pub provider: Option<serde_json::Value>,
98    #[serde(rename = "requestedByUser")]
99    pub requested_by_user: Option<serde_json::Value>,
100    #[serde(rename = "approvedByUser")]
101    pub approved_by_user: Option<serde_json::Value>,
102    #[serde(rename = "orderedByUser")]
103    pub ordered_by_user: Option<serde_json::Value>,
104    #[serde(rename = "receivedByUser")]
105    pub received_by_user: Option<serde_json::Value>,
106    #[serde(rename = "finishedByUser")]
107    pub finished_by_user: Option<serde_json::Value>,
108}
109
110pub struct ViewLotBmc;
111
112impl ViewLotBmc {
113    pub async fn get_details(_ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<ViewLotDetails> {
114        let stmt = r#"SELECT
115  lot.id,
116  lot.group_id,
117  lot.created_by,
118  lot.clone_id,
119  lot.provider_id,
120  lot.name,
121  lot.reference,
122  lot.requested_by,
123  lot.approved_by,
124  lot.ordered_by,
125  lot.received_by,
126  lot.finished_by,
127  lot.number,
128  lot.status,
129  lot.purpose,
130  lot.url,
131  lot.price,
132  lot.note,
133  lot.requested_at,
134  lot.ordered_at,
135  lot.received_at,
136  lot.finished_at,
137  lot.meta,
138  jsonb_build_object(
139    'id', reu.id,
140    'name', reu.name,
141    'isAdmin', reu.is_admin
142  ) AS requested_by_user,
143  jsonb_build_object(
144    'id', apu.id,
145    'name', apu.name,
146    'isAdmin', apu.is_admin
147  ) AS approved_by_user,
148  jsonb_build_object(
149    'id', oru.id,
150    'name', oru.name,
151    'isAdmin', oru.is_admin
152  ) AS ordered_by_user,
153  jsonb_build_object(
154    'id', rcu.id,
155    'name', rcu.name,
156    'isAdmin', rcu.is_admin
157  ) AS received_by_user,
158  jsonb_build_object(
159    'id', fiu.id,
160    'name', fiu.name,
161    'isAdmin', fiu.is_admin
162  ) AS finished_by_user,
163  jsonb_build_object(
164    'id', cl.id,
165    'name', cl.name
166  ) AS clone,
167  jsonb_build_object(
168    'id', pv.id,
169    'name', pv.name
170  ) AS provider
171FROM
172  lot
173LEFT JOIN 
174    member rem ON lot.requested_by = rem.id
175LEFT JOIN 
176    "user" reu ON rem.user_id = reu.id
177LEFT JOIN 
178    member apm ON lot.approved_by = apm.id
179LEFT JOIN 
180    "user" apu ON apm.user_id = apu.id
181LEFT JOIN 
182    member orm ON lot.ordered_by = orm.id
183LEFT JOIN 
184    "user" oru ON orm.user_id = oru.id
185LEFT JOIN 
186    member rcm ON lot.received_by = rcm.id
187LEFT JOIN 
188    "user" rcu ON rcm.user_id = rcu.id
189LEFT JOIN 
190    member fim ON lot.finished_by = fim.id
191LEFT JOIN 
192    "user" fiu ON fim.user_id = fiu.id
193LEFT JOIN 
194    clone cl ON lot.clone_id = cl.id
195LEFT JOIN 
196    provider pv ON lot.provider_id = pv.id
197WHERE
198  lot.id = $1"#;
199        let item: ViewLotDetails = sqlx::query_as::<_, ViewLotDetails>(stmt)
200            .bind(id)
201            .fetch_one(&mm.db)
202            .await?;
203        Ok(item)
204    }
205    pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<ViewLot> {
206        let item = LotBmc::get(ctx, mm, id).await?;
207        let clone = ViewCloneBmc::get(ctx, mm, item.clone_id).await?;
208        let provider = match item.provider_id {
209            Some(pid) => Some(ProviderBmc::get(ctx, mm, pid).await?),
210            None => None,
211        };
212        let ret = ViewLot {
213            id: item.id,
214            created_by: item.created_by,
215            storage: "storage_const".into(),
216            clone_id: item.clone_id,
217            provider_id: item.provider_id,
218            name: item.name,
219            reference: item.reference,
220            requested_by: item.requested_by,
221            approved_by: item.approved_by,
222            ordered_by: item.ordered_by,
223            received_by: item.received_by,
224            finished_by: item.finished_by,
225            status: item.status,
226            purpose: item.purpose,
227            number: item.number,
228            url: item.url,
229            price: item.price,
230            note: item.note,
231            requested_at: item.requested_at,
232            ordered_at: item.ordered_at,
233            received_at: item.received_at,
234            finished_at: item.finished_at,
235            clone,
236            validations: vec![],
237            provider,
238        };
239        Ok(ret)
240    }
241    pub async fn list(
242        ctx: &Ctx,
243        mm: &ModelManager,
244        group_id: Option<i32>,
245        filters: Option<Vec<LotFilter>>,
246        list_options: Option<ListOptions>,
247    ) -> Result<Vec<ViewLot>> {
248        let mut clone_map = HashMap::new();
249        let mut provider_map = HashMap::new();
250        let mut validation_map = HashMap::new();
251        if let Some(group_id) = group_id {
252            let clone_filters: Option<Vec<CloneFilter>> = match serde_json::from_value(json!([
253                {
254                    "group_id": {"$eq":group_id}
255                }
256            ])) {
257                Ok(ok) => Some(ok),
258                Err(_) => None,
259            };
260            let op = ListOptions {
261                limit: Some(10_000),
262                ..Default::default()
263            };
264            let clones: Vec<ViewClone> =
265                ViewCloneBmc::list(ctx, mm, Some(group_id), clone_filters, Some(op)).await?;
266            for clone in clones {
267                clone_map.insert(clone.id, clone);
268            }
269
270            let provider_filters: Option<Vec<ProviderFilter>> =
271                match serde_json::from_value(json!([
272                    {
273                        "group_id": {"$eq":group_id}
274                    }
275                ])) {
276                    Ok(ok) => Some(ok),
277                    Err(_) => None,
278                };
279            let providers: Vec<Provider> =
280                ProviderBmc::list(ctx, mm, provider_filters, None).await?;
281            for provider in providers {
282                provider_map.insert(provider.id, provider);
283            }
284            let validation_op = ListOptions {
285                limit: Some(100_000),
286                ..Default::default()
287            };
288
289            let validation_filters: Option<Vec<ValidationFilter>> =
290                match serde_json::from_value(json!([
291                    {
292                        "group_id": {"$eq":group_id}
293                    }
294                ])) {
295                    Ok(ok) => Some(ok),
296                    Err(_) => None,
297                };
298            let validations: Vec<Validation> =
299                ValidationBmc::list(ctx, mm, validation_filters, Some(validation_op)).await?;
300            for validation in validations {
301                let h = match validation_map.entry(validation.lot_id.unwrap_or(0)) {
302                    Entry::Occupied(o) => o.into_mut(),
303                    Entry::Vacant(v) => v.insert(vec![]),
304                };
305                h.push(validation);
306            }
307        }
308
309        let lots: Vec<Lot> = LotBmc::list(ctx, mm, filters, list_options).await?;
310        let mut returns = vec![];
311        for item in lots {
312            let clone = match clone_map.get(&{ item.clone_id }) {
313                Some(v) => v.clone(),
314                None => ViewClone::default(),
315            };
316            let mut provider = None;
317
318            if let Some(provider_id) = &item.provider_id {
319                provider = provider_map.get(provider_id).cloned();
320            }
321
322            returns.push(ViewLot {
323                id: item.id,
324                storage: "storage_const".into(),
325                created_by: item.created_by,
326                clone_id: item.clone_id,
327                provider_id: item.provider_id,
328                name: item.name,
329                reference: item.reference,
330                requested_by: item.requested_by,
331                approved_by: item.approved_by,
332                ordered_by: item.ordered_by,
333                received_by: item.received_by,
334                finished_by: item.finished_by,
335                status: item.status,
336                purpose: item.purpose,
337                number: item.number,
338                url: item.url,
339                price: item.price,
340                note: item.note,
341                requested_at: item.requested_at,
342                ordered_at: item.ordered_at,
343                received_at: item.received_at,
344                finished_at: item.finished_at,
345                clone,
346                validations: validation_map.get(&item.id).unwrap_or(&vec![]).clone(),
347                provider,
348            });
349        }
350
351        Ok(returns)
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358    use anyhow::Result;
359
360    #[ignore]
361    #[tokio::test]
362    async fn test_view_lot_list_all_ok() -> Result<()> {
363        let mm = ModelManager::new().await?;
364        let ctx = Ctx::root_ctx();
365
366        let lots = ViewLotBmc::list(&ctx, &mm, Some(1000), None, None).await?;
367
368        let _lots: Vec<ViewLot> = lots
369            .into_iter()
370            .filter(|t| t.name.starts_with("test_list_all_ok-lot"))
371            .collect();
372
373        Ok(())
374    }
375}