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