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}