Product variant in shopping cart

This commit is contained in:
eraden 2022-11-23 16:03:58 +01:00
parent abe0d693e8
commit f98d1e6903
12 changed files with 290 additions and 113 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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::{

View File

@ -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,

View File

@ -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,71 +191,41 @@ 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
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
"#,
"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(),
)
.load(
self.variant_ids.len(),
self.variant_ids.iter().map(|id| id.0),
|e| {
tracing::warn!("{e:?}");
Error::FindProductsByVariants(self.variant_ids.clone())
},
)
.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
"#,
"products.id =",
)
.with_size(200);
loader
.load(
self.product_ids.len(),
self.product_ids.into_iter().map(|id| *id),
|e| {
tracing::warn!("{e:?}");
Error::FindProducts
},
)
.await
}
}

View File

@ -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")]

View File

@ -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
}
}
}