Working tests, test detailed products
This commit is contained in:
parent
9c1b13bfb7
commit
c7e9b25b97
80
Cargo.lock
generated
80
Cargo.lock
generated
@ -830,12 +830,11 @@ dependencies = [
|
||||
name = "cart_manager"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix 0.13.0",
|
||||
"actix-rt",
|
||||
"channels",
|
||||
"chrono",
|
||||
"config",
|
||||
"dotenv",
|
||||
"fake",
|
||||
"futures 0.3.25",
|
||||
"model",
|
||||
"opentelemetry 0.17.0",
|
||||
@ -846,6 +845,7 @@ dependencies = [
|
||||
"sqlx",
|
||||
"sqlx-core",
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -967,6 +967,19 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c050367d967ced717c04b65d8c619d863ef9292ce0c5760028655a2fb298718c"
|
||||
dependencies = [
|
||||
"encode_unicode",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"terminal_size",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.7"
|
||||
@ -1385,6 +1398,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"dotenv",
|
||||
"fake",
|
||||
"handlebars",
|
||||
"model",
|
||||
"opentelemetry 0.17.0",
|
||||
@ -1395,6 +1409,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -1409,6 +1424,12 @@ version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1056f553da426e9c025a662efa48b52e62e0a3a7648aa2d15aeaaf7f0d329357"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.31"
|
||||
@ -2239,6 +2260,19 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "insta"
|
||||
version = "1.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "581d4e3314cae4536e5d22ffd23189d4a374696c5ef733eadafae0ed273fd303"
|
||||
dependencies = [
|
||||
"console",
|
||||
"lazy_static",
|
||||
"linked-hash-map",
|
||||
"similar",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
@ -2446,6 +2480,12 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||
|
||||
[[package]]
|
||||
name = "local-channel"
|
||||
version = "0.1.3"
|
||||
@ -2962,10 +3002,12 @@ dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"database_manager",
|
||||
"fake",
|
||||
"model",
|
||||
"pretty_env_logger",
|
||||
"rumqttc",
|
||||
"serde",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid 1.2.1",
|
||||
@ -3094,6 +3136,7 @@ dependencies = [
|
||||
"config",
|
||||
"database_manager",
|
||||
"derive_more",
|
||||
"fake",
|
||||
"model",
|
||||
"parking_lot 0.12.1",
|
||||
"pay_u",
|
||||
@ -3678,6 +3721,7 @@ dependencies = [
|
||||
"config",
|
||||
"derive_more",
|
||||
"dotenv",
|
||||
"fake",
|
||||
"futures 0.3.25",
|
||||
"model",
|
||||
"opentelemetry 0.17.0",
|
||||
@ -3688,6 +3732,7 @@ dependencies = [
|
||||
"serde",
|
||||
"sonic-channel",
|
||||
"tarpc",
|
||||
"testx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -3897,6 +3942,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "similar"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62ac7f900db32bf3fd12e0117dd3dc4da74bc52ebaac97f39668446d89694803"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.10"
|
||||
@ -4108,6 +4159,7 @@ dependencies = [
|
||||
"dotenv",
|
||||
"fake",
|
||||
"futures 0.3.25",
|
||||
"insta",
|
||||
"model",
|
||||
"opentelemetry 0.17.0",
|
||||
"opentelemetry-jaeger",
|
||||
@ -4161,9 +4213,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tarpc"
|
||||
version = "0.30.0"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dd84a0fdd485d04b67be6009a04603489c8cb00ade830e4dd2e3660bef855b1"
|
||||
checksum = "71a98cc1a0a9013e8df3900d09c597dd65cfc6ea4d42968629b1b9ea949acf8f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fnv",
|
||||
@ -4249,6 +4301,16 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "testx"
|
||||
version = "0.1.0"
|
||||
@ -4379,6 +4441,7 @@ dependencies = [
|
||||
"config",
|
||||
"database_manager",
|
||||
"derive_more",
|
||||
"fake",
|
||||
"futures 0.3.25",
|
||||
"futures-util",
|
||||
"hmac",
|
||||
@ -5244,6 +5307,15 @@ dependencies = [
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.11.2+zstd.1.5.2"
|
||||
|
@ -16,7 +16,7 @@ dotenv = { version = "0.15.0" }
|
||||
futures = { version = "0.3.25" }
|
||||
gumdrop = { version = "0.8.1" }
|
||||
json = { version = "0.12.4" }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ['db'] }
|
||||
opentelemetry = { version = "0.17.0" }
|
||||
opentelemetry-jaeger = { version = "0.17.0" }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
@ -24,7 +24,7 @@ rumqttc = { version = "*" }
|
||||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.6.2", features = [] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.6" }
|
||||
|
@ -12,7 +12,7 @@ CREATE TYPE "Role" AS ENUM (
|
||||
);
|
||||
|
||||
CREATE TABLE public.accounts (
|
||||
id integer NOT NULL,
|
||||
id serial NOT NULL,
|
||||
email character varying NOT NULL,
|
||||
login character varying NOT NULL,
|
||||
pass_hash character varying NOT NULL,
|
||||
|
@ -1,5 +1,5 @@
|
||||
CREATE TABLE public.account_addresses (
|
||||
id integer NOT NULL,
|
||||
id serial NOT NULL,
|
||||
name text NOT NULL,
|
||||
email text NOT NULL,
|
||||
street text NOT NULL,
|
||||
|
@ -17,19 +17,25 @@ pub enum Error {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllAccounts;
|
||||
pub struct AllAccounts {
|
||||
pub limit: i32,
|
||||
pub offset: i32,
|
||||
}
|
||||
|
||||
impl AllAccounts {
|
||||
pub async fn run(
|
||||
_msg: AllAccounts,
|
||||
self,
|
||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Vec<FullAccount>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT id, email, login, pass_hash, role, customer_id, state
|
||||
FROM accounts
|
||||
LIMIT $1 OFFSET $2
|
||||
"#,
|
||||
)
|
||||
.bind(self.limit)
|
||||
.bind(self.offset)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@ -207,6 +213,7 @@ mod tests {
|
||||
use model::*;
|
||||
|
||||
use super::*;
|
||||
use crate::db::Database;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
@ -274,7 +281,13 @@ mod tests {
|
||||
test_create_account(&mut t, None, None, None).await;
|
||||
test_create_account(&mut t, None, None, None).await;
|
||||
|
||||
let v: Vec<FullAccount> = AllAccounts.run(&mut t).await.unwrap();
|
||||
let v: Vec<FullAccount> = AllAccounts {
|
||||
limit: 200,
|
||||
offset: 0,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert!(v.len() >= 3);
|
||||
|
@ -194,6 +194,7 @@ mod test {
|
||||
|
||||
use super::super::accounts::CreateAccount;
|
||||
use super::*;
|
||||
use crate::db::Database;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
@ -267,13 +268,11 @@ mod test {
|
||||
address
|
||||
};
|
||||
|
||||
let found = super::find_account_address(
|
||||
FindAccountAddress {
|
||||
account_id: account.id,
|
||||
address_id: address.id,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let found = FindAccountAddress {
|
||||
account_id: account.id,
|
||||
address_id: address.id,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -43,7 +43,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = [] }
|
||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.6.2", features = [] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tera = { version = "1.15", features = [] }
|
||||
thiserror = { version = "1.0", features = [] }
|
||||
token_manager = { path = "../token_manager" }
|
||||
|
@ -8,14 +8,12 @@ name = "cart-manager"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
actix = { version = "0.13", features = [] }
|
||||
actix-rt = { version = "2.7", features = [] }
|
||||
channels = { path = "../channels" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
config = { path = "../config" }
|
||||
dotenv = { version = "0.15.0" }
|
||||
futures = { version = "0.3.25" }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
opentelemetry = { version = "0.17.0" }
|
||||
opentelemetry-jaeger = { version = "0.17.0" }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
@ -23,10 +21,14 @@ rumqttc = { version = "*" }
|
||||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.6.2", features = [] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.37" }
|
||||
tracing-opentelemetry = { version = "0.17.4" }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
uuid = { version = "0.8", features = ["serde", "v4"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -16,7 +16,7 @@ CREATE TYPE "QuantityUnit" AS ENUM (
|
||||
);
|
||||
|
||||
CREATE TABLE shopping_carts (
|
||||
id integer NOT NULL,
|
||||
id serial NOT NULL,
|
||||
buyer_id integer NOT NULL,
|
||||
payment_method "PaymentMethod" DEFAULT 'payment_on_the_spot'::"PaymentMethod" NOT NULL,
|
||||
state "ShoppingCartState" DEFAULT 'active'::"ShoppingCartState" NOT NULL,
|
||||
@ -24,7 +24,7 @@ CREATE TABLE shopping_carts (
|
||||
);
|
||||
|
||||
CREATE TABLE shopping_cart_items (
|
||||
id integer NOT NULL,
|
||||
id serial NOT NULL,
|
||||
product_id integer NOT NULL,
|
||||
shopping_cart_id integer,
|
||||
quantity integer DEFAULT 0 NOT NULL,
|
||||
|
@ -366,9 +366,9 @@ RETURNING id, product_id, shopping_cart_id, quantity, quantity_unit
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use config::UpdateConfig;
|
||||
use fake::Fake;
|
||||
use model::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::db::shopping_carts::*;
|
||||
use crate::db::Database;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
@ -376,42 +376,6 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
async fn test_product(t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Product {
|
||||
CreateProduct {
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
category: None,
|
||||
price: Price::from_u32(4687),
|
||||
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_account(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
login: Option<String>,
|
||||
email: Option<String>,
|
||||
hash: Option<String>,
|
||||
) -> FullAccount {
|
||||
use fake::faker::internet::en;
|
||||
let login: String = login.unwrap_or_else(|| en::Username().fake());
|
||||
let email: String = email.unwrap_or_else(|| en::FreeEmail().fake());
|
||||
let hash: String = hash.unwrap_or_else(|| en::Password(10..20).fake());
|
||||
|
||||
CreateAccount {
|
||||
email: Email::new(email),
|
||||
login: Login::new(login),
|
||||
pass_hash: PassHash::new(hash),
|
||||
role: Role::Admin,
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_shopping_cart(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
buyer_id: Option<AccountId>,
|
||||
@ -419,7 +383,7 @@ mod tests {
|
||||
) -> ShoppingCart {
|
||||
let buyer_id = match buyer_id {
|
||||
Some(id) => id,
|
||||
_ => test_account(&mut *t, None, None, None).await.id,
|
||||
_ => 1.into(),
|
||||
};
|
||||
|
||||
sqlx::query(
|
||||
@ -469,7 +433,7 @@ WHERE buyer_id = $1
|
||||
};
|
||||
let product_id = match product_id {
|
||||
Some(id) => id,
|
||||
_ => test_product(&mut *t).await.id,
|
||||
_ => 1.into(),
|
||||
};
|
||||
CreateShoppingCartItem {
|
||||
product_id,
|
||||
@ -482,7 +446,7 @@ WHERE buyer_id = $1
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn create() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
@ -491,11 +455,11 @@ WHERE buyer_id = $1
|
||||
testx::db_rollback!(t);
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn all() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
let account_id = 1.into();
|
||||
|
||||
let mut items = Vec::with_capacity(9);
|
||||
|
||||
@ -514,19 +478,17 @@ WHERE buyer_id = $1
|
||||
items.push(test_shopping_cart_item(&mut t, Some(cart3.id), None).await);
|
||||
items.push(test_shopping_cart_item(&mut t, Some(cart3.id), None).await);
|
||||
|
||||
let all = all_shopping_cart_items(AllShoppingCartItems, &mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
let all = AllShoppingCartItems.run(&mut t).await.unwrap();
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert_eq!(all, items)
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn account_cart_with_cart_id() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
let account_id = 1.into();
|
||||
|
||||
let mut items = Vec::with_capacity(9);
|
||||
|
||||
@ -545,13 +507,11 @@ WHERE buyer_id = $1
|
||||
test_shopping_cart_item(&mut t, Some(cart3.id), None).await;
|
||||
test_shopping_cart_item(&mut t, Some(cart3.id), None).await;
|
||||
|
||||
let all = account_shopping_cart_items(
|
||||
AccountShoppingCartItems {
|
||||
account_id,
|
||||
shopping_cart_id: Some(cart2.id),
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let all = AccountShoppingCartItems {
|
||||
account_id,
|
||||
shopping_cart_id: Some(cart2.id),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -559,11 +519,11 @@ WHERE buyer_id = $1
|
||||
assert_eq!(all, items)
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn account_cart_without_cart_id() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
let account_id = 1.into();
|
||||
|
||||
let mut items = Vec::with_capacity(9);
|
||||
|
||||
@ -582,13 +542,11 @@ WHERE buyer_id = $1
|
||||
items.push(test_shopping_cart_item(&mut t, Some(cart3.id), None).await);
|
||||
items.push(test_shopping_cart_item(&mut t, Some(cart3.id), None).await);
|
||||
|
||||
let all = account_shopping_cart_items(
|
||||
AccountShoppingCartItems {
|
||||
account_id,
|
||||
shopping_cart_id: None,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let all = AccountShoppingCartItems {
|
||||
account_id,
|
||||
shopping_cart_id: None,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -596,23 +554,21 @@ WHERE buyer_id = $1
|
||||
assert_eq!(all, items)
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn update() {
|
||||
testx::db_t_ref!(t);
|
||||
let account_id = test_account(&mut t, None, None, None).await.id;
|
||||
let account_id = 1.into();
|
||||
let cart1 = test_shopping_cart(&mut t, Some(account_id), ShoppingCartState::Closed).await;
|
||||
let item = test_shopping_cart_item(&mut t, Some(cart1.id), None).await;
|
||||
|
||||
let updated = update_shopping_cart_item(
|
||||
UpdateShoppingCartItem {
|
||||
id: item.id,
|
||||
product_id: item.product_id,
|
||||
shopping_cart_id: item.shopping_cart_id,
|
||||
quantity: Quantity::from_u32(987979879),
|
||||
quantity_unit: QuantityUnit::Kilogram,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let updated = UpdateShoppingCartItem {
|
||||
id: item.id,
|
||||
product_id: item.product_id,
|
||||
shopping_cart_id: item.shopping_cart_id,
|
||||
quantity: Quantity::from_u32(987979879),
|
||||
quantity_unit: QuantityUnit::Kilogram,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -253,8 +253,8 @@ WHERE buyer_id = $1 AND state = 'active'
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use config::UpdateConfig;
|
||||
use fake::Fake;
|
||||
use model::*;
|
||||
|
||||
use crate::db::Database;
|
||||
|
||||
pub struct NoOpts;
|
||||
|
||||
@ -262,85 +262,53 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
async fn test_account(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
login: Option<String>,
|
||||
email: Option<String>,
|
||||
hash: Option<String>,
|
||||
) -> FullAccount {
|
||||
use fake::faker::internet::en;
|
||||
let login: String = login.unwrap_or_else(|| en::Username().fake());
|
||||
let email: String = email.unwrap_or_else(|| en::FreeEmail().fake());
|
||||
let hash: String = hash.unwrap_or_else(|| en::Password(10..20).fake());
|
||||
|
||||
CreateAccount {
|
||||
email: Email::new(email),
|
||||
login: Login::new(login),
|
||||
pass_hash: PassHash::new(hash),
|
||||
role: Role::Admin,
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_shopping_cart(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
buyer_id: Option<AccountId>,
|
||||
) -> ShoppingCart {
|
||||
let buyer_id = match buyer_id {
|
||||
Some(id) => id,
|
||||
_ => test_account(&mut *t, None, None, None).await.id,
|
||||
_ => 1.into(),
|
||||
};
|
||||
|
||||
super::create_shopping_cart(
|
||||
CreateShoppingCart {
|
||||
buyer_id,
|
||||
payment_method: PaymentMethod::PaymentOnTheSpot,
|
||||
},
|
||||
t,
|
||||
)
|
||||
CreateShoppingCart {
|
||||
buyer_id,
|
||||
payment_method: PaymentMethod::PaymentOnTheSpot,
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn create_shopping_cart() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
let cart = super::create_shopping_cart(
|
||||
CreateShoppingCart {
|
||||
buyer_id: account.id,
|
||||
payment_method: PaymentMethod::PaymentOnTheSpot,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let cart = CreateShoppingCart {
|
||||
buyer_id: 1.into(),
|
||||
payment_method: PaymentMethod::PaymentOnTheSpot,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert!(cart.is_ok());
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn update_shopping_cart() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
let original = test_shopping_cart(&mut t, Some(1.into())).await;
|
||||
|
||||
let original = test_shopping_cart(&mut t, Some(account.id)).await;
|
||||
|
||||
let cart = super::update_shopping_cart(
|
||||
UpdateShoppingCart {
|
||||
id: original.id,
|
||||
buyer_id: account.id,
|
||||
payment_method: PaymentMethod::PayU,
|
||||
state: ShoppingCartState::Closed,
|
||||
checkout_notes: Some("Foo bar".into()),
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let cart = UpdateShoppingCart {
|
||||
id: original.id,
|
||||
buyer_id: 1.into(),
|
||||
payment_method: PaymentMethod::PayU,
|
||||
state: ShoppingCartState::Closed,
|
||||
checkout_notes: Some("Foo bar".into()),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -350,7 +318,7 @@ mod tests {
|
||||
cart,
|
||||
ShoppingCart {
|
||||
id: original.id,
|
||||
buyer_id: account.id,
|
||||
buyer_id: 1.into(),
|
||||
payment_method: PaymentMethod::PayU,
|
||||
state: ShoppingCartState::Closed,
|
||||
checkout_notes: Some("Foo bar".into())
|
||||
@ -358,29 +326,23 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn without_cart_ensure_shopping_cart() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
let cart = super::ensure_active_shopping_cart(
|
||||
EnsureActiveShoppingCart {
|
||||
buyer_id: account.id,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let cart = EnsureActiveShoppingCart { buyer_id: 1.into() }
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let id = cart.id;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert_eq!(
|
||||
cart,
|
||||
model::ShoppingCart {
|
||||
ShoppingCart {
|
||||
id,
|
||||
buyer_id: account.id,
|
||||
buyer_id: 1.into(),
|
||||
payment_method: Default::default(),
|
||||
state: ShoppingCartState::Active,
|
||||
checkout_notes: None
|
||||
@ -388,34 +350,26 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[actix::test]
|
||||
#[tokio::test]
|
||||
async fn with_inactive_cart_ensure_shopping_cart() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let account = test_account(&mut t, None, None, None).await;
|
||||
|
||||
let original = test_shopping_cart(&mut t, Some(account.id)).await;
|
||||
let _ = super::update_shopping_cart(
|
||||
UpdateShoppingCart {
|
||||
id: original.id,
|
||||
buyer_id: account.id,
|
||||
payment_method: Default::default(),
|
||||
state: ShoppingCartState::Closed,
|
||||
checkout_notes: None,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
let original = test_shopping_cart(&mut t, Some(1.into())).await;
|
||||
let _ = UpdateShoppingCart {
|
||||
id: original.id,
|
||||
buyer_id: 1.into(),
|
||||
payment_method: Default::default(),
|
||||
state: ShoppingCartState::Closed,
|
||||
checkout_notes: None,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let cart = super::ensure_active_shopping_cart(
|
||||
EnsureActiveShoppingCart {
|
||||
buyer_id: account.id,
|
||||
},
|
||||
&mut t,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let cart = EnsureActiveShoppingCart { buyer_id: 1.into() }
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert_ne!(original, cart);
|
||||
|
@ -14,7 +14,7 @@ pub struct Opts {}
|
||||
|
||||
impl UpdateConfig for Opts {}
|
||||
|
||||
#[actix::main]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
dotenv::dotenv().ok();
|
||||
init_tracing("account-manager");
|
||||
|
@ -19,7 +19,7 @@ futures = { version = "0.3.25" }
|
||||
model = { path = "../model" }
|
||||
rumqttc = { version = "0.17.0" }
|
||||
serde = { version = "*", features = ['derive'] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.37" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.37" }
|
||||
|
@ -1,7 +1,7 @@
|
||||
pub mod detailed_product {
|
||||
use model::v2::*;
|
||||
|
||||
use crate::stocks::Error;
|
||||
pub use crate::stocks::Error;
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Input {
|
||||
@ -21,10 +21,13 @@ pub mod detailed_product {
|
||||
pub mod detailed_products {
|
||||
use model::v2::*;
|
||||
|
||||
use crate::stocks::Error;
|
||||
pub use crate::stocks::Error;
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Input {}
|
||||
pub struct Input {
|
||||
pub limit: Limit,
|
||||
pub offset: Offset,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Details {
|
||||
|
@ -5,6 +5,8 @@ pub mod product_stock;
|
||||
pub mod product_variant;
|
||||
|
||||
pub use load::*;
|
||||
use model::v2::ProductVariantId;
|
||||
use model::ProductId;
|
||||
pub use product::*;
|
||||
pub use product_photo::*;
|
||||
pub use product_stock::*;
|
||||
@ -13,7 +15,18 @@ pub use product_variant::*;
|
||||
pub static CLIENT_NAME: &str = "stocks";
|
||||
|
||||
#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
||||
pub enum Error {}
|
||||
pub enum Error {
|
||||
#[error("Something went wrong")]
|
||||
InternalServerError,
|
||||
#[error("Failed to load products")]
|
||||
Products,
|
||||
#[error("Failed to load products for products {0:?}")]
|
||||
ProductVariants(Vec<ProductId>),
|
||||
#[error("Failed to load photos for products variants {0:?}")]
|
||||
VariantStocks(Vec<ProductVariantId>),
|
||||
#[error("Failed to load stocks for products variants {0:?}")]
|
||||
VariantPhotos(Vec<ProductVariantId>),
|
||||
}
|
||||
|
||||
pub mod rpc {
|
||||
use config::SharedAppConfig;
|
||||
|
@ -4,6 +4,6 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.6.2", features = [] }
|
||||
|
@ -2,10 +2,16 @@ use sqlx::Arguments;
|
||||
|
||||
pub type PgT<'l> = sqlx::Transaction<'l, sqlx::Postgres>;
|
||||
|
||||
pub struct Padding {
|
||||
pub limit: i32,
|
||||
pub offset: i32,
|
||||
}
|
||||
|
||||
pub struct MultiLoad<'transaction, 'transaction2, 'header, 'condition, T> {
|
||||
pool: &'transaction mut sqlx::Transaction<'transaction2, sqlx::Postgres>,
|
||||
header: &'header str,
|
||||
condition: &'condition str,
|
||||
padding: Option<Padding>,
|
||||
sort: Option<String>,
|
||||
size: Option<usize>,
|
||||
allow_over_max: bool,
|
||||
@ -26,6 +32,7 @@ where
|
||||
pool,
|
||||
header,
|
||||
condition,
|
||||
padding: None,
|
||||
sort: None,
|
||||
size: Some(200),
|
||||
allow_over_max: false,
|
||||
@ -48,6 +55,11 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_padding(mut self, limit: i32, offset: i32) -> Self {
|
||||
self.padding = Some(Padding { limit, offset });
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn load<'query, Error, ErrorFn, Ids>(
|
||||
&mut self,
|
||||
len: usize,
|
||||
@ -77,11 +89,13 @@ where
|
||||
},
|
||||
) {
|
||||
let query: String = self.header.into();
|
||||
let mut arg_cursor = 0;
|
||||
let mut query = ids.iter().enumerate().fold(query, |mut q, (idx, _id)| {
|
||||
if idx != 0 {
|
||||
q.push_str(" OR");
|
||||
}
|
||||
q.push_str(&format!(" {} ${}", self.condition, idx + 1));
|
||||
arg_cursor = idx;
|
||||
q
|
||||
});
|
||||
if let Some(s) = self.sort.as_deref() {
|
||||
@ -89,14 +103,32 @@ where
|
||||
query.push_str(s);
|
||||
query.push(' ');
|
||||
}
|
||||
let q = sqlx::query_as_with(
|
||||
query.as_str(),
|
||||
if self.padding.is_some() {
|
||||
arg_cursor += 1;
|
||||
query.push_str(&format!(
|
||||
"LIMIT ${} OFFSET ${}",
|
||||
arg_cursor + 1,
|
||||
arg_cursor + 2
|
||||
));
|
||||
// arg_cursor += 2;
|
||||
}
|
||||
|
||||
eprintln!("{}", query);
|
||||
|
||||
let mut args =
|
||||
ids.into_iter()
|
||||
.fold(sqlx::postgres::PgArguments::default(), |mut args, id| {
|
||||
eprintln!("id = {:?}", id);
|
||||
args.add(id);
|
||||
args
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
if let Some(Padding { limit, offset }) = self.padding {
|
||||
args.add(limit);
|
||||
args.add(offset);
|
||||
}
|
||||
|
||||
let q = sqlx::query_as_with(query.as_str(), args);
|
||||
|
||||
let records: Vec<T> = match q.fetch_all(&mut *self.pool).await {
|
||||
Ok(rec) => rec,
|
||||
|
@ -23,10 +23,14 @@ rumqttc = { version = "*" }
|
||||
sendgrid = { version = "0.18.1", features = ["async"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0", features = [] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.37" }
|
||||
tracing-opentelemetry = { version = "0.17.4" }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -8,7 +8,7 @@ pub mod encrypt;
|
||||
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops;
|
||||
use std::ops::BitOr;
|
||||
use std::ops::{BitOr, Range};
|
||||
use std::str::FromStr;
|
||||
|
||||
use derive_more::{Deref, DerefMut, Display, From};
|
||||
@ -311,6 +311,89 @@ impl ops::Mul<Quantity> for Price {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Ranged: Sized + From<u32> + Copy {
|
||||
fn in_range(self, range: Range<u32>) -> Self {
|
||||
let v = self.into_raw().try_into().unwrap_or(range.start);
|
||||
v.max(range.start).min(range.end).into()
|
||||
}
|
||||
|
||||
fn into_raw(self) -> i32;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Deref,
|
||||
From,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
pub struct Limit(NonNegative);
|
||||
|
||||
impl From<u32> for Limit {
|
||||
fn from(value: u32) -> Self {
|
||||
Self::from_u32(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for Limit {
|
||||
fn into_raw(self) -> i32 {
|
||||
self.0 .0
|
||||
}
|
||||
}
|
||||
|
||||
impl Limit {
|
||||
pub fn from_u32(price: u32) -> Self {
|
||||
Self(NonNegative(price as i32))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
#[derive(
|
||||
Default,
|
||||
Debug,
|
||||
Copy,
|
||||
Clone,
|
||||
Hash,
|
||||
PartialOrd,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Deref,
|
||||
From,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
pub struct Offset(NonNegative);
|
||||
|
||||
impl From<u32> for Offset {
|
||||
fn from(value: u32) -> Self {
|
||||
Self::from_u32(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for Offset {
|
||||
fn into_raw(self) -> i32 {
|
||||
self.0 .0
|
||||
}
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
pub fn from_u32(price: u32) -> Self {
|
||||
Self(NonNegative(price as i32))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
#[cfg_attr(feature = "db", derive(sqlx::Type))]
|
||||
#[cfg_attr(feature = "db", sqlx(transparent))]
|
||||
@ -905,7 +988,7 @@ pub mod v2 {
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use crate::{
|
||||
Day, Days, FileName, LocalPath, PhotoId, Price, ProductCategory, ProductId,
|
||||
Day, Days, FileName, Limit, LocalPath, Offset, PhotoId, Price, ProductCategory, ProductId,
|
||||
ProductLongDesc, ProductName, ProductPhotoId, ProductShortDesc, Quantity, QuantityUnit,
|
||||
RecordId, StockId, UniqueName,
|
||||
};
|
||||
@ -937,19 +1020,26 @@ pub mod v2 {
|
||||
#[serde(transparent)]
|
||||
pub struct ProductVariantId(RecordId);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct DetailedProduct {
|
||||
pub id: ProductId,
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DetailedProductVariant {
|
||||
pub id: ProductVariantId,
|
||||
pub name: ProductName,
|
||||
pub short_description: ProductShortDesc,
|
||||
pub long_description: ProductLongDesc,
|
||||
pub category: Option<ProductCategory>,
|
||||
pub price: Price,
|
||||
pub deliver_days_flag: Days,
|
||||
|
||||
pub stocks: Vec<Stock>,
|
||||
|
||||
pub photos: Vec<Photo>,
|
||||
pub photos: Vec<ProductLinkedPhoto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct DetailedProduct {
|
||||
pub id: ProductId,
|
||||
pub name: ProductName,
|
||||
pub category: Option<ProductCategory>,
|
||||
pub deliver_days_flag: Days,
|
||||
pub variants: Vec<DetailedProductVariant>,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "dummy", derive(fake::Dummy))]
|
||||
|
@ -9,10 +9,14 @@ actix-rt = { version = "2.7", features = [] }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
config = { path = "../config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
rumqttc = { version = "*" }
|
||||
serde = { version = "1.0.137", features = ["derive"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tracing = { version = "0.1.34" }
|
||||
uuid = { version = "1.2.1", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -10,7 +10,7 @@ chrono = { version = "0.4", features = ["serde"] }
|
||||
config = { path = "../config" }
|
||||
database_manager = { path = "../database_manager" }
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
pay_u = { version = '0.1', features = ["single-client"] }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
@ -21,4 +21,5 @@ tracing = { version = "0.1.34" }
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -332,6 +332,6 @@ mod tests {
|
||||
testx::db!(config, db);
|
||||
let db = db.start();
|
||||
|
||||
let manager = PaymentManager::build(config, db).await;
|
||||
let _manager = PaymentManager::build(config, db).await;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ config = { path = "../config" }
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
dotenv = { version = "0.15.0" }
|
||||
futures = { version = "0.3.25" }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
opentelemetry = { version = "0.17.0" }
|
||||
opentelemetry-jaeger = { version = "0.17.0" }
|
||||
parking_lot = { version = "0.12", features = [] }
|
||||
@ -24,7 +24,7 @@ pretty_env_logger = { version = "0.4", features = [] }
|
||||
rumqttc = { version = "*" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sonic-channel = { version = "1.1.0", features = ["ingest"] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.6" }
|
||||
@ -32,3 +32,7 @@ tracing-opentelemetry = { version = "0.17.4" }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
uuid = { version = "1.2.1", features = ["serde"] }
|
||||
whatlang = { version = "0.16.2" }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -15,7 +15,7 @@ db-utils = { path = "../db-utils" }
|
||||
derive_more = { version = "0.99", features = [] }
|
||||
dotenv = { version = "0.15.0" }
|
||||
futures = { version = "0.3.25" }
|
||||
model = { path = "../model" }
|
||||
model = { path = "../model", features = ["db"] }
|
||||
opentelemetry = { version = "0.17.0" }
|
||||
opentelemetry-jaeger = { version = "0.17.0" }
|
||||
pretty_env_logger = { version = "0.4", features = [] }
|
||||
@ -23,14 +23,15 @@ rumqttc = { version = "*" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.6.2", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] }
|
||||
sqlx-core = { version = "0.6.2", features = [] }
|
||||
tarpc = { version = "0.30.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
tarpc = { version = "0.31.0", features = ["tokio1", "serde-transport-bincode", "serde-transport", "serde", "serde-transport-json", "tcp"] }
|
||||
thiserror = { version = "1.0.31" }
|
||||
tokio = { version = "1.21.2", features = ['full'] }
|
||||
tracing = { version = "0.1.6" }
|
||||
tracing-opentelemetry = { version = "0.17.4" }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
uuid = { version = "1.2.1" }
|
||||
uuid = { version = "1.2.1", features = ['v4'] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
insta = { version = "1.21.0" }
|
||||
|
@ -8,21 +8,21 @@ CREATE TYPE "QuantityUnit" AS ENUM (
|
||||
);
|
||||
|
||||
CREATE TABLE photos (
|
||||
id integer NOT NULL PRIMARY KEY,
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
local_path character varying NOT NULL,
|
||||
file_name character varying NOT NULL,
|
||||
unique_name text DEFAULT (gen_random_uuid())::text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE products (
|
||||
id integer NOT NULL PRIMARY KEY,
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
"name" character varying NOT NULL,
|
||||
category character varying,
|
||||
deliver_days_flag integer DEFAULT 127 NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE product_variants (
|
||||
id integer NOT NULL PRIMARY KEY,
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
product_id integer REFERENCES products (id) NOT NULL,
|
||||
"name" character varying NOT NULL,
|
||||
short_description character varying NOT NULL,
|
||||
@ -32,7 +32,7 @@ CREATE TABLE product_variants (
|
||||
);
|
||||
|
||||
CREATE TABLE stocks (
|
||||
id integer NOT NULL PRIMARY KEY,
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
product_variant_id integer REFERENCES product_variants(id) NOT NULL,
|
||||
quantity integer DEFAULT 0 NOT NULL,
|
||||
quantity_unit "QuantityUnit" NOT NULL,
|
||||
@ -40,7 +40,7 @@ CREATE TABLE stocks (
|
||||
);
|
||||
|
||||
CREATE TABLE product_photos (
|
||||
id integer NOT NULL PRIMARY KEY,
|
||||
id serial NOT NULL PRIMARY KEY,
|
||||
product_variant_id integer REFERENCES product_variants(id) NOT NULL,
|
||||
photo_id integer REFERENCES photos(id) NOT NULL
|
||||
);
|
||||
|
@ -1,14 +1,19 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use channels::stocks::{detailed_product, detailed_products};
|
||||
use channels::AsyncClient;
|
||||
use config::SharedAppConfig;
|
||||
use db_utils::PgT;
|
||||
use model::v2::{DetailedProduct, DetailedProductVariant, Product, ProductVariant};
|
||||
use model::Limit;
|
||||
|
||||
use crate::db::Database;
|
||||
use crate::db::{Database, PhotosForProductVariants, ProductVariantsStock, ProductsVariants};
|
||||
|
||||
pub async fn detailed_product(
|
||||
input: detailed_product::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: detailed_product::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> detailed_product::Output {
|
||||
todo!()
|
||||
}
|
||||
@ -16,8 +21,316 @@ pub async fn detailed_product(
|
||||
pub async fn detailed_products(
|
||||
input: detailed_products::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> detailed_products::Output {
|
||||
todo!()
|
||||
let mut t = match db.pool().begin().await {
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e);
|
||||
return Err(detailed_products::Error::InternalServerError);
|
||||
}
|
||||
Ok(t) => t,
|
||||
};
|
||||
|
||||
let res = inner_detailed_products(input, &mut t, Some(_mqtt), Some(_config)).await;
|
||||
|
||||
t.commit().await.ok();
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
async fn inner_detailed_products(
|
||||
input: detailed_products::Input,
|
||||
t: &mut PgT<'_>,
|
||||
_mqtt: Option<AsyncClient>,
|
||||
_config: Option<SharedAppConfig>,
|
||||
) -> detailed_products::Output {
|
||||
let dbm = crate::db::AllProducts {
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
};
|
||||
let products = match dbm.run(&mut *t).await {
|
||||
Ok(products) => products,
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e);
|
||||
return Err(detailed_products::Error::Products);
|
||||
}
|
||||
};
|
||||
let dbm = ProductsVariants {
|
||||
product_ids: products.iter().map(|p| p.id).collect(),
|
||||
limit: Some(Limit::from_u32(products.len() as u32 * 10)),
|
||||
offset: Some(0.into()),
|
||||
};
|
||||
let variants = match dbm.run(&mut *t).await {
|
||||
Ok(variants) => variants,
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e);
|
||||
return Err(detailed_products::Error::ProductVariants(
|
||||
products.into_iter().map(|p| p.id).collect(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let dbm = ProductVariantsStock {
|
||||
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
||||
};
|
||||
let stocks = match dbm.run(&mut *t).await {
|
||||
Ok(stocks) => stocks,
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e);
|
||||
return Err(detailed_products::Error::VariantStocks(
|
||||
variants.into_iter().map(|p| p.id).collect(),
|
||||
));
|
||||
}
|
||||
};
|
||||
let dbm = PhotosForProductVariants {
|
||||
product_variant_ids: variants.iter().map(|p| p.id).collect(),
|
||||
};
|
||||
let photos = match dbm.run(t).await {
|
||||
Ok(photos) => photos,
|
||||
Err(e) => {
|
||||
tracing::error!("{}", e);
|
||||
return Err(detailed_products::Error::VariantPhotos(
|
||||
variants.into_iter().map(|p| p.id).collect(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let mut variants = {
|
||||
let len = variants.len();
|
||||
variants
|
||||
.into_iter()
|
||||
.fold(HashMap::with_capacity(len), |mut h, variant| {
|
||||
h.entry(variant.product_id)
|
||||
.or_insert_with(|| Vec::with_capacity(10))
|
||||
.push(variant);
|
||||
h
|
||||
})
|
||||
};
|
||||
|
||||
let mut stocks = {
|
||||
let len = stocks.len();
|
||||
stocks
|
||||
.into_iter()
|
||||
.fold(HashMap::with_capacity(len), |mut h, stock| {
|
||||
h.entry(stock.product_variant_id)
|
||||
.or_insert_with(|| Vec::with_capacity(10))
|
||||
.push(stock);
|
||||
h
|
||||
})
|
||||
};
|
||||
|
||||
let mut photos =
|
||||
photos
|
||||
.into_iter()
|
||||
.fold(HashMap::with_capacity(stocks.len()), |mut h, photo| {
|
||||
h.entry(photo.product_variant_id)
|
||||
.or_insert_with(|| Vec::with_capacity(10))
|
||||
.push(photo);
|
||||
h
|
||||
});
|
||||
|
||||
let products = products
|
||||
.into_iter()
|
||||
.map(
|
||||
|Product {
|
||||
id,
|
||||
name,
|
||||
category,
|
||||
deliver_days_flag,
|
||||
}| DetailedProduct {
|
||||
id,
|
||||
name,
|
||||
category,
|
||||
deliver_days_flag,
|
||||
variants: variants
|
||||
.remove(&id)
|
||||
.unwrap_or(vec![])
|
||||
.into_iter()
|
||||
.map(
|
||||
|ProductVariant {
|
||||
id,
|
||||
product_id: _,
|
||||
name,
|
||||
short_description,
|
||||
long_description,
|
||||
price,
|
||||
}| DetailedProductVariant {
|
||||
id,
|
||||
name,
|
||||
short_description,
|
||||
long_description,
|
||||
price,
|
||||
stocks: stocks.remove(&id).unwrap_or_default(),
|
||||
photos: photos.remove(&id).unwrap_or_default(),
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
Ok(detailed_products::Details { products })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use channels::stocks::detailed_products;
|
||||
use config::UpdateConfig;
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::actions::load::inner_detailed_products;
|
||||
use crate::db::*;
|
||||
|
||||
pub struct NoOpts;
|
||||
impl UpdateConfig for NoOpts {}
|
||||
|
||||
async fn test_product(t: &mut PgT<'_>) -> Product {
|
||||
CreateProduct {
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
category: None,
|
||||
deliver_days_flag: Days(vec![Day::Friday, Day::Sunday]),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
|
||||
CreateProductVariant {
|
||||
product_id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
price: Default::default(),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_photo(t: &mut PgT<'_>) -> Photo {
|
||||
CreatePhoto {
|
||||
local_path: LocalPath::new(format!("{}", Uuid::new_v4())),
|
||||
file_name: FileName::new(format!("{}", Uuid::new_v4())),
|
||||
unique_name: UniqueName::new(format!("{}", Uuid::new_v4())),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product_photo(
|
||||
product_variant_id: ProductVariantId,
|
||||
photo_id: PhotoId,
|
||||
t: &mut PgT<'_>,
|
||||
) -> ProductPhoto {
|
||||
CreateProductPhoto {
|
||||
product_variant_id,
|
||||
photo_id,
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn n_test_photo(
|
||||
n: usize,
|
||||
product_variant_id: ProductVariantId,
|
||||
t: &mut PgT<'_>,
|
||||
) -> Vec<(Photo, ProductPhoto)> {
|
||||
let mut res = Vec::with_capacity(n);
|
||||
|
||||
for _ in 0..n {
|
||||
let photo = test_photo(t).await;
|
||||
let product_photo = test_product_photo(product_variant_id, photo.id, t).await;
|
||||
|
||||
res.push((photo, product_photo));
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
async fn test_stock(product_variant_id: ProductVariantId, pool: &mut PgT<'_>) -> Stock {
|
||||
let quantity = Quantity::from_u32(345);
|
||||
let quantity_unit = QuantityUnit::Piece;
|
||||
|
||||
CreateStock {
|
||||
product_variant_id,
|
||||
quantity_unit,
|
||||
quantity,
|
||||
}
|
||||
.run(&mut *pool)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn n_test_variant(
|
||||
variant_count: usize,
|
||||
product_id: ProductId,
|
||||
t: &mut PgT<'_>,
|
||||
) -> Vec<(ProductVariant, Stock, Vec<(Photo, ProductPhoto)>)> {
|
||||
let mut variants = Vec::with_capacity(variant_count);
|
||||
|
||||
for _ in 0..variant_count {
|
||||
let variant = test_product_variant(product_id, t).await;
|
||||
let stock = test_stock(variant.id, t).await;
|
||||
let photos = n_test_photo(3, variant.id, t).await;
|
||||
|
||||
variants.push((variant, stock, photos));
|
||||
}
|
||||
variants
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_details() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let product_1 = test_product(&mut t).await;
|
||||
let _variants_1 = n_test_variant(3, product_1.id, &mut t).await;
|
||||
|
||||
let product_2 = test_product(&mut t).await;
|
||||
let _variants_2 = n_test_variant(5, product_2.id, &mut t).await;
|
||||
|
||||
let product_3 = test_product(&mut t).await;
|
||||
let _variants_2 = n_test_variant(2, product_3.id, &mut t).await;
|
||||
|
||||
let res = inner_detailed_products(
|
||||
detailed_products::Input {
|
||||
limit: Limit::from_u32(2000),
|
||||
offset: Offset::from_u32(0),
|
||||
},
|
||||
&mut t,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
|
||||
let mut res = res.unwrap();
|
||||
|
||||
assert_eq!(res.products.len(), 3);
|
||||
let product = res.products.remove(0);
|
||||
assert_eq!(product.variants.len(), 3);
|
||||
for variant in product.variants {
|
||||
assert_eq!(variant.photos.len(), 3);
|
||||
assert_eq!(variant.stocks.len(), 1);
|
||||
}
|
||||
|
||||
let product = res.products.remove(0);
|
||||
assert_eq!(product.variants.len(), 5);
|
||||
for variant in product.variants {
|
||||
assert_eq!(variant.photos.len(), 3);
|
||||
assert_eq!(variant.stocks.len(), 1);
|
||||
}
|
||||
|
||||
let product = res.products.remove(0);
|
||||
assert_eq!(product.variants.len(), 2);
|
||||
for variant in product.variants {
|
||||
assert_eq!(variant.photos.len(), 3);
|
||||
assert_eq!(variant.stocks.len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,28 +5,28 @@ use config::SharedAppConfig;
|
||||
use crate::db::Database;
|
||||
|
||||
pub async fn create_product(
|
||||
input: create_product::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: create_product::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> create_product::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update_product(
|
||||
input: update_product::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: update_product::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> update_product::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete_product(
|
||||
input: delete_product::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: delete_product::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> delete_product::Output {
|
||||
todo!()
|
||||
}
|
||||
|
@ -5,19 +5,19 @@ use config::SharedAppConfig;
|
||||
use crate::db::Database;
|
||||
|
||||
pub async fn add_product_photo(
|
||||
input: add_product_photo::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: add_product_photo::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> add_product_photo::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete_product_photo(
|
||||
input: delete_product_photo::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: delete_product_photo::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> delete_product_photo::Output {
|
||||
todo!()
|
||||
}
|
||||
|
@ -5,19 +5,19 @@ use config::SharedAppConfig;
|
||||
use crate::db::Database;
|
||||
|
||||
pub async fn create_product_stock(
|
||||
input: create_product_stock::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: create_product_stock::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> create_product_stock::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update_product_stock(
|
||||
input: update_product_stock::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: update_product_stock::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> update_product_stock::Output {
|
||||
todo!()
|
||||
}
|
||||
|
@ -5,28 +5,28 @@ use config::SharedAppConfig;
|
||||
use crate::db::Database;
|
||||
|
||||
pub async fn create_product_variant(
|
||||
input: create_product_variant::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: create_product_variant::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> create_product_variant::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update_product_variant(
|
||||
input: update_product_variant::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: update_product_variant::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> update_product_variant::Output {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete_product_variant(
|
||||
input: delete_product_variant::Input,
|
||||
db: Database,
|
||||
mqtt: AsyncClient,
|
||||
config: SharedAppConfig,
|
||||
_input: delete_product_variant::Input,
|
||||
_db: Database,
|
||||
_mqtt: AsyncClient,
|
||||
_config: SharedAppConfig,
|
||||
) -> delete_product_variant::Output {
|
||||
todo!()
|
||||
}
|
||||
|
@ -0,0 +1,939 @@
|
||||
---
|
||||
source: crates/stock_manager/./src/actions/load.rs
|
||||
expression: res
|
||||
---
|
||||
Details {
|
||||
products: [
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
136,
|
||||
),
|
||||
name: ProductName(
|
||||
"cfc46f2d-f56a-4192-86ac-669eee544e2c",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
125,
|
||||
),
|
||||
name: ProductName(
|
||||
"d2caf306-3e74-40a7-ae8d-67dffa1fe89c",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"2ff98474-8036-4593-b375-1b847f5feeb5",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"1edbf80a-c7f8-4289-b245-eb9342300876",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
67,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
125,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
163,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"5d04424a-6800-41df-914a-2e9a526e12e7",
|
||||
),
|
||||
file_name: FileName(
|
||||
"7e835666-264b-40c6-a346-5eeddb061acd",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"9ddd3f1f-8ec4-4810-8db2-58ebe3418a23",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
125,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
164,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"1057d8d9-5ba4-4eca-8ca6-97574a784bbc",
|
||||
),
|
||||
file_name: FileName(
|
||||
"ac6508fd-99f5-4674-a5b2-f349dd955fbf",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"cc2147d8-b349-4c41-9a49-fafc93c9e884",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
125,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
165,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b03eb5a2-c26a-47c8-822b-7619d00ebbed",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3902391c-c47c-465d-a2ec-623f53cce814",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"bbafe518-73ee-4685-8cc0-0cd2e814d32c",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
125,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
126,
|
||||
),
|
||||
name: ProductName(
|
||||
"1323b9bc-9c97-487d-afc8-2003c70f440f",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"3740f189-9464-4e41-8883-106598bb6e7e",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"e37d71aa-3bd1-42ec-915a-1fa1c761ec7b",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
68,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
126,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
166,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"1ced075e-4ab0-4c52-8e1f-cb0e0d07b95c",
|
||||
),
|
||||
file_name: FileName(
|
||||
"478de82f-b366-421a-abce-19779845b659",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"ffc7d82e-4401-4975-8469-bee5dc3345e7",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
126,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
167,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"2d46cc8c-8c8e-4100-ae18-ca935cbeb1d9",
|
||||
),
|
||||
file_name: FileName(
|
||||
"1ba9314e-d928-4b4c-b75c-4287c0644b75",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"b45017fd-a804-48cc-8f4d-c21c00e95f10",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
126,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
168,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"dda34f45-1686-46c9-bdcb-259e147718b2",
|
||||
),
|
||||
file_name: FileName(
|
||||
"1fdba406-e2fe-444b-ae78-1ded4d985b55",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"99243264-9098-4fa0-a3d0-4cd03248d66a",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
126,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
127,
|
||||
),
|
||||
name: ProductName(
|
||||
"0ff0a1e6-c458-4525-b431-c7c5bfa13515",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"ee7f70da-e46f-488b-9bc2-743ebc3066a3",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"d529826e-e647-4191-9fcf-8bc1ada01e9d",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
69,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
127,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
169,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"06bc914f-ff75-4eb2-8520-f509fdba1fd6",
|
||||
),
|
||||
file_name: FileName(
|
||||
"8db810d6-cc66-494e-989f-c604ae02bb61",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"4facf461-b86d-4ae6-b3d6-a0fd4b2e94c9",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
127,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
170,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8de3b87c-b1f9-4f31-9c07-c4efdc078a5e",
|
||||
),
|
||||
file_name: FileName(
|
||||
"26b5b728-917d-4750-8fc1-c31990824ed3",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"0656ea1a-4976-46fd-a7d2-550ea15b18ad",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
127,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
171,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"66da98b9-fa8a-4fa6-84e6-5c6911f1de2d",
|
||||
),
|
||||
file_name: FileName(
|
||||
"37033640-df1c-4f6a-a2ab-e100e1c5589f",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"717cf8ee-01fd-416a-a114-da0bbd6a3911",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
127,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
137,
|
||||
),
|
||||
name: ProductName(
|
||||
"7e774acc-e2da-4738-9314-fe9fe261e3b0",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
128,
|
||||
),
|
||||
name: ProductName(
|
||||
"9ea07670-3bdf-4590-a1d6-a3ca9a1b5f97",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"d64ca158-d322-40e6-909b-e61cf85174bc",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"dce8c61d-c9d6-4fda-9835-314fdce460da",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
70,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
128,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
172,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"73c82632-17c4-43c8-83ad-d48bc07aff88",
|
||||
),
|
||||
file_name: FileName(
|
||||
"648beb23-ffae-42db-b03a-36a872ae93f6",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"7b44021b-c2e7-4023-a07f-203aecbd0d4e",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
128,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
173,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3332438c-ac16-4620-b82e-65f971b37a8c",
|
||||
),
|
||||
file_name: FileName(
|
||||
"6946453d-aa6b-4f99-8c9b-43d1c6a8ae2f",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"6d8c6222-1b0f-4b32-9376-5593fefd4d76",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
128,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
174,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"a6af6492-147d-4ec2-9240-d1d9c520c37e",
|
||||
),
|
||||
file_name: FileName(
|
||||
"e55dac90-3aa2-499f-9551-45f6454b98bd",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"f8683c9c-5688-4490-82bc-24d1948ae46a",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
128,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
129,
|
||||
),
|
||||
name: ProductName(
|
||||
"6c85837d-f0e1-4e91-a8a7-f1953c0901a6",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"20e15392-208c-42f2-9de4-88d317168378",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"951e5740-4f36-46bd-a5fa-4978c5909afa",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
71,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
129,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
175,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"02760202-1c91-4122-b2c7-4b0a7dad3459",
|
||||
),
|
||||
file_name: FileName(
|
||||
"20e48ad2-e265-4349-bcb7-2c7c0bc577b2",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"ffe76596-3789-48a9-a22e-af4213a0c380",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
129,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
176,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"1c0a21a2-9825-4d2f-bb7d-a7cefab2db02",
|
||||
),
|
||||
file_name: FileName(
|
||||
"ebf4b435-e30b-4a08-a748-e04da7f86b66",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"bad53d6f-5dff-4c81-88df-836e839d60be",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
129,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
177,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b7f35012-f084-4390-8b30-aa1d66ce1737",
|
||||
),
|
||||
file_name: FileName(
|
||||
"277f595e-6285-4a1c-9ac2-a5efc7105062",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"3055730c-0a36-414a-abc9-1b84ded85769",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
129,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
130,
|
||||
),
|
||||
name: ProductName(
|
||||
"9ce07ea7-aa6d-4b4e-9af6-e86f3ce6b697",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"f5a72d7f-5252-4f75-a88d-e0ab9b334bf6",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"986fa45f-23f8-4a70-bf17-541f295baec0",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
72,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
130,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
178,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"6cda381b-df63-432f-a7c5-3d4496624d75",
|
||||
),
|
||||
file_name: FileName(
|
||||
"b541c928-5f93-4902-8ce7-2ed45b366b91",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"34f326f6-fefa-4ad3-8112-4ef942e98c54",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
130,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
179,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3830cff1-a923-4116-a946-8e7637645f7b",
|
||||
),
|
||||
file_name: FileName(
|
||||
"ef46ff42-cd3f-40a5-bec9-ab14734e3b1b",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"d89b8959-b9b8-4f7b-a7b4-d7548b01d6fa",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
130,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
180,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"0ac50443-0027-4151-8a0c-80e942fe25d1",
|
||||
),
|
||||
file_name: FileName(
|
||||
"c3650bf4-42e7-4376-bda8-af134bf1a4fa",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"84687975-eb4c-43a3-854d-6b4112d2e4a5",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
130,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
131,
|
||||
),
|
||||
name: ProductName(
|
||||
"b972dd90-5466-42f2-90f3-bae6615922dc",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"ee2b0694-34d1-4c05-a885-2e434df5cef7",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"20ea6a02-d76c-4a35-b1b2-3a97a440e6db",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
73,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
131,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
181,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b8077234-e7bc-40fd-ac84-907bbe9285ee",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3bf8ce7b-2639-4809-8b33-3b16200e7687",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"477c3e9e-d9a2-4858-98d9-c61dcec7b629",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
131,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
182,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"1e369270-54e1-4b19-9d9f-2b9204717cc3",
|
||||
),
|
||||
file_name: FileName(
|
||||
"7b168038-d1b7-43c1-b8a0-d3d7ce00edd3",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"1b0defdc-6220-4647-8403-1e990cf8370a",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
131,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
183,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8f3cbfa7-2653-491d-9a44-59f436de4d9d",
|
||||
),
|
||||
file_name: FileName(
|
||||
"72c9483b-1366-4a3e-8baf-92fe550c4190",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"b2820458-14ca-4b97-bf0e-ff6d434a8250",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
131,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
132,
|
||||
),
|
||||
name: ProductName(
|
||||
"7650e262-fc4d-4fcf-9068-6767c9398a11",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"0901ae8b-ab73-4332-9179-c6722b72b3b3",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"ec5b29ff-b961-4840-a2b9-ebdb0c189bc7",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
74,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
132,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
184,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"2851d6a1-e9af-4afd-9c03-99b1d38343e7",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3b41801e-fc52-4e4f-b377-dde05a234051",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"996e03cf-3951-4bfe-aba0-5f594b78ad4f",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
132,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
185,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3d777b4e-db95-4bec-b37e-5424f05c7aec",
|
||||
),
|
||||
file_name: FileName(
|
||||
"d5b5b067-09c8-4e77-8a43-08ad39d06858",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"bf7a3949-8caf-43cd-ac38-446b9b8327fa",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
132,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
186,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"4c61f99b-9e9a-4288-9a2c-3fe81ff4da11",
|
||||
),
|
||||
file_name: FileName(
|
||||
"5aa49987-04e0-4ce3-b34c-68cdbd5ef408",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"6a850bc2-286b-4e93-9c3b-1d2d2db2f363",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
132,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
138,
|
||||
),
|
||||
name: ProductName(
|
||||
"dd0ba76a-d598-41a5-9ebf-5094a5c1b386",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
133,
|
||||
),
|
||||
name: ProductName(
|
||||
"4d697353-4a3b-48f4-8877-430ac2f3404e",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"25fea571-497d-44a2-ba7f-a793b8a51ca0",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"7fb3d365-67df-4314-a8ba-3f9f3bf5ed14",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
75,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
133,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
187,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"1bcc22a0-ce12-40e6-8566-97bded0c8f39",
|
||||
),
|
||||
file_name: FileName(
|
||||
"91f8e0a9-b9f9-4538-a1d4-e4f3d2c8c923",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"8b879759-0276-4076-afbf-b3e0b07d5ba9",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
133,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
188,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8e49539d-0145-4e20-9017-4adc702fd4d5",
|
||||
),
|
||||
file_name: FileName(
|
||||
"a523ff42-1c2c-414a-b491-bedd396fa157",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"ebd39341-c06d-46a7-8a59-9a08dd6babab",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
133,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
189,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3653e4a4-5d17-4fef-ab55-69944d93c8ca",
|
||||
),
|
||||
file_name: FileName(
|
||||
"23aa3f97-db96-4311-9f77-d2acba75f6ab",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"1a7aea33-97e1-4867-855b-c12be218a1b4",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
133,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
134,
|
||||
),
|
||||
name: ProductName(
|
||||
"a8480794-391c-47bd-8fa3-54ea852827c5",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"ecca3052-db96-48ae-b451-d46045928181",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"43b98161-03b1-47b5-b7c5-9f9d58dbd64f",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
76,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
134,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
190,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"7e36e8c0-7141-4557-bb15-4c9fed8f9cdc",
|
||||
),
|
||||
file_name: FileName(
|
||||
"e4446e83-b766-4893-9306-09af45d04fce",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"e7f86bc8-bf06-4f3b-a5b0-4e3610acd826",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
134,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
191,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b61c67a2-b104-4ef9-9fe7-bf45427ad1e4",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3be64efc-782e-4b1d-bc2e-15902ddd400f",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"dc7c76bc-db93-48e4-ae20-35de71672f4c",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
134,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
192,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"c17c9655-4c10-4fa9-a549-e1bb60a42d21",
|
||||
),
|
||||
file_name: FileName(
|
||||
"c8ec1a1c-7264-4813-96f3-e3c776b5b33d",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"3c51860d-b4f9-45ef-9aef-7483fb9e249b",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
134,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -0,0 +1,940 @@
|
||||
---
|
||||
source: crates/stock_manager/./src/actions/load.rs
|
||||
assertion_line: 315
|
||||
expression: res
|
||||
---
|
||||
Details {
|
||||
products: [
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
139,
|
||||
),
|
||||
name: ProductName(
|
||||
"6757e180-f20e-4946-97f2-ef2e6cbe2480",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
135,
|
||||
),
|
||||
name: ProductName(
|
||||
"63348738-0540-4873-8272-7a4da873af66",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"436a6050-839e-49e8-8de5-38912b77319e",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"1e3f627b-72ec-431d-b970-56dbd5bcca55",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
77,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
135,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
193,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3974997e-c1a6-4e78-bfba-ca5fdfef2b99",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3fe1b917-0453-460e-9753-20d83ab773cb",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"47bb8098-58de-4ea6-b251-6adbac2a6192",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
135,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
194,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b9127a53-aa7d-4a38-bfee-606ef736523a",
|
||||
),
|
||||
file_name: FileName(
|
||||
"dd670ac5-cf3f-4231-a49f-21ea987e8d1a",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"a28c1adb-587f-42c9-a304-947557eb44fe",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
135,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
195,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"665c928f-ba35-4320-8917-9b09658a2303",
|
||||
),
|
||||
file_name: FileName(
|
||||
"30c5df9c-1be2-4523-9b54-ea82b7ca57ef",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"69065163-0684-4b03-82f4-ab147b692b4d",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
135,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
136,
|
||||
),
|
||||
name: ProductName(
|
||||
"a27d752e-5544-4679-96a4-ae8173628e8c",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"b84be335-8879-45b0-9b48-31ea9722c255",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"d096233f-f5b4-4978-83f3-08fb99c6a800",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
78,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
136,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
196,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"94672fee-f893-4413-a5c5-4b3f0645555a",
|
||||
),
|
||||
file_name: FileName(
|
||||
"f57af52b-46ac-44ed-8c7f-9b0e95c2fe44",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"aa29310b-ec70-4fc2-9879-e431e663507f",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
136,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
197,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"c6c55e0f-cc55-495e-9f0b-389f745c9d39",
|
||||
),
|
||||
file_name: FileName(
|
||||
"ed291f5f-a4e8-4097-9570-6fcf970c99dc",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"d2da4f18-6c36-4bfb-a7cc-3a02412aef34",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
136,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
198,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"59904639-ed9f-4a65-94b3-e44bd788acb1",
|
||||
),
|
||||
file_name: FileName(
|
||||
"6594df2d-82e8-4320-b542-2409581740d4",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"9bb9118e-9afa-4386-84a1-8f192eca6b4d",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
136,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
137,
|
||||
),
|
||||
name: ProductName(
|
||||
"a0ca4130-a0e1-46c9-b8aa-e66e76042097",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"5b22977e-9aa7-4c26-9030-42a42541d80c",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"dcd41ae9-ceb9-441c-bd38-835f57cfa0d8",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
79,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
137,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
199,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8e1241bd-a00c-4eda-a840-16a9f29e18bc",
|
||||
),
|
||||
file_name: FileName(
|
||||
"e32cb19e-5a9e-47bf-ae43-d5e191112b16",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"e806e980-e2a9-4e2a-81ac-ede89b4ccd56",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
137,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
200,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"712980c6-5dad-4012-b179-b580c0709da2",
|
||||
),
|
||||
file_name: FileName(
|
||||
"353a27ef-4833-4773-8e34-a072ea64d477",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"36c3dc37-fe62-44da-a894-7f739739374e",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
137,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
201,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"79648a75-389e-4c7d-9417-0370d77ddf30",
|
||||
),
|
||||
file_name: FileName(
|
||||
"c17bf5a9-c10e-4fe7-8cd0-187a4687c341",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"68d4c0cf-e493-4347-85cf-1b4d67ba22a9",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
137,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
140,
|
||||
),
|
||||
name: ProductName(
|
||||
"65a75053-5213-4e29-a182-e40f27a70fc6",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
138,
|
||||
),
|
||||
name: ProductName(
|
||||
"932b97a3-16d0-42bd-8260-69aa740a0ae0",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"766cb4bd-7702-4f68-a38d-65fb29b5d1ff",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"e3ec80d8-7296-417e-a3cc-4b747910b0c7",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
80,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
138,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
202,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"469b253d-bcb3-49d9-a9b3-c5ce5f4a1e48",
|
||||
),
|
||||
file_name: FileName(
|
||||
"ef1a3db6-76d0-4c42-9c6f-1a3bc709bfcf",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"8d9470a2-1a47-4ff1-b8dd-af72794c5a05",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
138,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
203,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"a12f7daa-d611-4f0b-bc78-f46042f5cdae",
|
||||
),
|
||||
file_name: FileName(
|
||||
"28c99b25-f092-4715-97cf-5a923a24d1e8",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"89d57281-fbd9-480d-bbd6-de50b4a16f09",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
138,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
204,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"859ad5a9-6d48-4fdc-be6b-cc4ffd30e123",
|
||||
),
|
||||
file_name: FileName(
|
||||
"580a6f3f-054c-4d2e-b99e-75a4d9026be9",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"23c1c21a-7a09-4c08-8cea-9a9634a04d99",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
138,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
139,
|
||||
),
|
||||
name: ProductName(
|
||||
"643f4e9d-0938-4dbd-b94a-9f528123dca2",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"0cf9a04a-6f02-4c41-9d78-d4a97c496c80",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"07931de8-00af-44df-8e86-18ab2f9f9b1d",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
81,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
139,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
205,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8369a83f-7268-4735-a51b-62dd1a364e1c",
|
||||
),
|
||||
file_name: FileName(
|
||||
"c5f0dbc5-3430-4364-ba21-5d602573f2f6",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"64cf47b7-ed11-4bb8-b11f-9be3c8d62bd7",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
139,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
206,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"b58d2ecd-ecf2-4fe0-9a3f-22ec9596b746",
|
||||
),
|
||||
file_name: FileName(
|
||||
"bde7e93e-a566-4e9e-a16a-e19e2566b98c",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"91561b33-a027-4bfd-baf7-a2f5d1ffc191",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
139,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
207,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"d2d9461c-9efb-4b20-893d-eb8479ae6c42",
|
||||
),
|
||||
file_name: FileName(
|
||||
"1d06b1de-7a4b-48d3-8346-75fd4dc6b069",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"b87937f7-dcce-4848-94e5-794c6a494f8f",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
139,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
140,
|
||||
),
|
||||
name: ProductName(
|
||||
"08fc623b-575c-402f-848a-bb3dd3cfc480",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"c114b73e-a695-4051-8c39-9e2746695f35",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"f7fa3c0c-d3e1-40a2-a3bf-9651c4021081",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
82,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
140,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
208,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"0ad8cfd9-da34-478e-8959-d6ad0dc98e2f",
|
||||
),
|
||||
file_name: FileName(
|
||||
"6c2b3581-c662-47a4-9d62-b21e6976759d",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"d1eb14ff-caa0-48a5-a607-2c444fb44f75",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
140,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
209,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"af6f87f7-255b-4064-a126-852f169af2eb",
|
||||
),
|
||||
file_name: FileName(
|
||||
"9e0d4d72-4d2d-48cc-940b-31b372c4a4c9",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"760ba903-0395-43c7-ad81-bb2d7bda32c4",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
140,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
210,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"f923f983-a580-4148-a140-67a666a96626",
|
||||
),
|
||||
file_name: FileName(
|
||||
"537b1067-2380-4fc7-9dd9-5b0c6c14f651",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"9053de2a-8b81-4c37-a235-09d4cb28dd76",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
140,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
141,
|
||||
),
|
||||
name: ProductName(
|
||||
"026b8f20-e70e-4471-b51b-c09305830c7b",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"936497e3-c668-48da-8f0f-88fae113a3f9",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"b677a454-1826-4294-badd-679accb4e143",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
83,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
141,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
211,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"062f4634-84dc-4a78-876a-929a43257ad1",
|
||||
),
|
||||
file_name: FileName(
|
||||
"a27a1304-e19c-4a84-ada5-ac80b96ac918",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"bf241c7a-c357-402b-acae-739f03580c15",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
141,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
212,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3b549514-5733-4432-abf3-a859ce8a06c7",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3fa04f35-6943-40a0-9ac6-bec48b4d0032",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"07e37cb5-54d5-47e6-a610-bf37f4af954c",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
141,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
213,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"acb0eae0-aefd-4ed9-9591-5f311ab660c6",
|
||||
),
|
||||
file_name: FileName(
|
||||
"3153a928-04a6-43fc-82ca-56621f3a17ae",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"79cfdf6a-8c45-418f-9914-96acafe95696",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
141,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
142,
|
||||
),
|
||||
name: ProductName(
|
||||
"8b78214e-9732-4cf7-8f97-f5bab9a90a93",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"130239a5-29b6-4dce-abd5-a62d07f36b92",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"eb8b4e25-172f-4e2d-a6c6-83187c73684c",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
84,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
142,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
214,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"bdc32dad-0dbb-40d9-8131-01886f7a4322",
|
||||
),
|
||||
file_name: FileName(
|
||||
"b762986e-9908-481f-b2aa-7129ecab73bc",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"c6149195-3412-4da1-86c6-7598307e7541",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
142,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
215,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"9d057572-933f-48b0-b2f6-117600b850cf",
|
||||
),
|
||||
file_name: FileName(
|
||||
"e4656c47-393d-4db6-ba0d-e8b559953425",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"b661f962-7e5a-4237-9f49-e00eb11e2431",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
142,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
216,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"d378747d-48f3-4f40-8ee0-467f9696bf39",
|
||||
),
|
||||
file_name: FileName(
|
||||
"493ae888-b415-4a1d-8a7c-0582971e7048",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"78cdec2b-33b1-4300-8e8f-3497dc93772b",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
142,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProduct {
|
||||
id: ProductId(
|
||||
141,
|
||||
),
|
||||
name: ProductName(
|
||||
"3a7903bf-fbda-4184-bea7-90412a15f9cb",
|
||||
),
|
||||
category: None,
|
||||
deliver_days_flag: Days(
|
||||
[
|
||||
Friday,
|
||||
Sunday,
|
||||
],
|
||||
),
|
||||
variants: [
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
143,
|
||||
),
|
||||
name: ProductName(
|
||||
"903a7945-4ee0-4c3f-9438-7a881d7dd1b1",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"d76f0686-f0dc-4ac8-ba09-3f83b468c7a3",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"185e393f-c705-49f8-afd6-dd472b9ea1a8",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
85,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
143,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
217,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8cc0c595-b557-4a91-8ec5-d2c47db2c622",
|
||||
),
|
||||
file_name: FileName(
|
||||
"131766fd-7acb-4a3e-97bc-730b7ddb95ad",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"ed213f02-464e-46b9-9435-a548fba8255f",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
143,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
218,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"c76bdd8b-46c1-4bbf-8598-76da38e6ed4f",
|
||||
),
|
||||
file_name: FileName(
|
||||
"9699a293-8acc-43c7-87ee-a31e454606f4",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"f1ea07aa-15e8-4760-ac43-9918517f6014",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
143,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
219,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8e71b79f-2543-4963-a1dd-1bd9e31d89ef",
|
||||
),
|
||||
file_name: FileName(
|
||||
"669dbc98-44ac-405d-a6c2-22d9ea318217",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"0e0254e5-5fb2-4005-941e-31fb9750d88a",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
143,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
DetailedProductVariant {
|
||||
id: ProductVariantId(
|
||||
144,
|
||||
),
|
||||
name: ProductName(
|
||||
"e1892b73-7753-44ab-b294-4329fafd5d8f",
|
||||
),
|
||||
short_description: ProductShortDesc(
|
||||
"2b95d63c-bb37-445f-814e-3244fd6ea36a",
|
||||
),
|
||||
long_description: ProductLongDesc(
|
||||
"4c9589f3-5fa5-478a-976b-2c82333b0b50",
|
||||
),
|
||||
price: Price(
|
||||
NonNegative(
|
||||
0,
|
||||
),
|
||||
),
|
||||
stocks: [
|
||||
Stock {
|
||||
id: StockId(
|
||||
86,
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
144,
|
||||
),
|
||||
quantity: Quantity(
|
||||
NonNegative(
|
||||
345,
|
||||
),
|
||||
),
|
||||
quantity_unit: Piece,
|
||||
},
|
||||
],
|
||||
photos: [
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
220,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"3a14ee12-5167-4319-9ff0-533b11f903bb",
|
||||
),
|
||||
file_name: FileName(
|
||||
"dc7e2524-6cfd-4114-b527-d86b50dbc76c",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"9a0c64aa-1199-4ba2-8d28-bb1eca95e622",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
144,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
221,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"8b151268-242a-40ac-8c6b-46bbd0c33d19",
|
||||
),
|
||||
file_name: FileName(
|
||||
"13f6efbb-0d50-45a7-b842-0c1a07ba44ce",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"64175464-9242-45ad-9663-6a88799d921e",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
144,
|
||||
),
|
||||
},
|
||||
ProductLinkedPhoto {
|
||||
photo_id: PhotoId(
|
||||
222,
|
||||
),
|
||||
local_path: LocalPath(
|
||||
"5d1e6c63-5944-4222-846a-a41159648d81",
|
||||
),
|
||||
file_name: FileName(
|
||||
"e81a611d-c050-4158-8fa1-6b29e285d792",
|
||||
),
|
||||
unique_name: UniqueName(
|
||||
"1b36c223-324a-45d2-839b-04fe1ce628da",
|
||||
),
|
||||
product_variant_id: ProductVariantId(
|
||||
144,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
@ -8,6 +8,12 @@ mod product_variants;
|
||||
mod products;
|
||||
mod stocks;
|
||||
|
||||
pub use photos::*;
|
||||
pub use product_photos::*;
|
||||
pub use product_variants::*;
|
||||
pub use products::*;
|
||||
pub use stocks::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pub pool: sqlx::PgPool,
|
||||
|
@ -15,8 +15,8 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllPhotos {
|
||||
pub limit: i32,
|
||||
pub offset: i32,
|
||||
pub limit: Limit,
|
||||
pub offset: Offset,
|
||||
}
|
||||
|
||||
impl AllPhotos {
|
||||
@ -208,8 +208,8 @@ mod tests {
|
||||
let p3 = test_photo(&mut t, None, None, None).await;
|
||||
|
||||
let all = AllPhotos {
|
||||
limit: 1000,
|
||||
offset: 0,
|
||||
limit: Limit::from_u32(1000),
|
||||
offset: Offset::from_u32(0),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
|
@ -1,8 +1,6 @@
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
|
||||
use crate::db::products::AllProducts;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Failed to attach photo to product")]
|
||||
@ -17,8 +15,8 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllProductPhotos {
|
||||
pub limit: Option<i32>,
|
||||
pub offset: Option<i32>,
|
||||
pub limit: Option<Limit>,
|
||||
pub offset: Option<Offset>,
|
||||
}
|
||||
|
||||
impl AllProductPhotos {
|
||||
@ -178,6 +176,7 @@ mod tests {
|
||||
let deleted = DeleteProductPhoto { id: p2.id }.run(&mut t).await.unwrap();
|
||||
|
||||
testx::db_rollback!(t);
|
||||
|
||||
assert_ne!(deleted, Some(p1));
|
||||
assert_eq!(deleted, Some(p2));
|
||||
assert_ne!(deleted, Some(p3));
|
||||
|
@ -1,7 +1,7 @@
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Failed to create product")]
|
||||
CreateProductVariant,
|
||||
@ -59,8 +59,8 @@ RETURNING id,
|
||||
#[derive(Debug)]
|
||||
pub struct ProductsVariants {
|
||||
pub product_ids: Vec<ProductId>,
|
||||
pub limit: Option<i32>,
|
||||
pub offset: Option<i32>,
|
||||
pub limit: Option<Limit>,
|
||||
pub offset: Option<Offset>,
|
||||
}
|
||||
|
||||
impl ProductsVariants {
|
||||
@ -79,14 +79,21 @@ INNER JOIN products ps
|
||||
ON pv.product_id = ps.id
|
||||
WHERE
|
||||
"#,
|
||||
"products.id = ",
|
||||
"ps.id = ",
|
||||
)
|
||||
.with_padding(
|
||||
self.limit.map(|n| **n).unwrap_or(200),
|
||||
*self.offset.map(|n| *n).unwrap_or_default(),
|
||||
)
|
||||
.allow_over_max()
|
||||
.with_size(1000)
|
||||
.load(
|
||||
self.product_ids.len(),
|
||||
self.product_ids.iter().copied().map(|id| *id),
|
||||
|_| Error::ProductsVariants(self.product_ids.clone()),
|
||||
|e| {
|
||||
tracing::error!("{}", e);
|
||||
Error::ProductsVariants(self.product_ids.clone())
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -111,3 +118,96 @@ WHERE id = $1
|
||||
.map_err(|_e| Error::DeleteProductVariant(self.product_variant_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use config::UpdateConfig;
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
use crate::db::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoOpts;
|
||||
impl UpdateConfig for NoOpts {}
|
||||
|
||||
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
|
||||
CreateProductVariant {
|
||||
product_id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
price: Default::default(),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product(
|
||||
t: &mut PgT<'_>,
|
||||
name: Option<String>,
|
||||
category: Option<ProductCategory>,
|
||||
deliver_days_flag: Option<Days>,
|
||||
) -> Product {
|
||||
CreateProduct {
|
||||
name: ProductName::new(name.unwrap_or_else(|| format!("{}", Uuid::new_v4()))),
|
||||
category,
|
||||
deliver_days_flag: deliver_days_flag
|
||||
.unwrap_or_else(|| Days(vec![Day::Friday, Day::Sunday])),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let product = test_product(&mut t, None, None, None).await;
|
||||
let dbm = CreateProductVariant {
|
||||
product_id: product.id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new("aosdjajsodjaoisdjoajs"),
|
||||
long_description: ProductLongDesc::new("jsa a98dh 9ahsd ha89shd 98aus 98asu "),
|
||||
price: Default::default(),
|
||||
};
|
||||
let res = dbm.run(&mut t).await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
|
||||
res.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn all() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let product = test_product(&mut t, None, None, None).await;
|
||||
let variant = test_product_variant(product.id, &mut t).await;
|
||||
|
||||
let res1 = ProductsVariants {
|
||||
product_ids: vec![product.id],
|
||||
limit: Some(200.into()),
|
||||
offset: Some(0.into()),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await;
|
||||
|
||||
let res2 = ProductsVariants {
|
||||
product_ids: vec![product.id],
|
||||
limit: Some(2.into()),
|
||||
offset: Some(100.into()),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
|
||||
assert_eq!(res1, Ok(vec![variant]));
|
||||
assert_eq!(res2, Ok(vec![]));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
use model::ShoppingCartId;
|
||||
use model::{Ranged, ShoppingCartId};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||
pub enum Error {
|
||||
@ -23,8 +24,8 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AllProducts {
|
||||
limit: i32,
|
||||
offset: i32,
|
||||
pub limit: Limit,
|
||||
pub offset: Offset,
|
||||
}
|
||||
|
||||
impl AllProducts {
|
||||
@ -192,15 +193,12 @@ RETURNING id,
|
||||
#[derive(Debug)]
|
||||
pub struct ShoppingCartProducts {
|
||||
pub shopping_cart_id: ShoppingCartId,
|
||||
pub limit: i32,
|
||||
pub offset: i32,
|
||||
pub limit: Limit,
|
||||
pub offset: Offset,
|
||||
}
|
||||
|
||||
impl ShoppingCartProducts {
|
||||
pub async fn shopping_cart_products<'e, E>(self, pool: E) -> Result<Vec<Product>>
|
||||
where
|
||||
E: sqlx::Executor<'e, Database = sqlx::Postgres>,
|
||||
{
|
||||
pub async fn run(self, t: &mut PgT<'_>) -> Result<Vec<Product>> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT products.id,
|
||||
@ -215,9 +213,9 @@ LIMIT $2 OFFSET $3
|
||||
"#,
|
||||
)
|
||||
.bind(self.shopping_cart_id)
|
||||
.bind(self.limit.min(1).max(200))
|
||||
.bind(self.offset.min(0))
|
||||
.fetch_all(pool)
|
||||
.bind(self.limit.in_range(1..200))
|
||||
.bind(self.offset.in_range(0..u32::MAX))
|
||||
.fetch_all(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
@ -280,10 +278,7 @@ mod tests {
|
||||
async fn test_product(
|
||||
t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
name: Option<String>,
|
||||
short_description: Option<String>,
|
||||
long_description: Option<String>,
|
||||
category: Option<ProductCategory>,
|
||||
price: Option<u32>,
|
||||
deliver_days_flag: Option<Days>,
|
||||
) -> Product {
|
||||
CreateProduct {
|
||||
@ -301,7 +296,7 @@ mod tests {
|
||||
async fn create() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
test_product(&mut t, None, None, None, None, None, None).await;
|
||||
test_product(&mut t, None, None, None).await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
}
|
||||
@ -310,13 +305,13 @@ mod tests {
|
||||
async fn all() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p3 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p1 = test_product(&mut t, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None).await;
|
||||
let p3 = test_product(&mut t, None, None, None).await;
|
||||
|
||||
let products = AllProducts {
|
||||
limit: 10000,
|
||||
offset: 0,
|
||||
limit: 10000.into(),
|
||||
offset: 0.into(),
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
@ -330,13 +325,11 @@ mod tests {
|
||||
async fn find() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let p1 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p3 = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let p1 = test_product(&mut t, None, None, None).await;
|
||||
let p2 = test_product(&mut t, None, None, None).await;
|
||||
let p3 = test_product(&mut t, None, None, None).await;
|
||||
|
||||
let product = find_product(FindProduct { product_id: p2.id }, &mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
let product = FindProduct { product_id: p2.id }.run(&mut t).await.unwrap();
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert_ne!(product, p1);
|
||||
@ -348,7 +341,7 @@ mod tests {
|
||||
async fn update() {
|
||||
testx::db_t_ref!(t);
|
||||
|
||||
let original = test_product(&mut t, None, None, None, None, None, None).await;
|
||||
let original = test_product(&mut t, None, None, None).await;
|
||||
let updated = UpdateProduct {
|
||||
id: original.id,
|
||||
name: ProductName::new("a9s0dja0sjd0jas09dj"),
|
||||
|
@ -1,7 +1,5 @@
|
||||
use model::v2::*;
|
||||
|
||||
use crate::db::products::AllProducts;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Unable to load all stocks")]
|
||||
@ -104,7 +102,7 @@ RETURNING id, product_variant_id, quantity, quantity_unit
|
||||
#[derive(Debug)]
|
||||
pub struct UpdateStock {
|
||||
pub id: StockId,
|
||||
pub product_id: ProductId,
|
||||
pub product_variant_id: ProductVariantId,
|
||||
pub quantity: Quantity,
|
||||
pub quantity_unit: QuantityUnit,
|
||||
}
|
||||
@ -121,7 +119,7 @@ WHERE id = $4
|
||||
RETURNING id, product_variant_id, quantity, quantity_unit
|
||||
"#,
|
||||
)
|
||||
.bind(self.product_id)
|
||||
.bind(self.product_variant_id)
|
||||
.bind(self.quantity)
|
||||
.bind(self.quantity_unit)
|
||||
.bind(self.id)
|
||||
@ -164,7 +162,7 @@ pub struct ProductVariantsStock {
|
||||
}
|
||||
|
||||
impl ProductVariantsStock {
|
||||
async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Vec<Stock>> {
|
||||
pub async fn run(self, pool: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Vec<Stock>> {
|
||||
db_utils::MultiLoad::new(
|
||||
pool,
|
||||
r#"
|
||||
@ -187,12 +185,12 @@ WHERE
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use config::UpdateConfig;
|
||||
use fake::faker::lorem::en as lorem;
|
||||
use fake::Fake;
|
||||
use db_utils::PgT;
|
||||
use model::v2::*;
|
||||
use model::Day;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::db::product_variants::CreateProductVariant;
|
||||
use crate::db::Database;
|
||||
|
||||
pub struct NoOpts;
|
||||
@ -213,6 +211,19 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_product_variant(product_id: ProductId, t: &mut PgT<'_>) -> ProductVariant {
|
||||
CreateProductVariant {
|
||||
product_id,
|
||||
name: ProductName::new(format!("{}", Uuid::new_v4())),
|
||||
short_description: ProductShortDesc::new(format!("{}", Uuid::new_v4())),
|
||||
long_description: ProductLongDesc::new(format!("{}", Uuid::new_v4())),
|
||||
price: Default::default(),
|
||||
}
|
||||
.run(t)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
async fn test_stock(
|
||||
pool: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
product_variant_id: Option<ProductVariantId>,
|
||||
@ -221,7 +232,10 @@ mod tests {
|
||||
) -> Stock {
|
||||
let product_variant_id = match product_variant_id {
|
||||
Some(id) => id,
|
||||
_ => test_product(&mut *pool).await.id,
|
||||
_ => {
|
||||
let product_id = test_product(&mut *pool).await.id;
|
||||
test_product_variant(product_id, &mut *pool).await.id
|
||||
}
|
||||
};
|
||||
let quantity = quantity.unwrap_or_else(|| Quantity::from_u32(345));
|
||||
let quantity_unit = quantity_unit.unwrap_or(QuantityUnit::Piece);
|
||||
@ -253,7 +267,7 @@ mod tests {
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
|
||||
let stocks: Vec<Stock> = ProductVariantsStock {
|
||||
product_variant_ids: vec![first.product_id, second.product_id],
|
||||
product_variant_ids: vec![first.product_variant_id, second.product_variant_id],
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
@ -289,18 +303,19 @@ mod tests {
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
|
||||
let second_id = second.id;
|
||||
let deleted: Option<Stock> = DeleteStock {
|
||||
stock_id: second.id,
|
||||
stock_id: second_id,
|
||||
}
|
||||
.run(&mut t)
|
||||
.await
|
||||
.unwrap();
|
||||
let reloaded = FindStock { id: second.id }.run(&mut t).await;
|
||||
let reloaded = FindStock { id: second_id }.run(&mut t).await;
|
||||
|
||||
testx::db_rollback!(t);
|
||||
assert_eq!(deleted, Some(second));
|
||||
assert_ne!(deleted, Some(first));
|
||||
assert_eq!(reloaded, Err(super::Error::NotFound));
|
||||
assert_eq!(reloaded, Err(super::Error::NotFound(second_id)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@ -310,10 +325,11 @@ mod tests {
|
||||
let first = test_stock(&mut t, None, None, None).await;
|
||||
let second = test_stock(&mut t, None, None, None).await;
|
||||
let another_product = test_product(&mut t).await;
|
||||
let another_product_variant = test_product_variant(another_product.id, &mut t).await;
|
||||
|
||||
let updated: Stock = UpdateStock {
|
||||
id: second.id,
|
||||
product_id: another_product.id,
|
||||
product_variant_id: another_product_variant.id,
|
||||
quantity: Quantity::from_u32(19191),
|
||||
quantity_unit: QuantityUnit::Gram,
|
||||
}
|
||||
@ -327,7 +343,7 @@ mod tests {
|
||||
updated,
|
||||
Stock {
|
||||
id: second.id,
|
||||
product_id: another_product.id,
|
||||
product_variant_id: another_product_variant.id,
|
||||
quantity: Quantity::from_u32(19191),
|
||||
quantity_unit: QuantityUnit::Gram,
|
||||
}
|
||||
|
@ -29,4 +29,5 @@ tracing = { version = "0.1.34" }
|
||||
uuid = { version = "1.2.1", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
fake = { version = "2.5.0" }
|
||||
testx = { path = "../testx" }
|
||||
|
@ -2,6 +2,13 @@
|
||||
|
||||
source .env
|
||||
|
||||
if [[ "$1" == "purge" ]];
|
||||
then
|
||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_accounts" || echo 0
|
||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_carts" || echo 0
|
||||
psql postgres postgres -c "DROP DATABASE ${DATABASE_NAME}_stocks" || echo 0
|
||||
fi
|
||||
|
||||
psql postgres postgres -c "CREATE DATABASE ${DATABASE_NAME}_accounts" || echo 0
|
||||
sqlx migrate run -D "${ACCOUNT_DATABASE_URL}" --source ./crates/account_manager/migrations
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user