Update cart and items, add cart state

This commit is contained in:
eraden 2022-04-16 12:48:38 +02:00
parent 160c1f2563
commit 5d757e84d5
6 changed files with 266 additions and 11 deletions

View File

@ -5,6 +5,7 @@ pub use account_orders::*;
pub use accounts::*; pub use accounts::*;
pub use order_items::*; pub use order_items::*;
pub use products::*; pub use products::*;
pub use shopping_cart_items::*;
pub use shopping_carts::*; pub use shopping_carts::*;
pub use stocks::*; pub use stocks::*;
@ -12,6 +13,7 @@ mod account_orders;
mod accounts; mod accounts;
mod order_items; mod order_items;
mod products; mod products;
mod shopping_cart_items;
mod shopping_carts; mod shopping_carts;
mod stocks; mod stocks;
@ -45,6 +47,8 @@ pub enum Error {
OrderItem(order_items::Error), OrderItem(order_items::Error),
#[error("{0}")] #[error("{0}")]
ShoppingCart(shopping_carts::Error), ShoppingCart(shopping_carts::Error),
#[error("{0}")]
ShoppingCartItem(shopping_cart_items::Error),
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;

View File

@ -0,0 +1,201 @@
use crate::async_handler;
use actix::{Handler, ResponseActFuture, WrapFuture};
use sqlx::PgPool;
use crate::database::Database;
use crate::model::*;
use super::Result;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Can't create shopping cart item")]
CantCreate,
#[error("Can't update shopping cart item")]
CantUpdate(ShoppingCartItemId),
#[error("Shopping cart does not exists")]
NotExists,
#[error("Failed to load all shopping cart items")]
All,
#[error("Failed to load account shopping cart items")]
AccountCarts,
#[error("Failed to load items for shopping cart {0}")]
CartItems(ShoppingCartId),
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<ShoppingCartItem>>")]
pub struct AllShoppingCartItems;
async_handler!(AllShoppingCartItems, all_shopping_cart_items, Vec<ShoppingCartItem>);
pub async fn all_shopping_cart_items(
_msg: AllShoppingCartItems,
pool: PgPool,
) -> Result<Vec<ShoppingCartItem>> {
sqlx::query_as(
r#"
SELECT id, product_id, shopping_cart_id, quantity, quantity_unit
FROM shopping_cart_items
"#,
)
.fetch_all(&pool)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::All)
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<ShoppingCartItem>>")]
pub struct AccountShoppingCartItems {
pub account_id: AccountId,
}
async_handler!(AccountShoppingCartItems, account_shopping_cart_items, Vec<ShoppingCartItem>);
pub async fn account_shopping_cart_items(
msg: AccountShoppingCartItems,
pool: PgPool,
) -> Result<Vec<ShoppingCartItem>> {
sqlx::query_as(
r#"
SELECT id, product_id, shopping_cart_id, quantity, quantity_unit
FROM shopping_cart_items
WHERE buyer_id = $1
"#,
)
.bind(msg.account_id)
.fetch_all(&pool)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::AccountCarts)
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<ShoppingCartItem>")]
pub struct CreateShoppingCartItem {
pub product_id: ProductId,
pub shopping_cart_id: ShoppingCartId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}
async_handler!(CreateShoppingCartItem, create_shopping_cart_item, ShoppingCartItem);
async fn create_shopping_cart_item(
msg: CreateShoppingCartItem,
db: PgPool,
) -> Result<ShoppingCartItem> {
sqlx::query_as(
r#"
INSERT INTO shopping_cart_items (product_id, shopping_cart_id, quantity, quantity_unit)
VALUES ($1, $2, $3, $4)
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
"#,
)
.bind(msg.product_id)
.bind(msg.shopping_cart_id)
.bind(msg.quantity)
.bind(msg.quantity_unit)
.fetch_one(&db)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::CantCreate)
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<ShoppingCartItem>")]
pub struct UpdateShoppingCartItem {
pub id: ShoppingCartItemId,
pub product_id: ProductId,
pub shopping_cart_id: ShoppingCartId,
pub quantity: Quantity,
pub quantity_unit: QuantityUnit,
}
async_handler!(UpdateShoppingCartItem, update_shopping_cart_item, ShoppingCartItem);
async fn update_shopping_cart_item(
msg: UpdateShoppingCartItem,
db: PgPool,
) -> Result<ShoppingCartItem> {
sqlx::query_as(
r#"
UPDATE shopping_cart_items
SET product_id = $2 AND shopping_cart_id = $3 AND quantity = $4 AND quantity_unit = $5
WHERE id = $1
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
"#,
)
.bind(msg.id)
.bind(msg.product_id)
.bind(msg.shopping_cart_id)
.bind(msg.quantity)
.bind(msg.quantity_unit)
.fetch_one(&db)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::CantUpdate(msg.id))
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<ShoppingCartItem>")]
pub struct FindShoppingCartItem {
pub id: ShoppingCartItemId,
}
async_handler!(FindShoppingCartItem, find_shopping_cart_item, ShoppingCartItem);
async fn find_shopping_cart_item(
msg: FindShoppingCartItem,
db: PgPool,
) -> Result<ShoppingCartItem> {
sqlx::query_as(
r#"
SELECT id, product_id, shopping_cart_id, quantity, quantity_unit
FROM shopping_cart_items
WHERE id = $1
"#,
)
.bind(msg.id)
.fetch_one(&db)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::NotExists)
})
}
#[derive(actix::Message)]
#[rtype(result = "Result<Vec<ShoppingCartItem>>")]
pub struct CartItems {
pub shopping_cart_id: ShoppingCartId,
}
async_handler!(CartItems, cart_items, Vec<ShoppingCartItem>);
async fn cart_items(msg: CartItems, pool: PgPool) -> Result<Vec<ShoppingCartItem>> {
let shopping_cart_id = msg.shopping_cart_id;
sqlx::query_as(
r#"
SELECT id, product_id, shopping_cart_id, quantity, quantity_unit
FROM shopping_cart_items
WHERE shopping_cart_id = $1
"#,
)
.bind(msg.shopping_cart_id)
.fetch_all(&pool)
.await
.map_err(|e| {
log::error!("{e:?}");
super::Error::ShoppingCartItem(Error::CartItems(shopping_cart_id))
})
}

