Functional contract
Patients and patient parents
Staff register an animal (the patient) with one or more patient parents (client contacts). Charts are Organization-scoped and visible from every Location. Required fields use in-form checks instead of the browser’s native validation bubble.
Records
| Model | What it is | Scope |
|---|---|---|
Patient | The animal | Organization |
Client | Patient parent contact (not a User) | Organization |
PatientParent | Join of a patient to a parent | Patient |
Breed | Shared dog / cat / other catalog | Platform |
PatientTag | Handling / temperament tag | Platform defaults + Organization custom |
PatientNote | Dated chart note with author | Patient |
PatientVaccine | Vaccine + certificate file | Patient |
Veterinarian | Patient’s vet (staff or external): name, business, phone, email | Patient |
MedicalCondition | Condition catalog | Platform |
PatientMedicalCondition | Reported condition | Patient |
One primary client per patient (the first parent) plus optional additional parents. A client may have many patients. Admission searches existing parents by name, email, or phone, or creates new ones in the same form.
Patient — required: name, species. Dog / Cat / Other shortcuts on admission. Optional: profile photo (S3; tap the tile to choose a file, or use the Take photo menu: browse / camera on this device, or a single-use QR code (15 min) to upload from any phone without signing in; the photo appears live and is attached on save — see Architecture → Hands-off uploads), breed, hair type/color, DOB (or age in years → estimated DOB), sex, spayed (female only), neutered (male only), weight (kg stored; metric/imperial display), tags, notes, vaccines, veterinarian, and medical conditions. Sterilization checkboxes stay hidden until sex is chosen. Soft-delete via disabled_at; deceased via deceased_at. Default directory and booking pickers are enabled and living (Patient.bookable).
Client — required: first name, last name, and a phone number or email (one of the two). Optional remaining contact fields, address, geocoded when possible, email/SMS/SMS-marketing notification toggles (on by default; off is opt-out), and disabled_at (stays on charts, omitted from the existing-parent picker).
Workspace
Patients live at /app/patients. Super admins manage breeds at /admin/breeds.
| Route | Who | Behavior |
|---|---|---|
GET /app/patients | patients.read | Table: avatar, name + badges, species / breed, age + sex, parent + contact, attention. Status filters with counts (enabled living by default) |
GET/POST /app/patients/new | patients.manage | Admission form with chart-matching sub-menu sections (one form, client-side panels) |
GET /app/patients/:id | patients.read | Identity card (avatar, badges, vitals, alerts) + chart tabs (?tab=): Overview, Vaccines, Medical conditions, Vets, Notes — see SOAP notes |
GET/PATCH /app/patients/:id/edit | patients.manage | Same tabbed sections as admission |
Destroy is not offered. Disabled and deceased are timestamp flags. Cross-organization ids return 404.
Reading order
The chart is read in one pass. The identity card answers who is this animal (avatar, name, status badges, species). The vitals strip answers what is stable (age, sex, weight, primary parent with contact). The alert strip answers what needs attention now and only appears when there is something: expired vaccines (danger), vaccines expiring within 60 days (caution), caution tags, and recorded medical conditions. The list repeats those alerts in its attention column.
Colour carries meaning, never decoration: danger for expired, caution for due soon, success for verified or opted in, brand for identity, neutral for plain facts. This section defines the shared layout for the rest of the platform — see Architecture → Section design.
RBAC
Org admin, location admin, receptionist, and veterinarian may read and manage. Organization manager may read only.
Out of scope
Voice-to-text, messaging, SMS, magic-link patient-parent access, drafts, hard-deleting patients, customer portal.
Full acceptance: docs/functional/patients.md.