bazzar/api/src/actors/order_manager.rs
2022-04-20 16:09:09 +02:00

140 lines
3.9 KiB
Rust

use actix::Message;
use crate::database::{self, SharedDatabase};
use crate::model::{
AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem,
};
#[macro_export]
macro_rules! order_async_handler {
($msg: ty, $async: ident, $res: ty) => {
impl actix::Handler<$msg> for OrderManager {
type Result = actix::ResponseActFuture<Self, Result<$res>>;
fn handle(&mut self, msg: $msg, _ctx: &mut Self::Context) -> Self::Result {
use actix::WrapFuture;
let db = self.db.clone();
Box::pin(async { $async(msg, db).await }.into_actor(self))
}
}
};
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Database actor failed")]
DatabaseInternal,
#[error("Shopping cart does not exists")]
ShoppingCart,
#[error("Failed to create account order")]
CreateAccountOrder,
}
pub type Result<T> = std::result::Result<T, Error>;
pub struct OrderManager {
db: SharedDatabase,
}
impl actix::Actor for OrderManager {
type Context = actix::Context<Self>;
}
impl OrderManager {
pub fn new(db: SharedDatabase) -> Self {
Self { db }
}
}
#[derive(Message, Debug)]
#[rtype(result = "Result<AccountOrder>")]
pub struct CreateOrder {
pub account_id: AccountId,
pub shopping_cart_id: ShoppingCartId,
}
order_async_handler!(CreateOrder, create_order, AccountOrder);
pub(crate) async fn create_order(msg: CreateOrder, db: SharedDatabase) -> Result<AccountOrder> {
let cart: ShoppingCart = match db
.send(database::FindShoppingCart {
id: msg.shopping_cart_id,
})
.await
{
Ok(Ok(cart)) => cart,
Ok(Err(e)) => {
log::error!("{e}");
return Err(Error::ShoppingCart);
}
Err(e) => {
log::error!("{e:?}");
return Err(Error::DatabaseInternal);
}
};
let items: Vec<ShoppingCartItem> = match db
.send(database::AccountShoppingCartItems {
account_id: cart.buyer_id,
shopping_cart_id: Some(cart.id),
})
.await
{
Ok(Ok(items)) => items,
Ok(Err(e)) => {
log::error!("{e}");
return Err(Error::ShoppingCart);
}
Err(e) => {
log::error!("{e:?}");
return Err(Error::DatabaseInternal);
}
};
let order = match db
.send(database::CreateAccountOrder {
shopping_cart_id: cart.id,
buyer_id: msg.account_id,
items: items
.into_iter()
.map(|item| database::create_order::OrderItem {
product_id: item.product_id,
quantity: item.quantity,
quantity_unit: item.quantity_unit,
})
.collect(),
})
.await
{
Ok(Ok(order)) => order,
Ok(Err(e)) => {
log::error!("{e}");
return Err(Error::CreateAccountOrder);
}
Err(e) => {
log::error!("{e:?}");
return Err(Error::DatabaseInternal);
}
};
Ok(order)
}
pub fn change(current: OrderStatus, next: OrderStatus) -> Option<OrderStatus> {
match (current, next) {
// paying
(OrderStatus::Confirmed, OrderStatus::Payed) => Some(OrderStatus::Payed),
// delivering
(OrderStatus::Confirmed | OrderStatus::Payed, OrderStatus::Delivered) => {
Some(OrderStatus::Delivered)
}
// cancelling
(OrderStatus::Confirmed, OrderStatus::Cancelled) => Some(OrderStatus::Cancelled),
(OrderStatus::Payed, OrderStatus::Cancelled) => Some(OrderStatus::RequireRefund),
(OrderStatus::Payed, OrderStatus::RequireRefund) => Some(OrderStatus::RequireRefund),
(OrderStatus::RequireRefund, OrderStatus::Refunded) => Some(OrderStatus::Refunded),
_ => None,
}
}