use actix::Message; use model::{ProductId, Quantity, QuantityUnit, Stock, StockId}; use sqlx::PgPool; use crate::{MultiLoad, Result}; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Unable to load all stocks")] All, #[error("Unable to create stock")] Create, #[error("Unable to update stock")] Update, #[error("Unable to delete stock")] Delete, #[error("Unable find stock for product")] ProductStock, } #[derive(Message)] #[rtype(result = "Result>")] pub struct AllStocks; crate::db_async_handler!(AllStocks, all_stocks, Vec); async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result> { sqlx::query_as( r#" SELECT id, product_id, quantity, quantity_unit FROM stocks "#, ) .fetch_all(&pool) .await .map_err(|e| { log::error!("{e:?}"); crate::Error::Stock(Error::All) }) } #[derive(Message)] #[rtype(result = "Result")] pub struct CreateStock { pub product_id: ProductId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } crate::db_async_handler!(CreateStock, create_stock, Stock); async fn create_stock(msg: CreateStock, pool: PgPool) -> Result { sqlx::query_as( r#" INSERT INTO stocks (product_id, quantity, quantity_unit) VALUES ($1, $2, $3) RETURNING id, product_id, quantity, quantity_unit "#, ) .bind(msg.product_id) .bind(msg.quantity) .bind(msg.quantity_unit) .fetch_one(&pool) .await .map_err(|e| { log::error!("{e:?}"); crate::Error::Stock(Error::Create) }) } #[derive(Message)] #[rtype(result = "Result")] pub struct UpdateStock { pub id: StockId, pub product_id: ProductId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } crate::db_async_handler!(UpdateStock, update_stock, Stock); async fn update_stock(msg: UpdateStock, pool: PgPool) -> Result { sqlx::query_as( r#" UPDATE stocks SET product_id = $1 AND quantity = $2 quantity_unit = $3 WHERE id = $4 RETURNING id, product_id, quantity, quantity_unit "#, ) .bind(msg.product_id) .bind(msg.quantity) .bind(msg.quantity_unit) .bind(msg.id) .fetch_one(&pool) .await .map_err(|e| { log::error!("{e:?}"); crate::Error::Stock(Error::Update) }) } #[derive(Message)] #[rtype(result = "Result>")] pub struct DeleteStock { pub stock_id: StockId, } crate::db_async_handler!(DeleteStock, delete_stock, Option); async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result> { sqlx::query_as( r#" DELETE FROM stocks WHERE id = $1 RETURNING id, product_id, quantity, quantity_unit "#, ) .bind(msg.stock_id) .fetch_optional(&pool) .await .map_err(|e| { log::error!("{e:?}"); crate::Error::Stock(Error::Delete) }) } #[derive(Message)] #[rtype(result = "Result>")] pub struct ProductsStock { pub product_ids: Vec, } crate::db_async_handler!( ProductsStock, product_stock, Vec, inner_product_stock ); async fn product_stock( msg: ProductsStock, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result> { Ok(MultiLoad::new( pool, r#" SELECT id, product_id, quantity, quantity_unit FROM stocks WHERE "#, " product_id =", ) .load( msg.product_ids.len(), msg.product_ids.into_iter().map(|id| *id), |_e| crate::Error::Stock(Error::ProductStock), ) .await?) }