163 lines
3.6 KiB
Rust
163 lines
3.6 KiB
Rust
use actix::Message;
|
|
use model::{ProductId, Quantity, QuantityUnit, Stock, StockId};
|
|
use sqlx::PgPool;
|
|
|
|
use crate::{MultiLoad, Result};
|
|
|
|
#[derive(Debug, Copy, Clone, serde::Serialize, 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<Vec<model::Stock>>")]
|
|
pub struct AllStocks;
|
|
|
|
crate::db_async_handler!(AllStocks, all_stocks, Vec<Stock>);
|
|
|
|
async fn all_stocks(_msg: AllStocks, pool: PgPool) -> Result<Vec<model::Stock>> {
|
|
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<model::Stock>")]
|
|
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<model::Stock> {
|
|
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<model::Stock>")]
|
|
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<model::Stock> {
|
|
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<Option<model::Stock>>")]
|
|
pub struct DeleteStock {
|
|
pub stock_id: StockId,
|
|
}
|
|
|
|
crate::db_async_handler!(DeleteStock, delete_stock, Option<model::Stock>);
|
|
|
|
async fn delete_stock(msg: DeleteStock, pool: PgPool) -> Result<Option<Stock>> {
|
|
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<Vec<model::Stock>>")]
|
|
pub struct ProductsStock {
|
|
pub product_ids: Vec<ProductId>,
|
|
}
|
|
|
|
crate::db_async_handler!(
|
|
ProductsStock,
|
|
product_stock,
|
|
Vec<model::Stock>,
|
|
inner_product_stock
|
|
);
|
|
|
|
async fn product_stock(
|
|
msg: ProductsStock,
|
|
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
) -> Result<Vec<model::Stock>> {
|
|
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?)
|
|
}
|