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(self, _db: &C, insert: bool) -> std::result::Result 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) } /// Whether the variant's inventory is tracked. A `None` stock means /// "available, not tracked" (always purchasable, unlimited). #[must_use] pub fn tracked(&self) -> bool { self.stock.is_some() } /// Whether the variant can currently be bought: untracked variants are always /// available; tracked ones need a positive quantity on hand. #[must_use] pub fn in_stock(&self) -> bool { self.stock.map_or(true, |s| s > 0) } /// Clamp a desired quantity to what's available: capped at the tracked stock, /// or left as-is (only floored at 0) when untracked. #[must_use] pub fn cap(&self, qty: i32) -> i32 { match self.stock { Some(s) => qty.clamp(0, s), None => qty.max(0), } } } // 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( db: &C, product_id: i32, ) -> Result, 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( db: &C, product_ids: &[i32], ) -> Result>, DbErr> { let mut map: std::collections::HashMap> = 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) } }