# D1 — one measurements store (clinical_observations)

Read `_COMMON.md` first. State: AF2 (commit 7551cca82) just added `kind`/`admission_id` to `EncounterResource` and EncounterService::open — keep them. Migrations already applied on dev must never be edited. Spec: `../design-data-layers.md` §3.3 (schema, set_id, readers, the five tables' fate table) and the D1 row of `../master-plan.md`. Migration range `2026_11_03_140000`–`140099`.

## Do (BE)
1. Migration: `clinical_observations` (id, company_id, patient_id, encounter_id nullable, history_question_id, value_num decimal(12,3) nullable, value_text nullable, observed_at datetime, set_id ulid string(26), source string(12) manual|device|import|portal|carried, recorded_by nullable, timestamps, softDeletes; idx (patient_id, history_question_id, observed_at), (encounter_id), (set_id), (company_id)).
2. Model `ClinicalObservation` (BaseModel/TenantAware) + `ObservationService`: `record(patientId, ?encounterId, array codeValues, source, userId): string setId` (validates question category=vitals or storage=observation; numeric value_num; one set_id per call); `latestFor(encounter|patient, codes)`; `series(patientId, code, ?from, ?to)`; `previousFor(patientId, code, beforeEncounterId)`; `is_danger` from the definition (normal/danger ranges, danger_values).
3. `VitalsService`: writes observations (dual-write: keep the current `clinical_examinations` write + the history-answers mirror behind config flag `clinic.vitals_legacy_write` (default true) for ONE release). Read paths move to `ObservationService::latestFor`.
4. Data migration `2026_11_03_140001_backfill_clinical_observations.php`: copy `patient_history_answers` rows whose question category=vitals (value_num) and `patient_vital_entries` rows into observations (encounter_id from the answer, observed_at = answer updated_at / vital entry recorded_on), one set_id per (patient, encounter|recorded_on). Idempotent (skip if an observation with the same patient/question/observed_at/source='import' exists). Do NOT delete the old rows yet.
5. Prefill: height (and any question with carry_forward=prefill) — `ObservationService::prefillFor(patientId, encounterId)` returns last value + observed_at + `stale` (older than stale_after_days). Expose via the vitals GET for an encounter: each code gets `{value, previous: {value, observed_at}, prefill: {value, observed_at, stale}}`.
6. Trend endpoint `GET /clinic/patients/{patient}/observations/series?code=bp_sys&from=&to=` (permission clinic.vitals.view) — do NOT edit `Modules/Clinic/routes/api.php` (another agent owns it now): put the exact route line in your report and test the controller through the route only after saying so; meanwhile test the service + controller method directly.
7. Readers switched to observations: `QueueController` has_vitals badge, `EncounterResource` vitals summary (read the file — AF2 added kind/admission_id there; keep them), `ClinicDashboardService` if it reads vitals, Nutrition readers that read weight/height (`PatientNutritionMetrics` and any reader of clinical_examinations in Modules/Nutrition — read, and switch only weight/height/bmi/waist/hip reads to `ObservationService`; leave InBody's own table untouched but have its save ALSO write weight/height/bmi/waist/hip observations with the same set_id).
8. Specialty measure definitions: seed (via a data migration, idempotent per company) the extra observation definitions from §3.3: nutrition waist, hip, fat_pct, muscle_kg, visceral_fat, bmr (clinical_type nutrition); gyn fundal_height, fetal_hr (gyn_ob); inpatient rr, on_oxygen, consciousness, pain, intake_ml, output_ml, news2 (is_computed, computed_key news2) — core with show_if `{k:'encounter.kind', in:['inpatient','emergency']}`. Only for companies that already have the core vitals questions.

## Do (FE) — `/home/moonui3/public_html/moon-erp/src/app/features/clinic/encounter/vitals-panel/` only (+ its service + i18n)
- Show «السابق 82» next to each value, a greyed prefilled height with a «تأكيد» action when stale/prefilled, and a small sparkline trend (inline SVG, no new dependency) opening the series dialog. RTL, ar/en keys via small targeted edits (other agents add keys to the same files) — do NOT bump I18N_VERSION. `ng build` must pass.

## Tests
`Modules/Clinic/tests/Feature/D1ObservationsTest.php`: record creates one set; latestFor/previousFor; prefill stale after N days; backfill copies vitals answers; queue has_vitals true from observations; series endpoint; dual-write flag off writes only observations. Run also any existing `*Vitals*Test.php` in Modules/Clinic/tests and keep green.

## Out of scope
HistoryService/HistoryQuestion* (E1e), hospital_vital_rounds (D4), prescriptions, attachments.
