Update cart and items, add cart state
This commit is contained in:
parent
160c1f2563
commit
5d757e84d5
@ -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>;
|
||||||
|
201
api/src/actors/database/shopping_cart_items.rs
Normal file
201
api/src/actors/database/shopping_cart_items.rs
Normal 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))
|
||||||
|
})
|
||||||
|
}
|
@ -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
|
||||||
"#,
|
"#,
|
||||||
|
@ -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(),
|
||||||
|
@ -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);
|
||||||
|
10
db/migrate/202204161233_add_cart_state.sql
Normal file
10
db/migrate/202204161233_add_cart_state.sql
Normal 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");
|
Loading…
Reference in New Issue
Block a user