now products have different options, like different parameters
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
//! `SeaORM` Entity assigning a discount profile to a business account.
|
||||
//! Hand-written to match the `account_discount_profiles` migration.
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -17,14 +16,6 @@ pub struct Model {
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::users::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::users::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Users,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::discount_profiles::Entity",
|
||||
from = "Column::DiscountProfileId",
|
||||
@@ -33,12 +24,14 @@ pub enum Relation {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
DiscountProfiles,
|
||||
}
|
||||
|
||||
impl Related<super::users::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Users.def()
|
||||
}
|
||||
#[sea_orm(
|
||||
belongs_to = "super::users::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::users::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Users,
|
||||
}
|
||||
|
||||
impl Related<super::discount_profiles::Entity> for Entity {
|
||||
@@ -46,3 +39,9 @@ impl Related<super::discount_profiles::Entity> for Entity {
|
||||
Relation::DiscountProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::users::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Users.def()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! `SeaORM` Entity for per-account negotiated product prices. Hand-written to
|
||||
//! match the `account_product_prices` migration (one row per (user, product)).
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -11,13 +10,21 @@ pub struct Model {
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
pub product_id: i32,
|
||||
pub price_cents: i64,
|
||||
pub user_id: i32,
|
||||
pub variant_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::product_variants::Entity",
|
||||
from = "Column::VariantId",
|
||||
to = "super::product_variants::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
ProductVariants,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::users::Entity",
|
||||
from = "Column::UserId",
|
||||
@@ -26,14 +33,12 @@ pub enum Relation {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Users,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::products::Entity",
|
||||
from = "Column::ProductId",
|
||||
to = "super::products::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Products,
|
||||
}
|
||||
|
||||
impl Related<super::product_variants::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ProductVariants.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::users::Entity> for Entity {
|
||||
@@ -41,9 +46,3 @@ impl Related<super::users::Entity> for Entity {
|
||||
Relation::Users.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Products.def()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
//! `SeaORM` Entity for an account's chosen profile when two assigned profiles
|
||||
//! cover one product. Hand-written to match the `account_product_resolutions`
|
||||
//! migration.
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,28 +11,12 @@ pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
pub product_id: i32,
|
||||
pub discount_profile_id: i32,
|
||||
pub variant_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::users::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::users::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Users,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::products::Entity",
|
||||
from = "Column::ProductId",
|
||||
to = "super::products::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Products,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::discount_profiles::Entity",
|
||||
from = "Column::DiscountProfileId",
|
||||
@@ -43,18 +25,22 @@ pub enum Relation {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
DiscountProfiles,
|
||||
}
|
||||
|
||||
impl Related<super::users::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Users.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Products.def()
|
||||
}
|
||||
#[sea_orm(
|
||||
belongs_to = "super::product_variants::Entity",
|
||||
from = "Column::VariantId",
|
||||
to = "super::product_variants::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
ProductVariants,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::users::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::users::Column::Id",
|
||||
on_update = "Cascade",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Users,
|
||||
}
|
||||
|
||||
impl Related<super::discount_profiles::Entity> for Entity {
|
||||
@@ -62,3 +48,15 @@ impl Related<super::discount_profiles::Entity> for Entity {
|
||||
Relation::DiscountProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::product_variants::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ProductVariants.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::users::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Users.def()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
//! `SeaORM` Entity assigning a discount profile to a whole audience (all
|
||||
//! personal viewers or all company accounts). Hand-written to match the
|
||||
//! `audience_discount_profiles` migration.
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -12,7 +10,6 @@ pub struct Model {
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
/// "personal" or "business".
|
||||
pub audience: String,
|
||||
pub discount_profile_id: i32,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -23,8 +23,6 @@ pub struct Model {
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::products::Entity")]
|
||||
Products,
|
||||
#[sea_orm(
|
||||
belongs_to = "Entity",
|
||||
from = "Column::ParentId",
|
||||
@@ -32,7 +30,9 @@ pub enum Relation {
|
||||
on_update = "Cascade",
|
||||
on_delete = "SetNull"
|
||||
)]
|
||||
Parent,
|
||||
SelfRef,
|
||||
#[sea_orm(has_many = "super::products::Entity")]
|
||||
Products,
|
||||
}
|
||||
|
||||
impl Related<super::products::Entity> for Entity {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! `SeaORM` Entity for customer shipping/contact profiles. Hand-written to match
|
||||
//! the `customer_profiles` migration (1:1 with `users` via a unique `user_id`).
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -11,18 +10,18 @@ pub struct Model {
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub user_id: i32,
|
||||
pub company_name: Option<String>,
|
||||
pub company_id: Option<String>,
|
||||
pub tax_id: Option<String>,
|
||||
pub vat_id: Option<String>,
|
||||
pub phone_prefix: Option<String>,
|
||||
pub phone: Option<String>,
|
||||
pub address: Option<String>,
|
||||
pub city: Option<String>,
|
||||
pub zip: Option<String>,
|
||||
pub country: Option<String>,
|
||||
#[sea_orm(unique)]
|
||||
pub user_id: i32,
|
||||
pub company_name: Option<String>,
|
||||
pub company_id: Option<String>,
|
||||
pub tax_id: Option<String>,
|
||||
pub vat_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! `SeaORM` Entity for a discount profile's product membership. Hand-written to
|
||||
//! match the `discount_profile_products` migration.
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! `SeaORM` Entity for reusable discount profiles. Hand-written to match the
|
||||
//! `discount_profiles` migration.
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -12,24 +11,20 @@ pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
/// Discount in basis points (5% = 500).
|
||||
pub percent_bp: i32,
|
||||
/// "include" (covers listed products) or "all_except" (covers all but them).
|
||||
pub scope_type: String,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::discount_profile_products::Entity")]
|
||||
DiscountProfileProducts,
|
||||
#[sea_orm(has_many = "super::account_discount_profiles::Entity")]
|
||||
AccountDiscountProfiles,
|
||||
}
|
||||
|
||||
impl Related<super::discount_profile_products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::DiscountProfileProducts.def()
|
||||
}
|
||||
#[sea_orm(has_many = "super::account_product_resolutions::Entity")]
|
||||
AccountProductResolutions,
|
||||
#[sea_orm(has_many = "super::audience_discount_profiles::Entity")]
|
||||
AudienceDiscountProfiles,
|
||||
#[sea_orm(has_many = "super::discount_profile_products::Entity")]
|
||||
DiscountProfileProducts,
|
||||
}
|
||||
|
||||
impl Related<super::account_discount_profiles::Entity> for Entity {
|
||||
@@ -37,3 +32,21 @@ impl Related<super::account_discount_profiles::Entity> for Entity {
|
||||
Relation::AccountDiscountProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::account_product_resolutions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountProductResolutions.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::audience_discount_profiles::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AudienceDiscountProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::discount_profile_products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::DiscountProfileProducts.def()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub mod prelude;
|
||||
|
||||
pub mod account_discount_profiles;
|
||||
pub mod account_product_prices;
|
||||
pub mod audience_discount_profiles;
|
||||
pub mod account_product_resolutions;
|
||||
pub mod audience_discount_profiles;
|
||||
pub mod audit_logs;
|
||||
pub mod categories;
|
||||
pub mod customer_profiles;
|
||||
pub mod discount_profile_products;
|
||||
pub mod discount_profiles;
|
||||
pub mod customer_profiles;
|
||||
pub mod o_auth2_sessions;
|
||||
pub mod order_items;
|
||||
pub mod orders;
|
||||
pub mod product_images;
|
||||
pub mod product_product_tags;
|
||||
pub mod product_tags;
|
||||
pub mod product_variants;
|
||||
pub mod products;
|
||||
pub mod shipping_methods;
|
||||
pub mod users;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
//! `SeaORM` Entity for loco-oauth2 sessions. Hand-written to match the
|
||||
//! `o_auth2_sessions` migration (the rest of `_entities/` is codegen; this table
|
||||
//! is owned by the loco-oauth2 integration).
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -8,12 +6,13 @@ use serde::{Deserialize, Serialize};
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "o_auth2_sessions")]
|
||||
pub struct Model {
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
#[sea_orm(unique)]
|
||||
pub session_id: String,
|
||||
pub expires_at: DateTimeUtc,
|
||||
pub expires_at: DateTimeWithTimeZone,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -15,6 +15,8 @@ pub struct Model {
|
||||
pub quantity: i32,
|
||||
pub order_id: i32,
|
||||
pub product_id: Option<i32>,
|
||||
pub variant_label: String,
|
||||
pub variant_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -27,6 +29,14 @@ pub enum Relation {
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Orders,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::product_variants::Entity",
|
||||
from = "Column::VariantId",
|
||||
to = "super::product_variants::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "SetNull"
|
||||
)]
|
||||
ProductVariants,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::products::Entity",
|
||||
from = "Column::ProductId",
|
||||
@@ -43,6 +53,12 @@ impl Related<super::orders::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::product_variants::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ProductVariants.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Products.def()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -13,17 +13,10 @@ pub struct Model {
|
||||
#[sea_orm(unique)]
|
||||
pub order_number: String,
|
||||
pub email: String,
|
||||
pub phone: Option<String>,
|
||||
pub customer_name: Option<String>,
|
||||
pub status: String,
|
||||
pub total_cents: i64,
|
||||
pub currency: String,
|
||||
pub user_id: Option<i32>,
|
||||
pub account_type: String,
|
||||
pub company_name: Option<String>,
|
||||
pub company_id: Option<String>,
|
||||
pub tax_id: Option<String>,
|
||||
pub vat_id: Option<String>,
|
||||
pub address: Option<String>,
|
||||
pub city: Option<String>,
|
||||
pub zip: Option<String>,
|
||||
@@ -39,6 +32,13 @@ pub struct Model {
|
||||
pub tracking_number: Option<String>,
|
||||
pub shipment_id: Option<String>,
|
||||
pub label_url: Option<String>,
|
||||
pub phone: Option<String>,
|
||||
pub account_type: String,
|
||||
pub company_name: Option<String>,
|
||||
pub company_id: Option<String>,
|
||||
pub tax_id: Option<String>,
|
||||
pub vat_id: Option<String>,
|
||||
pub user_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
pub use super::account_discount_profiles::Entity as AccountDiscountProfiles;
|
||||
pub use super::account_product_prices::Entity as AccountProductPrices;
|
||||
@@ -15,6 +15,7 @@ pub use super::orders::Entity as Orders;
|
||||
pub use super::product_images::Entity as ProductImages;
|
||||
pub use super::product_product_tags::Entity as ProductProductTags;
|
||||
pub use super::product_tags::Entity as ProductTags;
|
||||
pub use super::product_variants::Entity as ProductVariants;
|
||||
pub use super::products::Entity as Products;
|
||||
pub use super::shipping_methods::Entity as ShippingMethods;
|
||||
pub use super::users::Entity as Users;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
63
src/models/_entities/product_variants.rs
Normal file
63
src/models/_entities/product_variants.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
||||
#[sea_orm(table_name = "product_variants")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub created_at: DateTimeWithTimeZone,
|
||||
pub updated_at: DateTimeWithTimeZone,
|
||||
pub product_id: i32,
|
||||
pub label: String,
|
||||
pub position: i32,
|
||||
pub sku: Option<String>,
|
||||
pub stock: i32,
|
||||
pub price_cents: i64,
|
||||
pub sale_price_cents: Option<i64>,
|
||||
pub business_sale_price_cents: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::account_product_prices::Entity")]
|
||||
AccountProductPrices,
|
||||
#[sea_orm(has_many = "super::account_product_resolutions::Entity")]
|
||||
AccountProductResolutions,
|
||||
#[sea_orm(has_many = "super::order_items::Entity")]
|
||||
OrderItems,
|
||||
#[sea_orm(
|
||||
belongs_to = "super::products::Entity",
|
||||
from = "Column::ProductId",
|
||||
to = "super::products::Column::Id",
|
||||
on_update = "NoAction",
|
||||
on_delete = "Cascade"
|
||||
)]
|
||||
Products,
|
||||
}
|
||||
|
||||
impl Related<super::account_product_prices::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountProductPrices.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::account_product_resolutions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountProductResolutions.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::order_items::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::OrderItems.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::Products.def()
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -15,12 +15,7 @@ pub struct Model {
|
||||
pub slug: String,
|
||||
#[sea_orm(column_type = "Text", nullable)]
|
||||
pub description: Option<String>,
|
||||
pub price_cents: i64,
|
||||
pub sale_price_cents: Option<i64>,
|
||||
pub business_sale_price_cents: Option<i64>,
|
||||
pub currency: String,
|
||||
pub sku: Option<String>,
|
||||
pub stock: i32,
|
||||
pub view_count: i32,
|
||||
pub published: bool,
|
||||
pub published_at: Option<DateTimeWithTimeZone>,
|
||||
@@ -37,12 +32,16 @@ pub enum Relation {
|
||||
on_delete = "SetNull"
|
||||
)]
|
||||
Categories,
|
||||
#[sea_orm(has_many = "super::discount_profile_products::Entity")]
|
||||
DiscountProfileProducts,
|
||||
#[sea_orm(has_many = "super::order_items::Entity")]
|
||||
OrderItems,
|
||||
#[sea_orm(has_many = "super::product_images::Entity")]
|
||||
ProductImages,
|
||||
#[sea_orm(has_many = "super::product_product_tags::Entity")]
|
||||
ProductProductTags,
|
||||
#[sea_orm(has_many = "super::product_variants::Entity")]
|
||||
ProductVariants,
|
||||
}
|
||||
|
||||
impl Related<super::categories::Entity> for Entity {
|
||||
@@ -51,6 +50,12 @@ impl Related<super::categories::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::discount_profile_products::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::DiscountProfileProducts.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::order_items::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::OrderItems.def()
|
||||
@@ -69,6 +74,12 @@ impl Related<super::product_product_tags::Entity> for Entity {
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::product_variants::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::ProductVariants.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::product_tags::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::product_product_tags::Relation::ProductTags.def()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -26,15 +26,45 @@ pub struct Model {
|
||||
pub magic_link_expiration: Option<DateTimeWithTimeZone>,
|
||||
pub theme: String,
|
||||
pub account_type: String,
|
||||
#[sea_orm(column_type = "Text", nullable)]
|
||||
pub totp_secret: Option<String>,
|
||||
pub totp_enabled_at: Option<DateTimeWithTimeZone>,
|
||||
#[sea_orm(column_type = "Text", nullable)]
|
||||
pub totp_backup_codes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::account_discount_profiles::Entity")]
|
||||
AccountDiscountProfiles,
|
||||
#[sea_orm(has_many = "super::account_product_prices::Entity")]
|
||||
AccountProductPrices,
|
||||
#[sea_orm(has_many = "super::account_product_resolutions::Entity")]
|
||||
AccountProductResolutions,
|
||||
#[sea_orm(has_many = "super::audit_logs::Entity")]
|
||||
AuditLogs,
|
||||
#[sea_orm(has_one = "super::customer_profiles::Entity")]
|
||||
CustomerProfiles,
|
||||
#[sea_orm(has_many = "super::o_auth2_sessions::Entity")]
|
||||
OAuth2Sessions,
|
||||
}
|
||||
|
||||
impl Related<super::account_discount_profiles::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountDiscountProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::account_product_prices::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountProductPrices.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::account_product_resolutions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::AccountProductResolutions.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::audit_logs::Entity> for Entity {
|
||||
@@ -42,3 +72,15 @@ impl Related<super::audit_logs::Entity> for Entity {
|
||||
Relation::AuditLogs.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::customer_profiles::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::CustomerProfiles.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::o_auth2_sessions::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::OAuth2Sessions.def()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
|
||||
impl Model {
|
||||
/// All negotiated prices for one account, as a `(product_id -> cents)` map.
|
||||
/// All negotiated prices for one account, as a `(variant_id -> cents)` map.
|
||||
pub async fn map_for_user(
|
||||
db: &DatabaseConnection,
|
||||
user_id: i32,
|
||||
@@ -34,26 +34,26 @@ impl Model {
|
||||
.filter(Column::UserId.eq(user_id))
|
||||
.all(db)
|
||||
.await?;
|
||||
Ok(rows.into_iter().map(|r| (r.product_id, r.price_cents)).collect())
|
||||
Ok(rows.into_iter().map(|r| (r.variant_id, r.price_cents)).collect())
|
||||
}
|
||||
|
||||
/// Insert or update the negotiated price for `(user_id, product_id)`.
|
||||
/// Insert or update the negotiated price for `(user_id, variant_id)`.
|
||||
pub async fn upsert(
|
||||
db: &DatabaseConnection,
|
||||
user_id: i32,
|
||||
product_id: i32,
|
||||
variant_id: i32,
|
||||
price_cents: i64,
|
||||
) -> Result<Self, DbErr> {
|
||||
let existing = Entity::find()
|
||||
.filter(Column::UserId.eq(user_id))
|
||||
.filter(Column::ProductId.eq(product_id))
|
||||
.filter(Column::VariantId.eq(variant_id))
|
||||
.one(db)
|
||||
.await?;
|
||||
let mut active = match existing {
|
||||
Some(row) => row.into_active_model(),
|
||||
None => ActiveModel {
|
||||
user_id: ActiveValue::set(user_id),
|
||||
product_id: ActiveValue::set(product_id),
|
||||
variant_id: ActiveValue::set(variant_id),
|
||||
..Default::default()
|
||||
},
|
||||
};
|
||||
@@ -61,15 +61,15 @@ impl Model {
|
||||
active.save(db).await?.try_into_model()
|
||||
}
|
||||
|
||||
/// Remove the negotiated price for `(user_id, product_id)`, if any.
|
||||
/// Remove the negotiated price for `(user_id, variant_id)`, if any.
|
||||
pub async fn clear(
|
||||
db: &DatabaseConnection,
|
||||
user_id: i32,
|
||||
product_id: i32,
|
||||
variant_id: i32,
|
||||
) -> Result<(), DbErr> {
|
||||
Entity::delete_many()
|
||||
.filter(Column::UserId.eq(user_id))
|
||||
.filter(Column::ProductId.eq(product_id))
|
||||
.filter(Column::VariantId.eq(variant_id))
|
||||
.exec(db)
|
||||
.await?;
|
||||
Ok(())
|
||||
|
||||
@@ -24,3 +24,4 @@ pub mod product_tags;
|
||||
pub mod products;
|
||||
pub mod shipping_methods;
|
||||
pub mod users;
|
||||
pub mod product_variants;
|
||||
|
||||
@@ -17,7 +17,7 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
{
|
||||
if !insert && self.updated_at.is_unchanged() {
|
||||
let mut this = self;
|
||||
this.updated_at = sea_orm::ActiveValue::Set(Utc::now());
|
||||
this.updated_at = sea_orm::ActiveValue::Set(Utc::now().into());
|
||||
Ok(this)
|
||||
} else {
|
||||
Ok(self)
|
||||
@@ -34,7 +34,7 @@ impl OAuth2SessionsTrait<users::Model> for Model {
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or_else(|| ModelError::EntityNotFound)?;
|
||||
Ok(session.expires_at < Utc::now())
|
||||
Ok(session.expires_at < Utc::now().fixed_offset())
|
||||
}
|
||||
|
||||
/// Create or refresh the session row for `user` from the provider token.
|
||||
@@ -58,14 +58,14 @@ impl OAuth2SessionsTrait<users::Model> for Model {
|
||||
Some(session) => {
|
||||
let mut session: o_auth2_sessions::ActiveModel = session.into();
|
||||
session.session_id = ActiveValue::set(session_id);
|
||||
session.expires_at = ActiveValue::set(expires_at);
|
||||
session.updated_at = ActiveValue::set(Utc::now());
|
||||
session.expires_at = ActiveValue::set(expires_at.into());
|
||||
session.updated_at = ActiveValue::set(Utc::now().into());
|
||||
session.update(&txn).await?
|
||||
}
|
||||
None => {
|
||||
o_auth2_sessions::ActiveModel {
|
||||
session_id: ActiveValue::set(session_id),
|
||||
expires_at: ActiveValue::set(expires_at),
|
||||
expires_at: ActiveValue::set(expires_at.into()),
|
||||
user_id: ActiveValue::set(user.id),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{Set, TransactionTrait};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::_entities::{order_items, products, shipping_methods};
|
||||
use crate::models::_entities::{order_items, product_variants, products, shipping_methods};
|
||||
use crate::models::users;
|
||||
use crate::shared::pricing;
|
||||
pub use crate::models::_entities::orders::{ActiveModel, Column, Entity, Model};
|
||||
@@ -40,10 +40,10 @@ fn generate_order_number() -> String {
|
||||
format!("ORD-{suffix}")
|
||||
}
|
||||
|
||||
/// Atomically place an order for the given `(product_id, quantity)` lines:
|
||||
/// snapshot each product's price/name, decrement stock (re-checking inside the
|
||||
/// transaction so an item can't oversell between cart and pay), then write the
|
||||
/// order and its line items. Returns the persisted order.
|
||||
/// Atomically place an order for the given `(variant_id, quantity)` lines:
|
||||
/// snapshot each variant's price/name/label, decrement its stock (re-checking
|
||||
/// inside the transaction so an item can't oversell between cart and pay), then
|
||||
/// write the order and its line items. Returns the persisted order.
|
||||
pub async fn place(
|
||||
ctx: &AppContext,
|
||||
items: &[(i32, i32)],
|
||||
@@ -55,13 +55,17 @@ pub async fn place(
|
||||
let mut subtotal: i64 = 0;
|
||||
let mut currency = "EUR".to_string();
|
||||
let mut snapshots = Vec::new();
|
||||
for (product_id, qty) in items {
|
||||
let product = products::Entity::find_by_id(*product_id)
|
||||
for (variant_id, qty) in items {
|
||||
let variant = product_variants::Entity::find_by_id(*variant_id)
|
||||
.one(&txn)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("an item is no longer available".to_string()))?;
|
||||
let product = products::Entity::find_by_id(variant.product_id)
|
||||
.filter(products::Column::Published.eq(true))
|
||||
.one(&txn)
|
||||
.await?
|
||||
.ok_or_else(|| Error::BadRequest("a product is no longer available".to_string()))?;
|
||||
if product.stock < *qty {
|
||||
if variant.stock < *qty {
|
||||
return Err(Error::BadRequest(format!(
|
||||
"not enough stock for {}",
|
||||
product.name
|
||||
@@ -71,14 +75,14 @@ pub async fn place(
|
||||
// Snapshot the price the buyer actually pays — public sale or, for a
|
||||
// business account, their negotiated/lowest price (same resolver the
|
||||
// cart and storefront use).
|
||||
let unit_price_cents = pricing::price_for(ctx, &product, user).await?.price_cents;
|
||||
let unit_price_cents = pricing::price_variant(ctx, &variant, user).await?.price_cents;
|
||||
subtotal += unit_price_cents * i64::from(*qty);
|
||||
|
||||
let mut active = product.clone().into_active_model();
|
||||
active.stock = Set(product.stock - *qty);
|
||||
let mut active = variant.clone().into_active_model();
|
||||
active.stock = Set(variant.stock - *qty);
|
||||
active.update(&txn).await?;
|
||||
|
||||
snapshots.push((product.id, product.name, unit_price_cents, *qty));
|
||||
snapshots.push((product.id, variant.id, product.name, variant.label, unit_price_cents, *qty));
|
||||
}
|
||||
|
||||
let order = ActiveModel {
|
||||
@@ -111,11 +115,13 @@ pub async fn place(
|
||||
.insert(&txn)
|
||||
.await?;
|
||||
|
||||
for (product_id, name, unit_price_cents, qty) in snapshots {
|
||||
for (product_id, variant_id, name, variant_label, unit_price_cents, qty) in snapshots {
|
||||
order_items::ActiveModel {
|
||||
order_id: Set(order.id),
|
||||
product_id: Set(Some(product_id)),
|
||||
variant_id: Set(Some(variant_id)),
|
||||
product_name: Set(name),
|
||||
variant_label: Set(variant_label),
|
||||
unit_price_cents: Set(unit_price_cents),
|
||||
quantity: Set(qty),
|
||||
..Default::default()
|
||||
|
||||
89
src/models/product_variants.rs
Normal file
89
src/models/product_variants.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::QueryOrder;
|
||||
pub use super::_entities::product_variants::{ActiveModel, Column, Entity, Model};
|
||||
pub type ProductVariants = Entity;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
async fn before_save<C>(self, _db: &C, insert: bool) -> std::result::Result<Self, DbErr>
|
||||
where
|
||||
C: ConnectionTrait,
|
||||
{
|
||||
if !insert && self.updated_at.is_unchanged() {
|
||||
let mut this = self;
|
||||
this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into());
|
||||
Ok(this)
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implement your read-oriented logic here
|
||||
impl Model {
|
||||
/// Whether a discount is currently active: a sale price is set and is
|
||||
/// strictly below the regular price.
|
||||
#[must_use]
|
||||
pub fn on_sale(&self) -> bool {
|
||||
matches!(self.sale_price_cents, Some(sale) if sale < self.price_cents)
|
||||
}
|
||||
|
||||
/// The price actually charged: the sale price when [`Model::on_sale`],
|
||||
/// otherwise the regular price.
|
||||
#[must_use]
|
||||
pub fn effective_price_cents(&self) -> i64 {
|
||||
if self.on_sale() {
|
||||
self.sale_price_cents.unwrap_or(self.price_cents)
|
||||
} else {
|
||||
self.price_cents
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a baseline business discount (for all company accounts) is set and
|
||||
/// actually below the regular price.
|
||||
#[must_use]
|
||||
pub fn business_on_sale(&self) -> bool {
|
||||
matches!(self.business_sale_price_cents, Some(sale) if sale < self.price_cents)
|
||||
}
|
||||
}
|
||||
|
||||
// implement your write-oriented logic here
|
||||
impl ActiveModel {}
|
||||
|
||||
// implement your custom finders, selectors oriented logic here
|
||||
impl Entity {
|
||||
/// All variants for one product, in display order.
|
||||
pub async fn for_product<C: ConnectionTrait>(
|
||||
db: &C,
|
||||
product_id: i32,
|
||||
) -> Result<Vec<Model>, DbErr> {
|
||||
Entity::find()
|
||||
.filter(Column::ProductId.eq(product_id))
|
||||
.order_by_asc(Column::Position)
|
||||
.order_by_asc(Column::Id)
|
||||
.all(db)
|
||||
.await
|
||||
}
|
||||
|
||||
/// All variants for many products in one query, grouped by `product_id` and
|
||||
/// ordered within each group. Products with no variants are absent.
|
||||
pub async fn grouped_for_products<C: ConnectionTrait>(
|
||||
db: &C,
|
||||
product_ids: &[i32],
|
||||
) -> Result<std::collections::HashMap<i32, Vec<Model>>, DbErr> {
|
||||
let mut map: std::collections::HashMap<i32, Vec<Model>> = std::collections::HashMap::new();
|
||||
if product_ids.is_empty() {
|
||||
return Ok(map);
|
||||
}
|
||||
let rows = Entity::find()
|
||||
.filter(Column::ProductId.is_in(product_ids.to_vec()))
|
||||
.order_by_asc(Column::Position)
|
||||
.order_by_asc(Column::Id)
|
||||
.all(db)
|
||||
.await?;
|
||||
for row in rows {
|
||||
map.entry(row.product_id).or_default().push(row);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
}
|
||||
@@ -19,32 +19,7 @@ impl ActiveModelBehavior for ActiveModel {
|
||||
}
|
||||
|
||||
// implement your read-oriented logic here
|
||||
impl Model {
|
||||
/// Whether a discount is currently active: a sale price is set and is
|
||||
/// strictly below the regular price.
|
||||
#[must_use]
|
||||
pub fn on_sale(&self) -> bool {
|
||||
matches!(self.sale_price_cents, Some(sale) if sale < self.price_cents)
|
||||
}
|
||||
|
||||
/// The price actually charged: the sale price when [`Model::on_sale`],
|
||||
/// otherwise the regular price.
|
||||
#[must_use]
|
||||
pub fn effective_price_cents(&self) -> i64 {
|
||||
if self.on_sale() {
|
||||
self.sale_price_cents.unwrap_or(self.price_cents)
|
||||
} else {
|
||||
self.price_cents
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a baseline business discount (for all company accounts) is set and
|
||||
/// actually below the regular price.
|
||||
#[must_use]
|
||||
pub fn business_on_sale(&self) -> bool {
|
||||
matches!(self.business_sale_price_cents, Some(sale) if sale < self.price_cents)
|
||||
}
|
||||
}
|
||||
impl Model {}
|
||||
|
||||
// implement your write-oriented logic here
|
||||
impl ActiveModel {}
|
||||
|
||||
Reference in New Issue
Block a user