Skip to main content

domain/
entities.rs

1//! Core pet-resort entities and operational records.
2//!
3//! These structs and enums are the normalized domain facts used by workflow, policy, storage, and
4//! source adapters. They should be read as external contracts: every field is either a source-backed
5//! fact, a reviewable derived state, or a safety/labor signal used to reduce manual resort work
6//! without bypassing manager, medical, behavior, payment, or customer-message gates.
7
8use chrono::{DateTime, NaiveDate, Utc};
9use nutype::nutype;
10#[allow(unused_imports)]
11use serde::{Deserialize, Serialize};
12use uuid::Uuid;
13
14use bon::Builder;
15
16use crate::{
17    agent, care, customer, document, incident, location, message, payment, pet, policy, portal,
18    temperament, vaccine,
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
22/// Stable identifier for a resort location across source imports, policies, reports, and workflows.
23pub struct LocationId(pub Uuid);
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
26/// Stable identifier for the customer/account responsible for pets, reservations, messages, and payments.
27pub struct CustomerId(pub Uuid);
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
30/// Stable identifier for a pet whose care, temperament, vaccine, and reservation facts drive safety decisions.
31pub struct PetId(pub Uuid);
32
33/// Reservation-facing source vocabulary embedded in core entity records.
34pub mod reservation {
35    use serde::{Deserialize, Serialize};
36    use uuid::Uuid;
37
38    use super::PortalProvider;
39
40    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
41    /// Provider or source identifier retained as the stable join key.
42    pub struct Id(pub Uuid);
43
44    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
45    /// Normalized lifecycle states used to reconcile source-system data with domain workflows.
46    pub enum Status {
47        /// Inquiry state or source category preserved for normalized resort records.
48        Inquiry,
49        /// Reservation has been requested but not yet confirmed.
50        Requested,
51        /// Missing info state or source category preserved for normalized resort records.
52        MissingInfo,
53        /// Vaccine pending state or source category preserved for normalized resort records.
54        VaccinePending,
55        /// Special review state or source category preserved for normalized resort records.
56        SpecialReview,
57        /// Waitlisted state or source category preserved for normalized resort records.
58        Waitlisted,
59        /// Offered state or source category preserved for normalized resort records.
60        Offered,
61        /// Reservation has been accepted by the resort.
62        Confirmed,
63        /// Pet has arrived and is in care.
64        CheckedIn,
65        /// Active state or source category preserved for normalized resort records.
66        Active,
67        /// Pet has left care and the stay is complete.
68        CheckedOut,
69        /// Reservation is no longer active.
70        Cancelled,
71        /// Rejected state or source category preserved for normalized resort records.
72        Rejected,
73    }
74
75    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76    /// Origin channel for a reservation or operational fact before it becomes trusted domain evidence.
77    pub enum Source {
78        /// Portal state or source category preserved for normalized resort records.
79        Portal(PortalProvider),
80        /// Website form state or source category preserved for normalized resort records.
81        WebsiteForm,
82        /// Phone transcript state or source category preserved for normalized resort records.
83        PhoneTranscript,
84        /// Sms state or source category preserved for normalized resort records.
85        Sms,
86        /// Email state or source category preserved for normalized resort records.
87        Email,
88        /// Staff created state or source category preserved for normalized resort records.
89        StaffCreated,
90    }
91}
92
93#[nutype(
94    sanitize(trim),
95    validate(not_empty, len_char_max = 120),
96    derive(
97        Debug,
98        Clone,
99        PartialEq,
100        Eq,
101        PartialOrd,
102        Ord,
103        Hash,
104        Serialize,
105        Deserialize
106    )
107)]
108pub struct StaffId(String);
109
110/// Manager identifier used when approvals, overrides, or escalations require accountable leadership.
111#[nutype(
112    sanitize(trim),
113    validate(not_empty, len_char_max = 120),
114    derive(
115        Debug,
116        Clone,
117        PartialEq,
118        Eq,
119        PartialOrd,
120        Ord,
121        Hash,
122        Serialize,
123        Deserialize
124    )
125)]
126pub struct ManagerId(String);
127
128#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129/// Resort location record that scopes local capabilities, timezone, brand, and policy references.
130pub struct Location {
131    /// Source-backed id carried by this normalized pet-resort entity.
132    pub id: LocationId,
133    /// Source-backed brand carried by this normalized pet-resort entity.
134    pub brand: Brand,
135    /// Contact or display name used by staff.
136    pub name: location::Name,
137    /// Source-backed timezone carried by this normalized pet-resort entity.
138    pub timezone: location::Timezone,
139    /// Source-backed capabilities carried by this normalized pet-resort entity.
140    pub capabilities: Vec<ServiceKind>,
141    /// Source-backed policies carried by this normalized pet-resort entity.
142    pub policies: LocationPolicyRefs,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146/// Brand family used to group multi-site operating records without losing local resort identity.
147pub enum Brand {
148    /// Nva pet resorts state or source category preserved for normalized resort records.
149    NvaPetResorts,
150    /// Pet suites state or source category preserved for normalized resort records.
151    PetSuites,
152    /// Contact or display name used by staff.
153    NeighborhoodPetResort {
154        /// Name carried by this variant.
155        name: location::Name,
156    },
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
160/// References to the local policy set that controls automation, vaccine, and play-safety decisions.
161pub struct LocationPolicyRefs {
162    /// Source-backed vaccine policy ID carried by this normalized pet-resort entity.
163    pub vaccine_policy_id: policy::Id,
164    /// Source-backed deposit policy ID carried by this normalized pet-resort entity.
165    pub deposit_policy_id: policy::Id,
166    /// Source-backed playgroup policy ID carried by this normalized pet-resort entity.
167    pub playgroup_policy_id: policy::Id,
168}
169
170#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
171/// Customer/account profile used for reservation ownership, consent-sensitive messaging, and follow-up work.
172pub struct Customer {
173    /// Source-backed id carried by this normalized pet-resort entity.
174    pub id: CustomerId,
175    /// Source-backed full name carried by this normalized pet-resort entity.
176    pub full_name: customer::Name,
177    /// Source-backed email carried by this normalized pet-resort entity.
178    pub email: Option<customer::Email>,
179    /// Source-backed mobile phone carried by this normalized pet-resort entity.
180    pub mobile_phone: Option<customer::Phone>,
181    /// Source-backed preferred contact carried by this normalized pet-resort entity.
182    pub preferred_contact: ContactChannel,
183    /// Source-backed portal account carried by this normalized pet-resort entity.
184    pub portal_account: Option<PortalAccountRef>,
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
188/// Link to the customer portal account that supplied or owns source records.
189pub struct PortalAccountRef {
190    /// Source-backed provider carried by this normalized pet-resort entity.
191    pub provider: PortalProvider,
192    /// Source-backed external customer ID carried by this normalized pet-resort entity.
193    pub external_customer_id: portal::CustomerId,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197/// Portal provider that owns the account or operational record.
198pub enum PortalProvider {
199    /// Gingr reservation and pet-care operating system.
200    Gingr,
201    /// Non-dog, non-cat pet handled by exception policy.
202    Other(String),
203}
204
205#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
206/// Customer contact channel preference or observed route used by draft/message workflows.
207pub enum ContactChannel {
208    /// Email state or source category preserved for normalized resort records.
209    Email,
210    /// Sms state or source category preserved for normalized resort records.
211    Sms,
212    /// Phone state or source category preserved for normalized resort records.
213    Phone,
214    /// Portal state or source category preserved for normalized resort records.
215    Portal,
216}
217
218#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
219/// Pet profile carrying identity, species, age, sex, sterilization, temperament, and care facts for safe service decisions.
220pub struct Pet {
221    /// Source-backed id carried by this normalized pet-resort entity.
222    pub id: PetId,
223    /// Source-backed customer ID carried by this normalized pet-resort entity.
224    pub customer_id: CustomerId,
225    /// Contact or display name used by staff.
226    pub name: pet::Name,
227    /// Source-backed species carried by this normalized pet-resort entity.
228    pub species: Species,
229    /// Source-backed birth date carried by this normalized pet-resort entity.
230    pub birth_date: Option<NaiveDate>,
231    /// Source-backed sex carried by this normalized pet-resort entity.
232    pub sex: Option<Sex>,
233    /// Source-backed spay neuter status carried by this normalized pet-resort entity.
234    pub spay_neuter_status: SpayNeuterStatus,
235    #[builder(default)]
236    /// Source-backed temperament carried by this normalized pet-resort entity.
237    pub temperament: TemperamentProfile,
238    #[builder(default)]
239    /// Source-backed care profile carried by this normalized pet-resort entity.
240    pub care_profile: CareProfile,
241}
242
243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
244/// Pet species category used by boarding/daycare/play policies and labor planning.
245pub enum Species {
246    /// Dog guest, using dog-specific policy and capacity rules.
247    Dog,
248    /// Cat guest, using cat-specific policy and accommodation rules.
249    Cat,
250    /// Non-dog, non-cat pet handled by exception policy.
251    Other(String),
252}
253
254#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
255/// Recorded pet sex when the source system supplies it.
256pub enum Sex {
257    /// Female pet sex recorded for profile and policy context.
258    Female,
259    /// Male pet sex recorded for profile and policy context.
260    Male,
261    /// Provider role or status could not be mapped confidently.
262    Unknown,
263}
264
265#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
266/// Spay/neuter status used by group-play eligibility, safety review, and policy gating.
267pub enum SpayNeuterStatus {
268    /// Pet has been spayed for policy and playgroup eligibility checks.
269    Spayed,
270    /// Pet has been neutered for policy and playgroup eligibility checks.
271    Neutered,
272    /// Pet is intact and may trigger extra policy review.
273    Intact,
274    /// Provider role or status could not be mapped confidently.
275    Unknown,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder, Default)]
279/// Temperament evidence used to decide group-play, individual care, and behavior-review routing.
280pub struct TemperamentProfile {
281    #[builder(default)]
282    /// Source-backed group play observation carried by this normalized pet-resort entity.
283    pub group_play_observation: temperament::GroupPlayObservation,
284    #[builder(default)]
285    /// Source-backed people orientation carried by this normalized pet-resort entity.
286    pub people_orientation: temperament::PeopleOrientation,
287    #[builder(default)]
288    /// Source-backed rating carried by this normalized pet-resort entity.
289    pub rating: temperament::Rating,
290    #[builder(default)]
291    /// Source-backed behavior observations carried by this normalized pet-resort entity.
292    pub behavior_observations: Vec<temperament::BehaviorObservation>,
293    #[builder(default)]
294    /// Source-backed staff notes carried by this normalized pet-resort entity.
295    pub staff_notes: Vec<temperament::StaffNote>,
296}
297
298impl TemperamentProfile {
299    /// Reports whether temperament facts require staff evaluation before group play or similar services.
300    pub fn needs_staff_play_evaluation(&self) -> bool {
301        self.group_play_observation.needs_staff_evaluation()
302    }
303}
304
305#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
306/// Feeding, medication, handling, and special-care summary used for staff handoffs and briefings.
307pub struct CareProfile {
308    /// Source-backed feeding instructions carried by this normalized pet-resort entity.
309    pub feeding_instructions: Option<care::FeedingInstruction>,
310    /// Source-backed medications carried by this normalized pet-resort entity.
311    pub medications: Vec<MedicationInstruction>,
312    /// Source-backed allergies carried by this normalized pet-resort entity.
313    pub allergies: Vec<care::AllergyName>,
314    /// Source-backed medical conditions carried by this normalized pet-resort entity.
315    pub medical_conditions: Vec<care::MedicalConditionName>,
316    /// Source-backed emergency contact carried by this normalized pet-resort entity.
317    pub emergency_contact: Option<care::ContactRef>,
318    /// Source-backed veterinarian contact carried by this normalized pet-resort entity.
319    pub veterinarian_contact: Option<care::ContactRef>,
320}
321
322#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
323/// Medication instruction that must remain explicit for care safety and shift handoff evidence.
324pub struct MedicationInstruction {
325    /// Contact or display name used by staff.
326    pub name: care::MedicationName,
327    /// Source-backed dose carried by this normalized pet-resort entity.
328    pub dose: care::MedicationDose,
329    /// Source-backed schedule carried by this normalized pet-resort entity.
330    pub schedule: care::MedicationSchedule,
331    /// Source-backed review requirement carried by this normalized pet-resort entity.
332    pub review_requirement: care::MedicationReviewRequirement,
333}
334
335#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
336/// Reservation record tying customer, pet, service, status, deposit, add-ons, and safety stops together.
337pub struct Reservation {
338    /// Source-backed id carried by this normalized pet-resort entity.
339    pub id: reservation::Id,
340    /// Source-backed location ID carried by this normalized pet-resort entity.
341    pub location_id: LocationId,
342    /// Source-backed customer ID carried by this normalized pet-resort entity.
343    pub customer_id: CustomerId,
344    /// Source-backed pet IDs carried by this normalized pet-resort entity.
345    pub pet_ids: Vec<PetId>,
346    /// Requested service that drives scheduling and labor estimates.
347    pub service: ServiceKind,
348    /// Source-backed status carried by this normalized pet-resort entity.
349    pub status: reservation::Status,
350    /// Source-backed starts at carried by this normalized pet-resort entity.
351    pub starts_at: DateTime<Utc>,
352    /// Source-backed ends at carried by this normalized pet-resort entity.
353    pub ends_at: DateTime<Utc>,
354    /// Source-backed deposit carried by this normalized pet-resort entity.
355    pub deposit: Option<Deposit>,
356    /// Source-backed source carried by this normalized pet-resort entity.
357    pub source: reservation::Source,
358    #[builder(default)]
359    /// Source-backed requested add ons carried by this normalized pet-resort entity.
360    pub requested_add_ons: Vec<AddOn>,
361    #[builder(default)]
362    /// Source-backed hard stops carried by this normalized pet-resort entity.
363    pub hard_stops: Vec<HardStop>,
364}
365
366#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
367/// Resort service line used for labor planning, capacity, policy, upsell, and workflow routing.
368pub enum ServiceKind {
369    /// Overnight stay service line.
370    Boarding,
371    /// Single-day play visit without overnight lodging.
372    DayPlay,
373    /// Daytime boarding care with lodging-style supervision.
374    DayBoarding,
375    /// Grooming service line or care-note category.
376    Grooming,
377    /// Training service line or care-note category.
378    Training,
379    /// Day-spa service package.
380    DaySpa,
381}
382
383/// Shared deposit type used across the entities boundary.
384pub type Deposit = payment::Deposit;
385/// Shared payment status type used across the entities boundary.
386pub type PaymentStatus = payment::DepositStatus;
387
388#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
389/// Optional reservation add-ons that affect labor, revenue, care planning, or customer follow-up.
390pub enum AddOn {
391    /// Group-play add-on or accommodation feature.
392    GroupPlay,
393    /// Individual play add-on for pets not suited to group play.
394    IndividualPlay,
395    /// Premium suite with webcam visibility.
396    WebcamSuite,
397    /// Bath offered before departure from boarding.
398    ExitBath,
399    /// Progress report shared with the customer during care.
400    PawgressReport,
401    /// Medication service that requires care instructions.
402    MedicationAdministration,
403    /// Non-dog, non-cat pet handled by exception policy.
404    Other(crate::reservation::AddOnLabel),
405}
406
407#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
408/// Non-ignorable condition that blocks or routes a reservation before staff or customer action proceeds.
409pub enum HardStop {
410    /// Missing required vaccine state or source category preserved for normalized resort records.
411    MissingRequiredVaccine(policy::VaccineName),
412    /// Ineligible for group play state or source category preserved for normalized resort records.
413    IneligibleForGroupPlay(policy::play::IneligibilityReason),
414    /// Pet is in heat and requires policy handling.
415    InHeat,
416    /// Age below minimum weeks state or source category preserved for normalized resort records.
417    AgeBelowMinimumWeeks(crate::reservation::AgeThreshold),
418    /// Medical or medication information requires review before service.
419    MedicalOrMedicationReviewRequired,
420    /// Behavior history requires review before service.
421    BehaviorReviewRequired,
422    /// Deposit must be collected before the booking is secure.
423    DepositRequired,
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
427/// Stable identifier for a document artifact used as vaccine, waiver, medical, or incident evidence.
428pub struct DocumentId(pub Uuid);
429
430#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
431/// Stable identifier for a vaccine compliance record tied to a pet and proof document.
432pub struct VaccineRecordId(pub Uuid);
433
434/// Care-note vocabulary for staff-visible, customer-visible, and internal handoff notes.
435pub mod care_note {
436    use nutype::nutype;
437    #[allow(unused_imports)]
438    use serde::{Deserialize, Serialize};
439    use uuid::Uuid;
440
441    use super::{IncidentId, PetId, reservation};
442
443    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
444    /// Provider or source identifier retained as the stable join key.
445    pub struct Id(pub Uuid);
446
447    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
448    /// Subject that a care, document, incident, audit, or message record is about.
449    pub enum Subject {
450        /// Pet record participating in the workflow.
451        Pet(PetId),
452        /// Reservation record participating in the workflow.
453        Reservation(reservation::Id),
454        /// Incident record participating in the workflow.
455        Incident(IncidentId),
456    }
457
458    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
459    /// Care-note category used to route safety, feeding, medication, behavior, and staff handoff information.
460    pub enum Kind {
461        /// Feeding state or source category preserved for normalized resort records.
462        Feeding,
463        /// Medication state or source category preserved for normalized resort records.
464        Medication,
465        /// Medical state or source category preserved for normalized resort records.
466        Medical,
467        /// Behavior state or source category preserved for normalized resort records.
468        Behavior,
469        /// Grooming service line or care-note category.
470        Grooming,
471        /// Training service line or care-note category.
472        Training,
473        /// General state or source category preserved for normalized resort records.
474        General,
475    }
476
477    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
478    /// Visibility boundary that determines whether a care note may be shown to customers or only staff.
479    pub enum Visibility {
480        /// Internal only state or source category preserved for normalized resort records.
481        InternalOnly,
482        /// Customer visible state or source category preserved for normalized resort records.
483        CustomerVisible,
484        /// Customer visible after review state or source category preserved for normalized resort records.
485        CustomerVisibleAfterReview,
486    }
487
488    #[nutype(
489        sanitize(trim),
490        validate(not_empty, len_char_max = 2000),
491        derive(
492            Debug,
493            Clone,
494            PartialEq,
495            Eq,
496            PartialOrd,
497            Ord,
498            Hash,
499            Serialize,
500            Deserialize
501        )
502    )]
503    pub struct Body(String);
504}
505
506#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
507/// Stable identifier for a pet, customer, or operational incident requiring evidence and follow-up.
508pub struct IncidentId(pub Uuid);
509
510#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
511/// Stable identifier for a customer or internal message workflow.
512pub struct MessageId(pub Uuid);
513
514/// Approval record vocabulary for review-gated automation outcomes.
515pub mod approval {
516    use bon::Builder;
517    use chrono::{DateTime, Utc};
518    use serde::{Deserialize, Serialize};
519    use uuid::Uuid;
520
521    use super::{
522        ActorRef, DocumentId, IncidentId, MessageId, VaccineRecordId, policy, reservation,
523    };
524
525    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
526    /// Provider or source identifier retained as the stable join key.
527    pub struct Id(pub Uuid);
528
529    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
530    /// Approval record showing who decided, what target was reviewed, and what lifecycle state resulted.
531    pub struct Record {
532        /// Source-backed id carried by this normalized pet-resort entity.
533        pub id: Id,
534        /// Source-backed target carried by this normalized pet-resort entity.
535        pub target: Target,
536        /// Source-backed gate carried by this normalized pet-resort entity.
537        pub gate: policy::ReviewGate,
538        /// Source-backed lifecycle carried by this normalized pet-resort entity.
539        pub lifecycle: Lifecycle,
540        /// Source-backed requested by carried by this normalized pet-resort entity.
541        pub requested_by: ActorRef,
542        /// Source-backed requested at carried by this normalized pet-resort entity.
543        pub requested_at: DateTime<Utc>,
544        #[builder(default)]
545        /// Source-backed audit refs carried by this normalized pet-resort entity.
546        pub audit_refs: Vec<crate::audit::EventId>,
547    }
548
549    impl Record {
550        /// Returns the normalized operational status represented by this record.
551        pub fn status(&self) -> Status {
552            self.lifecycle.status()
553        }
554
555        /// Reports whether this approval gate currently applies to the target workflow.
556        pub fn is_applicable(&self) -> bool {
557            matches!(self.lifecycle, Lifecycle::Approved { .. })
558        }
559
560        /// Reports whether the review lifecycle has reached an approval, rejection, or non-applicable endpoint.
561        pub fn is_terminal_decision(&self) -> bool {
562            self.lifecycle.is_terminal_decision()
563        }
564
565        /// Returns the accountable actor and timestamp when the review reached a terminal decision.
566        pub fn decision_actor_and_time(&self) -> Option<(&ActorRef, DateTime<Utc>)> {
567            self.lifecycle.decision_actor_and_time()
568        }
569    }
570
571    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
572    /// Operational artifact that an approval gate is allowed to approve, reject, or mark non-applicable.
573    pub enum Target {
574        /// Reservation record participating in the workflow.
575        Reservation(reservation::Id),
576        /// Customer or pet document participating in review.
577        Document(DocumentId),
578        /// Vaccination document or status record under review.
579        VaccineRecord(VaccineRecordId),
580        /// Incident record participating in the workflow.
581        Incident(IncidentId),
582        /// Customer communication record participating in approval.
583        Message(MessageId),
584    }
585
586    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587    /// Approval lifecycle state for draft, requested, approved, rejected, or non-applicable review gates.
588    pub enum Lifecycle {
589        /// Approval requested state or source category preserved for normalized resort records.
590        ApprovalRequested,
591        /// Approved state or source category preserved for normalized resort records.
592        Approved {
593            /// Source-backed decided by carried by this normalized pet-resort entity.
594            decided_by: ActorRef,
595            /// Source-backed decided at carried by this normalized pet-resort entity.
596            decided_at: DateTime<Utc>,
597        },
598        /// Rejected state or source category preserved for normalized resort records.
599        Rejected {
600            /// Source-backed decided by carried by this normalized pet-resort entity.
601            decided_by: ActorRef,
602            /// Source-backed decided at carried by this normalized pet-resort entity.
603            decided_at: DateTime<Utc>,
604        },
605        /// Reservation is no longer active.
606        Cancelled,
607        /// Superseded state or source category preserved for normalized resort records.
608        Superseded,
609    }
610
611    impl Lifecycle {
612        /// Returns the normalized operational status represented by this record.
613        pub fn status(&self) -> Status {
614            match self {
615                Self::ApprovalRequested => Status::ApprovalRequested,
616                Self::Approved { .. } => Status::Approved,
617                Self::Rejected { .. } => Status::Rejected,
618                Self::Cancelled => Status::Cancelled,
619                Self::Superseded => Status::Superseded,
620            }
621        }
622
623        /// Reports whether the review lifecycle has reached an approval, rejection, or non-applicable endpoint.
624        pub fn is_terminal_decision(&self) -> bool {
625            matches!(self, Self::Approved { .. } | Self::Rejected { .. })
626        }
627
628        /// Returns the accountable actor and timestamp when the review reached a terminal decision.
629        pub fn decision_actor_and_time(&self) -> Option<(&ActorRef, DateTime<Utc>)> {
630            match self {
631                Self::Approved {
632                    decided_by,
633                    decided_at,
634                }
635                | Self::Rejected {
636                    decided_by,
637                    decided_at,
638                } => Some((decided_by, *decided_at)),
639                Self::ApprovalRequested | Self::Cancelled | Self::Superseded => None,
640            }
641        }
642    }
643
644    #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
645    /// Normalized lifecycle states used to reconcile source-system data with domain workflows.
646    pub enum Status {
647        /// Approval requested state or source category preserved for normalized resort records.
648        ApprovalRequested,
649        /// Approved state or source category preserved for normalized resort records.
650        Approved,
651        /// Rejected state or source category preserved for normalized resort records.
652        Rejected,
653        /// Reservation is no longer active.
654        Cancelled,
655        /// Superseded state or source category preserved for normalized resort records.
656        Superseded,
657    }
658}
659
660#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
661/// Document record tying storage, classification, source, scan, redaction, and review status together.
662pub struct Document {
663    /// Source-backed id carried by this normalized pet-resort entity.
664    pub id: DocumentId,
665    /// Source-backed location ID carried by this normalized pet-resort entity.
666    pub location_id: LocationId,
667    /// Source-backed subject carried by this normalized pet-resort entity.
668    pub subject: DocumentSubject,
669    /// Source-backed classification carried by this normalized pet-resort entity.
670    pub classification: document::Classification,
671    /// Source-backed source carried by this normalized pet-resort entity.
672    pub source: document::Source,
673    /// Source-backed uploaded by actor carried by this normalized pet-resort entity.
674    pub uploaded_by_actor: ActorRef,
675    /// Source-backed uploaded at carried by this normalized pet-resort entity.
676    pub uploaded_at: DateTime<Utc>,
677    /// Source-backed original file carried by this normalized pet-resort entity.
678    pub original_file: document::OriginalFile,
679    /// Source-backed storage ref carried by this normalized pet-resort entity.
680    pub storage_ref: document::StorageRef,
681    /// Source-backed virus scan status carried by this normalized pet-resort entity.
682    pub virus_scan_status: document::VirusScanStatus,
683    /// Source-backed pii redaction status carried by this normalized pet-resort entity.
684    pub pii_redaction_status: document::PiiRedactionStatus,
685    /// Source-backed verification status carried by this normalized pet-resort entity.
686    pub verification_status: document::Status,
687    #[builder(default)]
688    /// Source-backed audit refs carried by this normalized pet-resort entity.
689    pub audit_refs: Vec<crate::audit::EventId>,
690}
691
692impl Document {
693    /// Reports whether the document must be reviewed before agents or staff treat it as usable evidence.
694    pub fn requires_human_review_before_use(&self) -> bool {
695        matches!(
696            self.verification_status,
697            document::Status::Received
698                | document::Status::Extracting
699                | document::Status::ExtractionFailed
700                | document::Status::AwaitingReview
701                | document::Status::QuarantinedRejected
702        ) || !matches!(self.virus_scan_status, document::VirusScanStatus::Passed)
703    }
704}
705
706#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
707/// Entity or workflow subject a document is evidence for.
708pub enum DocumentSubject {
709    /// Customer record participating in the workflow.
710    Customer(CustomerId),
711    /// Pet record participating in the workflow.
712    Pet(PetId),
713    /// Reservation record participating in the workflow.
714    Reservation(reservation::Id),
715    /// Incident record participating in the workflow.
716    Incident(IncidentId),
717}
718
719#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
720/// Vaccine compliance record linking pet, vaccine name, expiration, proof document, and review status.
721pub struct VaccineRecord {
722    /// Source-backed id carried by this normalized pet-resort entity.
723    pub id: VaccineRecordId,
724    /// Pet receiving the grooming or care service.
725    pub pet_id: PetId,
726    /// Source-backed vaccine name carried by this normalized pet-resort entity.
727    pub vaccine_name: policy::VaccineName,
728    /// Source-backed source document ID carried by this normalized pet-resort entity.
729    pub source_document_id: DocumentId,
730    /// Source-backed status carried by this normalized pet-resort entity.
731    pub status: vaccine::Status,
732    /// Source-backed effective on carried by this normalized pet-resort entity.
733    pub effective_on: NaiveDate,
734    /// Source-backed expires on carried by this normalized pet-resort entity.
735    pub expires_on: Option<NaiveDate>,
736    /// Source-backed review gate carried by this normalized pet-resort entity.
737    pub review_gate: policy::ReviewGate,
738    #[builder(default)]
739    /// Source-backed audit refs carried by this normalized pet-resort entity.
740    pub audit_refs: Vec<crate::audit::EventId>,
741}
742
743impl VaccineRecord {
744    /// Reports whether vaccine proof is still unverified, rejected, or otherwise unsafe for compliance automation.
745    pub fn requires_human_review_before_compliance(&self) -> bool {
746        matches!(
747            self.status,
748            vaccine::Status::SuggestedExtracted
749                | vaccine::Status::PendingReview
750                | vaccine::Status::Rejected
751                | vaccine::Status::ExceptionRequested
752        )
753    }
754}
755
756#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
757/// Care note with author, visibility, subject, body, source, and review-sensitive timestamps.
758pub struct CareNote {
759    /// Source-backed id carried by this normalized pet-resort entity.
760    pub id: care_note::Id,
761    /// Source-backed subject carried by this normalized pet-resort entity.
762    pub subject: care_note::Subject,
763    /// Source-backed kind carried by this normalized pet-resort entity.
764    pub kind: care_note::Kind,
765    /// Source-backed visibility carried by this normalized pet-resort entity.
766    pub visibility: care_note::Visibility,
767    /// Source-backed body carried by this normalized pet-resort entity.
768    pub body: care_note::Body,
769    /// Source-backed author carried by this normalized pet-resort entity.
770    pub author: ActorRef,
771    /// Source-backed recorded at carried by this normalized pet-resort entity.
772    pub recorded_at: DateTime<Utc>,
773    #[builder(default)]
774    /// Source-backed audit refs carried by this normalized pet-resort entity.
775    pub audit_refs: Vec<crate::audit::EventId>,
776}
777
778impl CareNote {
779    /// Reports whether this care note may be surfaced to customers without an additional approval gate.
780    pub fn is_customer_visible_without_review(&self) -> bool {
781        matches!(self.visibility, care_note::Visibility::CustomerVisible)
782            && !matches!(
783                self.kind,
784                care_note::Kind::Medication | care_note::Kind::Medical | care_note::Kind::Behavior
785            )
786    }
787}
788
789#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
790/// Incident record used for manager attention, safety follow-up, customer messaging, and audit evidence.
791pub struct Incident {
792    /// Source-backed id carried by this normalized pet-resort entity.
793    pub id: IncidentId,
794    /// Source-backed location ID carried by this normalized pet-resort entity.
795    pub location_id: LocationId,
796    /// Source-backed primary subject carried by this normalized pet-resort entity.
797    pub primary_subject: IncidentSubject,
798    /// Source-backed category carried by this normalized pet-resort entity.
799    pub category: incident::Category,
800    /// Source-backed severity carried by this normalized pet-resort entity.
801    pub severity: incident::Severity,
802    /// Source-backed status carried by this normalized pet-resort entity.
803    pub status: incident::Status,
804    /// Source-backed reported by carried by this normalized pet-resort entity.
805    pub reported_by: ActorRef,
806    /// Source-backed reported at carried by this normalized pet-resort entity.
807    pub reported_at: DateTime<Utc>,
808    /// Source-backed summary carried by this normalized pet-resort entity.
809    pub summary: incident::Summary,
810    #[builder(default)]
811    /// Source-backed required review gates carried by this normalized pet-resort entity.
812    pub required_review_gates: Vec<policy::ReviewGate>,
813    #[builder(default)]
814    /// Source-backed audit refs carried by this normalized pet-resort entity.
815    pub audit_refs: Vec<crate::audit::EventId>,
816}
817
818impl Incident {
819    /// Reports whether the incident is still active enough to require manager attention.
820    pub fn requires_manager_attention(&self) -> bool {
821        matches!(
822            self.status,
823            incident::Status::NeedsManagerReview | incident::Status::LegalHold
824        ) || matches!(
825            self.severity,
826            incident::Severity::High | incident::Severity::Critical
827        ) || self
828            .required_review_gates
829            .contains(&policy::ReviewGate::ManagerApproval)
830    }
831}
832
833#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
834/// Entity or workflow subject affected by an incident.
835pub enum IncidentSubject {
836    /// Pet record participating in the workflow.
837    Pet(PetId),
838    /// Reservation record participating in the workflow.
839    Reservation(reservation::Id),
840    /// Customer record participating in the workflow.
841    Customer(CustomerId),
842    /// Resort location record participating in the workflow.
843    Location(LocationId),
844}
845
846#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Builder)]
847/// Customer/internal message record that tracks subject, channel, draft/reference body, approval, and delivery state.
848pub struct Message {
849    /// Source-backed id carried by this normalized pet-resort entity.
850    pub id: MessageId,
851    /// Source-backed subject carried by this normalized pet-resort entity.
852    pub subject: MessageSubject,
853    /// Source-backed direction carried by this normalized pet-resort entity.
854    pub direction: message::Direction,
855    /// Source-backed channel carried by this normalized pet-resort entity.
856    pub channel: message::Channel,
857    /// Source-backed status carried by this normalized pet-resort entity.
858    pub status: message::Status,
859    /// Source-backed body ref carried by this normalized pet-resort entity.
860    pub body_ref: message::BodyRef,
861    /// Source-backed approval gate carried by this normalized pet-resort entity.
862    pub approval_gate: Option<policy::ReviewGate>,
863    #[builder(default)]
864    /// Source-backed audit refs carried by this normalized pet-resort entity.
865    pub audit_refs: Vec<crate::audit::EventId>,
866}
867
868impl Message {
869    /// Reports whether the message is still a draft or awaiting approval before any outbound send.
870    pub fn requires_approval_before_send(&self) -> bool {
871        self.approval_gate.is_some()
872            || matches!(self.status, message::Status::ApprovalRequested)
873            || matches!(self.direction, message::Direction::OutboundDraft)
874    }
875}
876
877#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
878/// Entity or workflow subject that a message refers to.
879pub enum MessageSubject {
880    /// Customer record participating in the workflow.
881    Customer(CustomerId),
882    /// Pet record participating in the workflow.
883    Pet(PetId),
884    /// Reservation record participating in the workflow.
885    Reservation(reservation::Id),
886    /// Incident record participating in the workflow.
887    Incident(IncidentId),
888    /// Approval decision record participating in audit history.
889    Approval(approval::Id),
890}
891
892/// Audit vocabulary for source-backed event trails across automated and staff actions.
893pub mod audit {
894    use chrono::{DateTime, Utc};
895    use nutype::nutype;
896    #[allow(unused_imports)]
897    use serde::{Deserialize, Serialize};
898    use std::collections::BTreeMap;
899
900    use super::{
901        CustomerId, DocumentId, IncidentId, LocationId, MessageId, PetId, VaccineRecordId,
902        approval, care_note, reservation,
903    };
904
905    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
906    /// Audit event capturing actor, subject, action, timestamp, and metadata evidence.
907    pub struct Event {
908        /// Source-backed at carried by this normalized pet-resort entity.
909        pub at: DateTime<Utc>,
910        /// Source-backed actor carried by this normalized pet-resort entity.
911        pub actor: super::ActorRef,
912        /// Source-backed subject carried by this normalized pet-resort entity.
913        pub subject: Subject,
914        /// Source-backed action carried by this normalized pet-resort entity.
915        pub action: Action,
916        /// Source-backed metadata carried by this normalized pet-resort entity.
917        pub metadata: BTreeMap<MetadataKey, MetadataValue>,
918    }
919
920    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
921    /// Subject that a care, document, incident, audit, or message record is about.
922    pub enum Subject {
923        /// Customer record participating in the workflow.
924        Customer(CustomerId),
925        /// Pet record participating in the workflow.
926        Pet(PetId),
927        /// Reservation record participating in the workflow.
928        Reservation(reservation::Id),
929        /// Resort location record participating in the workflow.
930        Location(LocationId),
931        /// Customer or pet document participating in review.
932        Document(DocumentId),
933        /// Vaccination document or status record under review.
934        VaccineRecord(VaccineRecordId),
935        /// Care note state or source category preserved for normalized resort records.
936        CareNote(care_note::Id),
937        /// Incident record participating in the workflow.
938        Incident(IncidentId),
939        /// Customer communication record participating in approval.
940        Message(MessageId),
941        /// Approval decision record participating in audit history.
942        Approval(approval::Id),
943        /// Workflow event state or source category preserved for normalized resort records.
944        WorkflowEvent(crate::workflow::EventId),
945        /// External system object referenced from domain history.
946        External {
947            /// Source-backed provider carried by this normalized pet-resort entity.
948            provider: crate::workflow::external::Provider,
949            /// Source-backed id carried by this normalized pet-resort entity.
950            id: crate::workflow::external::Id,
951        },
952    }
953
954    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
955    /// Auditable action category produced by staff, source ingestion, policy, approval, or automation.
956    pub enum Action {
957        /// Customer profile updated state or source category preserved for normalized resort records.
958        CustomerProfileUpdated,
959        /// Pet profile updated state or source category preserved for normalized resort records.
960        PetProfileUpdated,
961        /// Reservation status suggested state or source category preserved for normalized resort records.
962        ReservationStatusSuggested,
963        /// Reservation status changed state or source category preserved for normalized resort records.
964        ReservationStatusChanged,
965        /// Policy decision recorded state or source category preserved for normalized resort records.
966        PolicyDecisionRecorded,
967        /// Document received state or source category preserved for normalized resort records.
968        DocumentReceived,
969        /// Vaccine record review requested state or source category preserved for normalized resort records.
970        VaccineRecordReviewRequested,
971        /// Incident status changed state or source category preserved for normalized resort records.
972        IncidentStatusChanged,
973        /// Message approval requested state or source category preserved for normalized resort records.
974        MessageApprovalRequested,
975        /// Approval decision recorded state or source category preserved for normalized resort records.
976        ApprovalDecisionRecorded,
977        /// Workflow event recorded state or source category preserved for normalized resort records.
978        WorkflowEventRecorded,
979        /// Extension point for provider-specific values not modeled directly.
980        Extension(ActionLabel),
981    }
982
983    /// Human-readable audit action label for imported or locally defined operational events.
984    #[nutype(
985        sanitize(trim),
986        validate(not_empty, len_char_max = 160),
987        derive(
988            Debug,
989            Clone,
990            PartialEq,
991            Eq,
992            PartialOrd,
993            Ord,
994            Hash,
995            Serialize,
996            Deserialize
997        )
998    )]
999    pub struct ActionLabel(String);
1000
1001    /// Audit metadata key used to preserve source evidence without flattening it into prose.
1002    #[nutype(
1003        sanitize(trim),
1004        validate(not_empty, len_char_max = 80),
1005        derive(
1006            Debug,
1007            Clone,
1008            PartialEq,
1009            Eq,
1010            PartialOrd,
1011            Ord,
1012            Hash,
1013            Serialize,
1014            Deserialize
1015        )
1016    )]
1017    pub struct MetadataKey(String);
1018
1019    /// Audit metadata value attached to an event for review, reporting, or source repair.
1020    #[nutype(
1021        sanitize(trim),
1022        validate(not_empty, len_char_max = 500),
1023        derive(
1024            Debug,
1025            Clone,
1026            PartialEq,
1027            Eq,
1028            PartialOrd,
1029            Ord,
1030            Hash,
1031            Serialize,
1032            Deserialize
1033        )
1034    )]
1035    pub struct MetadataValue(String);
1036}
1037
1038#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1039/// Actor that performed or is accountable for an audited action.
1040pub enum ActorRef {
1041    /// Customer record participating in the workflow.
1042    Customer(CustomerId),
1043    /// Source-backed staff ID carried by this normalized pet-resort entity.
1044    Staff {
1045        /// Staff id carried by this variant.
1046        staff_id: StaffId,
1047    },
1048    /// Source-backed manager ID carried by this normalized pet-resort entity.
1049    Manager {
1050        /// Manager id carried by this variant.
1051        manager_id: ManagerId,
1052    },
1053    /// System state or source category preserved for normalized resort records.
1054    System,
1055    /// Source-backed workflow carried by this normalized pet-resort entity.
1056    Agent {
1057        /// Workflow carried by this variant.
1058        workflow: agent::Name,
1059    },
1060}