Product variant in shopping cart
This commit is contained in:
parent
abe0d693e8
commit
f98d1e6903
@ -25,7 +25,7 @@ CREATE TABLE shopping_carts (
|
|||||||
|
|
||||||
CREATE TABLE shopping_cart_items (
|
CREATE TABLE shopping_cart_items (
|
||||||
id serial NOT NULL,
|
id serial NOT NULL,
|
||||||
product_id integer NOT NULL,
|
product_variant_id integer NOT NULL,
|
||||||
shopping_cart_id integer,
|
shopping_cart_id integer,
|
||||||
quantity integer DEFAULT 0 NOT NULL,
|
quantity integer DEFAULT 0 NOT NULL,
|
||||||
quantity_unit "QuantityUnit" NOT NULL,
|
quantity_unit "QuantityUnit" NOT NULL,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use model::v2::ProductVariantId;
|
||||||
use model::*;
|
use model::*;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -18,10 +19,10 @@ pub enum Error {
|
|||||||
CartItems(ShoppingCartId),
|
CartItems(ShoppingCartId),
|
||||||
#[error("Can't find shopping cart item doe to lack of identity")]
|
#[error("Can't find shopping cart item doe to lack of identity")]
|
||||||
NoIdentity,
|
NoIdentity,
|
||||||
#[error("Failed to update shopping cart item with id {shopping_cart_item_id:?} and/or product id {product_id:?}")]
|
#[error("Failed to update shopping cart item with id {shopping_cart_item_id:?} and/or product variant id {product_variant_id:?}")]
|
||||||
Update {
|
Update {
|
||||||
shopping_cart_item_id: Option<ShoppingCartItemId>,
|
shopping_cart_item_id: Option<ShoppingCartItemId>,
|
||||||
product_id: Option<ProductId>,
|
product_variant_id: Option<ProductVariantId>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +37,7 @@ impl AllShoppingCartItems {
|
|||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT shopping_cart_items.id,
|
SELECT shopping_cart_items.id,
|
||||||
shopping_cart_items.product_id,
|
shopping_cart_items.product_variant_id,
|
||||||
shopping_cart_items.shopping_cart_id,
|
shopping_cart_items.shopping_cart_id,
|
||||||
shopping_cart_items.quantity,
|
shopping_cart_items.quantity,
|
||||||
shopping_cart_items.quantity_unit
|
shopping_cart_items.quantity_unit
|
||||||
@ -72,7 +73,7 @@ impl AccountShoppingCartItems {
|
|||||||
Some(shopping_cart_id) => sqlx::query_as(
|
Some(shopping_cart_id) => sqlx::query_as(
|
||||||
r#"
|
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_variant_id as product_variant_id,
|
||||||
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
||||||
shopping_cart_items.quantity as quantity,
|
shopping_cart_items.quantity as quantity,
|
||||||
shopping_cart_items.quantity_unit as quantity_unit
|
shopping_cart_items.quantity_unit as quantity_unit
|
||||||
@ -88,7 +89,7 @@ ORDER BY shopping_cart_items.id
|
|||||||
None => sqlx::query_as(
|
None => sqlx::query_as(
|
||||||
r#"
|
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_variant_id as product_variant_id,
|
||||||
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
shopping_cart_items.shopping_cart_id as shopping_cart_id,
|
||||||
shopping_cart_items.quantity as quantity,
|
shopping_cart_items.quantity as quantity,
|
||||||
shopping_cart_items.quantity_unit as quantity_unit
|
shopping_cart_items.quantity_unit as quantity_unit
|
||||||
@ -112,7 +113,7 @@ ORDER BY shopping_cart_items.id
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CreateShoppingCartItem {
|
pub struct CreateShoppingCartItem {
|
||||||
pub product_id: ProductId,
|
pub product_variant_id: ProductVariantId,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
@ -126,12 +127,12 @@ impl CreateShoppingCartItem {
|
|||||||
let msg = self;
|
let msg = self;
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO shopping_cart_items (product_id, shopping_cart_id, quantity, quantity_unit)
|
INSERT INTO shopping_cart_items (product_variant_id, shopping_cart_id, quantity, quantity_unit)
|
||||||
VALUES ($1, $2, $3, $4)
|
VALUES ($1, $2, $3, $4)
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.product_id)
|
.bind(msg.product_variant_id)
|
||||||
.bind(msg.shopping_cart_id)
|
.bind(msg.shopping_cart_id)
|
||||||
.bind(msg.quantity)
|
.bind(msg.quantity)
|
||||||
.bind(msg.quantity_unit)
|
.bind(msg.quantity_unit)
|
||||||
@ -148,7 +149,7 @@ RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UpdateShoppingCartItem {
|
pub struct UpdateShoppingCartItem {
|
||||||
pub id: ShoppingCartItemId,
|
pub id: ShoppingCartItemId,
|
||||||
pub product_id: ProductId,
|
pub product_variant_id: ProductVariantId,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
@ -163,13 +164,13 @@ impl UpdateShoppingCartItem {
|
|||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
UPDATE shopping_cart_items
|
UPDATE shopping_cart_items
|
||||||
SET product_id = $2, shopping_cart_id = $3, quantity = $4, quantity_unit = $5
|
SET product_variant_id = $2, shopping_cart_id = $3, quantity = $4, quantity_unit = $5
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
||||||
.bind(msg.product_id)
|
.bind(msg.product_variant_id)
|
||||||
.bind(msg.shopping_cart_id)
|
.bind(msg.shopping_cart_id)
|
||||||
.bind(msg.quantity)
|
.bind(msg.quantity)
|
||||||
.bind(msg.quantity_unit)
|
.bind(msg.quantity_unit)
|
||||||
@ -197,7 +198,7 @@ impl DeleteShoppingCartItem {
|
|||||||
r#"
|
r#"
|
||||||
DELETE FROM shopping_cart_items
|
DELETE FROM shopping_cart_items
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.id)
|
.bind(msg.id)
|
||||||
@ -223,7 +224,7 @@ impl FindShoppingCartItem {
|
|||||||
let msg = self;
|
let msg = self;
|
||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id, product_id, shopping_cart_id, quantity, quantity_unit
|
SELECT id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
FROM shopping_cart_items
|
FROM shopping_cart_items
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
"#,
|
"#,
|
||||||
@ -240,7 +241,7 @@ WHERE id = $1
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ActiveCartItemByProduct {
|
pub struct ActiveCartItemByProduct {
|
||||||
pub product_id: ProductId,
|
pub product_variant_id: ProductVariantId,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveCartItemByProduct {
|
impl ActiveCartItemByProduct {
|
||||||
@ -252,19 +253,19 @@ impl ActiveCartItemByProduct {
|
|||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT shopping_cart_items.id,
|
SELECT shopping_cart_items.id,
|
||||||
shopping_cart_items.product_id,
|
shopping_cart_items.product_variant_id,
|
||||||
shopping_cart_items.shopping_cart_id,
|
shopping_cart_items.shopping_cart_id,
|
||||||
shopping_cart_items.quantity,
|
shopping_cart_items.quantity,
|
||||||
shopping_cart_items.quantity_unit
|
shopping_cart_items.quantity_unit
|
||||||
FROM shopping_cart_items
|
FROM shopping_cart_items
|
||||||
INNER JOIN shopping_carts
|
INNER JOIN shopping_carts
|
||||||
ON shopping_cart_items.shopping_cart_id = shopping_carts.id
|
ON shopping_cart_items.shopping_cart_id = shopping_carts.id
|
||||||
WHERE product_id = $1
|
WHERE product_variant_id = $1
|
||||||
AND shopping_carts.state = $2
|
AND shopping_carts.state = $2
|
||||||
ORDER BY shopping_cart_items.id ASC
|
ORDER BY shopping_cart_items.id ASC
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.product_id)
|
.bind(msg.product_variant_id)
|
||||||
.bind(model::ShoppingCartState::Active)
|
.bind(model::ShoppingCartState::Active)
|
||||||
.fetch_optional(t)
|
.fetch_optional(t)
|
||||||
.await
|
.await
|
||||||
@ -290,7 +291,7 @@ impl CartItems {
|
|||||||
sqlx::query_as(
|
sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
SELECT id,
|
SELECT id,
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit
|
quantity_unit
|
||||||
@ -313,7 +314,7 @@ ORDER BY shopping_cart_items.id ASC
|
|||||||
pub struct RemoveCartItem {
|
pub struct RemoveCartItem {
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
pub shopping_cart_item_id: Option<ShoppingCartItemId>,
|
pub shopping_cart_item_id: Option<ShoppingCartItemId>,
|
||||||
pub product_id: Option<ProductId>,
|
pub product_variant_id: Option<ProductVariantId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RemoveCartItem {
|
impl RemoveCartItem {
|
||||||
@ -322,35 +323,35 @@ impl RemoveCartItem {
|
|||||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<Option<ShoppingCartItem>> {
|
) -> Result<Option<ShoppingCartItem>> {
|
||||||
let msg = self;
|
let msg = self;
|
||||||
match (msg.shopping_cart_item_id, msg.product_id) {
|
match (msg.shopping_cart_item_id, msg.product_variant_id) {
|
||||||
(Some(shopping_cart_item_id), None) => sqlx::query_as(
|
(Some(shopping_cart_item_id), None) => sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM shopping_cart_items
|
DELETE FROM shopping_cart_items
|
||||||
WHERE shopping_cart_id = $1 AND id = $2
|
WHERE shopping_cart_id = $1 AND id = $2
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.shopping_cart_id)
|
.bind(msg.shopping_cart_id)
|
||||||
.bind(shopping_cart_item_id),
|
.bind(shopping_cart_item_id),
|
||||||
(Some(shopping_cart_item_id), Some(product_id)) => sqlx::query_as(
|
(Some(shopping_cart_item_id), Some(product_variant_id)) => sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM shopping_cart_items
|
DELETE FROM shopping_cart_items
|
||||||
WHERE shopping_cart_id = $1 AND id = $2 AND product_id = $3
|
WHERE shopping_cart_id = $1 AND id = $2 AND product_variant_id = $3
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.shopping_cart_id)
|
.bind(msg.shopping_cart_id)
|
||||||
.bind(shopping_cart_item_id)
|
.bind(shopping_cart_item_id)
|
||||||
.bind(product_id),
|
.bind(product_variant_id),
|
||||||
(None, Some(product_id)) => sqlx::query_as(
|
(None, Some(product_variant_id)) => sqlx::query_as(
|
||||||
r#"
|
r#"
|
||||||
DELETE FROM shopping_cart_items
|
DELETE FROM shopping_cart_items
|
||||||
WHERE shopping_cart_id = $1 AND product_id = $2
|
WHERE shopping_cart_id = $1 AND product_variant_id = $2
|
||||||
RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
RETURNING id, product_variant_id, shopping_cart_id, quantity, quantity_unit
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(msg.shopping_cart_id)
|
.bind(msg.shopping_cart_id)
|
||||||
.bind(product_id),
|
.bind(product_variant_id),
|
||||||
_ => return Err(Error::NoIdentity),
|
_ => return Err(Error::NoIdentity),
|
||||||
}
|
}
|
||||||
.fetch_optional(t)
|
.fetch_optional(t)
|
||||||
@ -359,7 +360,7 @@ RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
|||||||
tracing::error!("{e:?}");
|
tracing::error!("{e:?}");
|
||||||
Error::Update {
|
Error::Update {
|
||||||
shopping_cart_item_id: msg.shopping_cart_item_id,
|
shopping_cart_item_id: msg.shopping_cart_item_id,
|
||||||
product_id: msg.product_id,
|
product_variant_id: msg.product_variant_id,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -423,7 +424,7 @@ WHERE buyer_id = $1
|
|||||||
async fn test_shopping_cart_item(
|
async fn test_shopping_cart_item(
|
||||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
shopping_cart_id: Option<ShoppingCartId>,
|
shopping_cart_id: Option<ShoppingCartId>,
|
||||||
product_id: Option<ProductId>,
|
product_variant_id: Option<ProductVariantId>,
|
||||||
) -> ShoppingCartItem {
|
) -> ShoppingCartItem {
|
||||||
let shopping_cart_id = match shopping_cart_id {
|
let shopping_cart_id = match shopping_cart_id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
@ -433,12 +434,12 @@ WHERE buyer_id = $1
|
|||||||
.id
|
.id
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let product_id = match product_id {
|
let product_variant_id = match product_variant_id {
|
||||||
Some(id) => id,
|
Some(id) => id,
|
||||||
_ => 1.into(),
|
_ => 1.into(),
|
||||||
};
|
};
|
||||||
CreateShoppingCartItem {
|
CreateShoppingCartItem {
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity: Quantity::from_u32(496879),
|
quantity: Quantity::from_u32(496879),
|
||||||
quantity_unit: QuantityUnit::Gram,
|
quantity_unit: QuantityUnit::Gram,
|
||||||
@ -565,7 +566,7 @@ WHERE buyer_id = $1
|
|||||||
|
|
||||||
let updated = UpdateShoppingCartItem {
|
let updated = UpdateShoppingCartItem {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
product_id: item.product_id,
|
product_variant_id: item.product_variant_id,
|
||||||
shopping_cart_id: item.shopping_cart_id,
|
shopping_cart_id: item.shopping_cart_id,
|
||||||
quantity: Quantity::from_u32(987979879),
|
quantity: Quantity::from_u32(987979879),
|
||||||
quantity_unit: QuantityUnit::Kilogram,
|
quantity_unit: QuantityUnit::Kilogram,
|
||||||
@ -579,7 +580,7 @@ WHERE buyer_id = $1
|
|||||||
updated,
|
updated,
|
||||||
ShoppingCartItem {
|
ShoppingCartItem {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
product_id: item.product_id,
|
product_variant_id: item.product_variant_id,
|
||||||
shopping_cart_id: item.shopping_cart_id,
|
shopping_cart_id: item.shopping_cart_id,
|
||||||
quantity: Quantity::from_u32(987979879),
|
quantity: Quantity::from_u32(987979879),
|
||||||
quantity_unit: QuantityUnit::Kilogram,
|
quantity_unit: QuantityUnit::Kilogram,
|
||||||
|
@ -50,6 +50,10 @@ pub enum Error {
|
|||||||
UpdateProductStock(ProductVariantId),
|
UpdateProductStock(ProductVariantId),
|
||||||
#[error("Failed to load all product photos")]
|
#[error("Failed to load all product photos")]
|
||||||
AllProductPhotos,
|
AllProductPhotos,
|
||||||
|
#[error("Failed to load products {0:?}")]
|
||||||
|
FindProducts(Vec<ProductVariantId>),
|
||||||
|
#[error("Failed to load product variants {0:?}")]
|
||||||
|
FindProductVariants(Vec<ProductVariantId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod rpc {
|
pub mod rpc {
|
||||||
@ -113,6 +117,14 @@ pub mod rpc {
|
|||||||
|
|
||||||
/// List of products with stock size and photos
|
/// List of products with stock size and photos
|
||||||
async fn detailed_products(input: detailed_products::Input) -> detailed_products::Output;
|
async fn detailed_products(input: detailed_products::Input) -> detailed_products::Output;
|
||||||
|
|
||||||
|
/// List of products for shopping cart
|
||||||
|
async fn shopping_cart_products(input: find_products::Input) -> find_products::Output;
|
||||||
|
|
||||||
|
/// List of products variants for shopping cart
|
||||||
|
async fn shopping_cart_product_variants(
|
||||||
|
input: find_product_variants::Input,
|
||||||
|
) -> find_product_variants::Output;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_client(config: SharedAppConfig) -> StocksClient {
|
pub async fn create_client(config: SharedAppConfig) -> StocksClient {
|
||||||
|
@ -80,6 +80,46 @@ pub mod delete_product {
|
|||||||
pub type Output = Result<Details, Error>;
|
pub type Output = Result<Details, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod find_products {
|
||||||
|
use model::v2::*;
|
||||||
|
|
||||||
|
use crate::stocks::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Input {
|
||||||
|
pub variant_ids: Vec<ProductVariantId>,
|
||||||
|
pub limit: Limit,
|
||||||
|
pub offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Details {
|
||||||
|
pub products: Vec<Product>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Output = Result<Details, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod find_product_variants {
|
||||||
|
use model::v2::*;
|
||||||
|
|
||||||
|
use crate::stocks::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Input {
|
||||||
|
pub variant_ids: Vec<ProductVariantId>,
|
||||||
|
pub limit: Limit,
|
||||||
|
pub offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Details {
|
||||||
|
pub product_variants: Vec<ProductVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Output = Result<Details, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Topic {
|
pub enum Topic {
|
||||||
ProductCreated,
|
ProductCreated,
|
||||||
|
@ -173,7 +173,7 @@ pub struct Order {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct ShoppingCartItem {
|
pub struct ShoppingCartItem {
|
||||||
pub id: ShoppingCartItemId,
|
pub id: ShoppingCartItemId,
|
||||||
pub product_id: ProductId,
|
pub product_variant_id: ProductVariantId,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
@ -183,7 +183,7 @@ impl From<crate::ShoppingCartItem> for ShoppingCartItem {
|
|||||||
fn from(
|
fn from(
|
||||||
crate::ShoppingCartItem {
|
crate::ShoppingCartItem {
|
||||||
id,
|
id,
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit,
|
quantity_unit,
|
||||||
@ -191,7 +191,7 @@ impl From<crate::ShoppingCartItem> for ShoppingCartItem {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit,
|
quantity_unit,
|
||||||
@ -233,13 +233,13 @@ impl From<(crate::ShoppingCart, Vec<crate::ShoppingCartItem>)> for ShoppingCart
|
|||||||
.map(
|
.map(
|
||||||
|crate::ShoppingCartItem {
|
|crate::ShoppingCartItem {
|
||||||
id,
|
id,
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit,
|
quantity_unit,
|
||||||
}| ShoppingCartItem {
|
}| ShoppingCartItem {
|
||||||
id,
|
id,
|
||||||
product_id,
|
product_variant_id,
|
||||||
shopping_cart_id,
|
shopping_cart_id,
|
||||||
quantity,
|
quantity,
|
||||||
quantity_unit,
|
quantity_unit,
|
||||||
|
@ -14,6 +14,7 @@ use serde::de::{Error, Visitor};
|
|||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
pub use crate::encrypt::*;
|
pub use crate::encrypt::*;
|
||||||
|
use crate::v2::ProductVariantId;
|
||||||
|
|
||||||
#[derive(Debug, Hash, thiserror::Error)]
|
#[derive(Debug, Hash, thiserror::Error)]
|
||||||
pub enum TransformError {
|
pub enum TransformError {
|
||||||
@ -961,7 +962,7 @@ pub mod v2 {
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
Day, Days, FileName, Limit, LocalPath, Offset, PhotoId, Price, ProductCategory, ProductId,
|
Day, Days, FileName, Limit, LocalPath, Offset, PhotoId, Price, ProductCategory, ProductId,
|
||||||
ProductLongDesc, ProductName, ProductPhotoId, ProductShortDesc, Quantity, QuantityUnit,
|
ProductLongDesc, ProductName, ProductPhotoId, ProductShortDesc, Quantity, QuantityUnit,
|
||||||
RecordId, StockId, UniqueName,
|
RecordId, ShoppingCartId, StockId, UniqueName,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||||
@ -988,7 +989,7 @@ pub mod v2 {
|
|||||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Deref, From)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Deref, From)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct ProductVariantId(RecordId);
|
pub struct ProductVariantId(pub RecordId);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct DetailedProductVariant {
|
pub struct DetailedProductVariant {
|
||||||
@ -1215,7 +1216,7 @@ pub struct ShoppingCartItemId(RecordId);
|
|||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ShoppingCartItem {
|
pub struct ShoppingCartItem {
|
||||||
pub id: ShoppingCartItemId,
|
pub id: ShoppingCartItemId,
|
||||||
pub product_id: ProductId,
|
pub product_variant_id: ProductVariantId,
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub shopping_cart_id: ShoppingCartId,
|
||||||
pub quantity: Quantity,
|
pub quantity: Quantity,
|
||||||
pub quantity_unit: QuantityUnit,
|
pub quantity_unit: QuantityUnit,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use channels::stocks::{create_product, delete_product, update_product, Error};
|
use channels::stocks::{create_product, delete_product, find_products, update_product, Error};
|
||||||
use channels::AsyncClient;
|
use channels::AsyncClient;
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
use db_utils::PgT;
|
use db_utils::PgT;
|
||||||
@ -195,6 +195,49 @@ pub async fn inner_delete_product(
|
|||||||
dbm.run(t).await.map(|product| (input.product_id, product))
|
dbm.run(t).await.map(|product| (input.product_id, product))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_products(
|
||||||
|
input: find_products::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> find_products::Output {
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
|
||||||
|
match inner_find_products(input, &mut t).await {
|
||||||
|
Ok(details) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
Err(Error::InternalServerError)
|
||||||
|
} else {
|
||||||
|
Ok(details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
t.rollback().await.ok();
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_find_products(
|
||||||
|
input: find_products::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> find_products::Output {
|
||||||
|
let dbm = crate::db::ShoppingCartProducts {
|
||||||
|
variant_ids: input.variant_ids.clone(),
|
||||||
|
limit: input.limit,
|
||||||
|
offset: input.offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
match dbm.run(t).await {
|
||||||
|
Ok(products) => Ok(find_products::Details { products }),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("{}", e);
|
||||||
|
Err(Error::FindProducts(input.variant_ids))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use config::UpdateConfig;
|
use config::UpdateConfig;
|
||||||
@ -303,4 +346,11 @@ mod tests {
|
|||||||
let (id, _new_product) = res.unwrap();
|
let (id, _new_product) = res.unwrap();
|
||||||
assert_eq!(id, product.id);
|
assert_eq!(id, product.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn shopping_cart_products() {
|
||||||
|
testx::db_t_ref!(t);
|
||||||
|
|
||||||
|
testx::db_rollback!(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use channels::stocks::{
|
use channels::stocks::{
|
||||||
create_product_variant, delete_product_variant, update_product_variant, Error,
|
create_product_variant, delete_product_variant, find_product_variants, update_product_variant,
|
||||||
|
Error,
|
||||||
};
|
};
|
||||||
use channels::AsyncClient;
|
use channels::AsyncClient;
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
@ -151,6 +152,51 @@ async fn inner_delete_product_variant(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn find_product_variants(
|
||||||
|
input: find_product_variants::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> find_product_variants::Output {
|
||||||
|
let variant_ids = input.variant_ids.clone();
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
match inner_find_product_variants(input, &mut t).await {
|
||||||
|
Ok(details) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::warn!("{}", e);
|
||||||
|
Err(Error::FindProductVariants(variant_ids))
|
||||||
|
} else {
|
||||||
|
Ok(details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
t.rollback().await.ok();
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn inner_find_product_variants(
|
||||||
|
input: find_product_variants::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> find_product_variants::Output {
|
||||||
|
let dbm = crate::db::ShoppingCartProductVariants {
|
||||||
|
variant_ids: input.variant_ids.clone(),
|
||||||
|
limit: input.limit,
|
||||||
|
offset: input.offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
match dbm.run(t).await {
|
||||||
|
Ok(variants) => Ok(find_product_variants::Details {
|
||||||
|
product_variants: variants,
|
||||||
|
}),
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("{}", e);
|
||||||
|
Err(Error::FindProductVariants(input.variant_ids))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use channels::stocks::{
|
use channels::stocks::{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use db_utils::PgT;
|
use db_utils::PgT;
|
||||||
use model::v2::*;
|
use model::v2::*;
|
||||||
|
use model::Ranged;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@ -7,12 +8,54 @@ pub enum Error {
|
|||||||
CreateProductVariant,
|
CreateProductVariant,
|
||||||
#[error("Failed to load variants for products {0:?}")]
|
#[error("Failed to load variants for products {0:?}")]
|
||||||
ProductsVariants(Vec<ProductId>),
|
ProductsVariants(Vec<ProductId>),
|
||||||
|
#[error("Failed to load variants with ids {0:?}")]
|
||||||
|
FindProductsVariants(Vec<ProductVariantId>),
|
||||||
#[error("Failed to delete product variant {0:?}")]
|
#[error("Failed to delete product variant {0:?}")]
|
||||||
DeleteProductVariant(ProductVariantId),
|
DeleteProductVariant(ProductVariantId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ShoppingCartProductVariants {
|
||||||
|
pub variant_ids: Vec<ProductVariantId>,
|
||||||
|
pub limit: Limit,
|
||||||
|
pub offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShoppingCartProductVariants {
|
||||||
|
pub async fn run(self, t: &mut PgT<'_>) -> Result<Vec<ProductVariant>> {
|
||||||
|
db_utils::MultiLoad::new(
|
||||||
|
t,
|
||||||
|
r#"
|
||||||
|
SELECT id,
|
||||||
|
product_id,
|
||||||
|
name,
|
||||||
|
short_description,
|
||||||
|
long_description,
|
||||||
|
price
|
||||||
|
FROM product_variants
|
||||||
|
WHERE
|
||||||
|
"#,
|
||||||
|
"shopping_cart_id = $1",
|
||||||
|
)
|
||||||
|
.with_sorting("products.id")
|
||||||
|
.with_padding(
|
||||||
|
self.limit.in_range(1..200).into_raw(),
|
||||||
|
self.offset.in_range(0..u32::MAX).into_raw(),
|
||||||
|
)
|
||||||
|
.load(
|
||||||
|
self.variant_ids.len(),
|
||||||
|
self.variant_ids.iter().map(|id| id.0),
|
||||||
|
|e| {
|
||||||
|
tracing::warn!("{e:?}");
|
||||||
|
Error::FindProductsVariants(self.variant_ids.clone())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CreateProductVariant {
|
pub struct CreateProductVariant {
|
||||||
pub product_id: ProductId,
|
pub product_id: ProductId,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use db_utils::PgT;
|
use db_utils::PgT;
|
||||||
use model::v2::*;
|
use model::v2::*;
|
||||||
use model::{Ranged, ShoppingCartId};
|
use model::Ranged;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Unable to load all products")]
|
#[error("Unable to load all products")]
|
||||||
All,
|
All,
|
||||||
@ -12,12 +12,10 @@ pub enum Error {
|
|||||||
Update(ProductId),
|
Update(ProductId),
|
||||||
#[error("Unable to delete product")]
|
#[error("Unable to delete product")]
|
||||||
Delete(ProductId),
|
Delete(ProductId),
|
||||||
#[error("Unable to find products for shopping cart")]
|
|
||||||
ShoppingCartProducts(ShoppingCartId),
|
|
||||||
#[error("Product with id {0} can't be found")]
|
#[error("Product with id {0} can't be found")]
|
||||||
Single(ProductId),
|
Single(ProductId),
|
||||||
#[error("Failed to load products for given ids")]
|
#[error("Failed to load products for given variant ids")]
|
||||||
FindProducts,
|
FindProductsByVariants(Vec<ProductVariantId>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -44,8 +42,8 @@ ORDER BY id
|
|||||||
LIMIT $1 OFFSET $2
|
LIMIT $1 OFFSET $2
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.bind(self.limit.max(1).min(200))
|
.bind(self.limit.in_range(1..200))
|
||||||
.bind(self.offset.max(0))
|
.bind(self.offset.in_range(0..u32::MAX))
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
@ -193,68 +191,38 @@ RETURNING id,
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ShoppingCartProducts {
|
pub struct ShoppingCartProducts {
|
||||||
pub shopping_cart_id: ShoppingCartId,
|
pub variant_ids: Vec<ProductVariantId>,
|
||||||
pub limit: Limit,
|
pub limit: Limit,
|
||||||
pub offset: Offset,
|
pub offset: Offset,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShoppingCartProducts {
|
impl ShoppingCartProducts {
|
||||||
pub async fn run(self, t: &mut PgT<'_>) -> Result<Vec<Product>> {
|
pub async fn run(self, t: &mut PgT<'_>) -> Result<Vec<Product>> {
|
||||||
sqlx::query_as(
|
db_utils::MultiLoad::new(
|
||||||
|
t,
|
||||||
r#"
|
r#"
|
||||||
SELECT products.id,
|
SELECT p.id,
|
||||||
products.name,
|
p.name,
|
||||||
products.category,
|
p.category,
|
||||||
products.deliver_days_flag
|
p.deliver_days_flag
|
||||||
FROM products
|
FROM products p
|
||||||
INNER JOIN shopping_cart_items ON shopping_cart_items.product_id = products.id
|
INNER JOIN product_variants v
|
||||||
WHERE shopping_cart_id = $1
|
ON v.product_id = p.id
|
||||||
ORDER BY products.id
|
|
||||||
LIMIT $2 OFFSET $3
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(self.shopping_cart_id)
|
|
||||||
.bind(self.limit.in_range(1..200))
|
|
||||||
.bind(self.offset.in_range(0..u32::MAX))
|
|
||||||
.fetch_all(t)
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
tracing::warn!("{e:?}");
|
|
||||||
Error::ShoppingCartProducts(self.shopping_cart_id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FindProducts {
|
|
||||||
pub product_ids: Vec<ProductId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FindProducts {
|
|
||||||
pub async fn run(
|
|
||||||
self,
|
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
|
||||||
) -> Result<Vec<Product>> {
|
|
||||||
let mut loader = db_utils::MultiLoad::new(
|
|
||||||
pool,
|
|
||||||
r#"
|
|
||||||
SELECT id,
|
|
||||||
name,
|
|
||||||
category,
|
|
||||||
deliver_days_flag
|
|
||||||
FROM products
|
|
||||||
WHERE
|
WHERE
|
||||||
"#,
|
"#,
|
||||||
"products.id =",
|
"p.id = $1",
|
||||||
|
)
|
||||||
|
.with_sorting("p.id")
|
||||||
|
.with_padding(
|
||||||
|
self.limit.in_range(1..200).into_raw(),
|
||||||
|
self.offset.in_range(0..u32::MAX).into_raw(),
|
||||||
)
|
)
|
||||||
.with_size(200);
|
|
||||||
loader
|
|
||||||
.load(
|
.load(
|
||||||
self.product_ids.len(),
|
self.variant_ids.len(),
|
||||||
self.product_ids.into_iter().map(|id| *id),
|
self.variant_ids.iter().map(|id| id.0),
|
||||||
|e| {
|
|e| {
|
||||||
tracing::warn!("{e:?}");
|
tracing::warn!("{e:?}");
|
||||||
Error::FindProducts
|
Error::FindProductsByVariants(self.variant_ids.clone())
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
@ -10,8 +10,8 @@ pub enum Error {
|
|||||||
Update(StockId),
|
Update(StockId),
|
||||||
#[error("Unable to delete stock {0:?}")]
|
#[error("Unable to delete stock {0:?}")]
|
||||||
Delete(StockId),
|
Delete(StockId),
|
||||||
#[error("Unable to delete all stock for variant {0:?}")]
|
// #[error("Unable to delete all stock for variant {0:?}")]
|
||||||
DeleteAllProductStocks(ProductId),
|
// DeleteAllProductStocks(ProductId),
|
||||||
#[error("Unable find stock for product")]
|
#[error("Unable find stock for product")]
|
||||||
ProductVariantStock,
|
ProductVariantStock,
|
||||||
#[error("Stock {0:?} does not exists")]
|
#[error("Stock {0:?} does not exists")]
|
||||||
|
@ -125,6 +125,22 @@ pub mod rpc {
|
|||||||
) -> detailed_products::Output {
|
) -> detailed_products::Output {
|
||||||
actions::detailed_products(input, self.db, self.mqtt_client, self.config).await
|
actions::detailed_products(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn shopping_cart_products(
|
||||||
|
self,
|
||||||
|
_: context::Context,
|
||||||
|
input: find_products::Input,
|
||||||
|
) -> find_products::Output {
|
||||||
|
actions::find_products(input, self.db, self.mqtt_client, self.config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shopping_cart_product_variants(
|
||||||
|
self,
|
||||||
|
_: context::Context,
|
||||||
|
input: find_product_variants::Input,
|
||||||
|
) -> find_product_variants::Output {
|
||||||
|
actions::find_product_variants(input, self.db, self.mqtt_client, self.config).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user