Add create order
This commit is contained in:
parent
e7446e7df2
commit
75a68a317c
@ -1,7 +1,9 @@
|
|||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::Result;
|
use super::Result;
|
||||||
use crate::database::Database;
|
use crate::database::{
|
||||||
|
create_order_item, shopping_cart_set_state, CreateOrderItem, Database, ShoppingCartSetState,
|
||||||
|
};
|
||||||
use crate::db_async_handler;
|
use crate::db_async_handler;
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
|
|
||||||
@ -41,12 +43,22 @@ FROM account_orders
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod create_order {
|
||||||
|
use crate::model::{ProductId, Quantity, QuantityUnit};
|
||||||
|
|
||||||
|
pub struct OrderItem {
|
||||||
|
pub product_id: ProductId,
|
||||||
|
pub quantity: Quantity,
|
||||||
|
pub quantity_unit: QuantityUnit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<AccountOrder>")]
|
||||||
pub struct CreateAccountOrder {
|
pub struct CreateAccountOrder {
|
||||||
pub buyer_id: AccountId,
|
pub buyer_id: AccountId,
|
||||||
pub status: OrderStatus,
|
pub items: Vec<create_order::OrderItem>,
|
||||||
pub order_id: Option<OrderId>,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(CreateAccountOrder, create_account_order, AccountOrder);
|
db_async_handler!(CreateAccountOrder, create_account_order, AccountOrder);
|
||||||
@ -55,22 +67,64 @@ pub(crate) async fn create_account_order(
|
|||||||
msg: CreateAccountOrder,
|
msg: CreateAccountOrder,
|
||||||
db: PgPool,
|
db: PgPool,
|
||||||
) -> Result<AccountOrder> {
|
) -> Result<AccountOrder> {
|
||||||
sqlx::query_as(
|
let mut t = db.begin().await?;
|
||||||
|
|
||||||
|
let order: AccountOrder = match sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO account_orders (buyer_id, status, order_id)
|
INSERT INTO account_orders (buyer_id, status)
|
||||||
VALUES ($1, $2, $3)
|
VALUES ($1, $2, $3)
|
||||||
RETURNING id, buyer_id, status, order_id
|
RETURNING id, buyer_id, status
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.buyer_id)
|
.bind(msg.buyer_id)
|
||||||
.bind(msg.status)
|
.bind(OrderStatus::Confirmed)
|
||||||
.bind(msg.order_id)
|
.fetch_one(&mut t)
|
||||||
.fetch_one(&db)
|
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
{
|
||||||
|
Ok(order) => order,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e:?}");
|
||||||
|
t.rollback().await.ok();
|
||||||
|
return Err(super::Error::AccountOrder(Error::CantCreate));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for item in msg.items {
|
||||||
|
if let Err(e) = create_order_item(
|
||||||
|
CreateOrderItem {
|
||||||
|
product_id: item.product_id,
|
||||||
|
order_id: order.id,
|
||||||
|
quantity: item.quantity,
|
||||||
|
quantity_unit: item.quantity_unit,
|
||||||
|
},
|
||||||
|
&mut t,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
log::error!("{e:?}");
|
||||||
|
|
||||||
|
t.rollback().await.ok();
|
||||||
|
return Err(super::Error::AccountOrder(Error::CantCreate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = shopping_cart_set_state(
|
||||||
|
ShoppingCartSetState {
|
||||||
|
id: msg.shopping_cart_id,
|
||||||
|
state: ShoppingCartState::Closed,
|
||||||
|
},
|
||||||
|
&mut t,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
super::Error::AccountOrder(Error::CantCreate)
|
|
||||||
})
|
t.rollback().await.ok();
|
||||||
|
return Err(super::Error::AccountOrder(Error::CantCreate));
|
||||||
|
};
|
||||||
|
|
||||||
|
t.commit().await.ok();
|
||||||
|
|
||||||
|
Ok(order)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
|
@ -26,7 +26,7 @@ db_async_handler!(AllOrderItems, all_order_items, Vec<OrderItem>);
|
|||||||
pub(crate) async fn all_order_items(_msg: AllOrderItems, pool: PgPool) -> Result<Vec<OrderItem>> {
|
pub(crate) async fn all_order_items(_msg: AllOrderItems, pool: PgPool) -> Result<Vec<OrderItem>> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, buyer_id, status
|
SELECT id, product_id, order_id, quantity, quantity_unit
|
||||||
FROM order_items
|
FROM order_items
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
@ -41,23 +41,34 @@ FROM order_items
|
|||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<OrderItem>")]
|
#[rtype(result = "Result<OrderItem>")]
|
||||||
pub struct CreateOrderItem {
|
pub struct CreateOrderItem {
|
||||||
pub buyer_id: AccountId,
|
pub product_id: ProductId,
|
||||||
pub status: OrderStatus,
|
pub order_id: AccountOrderId,
|
||||||
|
pub quantity: Quantity,
|
||||||
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(CreateOrderItem, create_order_item, OrderItem);
|
db_async_handler!(CreateOrderItem, inner_create_order_item, OrderItem);
|
||||||
|
|
||||||
pub(crate) async fn create_order_item(msg: CreateOrderItem, db: PgPool) -> Result<OrderItem> {
|
async fn inner_create_order_item(msg: CreateOrderItem, db: PgPool) -> Result<OrderItem> {
|
||||||
|
create_order_item(msg, &db).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn create_order_item<'e, E>(msg: CreateOrderItem, db: E) -> Result<OrderItem>
|
||||||
|
where
|
||||||
|
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
|
||||||
|
{
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO order_items (buyer_id, status)
|
INSERT INTO order_items (product_id, order_id, quantity, quantity_unit)
|
||||||
VALUES ($1, $2)
|
VALUES ($1, $2, $3, $4)
|
||||||
RETURNING id, buyer_id, status
|
RETURNING id, product_id, order_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.buyer_id)
|
.bind(msg.product_id)
|
||||||
.bind(msg.status)
|
.bind(msg.order_id)
|
||||||
.fetch_one(&db)
|
.bind(msg.quantity)
|
||||||
|
.bind(msg.quantity_unit)
|
||||||
|
.fetch_one(db)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
@ -76,7 +87,7 @@ db_async_handler!(FindOrderItem, find_order_item, OrderItem);
|
|||||||
pub(crate) async fn find_order_item(msg: FindOrderItem, db: PgPool) -> Result<OrderItem> {
|
pub(crate) async fn find_order_item(msg: FindOrderItem, db: PgPool) -> Result<OrderItem> {
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, buyer_id, status
|
SELECT id, product_id, order_id, quantity, quantity_unit
|
||||||
FROM order_items
|
FROM order_items
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
"#,
|
"#,
|
||||||
|
@ -60,6 +60,7 @@ FROM shopping_cart_items
|
|||||||
#[rtype(result = "Result<Vec<ShoppingCartItem>>")]
|
#[rtype(result = "Result<Vec<ShoppingCartItem>>")]
|
||||||
pub struct AccountShoppingCartItems {
|
pub struct AccountShoppingCartItems {
|
||||||
pub account_id: AccountId,
|
pub account_id: AccountId,
|
||||||
|
pub shopping_cart_id: Option<ShoppingCartId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
db_async_handler!(
|
db_async_handler!(
|
||||||
@ -72,8 +73,24 @@ pub(crate) async fn account_shopping_cart_items(
|
|||||||
msg: AccountShoppingCartItems,
|
msg: AccountShoppingCartItems,
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
) -> Result<Vec<ShoppingCartItem>> {
|
) -> Result<Vec<ShoppingCartItem>> {
|
||||||
sqlx::query_as(
|
match msg.shopping_cart_id {
|
||||||
r#"
|
Some(shopping_cart_id) => sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
SELECT shopping_cart_items.id as id,
|
||||||
|
shopping_cart_items.product_id as product_id,
|
||||||
|
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
||||||
|
shopping_cart_items.quantity as quantity,
|
||||||
|
shopping_cart_items.quantity_unit as quantity_unit
|
||||||
|
FROM shopping_cart_items
|
||||||
|
LEFT JOIN shopping_carts
|
||||||
|
ON shopping_carts.id = shopping_cart_id
|
||||||
|
WHERE shopping_carts.buyer_id = $1 AND shopping_carts.id = $2
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.account_id)
|
||||||
|
.bind(shopping_cart_id),
|
||||||
|
None => sqlx::query_as(
|
||||||
|
r#"
|
||||||
SELECT shopping_cart_items.id as id,
|
SELECT shopping_cart_items.id as id,
|
||||||
shopping_cart_items.product_id as product_id,
|
shopping_cart_items.product_id as product_id,
|
||||||
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
||||||
@ -84,8 +101,9 @@ LEFT JOIN shopping_carts
|
|||||||
ON shopping_carts.id = shopping_cart_id
|
ON shopping_carts.id = shopping_cart_id
|
||||||
WHERE shopping_carts.buyer_id = $1
|
WHERE shopping_carts.buyer_id = $1
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.account_id)
|
.bind(msg.account_id),
|
||||||
|
}
|
||||||
.fetch_all(&pool)
|
.fetch_all(&pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
|
@ -153,6 +153,51 @@ RETURNING id, buyer_id, payment_method, state
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(actix::Message)]
|
||||||
|
#[rtype(result = "Result<ShoppingCart>")]
|
||||||
|
pub struct ShoppingCartSetState {
|
||||||
|
pub id: ShoppingCartId,
|
||||||
|
pub state: ShoppingCartState,
|
||||||
|
}
|
||||||
|
|
||||||
|
db_async_handler!(
|
||||||
|
ShoppingCartSetState,
|
||||||
|
inner_shopping_cart_set_state,
|
||||||
|
ShoppingCart
|
||||||
|
);
|
||||||
|
|
||||||
|
async fn inner_shopping_cart_set_state(
|
||||||
|
msg: ShoppingCartSetState,
|
||||||
|
pool: PgPool,
|
||||||
|
) -> Result<ShoppingCart> {
|
||||||
|
shopping_cart_set_state(msg, &pool).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn shopping_cart_set_state<'e, E>(
|
||||||
|
msg: ShoppingCartSetState,
|
||||||
|
pool: E,
|
||||||
|
) -> Result<ShoppingCart>
|
||||||
|
where
|
||||||
|
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
|
||||||
|
{
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
UPDATE shopping_carts
|
||||||
|
SET state = $2
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id, buyer_id, payment_method, state
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(msg.id)
|
||||||
|
.bind(msg.state)
|
||||||
|
.fetch_one(pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
log::error!("{e:?}");
|
||||||
|
super::Error::ShoppingCart(Error::CantUpdate(msg.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(actix::Message)]
|
#[derive(actix::Message)]
|
||||||
#[rtype(result = "Result<ShoppingCart>")]
|
#[rtype(result = "Result<ShoppingCart>")]
|
||||||
pub struct FindShoppingCart {
|
pub struct FindShoppingCart {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use actix::Addr;
|
use actix::Message;
|
||||||
use actix_web::Message;
|
|
||||||
use sqlx_core::postgres::PgPool;
|
|
||||||
|
|
||||||
use crate::database::{Database, SharedDatabase, self};
|
use crate::database::{self, SharedDatabase};
|
||||||
use crate::model::{AccountOrder, OrderStatus, ShoppingCartId};
|
use crate::model::{
|
||||||
|
AccountId, AccountOrder, OrderStatus, ShoppingCart, ShoppingCartId, ShoppingCartItem,
|
||||||
|
};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! order_async_handler {
|
macro_rules! order_async_handler {
|
||||||
@ -21,7 +21,14 @@ macro_rules! order_async_handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum 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 type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
@ -42,11 +49,73 @@ impl OrderManager {
|
|||||||
#[derive(Message, Debug)]
|
#[derive(Message, Debug)]
|
||||||
#[rtype(result = "Result<AccountOrder>")]
|
#[rtype(result = "Result<AccountOrder>")]
|
||||||
pub struct CreateOrder {
|
pub struct CreateOrder {
|
||||||
|
pub account_id: AccountId,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
order_async_handler!(CreateOrder, create_order, AccountOrder);
|
||||||
|
|
||||||
pub(crate) async fn create_order(msg: CreateOrder, db: SharedDatabase) -> Result<AccountOrder> {
|
pub(crate) async fn create_order(msg: CreateOrder, db: SharedDatabase) -> Result<AccountOrder> {
|
||||||
let cart = match db.send(database)
|
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> {
|
pub fn change(current: OrderStatus, next: OrderStatus) -> Option<OrderStatus> {
|
||||||
|
@ -1 +1 @@
|
|||||||
use crate::model::OrderStatus;
|
|
||||||
|
@ -13,7 +13,7 @@ use jemallocator::Jemalloc;
|
|||||||
use password_hash::SaltString;
|
use password_hash::SaltString;
|
||||||
use validator::{validate_email, validate_length};
|
use validator::{validate_email, validate_length};
|
||||||
|
|
||||||
use crate::actors::{database, token_manager};
|
use crate::actors::{database, order_manager, token_manager};
|
||||||
use crate::logic::encrypt_password;
|
use crate::logic::encrypt_password;
|
||||||
use crate::model::{Email, Login, PassHash, Password, Role};
|
use crate::model::{Email, Login, PassHash, Password, Role};
|
||||||
|
|
||||||
@ -172,6 +172,7 @@ async fn server(opts: ServerOpts) -> Result<()> {
|
|||||||
let config = Arc::new(Config::load());
|
let config = Arc::new(Config::load());
|
||||||
let db = database::Database::build(&opts.db_url()).await?.start();
|
let db = database::Database::build(&opts.db_url()).await?.start();
|
||||||
let token_manager = token_manager::TokenManager::new(db.clone()).start();
|
let token_manager = token_manager::TokenManager::new(db.clone()).start();
|
||||||
|
let order_manager = order_manager::OrderManager::new(db.clone()).start();
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
@ -185,6 +186,7 @@ async fn server(opts: ServerOpts) -> Result<()> {
|
|||||||
.app_data(Data::new(config.clone()))
|
.app_data(Data::new(config.clone()))
|
||||||
.app_data(Data::new(db.clone()))
|
.app_data(Data::new(db.clone()))
|
||||||
.app_data(Data::new(token_manager.clone()))
|
.app_data(Data::new(token_manager.clone()))
|
||||||
|
.app_data(Data::new(order_manager.clone()))
|
||||||
.configure(routes::configure)
|
.configure(routes::configure)
|
||||||
// .default_service(web::to(HttpResponse::Ok))
|
// .default_service(web::to(HttpResponse::Ok))
|
||||||
})
|
})
|
||||||
|
@ -390,7 +390,7 @@ impl PartialEq<PasswordConfirmation> for Password {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, From)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display, From)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct AccountId(RecordId);
|
pub struct AccountId(RecordId);
|
||||||
@ -488,7 +488,7 @@ pub struct Stock {
|
|||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Deref)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct AccountOrderId(RecordId);
|
pub struct AccountOrderId(RecordId);
|
||||||
@ -506,7 +506,7 @@ pub struct AccountOrder {
|
|||||||
pub order_id: Option<OrderId>,
|
pub order_id: Option<OrderId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct OrderItemId(pub RecordId);
|
pub struct OrderItemId(pub RecordId);
|
||||||
@ -515,12 +515,12 @@ pub struct OrderItemId(pub RecordId);
|
|||||||
pub struct OrderItem {
|
pub struct OrderItem {
|
||||||
pub id: OrderItemId,
|
pub id: OrderItemId,
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
pub order_id: OrderItemId,
|
pub order_id: AccountOrderId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct ShoppingCartId(pub RecordId);
|
pub struct ShoppingCartId(pub RecordId);
|
||||||
@ -533,7 +533,7 @@ pub struct ShoppingCart {
|
|||||||
pub state: ShoppingCartState,
|
pub state: ShoppingCartState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)]
|
#[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)]
|
||||||
#[sqlx(transparent)]
|
#[sqlx(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct ShoppingCartItemId(RecordId);
|
pub struct ShoppingCartItemId(RecordId);
|
||||||
|
@ -104,14 +104,14 @@ impl Responder for Error {
|
|||||||
msg: format!("{}", self),
|
msg: format!("{}", self),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Error::Public(PublicError::ApiV1(V1Error::AddItem | V1Error::RemoveItem)) => {
|
Error::Public(PublicError::ApiV1(
|
||||||
HttpResponse::BadRequest()
|
V1Error::AddItem | V1Error::RemoveItem | V1Error::AddOrder,
|
||||||
.content_type("application/json")
|
)) => HttpResponse::BadRequest()
|
||||||
.json(ReqFailure {
|
.content_type("application/json")
|
||||||
success: false,
|
.json(ReqFailure {
|
||||||
msg: format!("{}", self),
|
success: false,
|
||||||
})
|
msg: format!("{}", self),
|
||||||
}
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ pub enum Error {
|
|||||||
RemoveItem,
|
RemoveItem,
|
||||||
#[error("Failed to add shopping cart item")]
|
#[error("Failed to add shopping cart item")]
|
||||||
AddItem,
|
AddItem,
|
||||||
|
|
||||||
|
#[error("Failed to create order")]
|
||||||
|
AddOrder,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn configure(config: &mut ServiceConfig) {
|
pub(crate) fn configure(config: &mut ServiceConfig) {
|
||||||
|
@ -7,14 +7,15 @@ use crate::actors::cart_manager;
|
|||||||
use crate::actors::cart_manager::CartManager;
|
use crate::actors::cart_manager::CartManager;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::model::{
|
use crate::model::{
|
||||||
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCart, ShoppingCartItem,
|
AccountId, ProductId, Quantity, QuantityUnit, ShoppingCart, ShoppingCartId, ShoppingCartItem,
|
||||||
ShoppingCartItemId,
|
ShoppingCartItemId,
|
||||||
};
|
};
|
||||||
|
use crate::order_manager::OrderManager;
|
||||||
use crate::routes::public::api_v1::ShoppingCartError;
|
use crate::routes::public::api_v1::ShoppingCartError;
|
||||||
use crate::routes::public::Error as PublicError;
|
use crate::routes::public::Error as PublicError;
|
||||||
use crate::routes::{RequireUser, Result};
|
use crate::routes::{RequireUser, Result};
|
||||||
use crate::token_manager::TokenManager;
|
use crate::token_manager::TokenManager;
|
||||||
use crate::{database, routes};
|
use crate::{database, order_manager, routes};
|
||||||
|
|
||||||
#[get("/shopping-cart")]
|
#[get("/shopping-cart")]
|
||||||
async fn shopping_cart(
|
async fn shopping_cart(
|
||||||
@ -68,6 +69,7 @@ async fn shopping_cart_items(
|
|||||||
match db
|
match db
|
||||||
.send(database::AccountShoppingCartItems {
|
.send(database::AccountShoppingCartItems {
|
||||||
account_id: cart.buyer_id,
|
account_id: cart.buyer_id,
|
||||||
|
shopping_cart_id: Some(cart.id),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -186,7 +188,41 @@ async fn delete_cart_item(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn create_order() {}
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct CreateOrderInput {
|
||||||
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/order")]
|
||||||
|
pub(crate) async fn create_order(
|
||||||
|
Json(payload): Json<CreateOrderInput>,
|
||||||
|
tm: Data<Addr<TokenManager>>,
|
||||||
|
credentials: BearerAuth,
|
||||||
|
om: Data<Addr<OrderManager>>,
|
||||||
|
) -> routes::Result<HttpResponse> {
|
||||||
|
let (token, _) = credentials.require_user(tm.into_inner()).await?;
|
||||||
|
let order = match om
|
||||||
|
.send(order_manager::CreateOrder {
|
||||||
|
account_id: AccountId::from(token.subject),
|
||||||
|
shopping_cart_id: payload.shopping_cart_id,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Ok(order)) => order,
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
log::error!("{e}");
|
||||||
|
return Err(routes::Error::Public(PublicError::ApiV1(
|
||||||
|
super::Error::AddOrder,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e}");
|
||||||
|
return Err(routes::Error::Public(PublicError::DatabaseConnection));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(HttpResponse::Created().json(order))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn configure(config: &mut ServiceConfig) {
|
pub(crate) fn configure(config: &mut ServiceConfig) {
|
||||||
config.service(scope("")
|
config.service(scope("")
|
||||||
@ -195,5 +231,6 @@ pub(crate) fn configure(config: &mut ServiceConfig) {
|
|||||||
.scope("customer_id role subject audience expiration_time not_before_time issued_at_time"))
|
.scope("customer_id role subject audience expiration_time not_before_time issued_at_time"))
|
||||||
.service(shopping_cart)
|
.service(shopping_cart)
|
||||||
.service(shopping_cart_items)
|
.service(shopping_cart_items)
|
||||||
.service(delete_cart_item));
|
.service(delete_cart_item)
|
||||||
|
.service(create_order));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user