#[cfg(feature = "dummy")] use fake::Fake; use model::*; use super::Result; use crate::db_async_handler; #[derive(Debug, Copy, Clone, PartialEq, serde::Serialize, thiserror::Error)] pub enum Error { #[error("Can't create order item")] CantCreate, #[error("Can't find order item doe to lack of identity")] NoIdentity, #[error("Order item does not exists")] NotExists, #[error("Failed to load all order items")] All, #[error("Failed to load order items")] OrderItems, } #[derive(actix::Message)] #[rtype(result = "Result>")] pub struct AllOrderItems; db_async_handler!( AllOrderItems, all_order_items, Vec, inner_all_order_items ); pub(crate) async fn all_order_items( _msg: AllOrderItems, t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result> { sqlx::query_as( r#" SELECT id, product_id, order_id, quantity, quantity_unit FROM order_items ORDER BY id DESC "#, ) .fetch_all(t) .await .map_err(|e| { tracing::error!("{e:?}"); Error::All.into() }) } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(actix::Message)] #[rtype(result = "Result")] pub struct CreateOrderItem { pub product_id: ProductId, pub order_id: OrderId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } db_async_handler!( CreateOrderItem, create_order_item, OrderItem, inner_create_order_item ); pub(crate) async fn create_order_item( msg: CreateOrderItem, t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result { sqlx::query_as( r#" INSERT INTO order_items (product_id, order_id, quantity, quantity_unit) VALUES ($1, $2, $3, $4) RETURNING id, product_id, order_id, quantity, quantity_unit "#, ) .bind(msg.product_id) .bind(msg.order_id) .bind(msg.quantity) .bind(msg.quantity_unit) .fetch_one(t) .await .map_err(|e| { tracing::error!("{e:?}"); dbg!(e); super::Error::OrderItem(Error::CantCreate) }) } #[derive(actix::Message)] #[rtype(result = "Result")] pub struct FindOrderItem { pub id: OrderItemId, } db_async_handler!(FindOrderItem, find_order_item, OrderItem, inner_find_order); pub(crate) async fn find_order_item( msg: FindOrderItem, t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result { sqlx::query_as( r#" SELECT id, product_id, order_id, quantity, quantity_unit FROM order_items WHERE id = $1 "#, ) .bind(msg.id) .fetch_one(t) .await .map_err(|e| { tracing::error!("{e:?}"); Error::NotExists.into() }) } #[derive(actix::Message)] #[rtype(result = "Result>")] pub struct OrderItems { pub order_id: OrderId, } db_async_handler!(OrderItems, order_items, Vec, inner_order_items); pub(crate) async fn order_items( msg: OrderItems, t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> Result> { sqlx::query_as( r#" SELECT id, product_id, order_id, quantity, quantity_unit FROM order_items WHERE order_id = $1 ORDER BY id DESC "#, ) .bind(msg.order_id) .fetch_all(t) .await .map_err(|e| { tracing::error!("{e:?}"); Error::OrderItems.into() }) } #[cfg(test)] mod tests { use config::UpdateConfig; use fake::Fake; use model::*; use uuid::Uuid; pub struct NoOpts; impl UpdateConfig for NoOpts {} use crate::*; async fn test_order_address( t: &mut sqlx::Transaction<'_, sqlx::Postgres>, ) -> model::OrderAddress { use fake::faker::address::en; let zip: String = en::ZipCode().fake(); create_order_address( CreateOrderAddress { name: Default::default(), email: Default::default(), street: Default::default(), city: Default::default(), country: Default::default(), zip: Zip::new(zip), phone: Default::default(), }, t, ) .await .unwrap() } async fn test_account(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> FullAccount { use fake::faker::internet::en; let login: String = en::Username().fake(); let email: String = en::FreeEmail().fake(); let hash: String = en::Password(11..20).fake(); create_account( CreateAccount { email: Email::new(email), login: Login::new(login), pass_hash: PassHash::new(hash), role: Role::Admin, }, t, ) .await .unwrap() } async fn test_shopping_cart( t: &mut sqlx::Transaction<'_, sqlx::Postgres>, buyer_id: AccountId, ) -> ShoppingCart { let cart = create_shopping_cart( CreateShoppingCart { buyer_id, payment_method: PaymentMethod::PaymentOnTheSpot, }, &mut *t, ) .await .unwrap(); update_shopping_cart( UpdateShoppingCart { id: cart.id, buyer_id: cart.buyer_id, payment_method: cart.payment_method, state: ShoppingCartState::Active, checkout_notes: None, }, &mut *t, ) .await .unwrap() } async fn test_order(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Order { let buyer_id = test_account(t).await.id; crate::create_order( CreateOrder { buyer_id, items: vec![], shopping_cart_id: Some(test_shopping_cart(t, buyer_id).await.id), checkout_notes: None, delivery_address_id: test_order_address(t).await.id, }, t, ) .await .unwrap() } async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product { crate::create_product( CreateProduct { name: ProductName::new(format!("{}", Uuid::new_v4())), short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())), long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())), category: None, price: Price::from_u32(4688), deliver_days_flag: Day::Friday | Day::Saturday | Day::Sunday, }, t, ) .await .unwrap() } async fn test_order_item( t: &mut sqlx::Transaction<'_, sqlx::Postgres>, order_id: Option, ) -> OrderItem { let order_id = match order_id { Some(id) => id, _ => test_order(&mut *t).await.id, }; let product_id = test_product(&mut *t).await.id; super::create_order_item( CreateOrderItem { product_id, order_id, quantity: Default::default(), quantity_unit: QuantityUnit::Gram, }, t, ) .await .unwrap() } #[actix::test] async fn create() { testx::db_t!(t); test_order_item(&mut t, None).await; testx::db_rollback!(t); } #[actix::test] async fn order_items() { testx::db_t!(t); let order1 = test_order(&mut t).await; test_order_item(&mut t, Some(order1.id)).await; test_order_item(&mut t, Some(order1.id)).await; test_order_item(&mut t, Some(order1.id)).await; let mut expected = vec![]; let order2 = test_order(&mut t).await; let item1 = test_order_item(&mut t, Some(order2.id)).await; let item2 = test_order_item(&mut t, Some(order2.id)).await; let item3 = test_order_item(&mut t, Some(order2.id)).await; expected.push(item3); expected.push(item2); expected.push(item1); let order3 = test_order(&mut t).await; test_order_item(&mut t, Some(order3.id)).await; test_order_item(&mut t, Some(order3.id)).await; test_order_item(&mut t, Some(order3.id)).await; let loaded = super::order_items( OrderItems { order_id: order2.id, }, &mut t, ) .await; testx::db_rollback!(t); assert_eq!(loaded, Ok(expected)); } }