Skip to main content

domain/retail/
mod.rs

1//! Retail service-line contracts for POS sale eligibility, inventory position, reorder workflows, vendor partnerships, and care-safe product recommendations.
2//!
3//! Retail products such as supplements, boarding diets, and coat-care items can lift revenue and guest experience, but customer-facing copy and product recommendations must remain evidence-backed. This module keeps SKU/catalog facts, stock thresholds, checkout attachment, reorder tasks, and safe upsell recommendations behind review gates so automation drafts opportunities without promising medical outcomes or bypassing inventory/POS policy.
4
5use bon::Builder;
6use serde::{Deserialize, Serialize};
7
8/// Inventory boundary for stock position, availability, reorder thresholds, and sellable-unit checks.
9pub mod inventory;
10/// POS boundary for standalone sales, reservation-checkout attachments, price exceptions, and comps.
11pub mod pos;
12/// Product catalog boundary for SKUs, location offerings, sellability, and in-house consumable use.
13pub mod product;
14/// Recommendation boundary for personalized retail upsells with inventory, preference, and care-safety gates.
15pub mod recommendation;
16/// Reorder boundary for manager tasks, vendor-managed notices, and no-action threshold decisions.
17pub mod reorder;
18/// Vendor boundary for partner-product catalog relationships and externally managed assortments.
19pub mod vendor;
20
21pub use product::{LocationOffering, OfferingStatus, Product, Sku, SkuError};
22pub use vendor::Partner;
23
24/// Result type returned by fallible retail operations.
25pub type Result<T> = std::result::Result<T, Error>;
26
27#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
28/// Retail validation failures that prevent impossible stock math or unsupported recommendations from becoming workflow facts.
29pub enum Error {
30    #[error("retail inventory position cannot reserve more units than are on hand")]
31    /// Reserved units exceed on hand retail operational signal for inventory, POS, reorder, recommendation, or review handling.
32    ReservedUnitsExceedOnHand,
33    #[error("retail recommendation rationale is required")]
34    /// Missing rationale retail operational signal for inventory, POS, reorder, recommendation, or review handling.
35    MissingRationale,
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
39/// Location retail contract tying catalog product, POS policy, inventory policy, recommendation rule, and reorder policy together.
40pub struct Contract {
41    /// Source-derived product carried by this retail contract.
42    pub product: Product,
43    /// Source-derived pos carried by this retail contract.
44    pub pos: pos::Policy,
45    /// Source-derived inventory carried by this retail contract.
46    pub inventory: inventory::Policy,
47    /// Source-derived recommendation carried by this retail contract.
48    pub recommendation: recommendation::Rule,
49    /// Source-derived reorder carried by this retail contract.
50    pub reorder: reorder::Policy,
51}
52
53impl Contract {
54    /// Reports whether the contracted inventory threshold indicates manager/vendor reorder attention is due.
55    pub fn should_reorder(&self) -> bool {
56        matches!(self.inventory, inventory::Policy::Tracked { on_hand, reorder_at } if on_hand.get() <= reorder_at.get())
57    }
58
59    /// Builds a representative PetSuites-style retail contract for docs/tests without claiming it is live policy.
60    pub fn standard_petsuites() -> Self {
61        Self::builder()
62            .product(Product::new(
63                Sku::try_new("PETSUITES-RETAIL").unwrap(),
64                product::Category::PersonalizedUpsell,
65            ))
66            .pos(pos::Policy::IntegratedWithReservationCheckout)
67            .inventory(inventory::Policy::Tracked {
68                on_hand: inventory::UnitCount::try_new(1).unwrap(),
69                reorder_at: inventory::UnitCount::try_new(10).unwrap(),
70            })
71            .recommendation(recommendation::Rule::AnxietySupportAfterBoarding)
72            .reorder(reorder::Policy::AutoCreateManagerTask)
73            .build()
74    }
75}