View File

@ -11,8 +11,8 @@ use super::Result;
pub enum Error { pub enum Error {
#[error("Can't create shopping cart")] #[error("Can't create shopping cart")]
CantCreate, CantCreate,
#[error("Can't find shopping cart does to lack of identity")] #[error("Can't update shopping cart {0}")]
NoIdentity, CantUpdate(ShoppingCartId),
#[error("Shopping cart does not exists")] #[error("Shopping cart does not exists")]
NotExists, NotExists,
#[error("Failed to load all shopping carts")] #[error("Failed to load all shopping carts")]
@ -30,7 +30,7 @@ async_handler!(AllShoppingCarts, all_shopping_carts, Vec<ShoppingCart>);
pub async fn all_shopping_carts(_msg: AllShoppingCarts, pool: PgPool) -> Result<Vec<ShoppingCart>> { pub async fn all_shopping_carts(_msg: AllShoppingCarts, pool: PgPool) -> Result<Vec<ShoppingCart>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, payment_method SELECT id, buyer_id, payment_method, state
FROM shopping_carts FROM shopping_carts
"#, "#,
) )
@ -56,12 +56,12 @@ pub async fn account_shopping_carts(
) -> Result<Vec<ShoppingCart>> { ) -> Result<Vec<ShoppingCart>> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, payment_method SELECT id, buyer_id, payment_method, state
FROM shopping_carts FROM shopping_carts
WHERE buyer_id = $1 WHERE buyer_id = $1
"#, "#,
) )
.bind(msg.acocunt_id) .bind(msg.account_id)
.fetch_all(&pool) .fetch_all(&pool)
.await .await
.map_err(|e| { .map_err(|e| {
@ -84,7 +84,7 @@ async fn create_shopping_cart(msg: CreateShoppingCart, db: PgPool) -> Result<Sho
r#" r#"
INSERT INTO shopping_carts (buyer_id, payment_method) INSERT INTO shopping_carts (buyer_id, payment_method)
VALUES ($1, $2) VALUES ($1, $2)
RETURNING id, buyer_id, payment_method RETURNING id, buyer_id, payment_method, state
"#, "#,
) )
.bind(msg.buyer_id) .bind(msg.buyer_id)
@ -97,6 +97,38 @@ RETURNING id, buyer_id, payment_method
}) })
} }
#[derive(actix::Message)]
#[rtype(result = "Result<ShoppingCart>")]
pub struct UpdateShoppingCart {
pub id: ShoppingCartId,
pub buyer_id: AccountId,
pub payment_method: PaymentMethod,
pub state: ShoppingCartState,
}
async_handler!(UpdateShoppingCart, update_shopping_cart, ShoppingCart);
async fn update_shopping_cart(msg: UpdateShoppingCart, db: PgPool) -> Result<ShoppingCart> {
sqlx::query_as(
r#"
UPDATE shopping_carts
SET buyer_id = $2 AND payment_method = $2 AND state = $4
WHERE id = $1
RETURNING id, buyer_id, payment_method, state
"#,
)
.bind(msg.id)
.bind(msg.buyer_id)
.bind(msg.payment_method)
.bind(msg.state)
.fetch_one(&db)
.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 {
@ -108,7 +140,7 @@ async_handler!(FindShoppingCart, find_shopping_cart, ShoppingCart);
async fn find_shopping_cart(msg: FindShoppingCart, db: PgPool) -> Result<ShoppingCart> { async fn find_shopping_cart(msg: FindShoppingCart, db: PgPool) -> Result<ShoppingCart> {
sqlx::query_as( sqlx::query_as(
r#" r#"
SELECT id, buyer_id, payment_method SELECT id, buyer_id, payment_method, state
FROM shopping_carts FROM shopping_carts
WHERE id = $1 WHERE id = $1
"#, "#,

View File

@ -161,7 +161,7 @@ async fn server(opts: ServerOpts) -> Result<()> {
App::new() App::new()
.wrap(Logger::default()) .wrap(Logger::default())
.wrap(actix_web::middleware::Compress::default()) .wrap(actix_web::middleware::Compress::default())
.wrap(actix_web::middleware::NormalizePath::default()) .wrap(actix_web::middleware::NormalizePath::trim())
.wrap(SessionMiddleware::new( .wrap(SessionMiddleware::new(
RedisActorSessionStore::new(redis_connection_string), RedisActorSessionStore::new(redis_connection_string),
secret_key.clone(), secret_key.clone(),

View File

@ -54,6 +54,13 @@ pub enum PaymentMethod {
PaymentOnTheSpot, PaymentOnTheSpot,
} }
#[derive(sqlx::Type, Copy, Clone, Debug, Display, Deserialize, Serialize)]
#[sqlx(rename_all = "lowercase")]
pub enum ShoppingCartState {
Active,
Closed,
}
#[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[derive(sqlx::Type, Serialize, Deserialize, Deref)]
#[sqlx(transparent)] #[sqlx(transparent)]
#[serde(transparent)] #[serde(transparent)]
@ -280,7 +287,7 @@ pub struct AccountOrder {
pub status: OrderStatus, pub status: OrderStatus,
} }
#[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref)]
#[sqlx(transparent)] #[sqlx(transparent)]
#[serde(transparent)] #[serde(transparent)]
pub struct OrderItemId(pub RecordId); pub struct OrderItemId(pub RecordId);
@ -294,7 +301,7 @@ pub struct OrderItem {
pub quantity_unit: QuantityUnit, pub quantity_unit: QuantityUnit,
} }
#[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)]
#[sqlx(transparent)] #[sqlx(transparent)]
#[serde(transparent)] #[serde(transparent)]
pub struct ShoppingCartId(pub RecordId); pub struct ShoppingCartId(pub RecordId);
@ -304,9 +311,10 @@ pub struct ShoppingCart {
pub id: ShoppingCartId, pub id: ShoppingCartId,
pub buyer_id: AccountId, pub buyer_id: AccountId,
pub payment_method: PaymentMethod, pub payment_method: PaymentMethod,
pub state: ShoppingCartState,
} }
#[derive(sqlx::Type, Serialize, Deserialize, Deref)] #[derive(sqlx::Type, Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)]
#[sqlx(transparent)] #[sqlx(transparent)]
#[serde(transparent)] #[serde(transparent)]
pub struct ShoppingCartItemId(pub RecordId); pub struct ShoppingCartItemId(pub RecordId);

View File

@ -0,0 +1,10 @@
CREATE TYPE "ShoppingCartState" AS ENUM (
'active',
'closed'
);
ALTER TABLE shopping_carts
ADD COLUMN "state" "ShoppingCartState" NOT NULL DEFAULT 'active';
ALTER TABLE shopping_carts
ADD CONSTRAINT single_active_cart UNIQUE (buyer_id, "state");