domain/boarding/upsell.rs
1//! Boarding upsell recommendations for exit baths, play, grooming, suite, and training offers.
2//!
3//! Recommendations carry care-safety gates so labor-saving offer drafting never bypasses staff
4//! review for allergies, medications, medical conditions, behavior context, or customer messaging.
5
6use super::*;
7use crate::{entities, policy};
8
9#[derive(Debug, Clone, Default)]
10/// Boarding upsell policy that classifies offer eligibility from care evidence.
11pub struct Policy;
12
13impl Policy {
14 /// Evaluates whether an exit-bath offer can be drafted or must be held for care-team review.
15 pub fn evaluate_exit_bath(
16 &self,
17 reservation_id: entities::reservation::Id,
18 pet_id: PetId,
19 care_profile: &entities::CareProfile,
20 ) -> Recommendation {
21 let eligibility = if care_profile.allergies.is_empty()
22 && care_profile.medical_conditions.is_empty()
23 && care_profile.medications.is_empty()
24 {
25 Eligibility::Eligible
26 } else {
27 Eligibility::NeedsStaffReview {
28 gate: policy::ReviewGate::MedicalDocumentReview,
29 reason: ReviewReason::CareSafetyAmbiguity,
30 }
31 };
32
33 Recommendation {
34 reservation_id,
35 pet_id,
36 opportunity: Opportunity::ExitBath,
37 eligibility,
38 }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
43/// Staff-reviewable boarding upsell recommendation with source reservation, pet, and eligibility evidence.
44pub struct Recommendation {
45 /// Boarding reservation the offer would attach to.
46 pub reservation_id: entities::reservation::Id,
47 /// Pet whose stay evidence drives the upsell recommendation.
48 pub pet_id: PetId,
49 /// Upsell opportunity identified for this stay.
50 pub opportunity: Opportunity,
51 /// Safety and review state controlling whether staff may offer the upsell.
52 pub eligibility: Eligibility,
53}
54
55impl Recommendation {
56 /// Returns the approval gate required before any recommendation becomes customer-facing.
57 pub fn customer_offer_gate(&self) -> Option<policy::ReviewGate> {
58 Some(policy::ReviewGate::CustomerMessageApproval)
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
63/// Boarding-adjacent revenue opportunities that can be recommended from stay evidence.
64pub enum Opportunity {
65 /// Bath offered before departure from boarding.
66 ExitBath,
67 /// Additional enrichment or playtime during the boarding stay.
68 Playtime,
69 /// Grooming service line or care-note category.
70 Grooming,
71 /// Upgrade opportunity for a higher-tier boarding accommodation.
72 PremiumSuite,
73 /// Training service line or care-note category.
74 Training,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
78/// Safety and review status for presenting a boarding upsell recommendation.
79pub enum Eligibility {
80 /// Source care evidence shows no safety ambiguity, so staff may review the offer normally.
81 Eligible,
82 /// Recommendation should not be shown because context makes the offer inappropriate.
83 Suppressed {
84 /// Reason this recommendation is suppressed or must be reviewed before use.
85 reason: SuppressionReason,
86 },
87 /// Care, behavior, or capacity evidence requires staff review before the offer is used.
88 NeedsStaffReview {
89 /// Review gate staff must clear before presenting this recommendation.
90 gate: policy::ReviewGate,
91 /// Reason this recommendation is suppressed or must be reviewed before use.
92 reason: ReviewReason,
93 },
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
97/// Reasons an otherwise possible upsell should be hidden from staff or customer drafts.
98pub enum SuppressionReason {
99 /// A recent similar offer exists, so repeating it would add noise instead of labor savings.
100 DuplicateRecentOffer,
101 /// Billing, refund, or complaint context makes a sales offer unsafe or inappropriate.
102 PaymentOrComplaintContext,
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
106/// Reasons staff must review an upsell recommendation before use.
107pub enum ReviewReason {
108 /// Allergies, medications, or medical conditions make the offer safety-sensitive.
109 CareSafetyAmbiguity,
110 /// Behavior notes make the offer sensitive enough for staff review.
111 BehaviorContextSensitive,
112 /// Inventory or staffing evidence is needed before staff can offer the upgrade.
113 CapacityEvidenceRequired,
114}