Skip to main content

domain/daycare/
eligibility.rs

1//! Daycare group-play eligibility policy for source-grounded staff review.
2//!
3//! ## Operator-summary
4//!
5//! This module supports the daycare group-play eligibility queue: it combines species,
6//! requested care mode, current temperament assessment, vaccine readiness, spay/neuter
7//! status, active incident restriction, and staffing coverage before a pet enters group play.
8//! It can reduce labor by producing a deterministic staff-review reason instead of asking
9//! front-desk or play-yard staff to manually reconcile every source note and policy gate.
10//!
11//! It must not automate live admission to group play, vaccine acceptance, behavior clearance,
12//! staffing overrides, or customer promises. Authoritative facts remain the pet profile,
13//! reviewed temperament/vaccine records, local play policy, incident disposition, and
14//! coverage decision. Review gates protect pets, customers, and staff by routing missing
15//! temperament, uncertain vaccine proof, spay/neuter concerns, insufficient staffing, or
16//! incident suspension to behavior, medical-document, manager, or staff review before use.
17//!
18//! ```
19//! use domain::{daycare, entities, policy};
20//! use uuid::Uuid;
21//!
22//! let evidence = daycare::eligibility::Evidence::builder()
23//!     .pet_id(entities::PetId(Uuid::nil()))
24//!     .species(entities::Species::Dog)
25//!     .service(daycare::ServiceVariant::AllDayPlay)
26//!     .temperament(daycare::eligibility::TemperamentAssessmentFreshness::Missing)
27//!     .vaccines(daycare::eligibility::VaccineReadiness::Current)
28//!     .spay_neuter(entities::SpayNeuterStatus::Neutered)
29//!     .incident(daycare::incident::Restriction::None)
30//!     .staff_coverage(daycare::coverage::Decision::Sufficient)
31//!     .build();
32//!
33//! assert_eq!(
34//!     daycare::eligibility::GroupPlayPolicy.evaluate(&evidence),
35//!     daycare::eligibility::GroupPlayDecision::NeedsStaffReview {
36//!         reason: daycare::eligibility::ReviewReason::MissingCurrentTemperamentAssessment,
37//!         gate: policy::ReviewGate::BehaviorReview,
38//!     },
39//! );
40//! ```
41
42use super::*;
43use crate::{entities, policy};
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
46/// Eligibility evidence staff review before a pet may enter daycare group play.
47pub struct Evidence {
48    /// Pet whose daycare eligibility is being evaluated.
49    pub pet_id: PetId,
50    /// Pet species used to prevent group-play rules from applying to unsupported care modes.
51    pub species: entities::Species,
52    /// Requested service that drives scheduling and labor estimates.
53    pub service: ServiceVariant,
54    /// Freshness of temperament assessment required for safe group assignment.
55    pub temperament: TemperamentAssessmentFreshness,
56    /// Vaccine proof readiness from source records or staff review.
57    pub vaccines: VaccineReadiness,
58    /// Spay/neuter status used for group-play policy review.
59    pub spay_neuter: entities::SpayNeuterStatus,
60    /// Active incident restriction that may suspend group play.
61    pub incident: incident::Restriction,
62    /// Current staffing coverage decision used before admitting a pet to group play.
63    pub staff_coverage: coverage::Decision,
64}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67/// Freshness state of the temperament assessment required for daycare group play.
68pub enum TemperamentAssessmentFreshness {
69    /// Source evidence is current and can be used without additional review.
70    Current,
71    /// Evidence exists but is stale, so staff must review before group play.
72    Stale,
73    /// Required source evidence is missing and must be collected or reviewed.
74    Missing,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
78/// Vaccine-proof readiness state for daycare eligibility decisions.
79pub enum VaccineReadiness {
80    /// Source evidence is current and can be used without additional review.
81    Current,
82    /// Vaccine documentation is absent and requires medical-document review.
83    MissingProof,
84    /// Vaccine status could not be mapped confidently and must be reviewed.
85    Unknown,
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
89/// Eligibility outcome for admitting a pet to daycare group play.
90pub enum GroupPlayDecision {
91    /// Pet has sufficient current evidence for group-play admission.
92    Eligible {
93        /// Evidence basis that justified the eligible outcome.
94        basis: EligibleBasis,
95    },
96    /// Staff must review missing, stale, or sensitive evidence before group play.
97    NeedsStaffReview {
98        /// Operational reason the pet cannot be auto-cleared for group play.
99        reason: ReviewReason,
100        /// Human review gate required to clear the eligibility issue.
101        gate: policy::ReviewGate,
102    },
103    /// The requested service or care mode is not eligible for group play.
104    Ineligible {
105        /// Operational reason the pet cannot be auto-cleared for group play.
106        reason: DenialReason,
107    },
108    /// An incident restriction suspends group play pending manager review.
109    TemporarilySuspended {
110        /// Pet whose daycare eligibility is being evaluated.
111        pet_id: PetId,
112        /// Human review gate required to clear the eligibility issue.
113        gate: policy::ReviewGate,
114    },
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
118/// Evidence basis proving a pet is eligible for group play.
119pub enum EligibleBasis {
120    /// Current source evidence satisfies all configured group-play gates.
121    CurrentEvidence,
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
125/// Reasons daycare group-play eligibility requires staff review.
126pub enum ReviewReason {
127    /// Temperament assessment is missing or stale and requires behavior review.
128    MissingCurrentTemperamentAssessment,
129    /// Vaccine proof is missing or uncertain and requires medical-document review.
130    VaccineProofRequiresReview,
131    /// Spay/neuter status requires staff review before group play.
132    SpayNeuterStatusRequiresReview,
133    /// Staffing coverage is insufficient or unknown for safe group play.
134    StaffCoverageRequiresReview,
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
138/// Reasons a pet is not eligible for the requested group-play care mode.
139pub enum DenialReason {
140    /// Requested service or care mode does not support group play for this species.
141    ServiceUnavailableForSpeciesOrCareMode,
142}
143
144#[derive(Debug, Clone, Default)]
145/// Deterministic policy that converts source evidence into daycare group-play eligibility.
146pub struct GroupPlayPolicy;
147
148impl GroupPlayPolicy {
149    /// Evaluates species, service, temperament, vaccine, spay/neuter, incident, and coverage gates for group play.
150    pub fn evaluate(&self, evidence: &Evidence) -> GroupPlayDecision {
151        if let incident::Restriction::SuspendedPendingManagerReview { pet_id } = evidence.incident {
152            return GroupPlayDecision::TemporarilySuspended {
153                pet_id,
154                gate: policy::ReviewGate::ManagerApproval,
155            };
156        }
157        if !matches!(evidence.species, entities::Species::Dog)
158            || !matches!(
159                evidence.service.care_mode(),
160                CareMode::DogGroupPlay | CareMode::DogHybridPlayAndRoom
161            )
162        {
163            return GroupPlayDecision::Ineligible {
164                reason: DenialReason::ServiceUnavailableForSpeciesOrCareMode,
165            };
166        }
167        if !matches!(
168            evidence.temperament,
169            TemperamentAssessmentFreshness::Current
170        ) {
171            return GroupPlayDecision::NeedsStaffReview {
172                reason: ReviewReason::MissingCurrentTemperamentAssessment,
173                gate: policy::ReviewGate::BehaviorReview,
174            };
175        }
176        if !matches!(evidence.vaccines, VaccineReadiness::Current) {
177            return GroupPlayDecision::NeedsStaffReview {
178                reason: ReviewReason::VaccineProofRequiresReview,
179                gate: policy::ReviewGate::MedicalDocumentReview,
180            };
181        }
182        if matches!(
183            evidence.spay_neuter,
184            entities::SpayNeuterStatus::Intact | entities::SpayNeuterStatus::Unknown
185        ) {
186            return GroupPlayDecision::NeedsStaffReview {
187                reason: ReviewReason::SpayNeuterStatusRequiresReview,
188                gate: policy::ReviewGate::BehaviorReview,
189            };
190        }
191        if !matches!(evidence.staff_coverage, coverage::Decision::Sufficient) {
192            return GroupPlayDecision::NeedsStaffReview {
193                reason: ReviewReason::StaffCoverageRequiresReview,
194                gate: policy::ReviewGate::ManagerApproval,
195            };
196        }
197        GroupPlayDecision::Eligible {
198            basis: EligibleBasis::CurrentEvidence,
199        }
200    }
201}