Implement categories
This commit is contained in:
parent
864f9842f0
commit
df1594e3f4
@ -24,4 +24,4 @@ thiserror = { version = "1.0.37" }
|
|||||||
tokio = { version = "1.21.2", features = ['full'] }
|
tokio = { version = "1.21.2", features = ['full'] }
|
||||||
tracing = { version = "0.1.37" }
|
tracing = { version = "0.1.37" }
|
||||||
whatlang = { version = "0.16.2" }
|
whatlang = { version = "0.16.2" }
|
||||||
strum = { version = "0.24.1", features = ['strum_macros', 'default'] }
|
strum = { version = "0.24.1", features = ['strum_macros', 'default', 'derive'] }
|
||||||
|
@ -1,4 +1,58 @@
|
|||||||
use strum::IntoStaticStr;
|
use rumqttc::QoS;
|
||||||
|
|
||||||
|
use crate::AsyncClient;
|
||||||
|
|
||||||
|
impl AsyncClient {
|
||||||
|
pub async fn emit_category_created(&self, category: &model::v2::Category) {
|
||||||
|
self.publish_or_log(Topic::CategoryCreated, QoS::AtLeastOnce, true, category)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn emit_category_updated(&self, category: &model::v2::Category) {
|
||||||
|
self.publish_or_log(Topic::CategoryUpdated, QoS::AtLeastOnce, true, category)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn emit_category_deleted(&self, category_id: &model::v2::CategoryId) {
|
||||||
|
self.publish_or_log(Topic::CategoryDeleted, QoS::AtLeastOnce, true, category_id)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub enum Topic {
|
||||||
|
CategoryCreated,
|
||||||
|
CategoryUpdated,
|
||||||
|
CategoryDeleted,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Topic {
|
||||||
|
pub fn to_str(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Topic::CategoryCreated => "category/created",
|
||||||
|
Topic::CategoryUpdated => "category/updated",
|
||||||
|
Topic::CategoryDeleted => "category/deleted",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for Topic {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.to_str().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> PartialEq<&'s str> for Topic {
|
||||||
|
fn eq(&self, other: &&'s str) -> bool {
|
||||||
|
self.to_str() == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<String> for Topic {
|
||||||
|
fn eq(&self, other: &String) -> bool {
|
||||||
|
self.to_str() == other.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod create_category {
|
pub mod create_category {
|
||||||
use model::v2::*;
|
use model::v2::*;
|
||||||
@ -32,7 +86,9 @@ pub mod delete_category {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Details {}
|
pub struct Details {
|
||||||
|
pub category: Option<Category>,
|
||||||
|
}
|
||||||
|
|
||||||
pub type Output = Result<Details, Error>;
|
pub type Output = Result<Details, Error>;
|
||||||
}
|
}
|
||||||
@ -53,8 +109,7 @@ pub mod update_category {
|
|||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Details {
|
pub struct Details {
|
||||||
pub limit: Limit,
|
pub category: Category,
|
||||||
pub offset: Offset,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Output = Result<Details, Error>;
|
pub type Output = Result<Details, Error>;
|
||||||
@ -66,7 +121,10 @@ pub mod all_categories {
|
|||||||
use crate::stocks::Error;
|
use crate::stocks::Error;
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Input {}
|
pub struct Input {
|
||||||
|
pub limit: Limit,
|
||||||
|
pub offset: Offset,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Details {
|
pub struct Details {
|
||||||
@ -75,12 +133,3 @@ pub mod all_categories {
|
|||||||
|
|
||||||
pub type Output = Result<Details, Error>;
|
pub type Output = Result<Details, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Copy, Clone, Debug, PartialOrd, PartialEq, serde::Serialize, serde::Deserialize, IntoStaticStr,
|
|
||||||
)]
|
|
||||||
pub enum Topic {
|
|
||||||
CategoryCreated,
|
|
||||||
CategoryUpdated,
|
|
||||||
CategoryDeleted,
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ pub mod product_variant;
|
|||||||
|
|
||||||
pub use categories::*;
|
pub use categories::*;
|
||||||
pub use load::*;
|
pub use load::*;
|
||||||
use model::v2::{ProductId, ProductVariantId};
|
use model::v2::{CategoryId, ProductId, ProductVariantId};
|
||||||
pub use product::*;
|
pub use product::*;
|
||||||
pub use product_photo::*;
|
pub use product_photo::*;
|
||||||
pub use product_stock::*;
|
pub use product_stock::*;
|
||||||
@ -56,7 +56,13 @@ pub enum Error {
|
|||||||
#[error("Failed to load product variants {0:?}")]
|
#[error("Failed to load product variants {0:?}")]
|
||||||
FindProductVariants(Vec<ProductVariantId>),
|
FindProductVariants(Vec<ProductVariantId>),
|
||||||
#[error("Failed to load all categories")]
|
#[error("Failed to load all categories")]
|
||||||
Categories,
|
AllCategories,
|
||||||
|
#[error("Failed to crate products category")]
|
||||||
|
CreateCategory,
|
||||||
|
#[error("Failed to delete products category {0:?}")]
|
||||||
|
DeleteCategory(CategoryId),
|
||||||
|
#[error("Failed to update products category {0:?}")]
|
||||||
|
UpdateCategory(CategoryId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod rpc {
|
pub mod rpc {
|
||||||
@ -67,10 +73,13 @@ pub mod rpc {
|
|||||||
use crate::stocks::product_photo::*;
|
use crate::stocks::product_photo::*;
|
||||||
use crate::stocks::product_stock::*;
|
use crate::stocks::product_stock::*;
|
||||||
use crate::stocks::product_variant::*;
|
use crate::stocks::product_variant::*;
|
||||||
|
use crate::stocks::{all_categories, create_category, delete_category, update_category};
|
||||||
|
|
||||||
#[tarpc::service]
|
#[tarpc::service]
|
||||||
pub trait Stocks {
|
pub trait Stocks {
|
||||||
// Product
|
// ####################
|
||||||
|
// PRODUCT
|
||||||
|
// ####################
|
||||||
/// Create new product.
|
/// Create new product.
|
||||||
async fn create_product(input: create_product::Input) -> create_product::Output;
|
async fn create_product(input: create_product::Input) -> create_product::Output;
|
||||||
/// Update product information.
|
/// Update product information.
|
||||||
@ -78,7 +87,21 @@ pub mod rpc {
|
|||||||
/// Delete product.
|
/// Delete product.
|
||||||
async fn delete_product(input: delete_product::Input) -> delete_product::Output;
|
async fn delete_product(input: delete_product::Input) -> delete_product::Output;
|
||||||
|
|
||||||
// Product variant
|
// ####################
|
||||||
|
// DETAILED PRODUCT
|
||||||
|
// ####################
|
||||||
|
/// Single product with stock size and photos
|
||||||
|
async fn detailed_product(input: detailed_product::Input) -> detailed_product::Output;
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
// ####################
|
||||||
|
// PRODUCT VARIANTS
|
||||||
|
// ####################
|
||||||
/// Create new variant of the product.
|
/// Create new variant of the product.
|
||||||
async fn create_product_variant(
|
async fn create_product_variant(
|
||||||
input: create_product_variant::Input,
|
input: create_product_variant::Input,
|
||||||
@ -92,7 +115,14 @@ pub mod rpc {
|
|||||||
input: delete_product_variant::Input,
|
input: delete_product_variant::Input,
|
||||||
) -> delete_product_variant::Output;
|
) -> delete_product_variant::Output;
|
||||||
|
|
||||||
// Product photo
|
/// List of products variants for shopping cart
|
||||||
|
async fn shopping_cart_product_variants(
|
||||||
|
input: find_product_variants::Input,
|
||||||
|
) -> find_product_variants::Output;
|
||||||
|
|
||||||
|
// ####################
|
||||||
|
// PRODUCT PHOTO
|
||||||
|
// ####################
|
||||||
/// Load all photos
|
/// Load all photos
|
||||||
async fn all_product_photo(input: all_product_photo::Input) -> all_product_photo::Output;
|
async fn all_product_photo(input: all_product_photo::Input) -> all_product_photo::Output;
|
||||||
|
|
||||||
@ -104,7 +134,9 @@ pub mod rpc {
|
|||||||
input: delete_product_photo::Input,
|
input: delete_product_photo::Input,
|
||||||
) -> delete_product_photo::Output;
|
) -> delete_product_photo::Output;
|
||||||
|
|
||||||
// Product stock
|
// ####################
|
||||||
|
// PRODUCT STOCK
|
||||||
|
// ####################
|
||||||
/// Create product stock.
|
/// Create product stock.
|
||||||
async fn create_product_stock(
|
async fn create_product_stock(
|
||||||
input: create_product_stock::Input,
|
input: create_product_stock::Input,
|
||||||
@ -114,22 +146,20 @@ pub mod rpc {
|
|||||||
input: update_product_stock::Input,
|
input: update_product_stock::Input,
|
||||||
) -> update_product_stock::Output;
|
) -> update_product_stock::Output;
|
||||||
|
|
||||||
// Load
|
// ####################
|
||||||
/// Single product with stock size and photos
|
// CATEGORIES
|
||||||
async fn detailed_product(input: detailed_product::Input) -> detailed_product::Output;
|
// ####################
|
||||||
|
/// Create new products category
|
||||||
|
async fn create_category(input: create_category::Input) -> create_category::Output;
|
||||||
|
|
||||||
/// List of products with stock size and photos
|
/// Delete existing products category
|
||||||
async fn detailed_products(input: detailed_products::Input) -> detailed_products::Output;
|
async fn delete_category(input: delete_category::Input) -> delete_category::Output;
|
||||||
|
|
||||||
/// List of products for shopping cart
|
///Change existing products category
|
||||||
async fn shopping_cart_products(input: find_products::Input) -> find_products::Output;
|
async fn update_category(input: update_category::Input) -> update_category::Output;
|
||||||
|
|
||||||
/// List of products variants for shopping cart
|
/// List of call products categories
|
||||||
async fn shopping_cart_product_variants(
|
async fn all_categories(input: all_categories::Input) -> all_categories::Output;
|
||||||
input: find_product_variants::Input,
|
|
||||||
) -> find_product_variants::Output;
|
|
||||||
|
|
||||||
// async fn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_client(config: SharedAppConfig) -> StocksClient {
|
pub async fn create_client(config: SharedAppConfig) -> StocksClient {
|
||||||
|
@ -1626,6 +1626,7 @@ pub mod v2 {
|
|||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
Clone,
|
Clone,
|
||||||
|
Copy,
|
||||||
Default,
|
Default,
|
||||||
PartialOrd,
|
PartialOrd,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
|
176
crates/stock_manager/src/actions/categories.rs
Normal file
176
crates/stock_manager/src/actions/categories.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
use channels::stocks::{all_categories, create_category, delete_category, update_category, Error};
|
||||||
|
use channels::AsyncClient;
|
||||||
|
use config::SharedAppConfig;
|
||||||
|
use db_utils::PgT;
|
||||||
|
|
||||||
|
use crate::db::{AllCategories, CreateCategory, Database, DeleteCategory, UpdateCategory};
|
||||||
|
use crate::{begin_t, dbm_run};
|
||||||
|
|
||||||
|
pub async fn create_category(
|
||||||
|
input: create_category::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> create_category::Output {
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
|
||||||
|
let res = inner_create_category(input, &mut t).await;
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(v) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
return Err(Error::InternalServerError);
|
||||||
|
} else {
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Err(e) = t.rollback().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_create_category(
|
||||||
|
input: create_category::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> create_category::Output {
|
||||||
|
let dbm = CreateCategory {
|
||||||
|
parent_id: input.parent_id,
|
||||||
|
name: input.name,
|
||||||
|
key: input.key,
|
||||||
|
svg: input.svg,
|
||||||
|
};
|
||||||
|
Ok(create_category::Details {
|
||||||
|
product: dbm_run!(dbm, t, Error::CreateCategory),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_category(
|
||||||
|
input: delete_category::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> delete_category::Output {
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
|
||||||
|
let res = inner_delete_category(input, &mut t).await;
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(v) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
return Err(Error::InternalServerError);
|
||||||
|
} else {
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Err(e) = t.rollback().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_delete_category(
|
||||||
|
input: delete_category::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> delete_category::Output {
|
||||||
|
let dbm = DeleteCategory {
|
||||||
|
category_id: input.category_id,
|
||||||
|
};
|
||||||
|
Ok(delete_category::Details {
|
||||||
|
category: dbm_run!(dbm, t, Error::DeleteCategory(input.category_id)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_category(
|
||||||
|
input: update_category::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> update_category::Output {
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
|
||||||
|
let res = inner_update_category(input, &mut t).await;
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(v) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
return Err(Error::InternalServerError);
|
||||||
|
} else {
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Err(e) = t.rollback().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_update_category(
|
||||||
|
input: update_category::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> update_category::Output {
|
||||||
|
let dbm = UpdateCategory {
|
||||||
|
id: input.id,
|
||||||
|
parent_id: input.parent_id,
|
||||||
|
name: input.name,
|
||||||
|
key: input.key,
|
||||||
|
svg: input.svg,
|
||||||
|
};
|
||||||
|
Ok(update_category::Details {
|
||||||
|
category: dbm_run!(dbm, t, Error::UpdateCategory(input.id)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn all_categories(
|
||||||
|
input: all_categories::Input,
|
||||||
|
db: Database,
|
||||||
|
_mqtt: AsyncClient,
|
||||||
|
_config: SharedAppConfig,
|
||||||
|
) -> all_categories::Output {
|
||||||
|
let mut t = begin_t!(db, Error::InternalServerError);
|
||||||
|
|
||||||
|
let res = inner_all_categories(input, &mut t).await;
|
||||||
|
|
||||||
|
match res {
|
||||||
|
Ok(v) => {
|
||||||
|
if let Err(e) = t.commit().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
return Err(Error::InternalServerError);
|
||||||
|
} else {
|
||||||
|
Ok(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Err(e) = t.rollback().await {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
}
|
||||||
|
Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inner_all_categories(
|
||||||
|
input: all_categories::Input,
|
||||||
|
t: &mut PgT<'_>,
|
||||||
|
) -> all_categories::Output {
|
||||||
|
let dbm = AllCategories {
|
||||||
|
limit: input.limit,
|
||||||
|
offset: input.offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(all_categories::Details {
|
||||||
|
categories: dbm_run!(dbm, t, Error::AllCategories),
|
||||||
|
})
|
||||||
|
}
|
@ -61,7 +61,7 @@ async fn inner_detailed_product(
|
|||||||
limit: Limit::from_u32(2000),
|
limit: Limit::from_u32(2000),
|
||||||
offset: Offset::from_u32(0),
|
offset: Offset::from_u32(0),
|
||||||
};
|
};
|
||||||
let categories = dbm_run!(dbm, t, Error::Categories);
|
let categories = dbm_run!(dbm, t, Error::AllCategories);
|
||||||
|
|
||||||
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
||||||
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
||||||
@ -146,7 +146,7 @@ async fn inner_detailed_products(
|
|||||||
limit: Limit::from_u32(2000),
|
limit: Limit::from_u32(2000),
|
||||||
offset: Offset::from_u32(0),
|
offset: Offset::from_u32(0),
|
||||||
};
|
};
|
||||||
let categories = dbm_run!(dbm, t, Error::Categories);
|
let categories = dbm_run!(dbm, t, Error::AllCategories);
|
||||||
|
|
||||||
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
let mut variants = utils::vec_to_hash_vec(variants, 10, |p| p.product_id);
|
||||||
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
let mut stocks = utils::vec_to_hash_vec(stocks, 10, |s| s.product_variant_id);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
pub mod categories;
|
||||||
pub mod load;
|
pub mod load;
|
||||||
pub mod product;
|
pub mod product;
|
||||||
pub mod product_photo;
|
pub mod product_photo;
|
||||||
pub mod product_stock;
|
pub mod product_stock;
|
||||||
pub mod product_variant;
|
pub mod product_variant;
|
||||||
|
|
||||||
|
pub use categories::*;
|
||||||
pub use load::*;
|
pub use load::*;
|
||||||
pub use product::*;
|
pub use product::*;
|
||||||
pub use product_photo::*;
|
pub use product_photo::*;
|
||||||
|
@ -8,6 +8,10 @@ pub enum Error {
|
|||||||
All,
|
All,
|
||||||
#[error("Failed to create category")]
|
#[error("Failed to create category")]
|
||||||
Create,
|
Create,
|
||||||
|
#[error("Failed to delete category {0:?}")]
|
||||||
|
Delete(CategoryId),
|
||||||
|
#[error("Failed to update category {0:?}")]
|
||||||
|
Update(CategoryId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@ -46,6 +50,74 @@ RETURNING id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UpdateCategory {
|
||||||
|
pub id: CategoryId,
|
||||||
|
pub parent_id: Option<CategoryId>,
|
||||||
|
pub name: CategoryName,
|
||||||
|
pub key: CategoryKey,
|
||||||
|
pub svg: CategorySvg,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpdateCategory {
|
||||||
|
/// Update category
|
||||||
|
///
|
||||||
|
/// ## Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// async fn change(t: &mut PgT<'_>) {
|
||||||
|
/// let dbm = UpdateCategory {};
|
||||||
|
/// dbm.run(t).await;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub async fn run(self, t: &mut PgT<'_>) -> Result<Category> {
|
||||||
|
sqlx::query_as(
|
||||||
|
r#"
|
||||||
|
UPDATE categories
|
||||||
|
SET parent_id = $2,
|
||||||
|
name = $3,
|
||||||
|
key = $4,
|
||||||
|
svg = $5
|
||||||
|
WHERE id = $1
|
||||||
|
RETURNING id,
|
||||||
|
parent_id,
|
||||||
|
name,
|
||||||
|
key,
|
||||||
|
svg
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(self.id)
|
||||||
|
.bind(self.parent_id)
|
||||||
|
.bind(self.name)
|
||||||
|
.bind(self.key)
|
||||||
|
.bind(self.svg)
|
||||||
|
.fetch_one(t)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::warn!("{e:?}");
|
||||||
|
dbg!(e);
|
||||||
|
Error::Update(self.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DeleteCategory {
|
||||||
|
pub category_id: CategoryId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeleteCategory {
|
||||||
|
pub async fn run(self, t: &mut PgT<'_>) -> Result<Option<Category>> {
|
||||||
|
sqlx::query_as("DELETE FROM categories WHERE id = $1")
|
||||||
|
.bind(self.category_id)
|
||||||
|
.fetch_optional(t)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
tracing::error!("{}", e);
|
||||||
|
dbg!(e);
|
||||||
|
Error::Delete(self.category_id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AllCategories {
|
pub struct AllCategories {
|
||||||
pub limit: Limit,
|
pub limit: Limit,
|
||||||
pub offset: Offset,
|
pub offset: Offset,
|
||||||
@ -84,7 +156,7 @@ mod tests {
|
|||||||
use model::v2::{Category, CategoryName};
|
use model::v2::{Category, CategoryName};
|
||||||
use model::{Limit, Offset};
|
use model::{Limit, Offset};
|
||||||
|
|
||||||
use crate::db::{AllCategories, CreateCategory, Database};
|
use crate::db::{AllCategories, CreateCategory, Database, DeleteCategory, UpdateCategory};
|
||||||
|
|
||||||
struct NoOpts;
|
struct NoOpts;
|
||||||
|
|
||||||
@ -121,4 +193,88 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(res.unwrap().len(), 3);
|
assert_eq!(res.unwrap().len(), 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn delete_one() {
|
||||||
|
testx::db_t_ref!(t);
|
||||||
|
|
||||||
|
test_category("Electronics".into(), &mut t).await;
|
||||||
|
let second = test_category("Shoes".into(), &mut t).await;
|
||||||
|
test_category("Pants".into(), &mut t).await;
|
||||||
|
|
||||||
|
let len1 = AllCategories {
|
||||||
|
limit: Limit::from_u32(2000),
|
||||||
|
offset: Offset::from_u32(0),
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let deleted1 = DeleteCategory {
|
||||||
|
category_id: second.id,
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
let deleted2 = DeleteCategory {
|
||||||
|
category_id: second.id,
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let len2 = AllCategories {
|
||||||
|
limit: Limit::from_u32(2000),
|
||||||
|
offset: Offset::from_u32(0),
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
testx::db_rollback!(t);
|
||||||
|
|
||||||
|
let len1 = len1.unwrap();
|
||||||
|
let deleted1 = deleted1.unwrap();
|
||||||
|
let deleted2 = deleted2.unwrap();
|
||||||
|
let len2 = len2.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(len1.len(), 3);
|
||||||
|
assert_eq!(len2.len(), 2);
|
||||||
|
assert!(deleted1.is_some());
|
||||||
|
assert!(deleted2.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_one() {
|
||||||
|
testx::db_t_ref!(t);
|
||||||
|
|
||||||
|
let first = test_category("Electronics".into(), &mut t).await;
|
||||||
|
let second = test_category("Shoes".into(), &mut t).await;
|
||||||
|
let third = test_category("Pants".into(), &mut t).await;
|
||||||
|
|
||||||
|
let res = UpdateCategory {
|
||||||
|
id: second.id,
|
||||||
|
parent_id: None,
|
||||||
|
name: "Wearables".into(),
|
||||||
|
key: second.key.clone(),
|
||||||
|
svg: second.svg.clone(),
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let all = AllCategories {
|
||||||
|
limit: Limit::from_u32(2000),
|
||||||
|
offset: Offset::from_u32(0),
|
||||||
|
}
|
||||||
|
.run(&mut t)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
testx::db_rollback!(t);
|
||||||
|
|
||||||
|
let res = res.unwrap();
|
||||||
|
let all = all.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(res.name, "Wearables".into());
|
||||||
|
assert_eq!(res.id, second.id);
|
||||||
|
|
||||||
|
assert_eq!(all[0].name, first.name);
|
||||||
|
assert_eq!(all[1].name, "Wearables".into());
|
||||||
|
assert_eq!(all[2].name, third.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,75 +2,75 @@ use model::v2::*;
|
|||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("Unable to load all stocks")]
|
// #[error("Unable to load all stocks")]
|
||||||
All,
|
// All,
|
||||||
#[error("Unable to create stock")]
|
#[error("Unable to create stock")]
|
||||||
Create,
|
Create,
|
||||||
#[error("Unable to update stock {0:?}")]
|
#[error("Unable to update stock {0:?}")]
|
||||||
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")]
|
||||||
NotFound(StockId),
|
// NotFound(StockId),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub struct AllStocks {
|
// pub struct AllStocks {
|
||||||
pub limit: i32,
|
// pub limit: i32,
|
||||||
pub offset: i32,
|
// pub offset: i32,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl AllStocks {
|
// impl AllStocks {
|
||||||
pub async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Vec<Stock>> {
|
// pub async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>)
|
||||||
sqlx::query_as(
|
// -> Result<Vec<Stock>> { sqlx::query_as(
|
||||||
r#"
|
// r#"
|
||||||
SELECT id, product_variant_id, quantity, quantity_unit
|
// SELECT id, product_variant_id, quantity, quantity_unit
|
||||||
FROM stocks
|
// FROM stocks
|
||||||
ORDER BY id ASC
|
// ORDER BY id ASC
|
||||||
LIMIT $1 OFFSET $2
|
// LIMIT $1 OFFSET $2
|
||||||
"#,
|
// "#,
|
||||||
)
|
// )
|
||||||
.bind(self.limit)
|
// .bind(self.limit)
|
||||||
.bind(self.offset)
|
// .bind(self.offset)
|
||||||
.fetch_all(pool)
|
// .fetch_all(pool)
|
||||||
.await
|
// .await
|
||||||
.map_err(|e| {
|
// .map_err(|e| {
|
||||||
tracing::warn!("{e:?}");
|
// tracing::warn!("{e:?}");
|
||||||
Error::All
|
// Error::All
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub struct FindStock {
|
// pub struct FindStock {
|
||||||
pub id: StockId,
|
// pub id: StockId,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl FindStock {
|
// impl FindStock {
|
||||||
pub async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Stock> {
|
// pub async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>)
|
||||||
sqlx::query_as(
|
// -> Result<Stock> { sqlx::query_as(
|
||||||
r#"
|
// r#"
|
||||||
SELECT id, product_variant_id, quantity, quantity_unit
|
// SELECT id, product_variant_id, quantity, quantity_unit
|
||||||
FROM stocks
|
// FROM stocks
|
||||||
WHERE id = $1
|
// WHERE id = $1
|
||||||
"#,
|
// "#,
|
||||||
)
|
// )
|
||||||
.bind(self.id)
|
// .bind(self.id)
|
||||||
.fetch_one(pool)
|
// .fetch_one(pool)
|
||||||
.await
|
// .await
|
||||||
.map_err(|e| {
|
// .map_err(|e| {
|
||||||
tracing::warn!("{e:?}");
|
// tracing::warn!("{e:?}");
|
||||||
dbg!(e);
|
// dbg!(e);
|
||||||
Error::NotFound(self.id)
|
// Error::NotFound(self.id)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CreateStock {
|
pub struct CreateStock {
|
||||||
@ -134,32 +134,32 @@ RETURNING id, product_variant_id, quantity, quantity_unit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
// #[derive(Debug)]
|
||||||
pub struct DeleteStock {
|
// pub struct DeleteStock {
|
||||||
pub stock_id: StockId,
|
// pub stock_id: StockId,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
impl DeleteStock {
|
// impl DeleteStock {
|
||||||
pub async fn run(
|
// pub async fn run(
|
||||||
self,
|
// self,
|
||||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
// pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||||
) -> Result<Option<Stock>> {
|
// ) -> Result<Option<Stock>> {
|
||||||
sqlx::query_as(
|
// sqlx::query_as(
|
||||||
r#"
|
// r#"
|
||||||
DELETE FROM stocks
|
// DELETE FROM stocks
|
||||||
WHERE id = $1
|
// WHERE id = $1
|
||||||
RETURNING id, product_variant_id, quantity, quantity_unit
|
// RETURNING id, product_variant_id, quantity, quantity_unit
|
||||||
"#,
|
// "#,
|
||||||
)
|
// )
|
||||||
.bind(self.stock_id)
|
// .bind(self.stock_id)
|
||||||
.fetch_optional(pool)
|
// .fetch_optional(pool)
|
||||||
.await
|
// .await
|
||||||
.map_err(|e| {
|
// .map_err(|e| {
|
||||||
tracing::warn!("{e:?}");
|
// tracing::warn!("{e:?}");
|
||||||
Error::Delete(self.stock_id)
|
// Error::Delete(self.stock_id)
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProductVariantsStock {
|
pub struct ProductVariantsStock {
|
||||||
|
@ -6,6 +6,7 @@ use crate::db::Database;
|
|||||||
use crate::rpc::rpc::StocksServer;
|
use crate::rpc::rpc::StocksServer;
|
||||||
|
|
||||||
pub mod rpc {
|
pub mod rpc {
|
||||||
|
use channels::stocks::create_category::{Input, Output};
|
||||||
use channels::stocks::rpc::Stocks;
|
use channels::stocks::rpc::Stocks;
|
||||||
use channels::stocks::*;
|
use channels::stocks::*;
|
||||||
use config::SharedAppConfig;
|
use config::SharedAppConfig;
|
||||||
@ -46,6 +47,30 @@ pub mod rpc {
|
|||||||
actions::delete_product(input, self.db, self.mqtt_client, self.config).await
|
actions::delete_product(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn detailed_product(
|
||||||
|
self,
|
||||||
|
_: context::Context,
|
||||||
|
input: detailed_product::Input,
|
||||||
|
) -> detailed_product::Output {
|
||||||
|
actions::detailed_product(input, self.db, self.mqtt_client, self.config).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn detailed_products(
|
||||||
|
self,
|
||||||
|
_: context::Context,
|
||||||
|
input: detailed_products::Input,
|
||||||
|
) -> 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 create_product_variant(
|
async fn create_product_variant(
|
||||||
self,
|
self,
|
||||||
_: context::Context,
|
_: context::Context,
|
||||||
@ -70,6 +95,14 @@ pub mod rpc {
|
|||||||
actions::delete_product_variant(input, self.db, self.mqtt_client, self.config).await
|
actions::delete_product_variant(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
|
||||||
|
}
|
||||||
|
|
||||||
async fn all_product_photo(
|
async fn all_product_photo(
|
||||||
self,
|
self,
|
||||||
_: context::Context,
|
_: context::Context,
|
||||||
@ -110,36 +143,32 @@ pub mod rpc {
|
|||||||
actions::update_product_stock(input, self.db, self.mqtt_client, self.config).await
|
actions::update_product_stock(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn detailed_product(
|
async fn create_category(self, _: context::Context, input: Input) -> Output {
|
||||||
self,
|
actions::create_category(input, self.db, self.mqtt_client, self.config).await
|
||||||
_: context::Context,
|
|
||||||
input: detailed_product::Input,
|
|
||||||
) -> detailed_product::Output {
|
|
||||||
actions::detailed_product(input, self.db, self.mqtt_client, self.config).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn detailed_products(
|
async fn delete_category(
|
||||||
self,
|
self,
|
||||||
_: context::Context,
|
_: context::Context,
|
||||||
input: detailed_products::Input,
|
input: delete_category::Input,
|
||||||
) -> detailed_products::Output {
|
) -> delete_category::Output {
|
||||||
actions::detailed_products(input, self.db, self.mqtt_client, self.config).await
|
actions::delete_category(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shopping_cart_products(
|
async fn update_category(
|
||||||
self,
|
self,
|
||||||
_: context::Context,
|
_: context::Context,
|
||||||
input: find_products::Input,
|
input: update_category::Input,
|
||||||
) -> find_products::Output {
|
) -> update_category::Output {
|
||||||
actions::find_products(input, self.db, self.mqtt_client, self.config).await
|
actions::update_category(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn shopping_cart_product_variants(
|
async fn all_categories(
|
||||||
self,
|
self,
|
||||||
_: context::Context,
|
_: context::Context,
|
||||||
input: find_product_variants::Input,
|
input: all_categories::Input,
|
||||||
) -> find_product_variants::Output {
|
) -> all_categories::Output {
|
||||||
actions::find_product_variants(input, self.db, self.mqtt_client, self.config).await
|
actions::all_categories(input, self.db, self.mqtt_client, self.config).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user