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