329 lines
8.3 KiB
Rust
329 lines
8.3 KiB
Rust
#[cfg(feature = "dummy")]
|
|
use fake::Fake;
|
|
use model::*;
|
|
|
|
use super::Result;
|
|
use crate::db_async_handler;
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, 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<Vec<OrderItem>>")]
|
|
pub struct AllOrderItems;
|
|
|
|
db_async_handler!(
|
|
AllOrderItems,
|
|
all_order_items,
|
|
Vec<OrderItem>,
|
|
inner_all_order_items
|
|
);
|
|
|
|
pub(crate) async fn all_order_items(
|
|
_msg: AllOrderItems,
|
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
) -> Result<Vec<OrderItem>> {
|
|
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()
|
|
})
|
|
}
|
|
|
|
#[derive(actix::Message)]
|
|
#[rtype(result = "Result<OrderItem>")]
|
|
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<OrderItem> {
|
|
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<OrderItem>")]
|
|
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<OrderItem> {
|
|
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<Vec<OrderItem>>")]
|
|
pub struct OrderItems {
|
|
pub order_id: OrderId,
|
|
}
|
|
|
|
db_async_handler!(OrderItems, order_items, Vec<OrderItem>, inner_order_items);
|
|
|
|
pub(crate) async fn order_items(
|
|
msg: OrderItems,
|
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
) -> Result<Vec<OrderItem>> {
|
|
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<OrderId>,
|
|
) -> 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_ref!(t);
|
|
|
|
test_order_item(&mut t, None).await;
|
|
|
|
testx::db_rollback!(t);
|
|
}
|
|
|
|
#[actix::test]
|
|
async fn order_items() {
|
|
testx::db_t_ref!(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));
|
|
}
|
|
}
|