# P2 — inpatient medications on the prescription (MAR on `prescription_items kind=inpatient`), dose billing + stock issue decoupled, discharge Rx

Read `_COMMON.md` first. P2 is **two serial WPs**: **P2a** (BE: schema + migration of the MAR + NursingService on prescription items + discharge Rx) → **P2b** (FE: the admission file orders medicines through the shared prescription component; nursing panel MAR reads the new shape). Read the shared parts plus your own section.

**Depends on:** P1 ✅ **including P1-FE** (P1-FE is 🔄 on `features/clinic/encounter/prescription/**` right now — P2b edits that component, P2a does not), P0 ✅ (`encounters.kind/admission_id`, `MedicationRoute` 13 values, `MedicationFrequency`). P2a can start as soon as P1-BE ✅ (it is). P2b needs P2a ✅ and P1-FE ✅. D2a's `170003` (prescriptions.encounter_id nullable) is NOT needed — every inpatient/discharge Rx lives on the admission encounter.

**Review: [FIN]** — Decision 3 changes money/GL behaviour (uncharged doses now post consumption GL DR consumables_expense / CR inventory; charged-dose folio ref changes). The orchestrator routes P2a through the design review (Fable/advisor) before commit.

Migration range **`2026_11_03_230000`–`230099`** (Hospital module migrations folder for Hospital tables, Clinic folder for Clinic tables — both use this range).

## Spec (read before coding)
- `../design.md` §4.1 — the paragraph «**المستشفى (MAR) ← نفس الموديل**» (split of `hospital_med_orders` into clinical → `prescription_items` and financial/stock → `hospital_med_charging`; `hospital_med_admins.prescription_item_id`; NursingService slots from `prescription_items.times`; stock issue fix; discharge Rx = `kind=discharge`), §4.1 rows «State machine» (inpatient item `active → stopped | completed`), «Permissions» (`clinic.prescriptions.inpatient`), §5 «**المستشفى = سياق مش تخصص**» (the admission file hosts `app-clinic-prescription [kind=inpatient]`), §7 step 4 (MAR → prescriptions migration + one-release dual-write + routes `api.hospital.nursing.orders.*` stay and call Clinic actions), §8 row P2 (owner acceptance).
- `../master-plan.md` row P2 (acceptance: «دوا من نفس كومبوننت الروشتة · جرعة تتحاسب ويتصرف المخزون · روشتة خروج في «الأدوية الحالية»»).
- `../design-data-layers.md` §8 row D2 + `tasks/D2-facts-alerts-header.md` D2a §9 `HomeMedicationService::activeFor` — «الأدوية الحالية» = active items of kinds `home`, `discharge`, recent `outpatient`. P2 only has to make discharge items exist with the right `item_status`/`ends_at`; it does not build the reader.

## Code reality (verified 2026-10-22 — trust this over the docs)
**Hospital** (`/home/moonui3/moon-erp-be/Modules/Hospital`, no Events/Listeners folders, no code references `prescriptions`):
- `hospital_med_orders` (`database/migrations/2026_10_12_100005_create_hospital_nursing_tables.php` L24-46): company_id, admission_id, **drug string NOT NULL (free text)**, dose(80), route(20) default 'oral', frequency(12) default 'bid', times json, prn bool, starts_at NOT NULL, ends_at, status(10) default 'active', product_id, warehouse_id, unit_price decimal(12,3), charge_per_dose bool, notes(500), ordered_by, stopped_by, stopped_at.
- `hospital_med_admins` (L49-62): company_id, order_id, admission_id, scheduled_at, given_at, status(10) default 'given' (given|held|refused|missed), notes(255), service_order_line_id, given_by; **UNIQUE (order_id, scheduled_at) `hosp_mar_slot`**.
- Models `app/Models/MedOrder.php`, `MedAdmin.php`, `Admission.php` (`IN_BED`, `isInBed()` L113, `DISCHARGE_TYPES`).
- `app/Services/NursingService.php`: `FREQ` (L24-28: od, bid, tid, qid, q4h, q6h, q8h, q12h, hs, stat, prn), `ROUTES` (L30, 10 values — ≠ `MedicationRoute`), `order()` L49, `stop()` L70, `mar(Admission, date)` L78 (slots = day + `times[]`, skipped before `starts_at − 59 min` / after `ends_at`; state = admin status | stopped | missed (< now−1h) | due (≤ now+1h) | upcoming; PRN = no slots + `prn_given[]`), `administer(MedOrder, ?slot, status, ?notes, User)` L110 (transaction, `firstOrNew(order_id, scheduled_at)`, `slot_done` 422), `station()` L231 (due/missed counts from `mar(today)`).
- **Billing + stock today (L129-135):** `if ($status === 'given' && $o->charge_per_dose && (float) $o->unit_price > 0) { $line = $this->folio->charge($a, $this->catalog->itemService($companyId, 'medication', $userId), 1, …, ['hospital_med', $o->id], null, 'reception', (float) $o->unit_price); …; if ($o->product_id && $o->warehouse_id) { $this->consumption->issue(...) } }` → **the stock issue is nested inside the charge branch**: an uncharged dose never leaves stock.
- `Folio::charge(Admission, int $serviceId, float $qty, string $descAr, string $descEn, User|int, float $discount = 0.0, ?array $ref = null, ?int $doctorId = null, ?string $role = 'reception', ?float $unitPrice = null): ServiceOrderLine` (`app/Services/Folio.php` L51; line_type ClinicService, `catalog_ref_type/id = $ref`, `encounter_id = $a->encounter_id`).
- `ConsumptionService::issue(Admission $a, array $items, string $noteAr, int $userId): ?InventoryIssue` (L36): items `{product_id, warehouse_id, quantity, unit_id?}` (unit defaults to `products.base_unit_id`), approves the issue, posts DR consumables_expense / CR inventory, 422 `stock_failed` on shortage.
- `app/Http/Controllers/NursingController.php` validation: `order` L43-45 (drug required string, frequency **required**, product_id/warehouse_id/unit_price/charge_per_dose…), `administer` L62.
- Routes (`routes/api.php`, prefix `api/hospital`, names `api.hospital.`): L145 GET `admissions/{admission}/nursing` (chart), L146 POST `admissions/{admission}/med-orders` (`nursing.orders.store`, `permission:hospital.clinical.record|hospital.nursing.record`), L147 POST `med-orders/{order}/stop`, L148 POST `med-orders/{order}/administer` (`$nurse` = `hospital.nursing.record|hospital.clinical.record|hospital.admissions.manage`).
- `AdmissionService::start()` L123-145 opens the encounter with `kind=inpatient|day_case` + `admission_id` (only once admitted; a `requested` admission has no encounter). `orderDischarge()` L178 writes discharge_type/summary/instructions/follow_up_on — **nothing about medicines**. `discharge()` L219 closes the bill/beds then `encounters->markDone()`.
- **Encounter lock:** `Encounter::assertEditable()` throws only for `signed` / `cancelled` (`Models/Encounter.php` L243-261). `markDone()` leaves the encounter `done` → still editable. So a discharge Rx can be issued before or after the billing discharge, until the encounter is signed.
- Settings: `database/seeders/HospitalSettingDefinitionSeeder.php` (tuples `[key, type, default, allowed, labelEn, labelAr, descEn, descAr]`, display_group `clinic_hospital`) + reader `app/Support/HospitalSettings.php` (`for($companyId)`; a new key must be added to its return array). **`hospital.mar_on_prescriptions` does not exist anywhere** (design.md §7 step 4 named it).
- Tests: `tests/Feature/HospitalNursingTest.php` (MAR slots/PRN/charged dose L21, NEWS2 + station L48), `HospitalAdmissionTest.php`, `Af2AdmissionEncounterKindTest.php`.

**Clinic (prescriptions after P1):**
- `prescriptions`: kind(16) default outpatient (`PrescriptionKind`: outpatient|inpatient|discharge|home), subject, dispense_status (`DispenseStatus`: pending|partial|dispensed|not_applicable), **admission_id** (indexed), `encounter_id` NOT NULL, status (`PrescriptionStatus`: issued|printed|dispensed(reserved)|cancelled).
- `prescription_items`: drug_id nullable, clinic_drug_id, drug_name(191), dosage_form_id, form, sentence, dose/frequency/duration (nullable), route (`MedicationRoute` cast), frequency_code (`MedicationFrequency` cast), **times json, prn, starts_at, ends_at, stopped_at, stopped_by, item_status** (`PrescriptionItemStatus` active|stopped|completed), dispensed_qty.
- `Actions/IssuePrescription::handle(Encounter, array): Prescription` — `assertEditable()`, `PrescriptionRules::rules($companyId)` (products mode → `drug_id` required; formulary mode → `clinic_drug_id` required; item = sentence OR dose+frequency+duration), **kind defaults to inpatient when `$encounter->isInpatientKind()`** (L59-61), `admission_id` from the encounter (L88), fires `PrescriptionIssued` after commit (no listeners).
- `Support/PrescriptionItemBuilder` — for inpatient Rx defaults `times` from `frequency_code` (`MedicationFrequency::defaultTimes()`) and `starts_at = now()`.
- `Actions/StopPrescriptionItem::handle(Prescription, PrescriptionItem, ?userId)` — inpatient only, active only, fires `PrescriptionItemStopped` (event **not mapped** in `Clinic/app/Providers/EventServiceProvider.php`; `PrescriptionIssued => []` at L211). Route `POST clinic/prescriptions/{p}/items/{i}/stop` (`clinic.prescriptions.inpatient`) exists.
- `Enums/MedicationFrequency`: once, stat, od, bd, tds, qds, q6h, q8h, q12h, qhs, prn, weekly, monthly — **no `q4h`**. `defaultTimes()` matches NursingService::FREQ for the shared codes. `Enums/MedicationRoute`: 13 values incl. sublingual/ng/other (superset of NursingService::ROUTES).
- FE `features/clinic/encounter/prescription/prescription.component.ts` inputs today: `encounterId`, `locked`, `patientId` — **no `kind`** input (design.md §4.1 FE row assumed one).
- FE `features/hospital/nursing/nursing-panel.component.ts`: FREQS/ROUTES mirrored at L11-12; the order form (`ord` signal L43, `saveOrder()` L90-97) sends `{drug, dose, route, frequency, days, charge_per_dose, unit_price}` — **never product_id / warehouse_id / times**, so today no dose from the UI ever issues stock. Admission file `features/hospital/admissions/admission-file.component.*` (tabs `bill|nursing|rs|surgery|stays|money|log|discharge`, nursing tab html L115 `<app-nursing-panel [admissionId] [inBed]>`, discharge-order dialog html L267-279 / `doDischargeOrder()` ts L209).

## Decisions already made (do not re-litigate)
1. **Clinical truth = `prescription_items` under one `prescriptions kind=inpatient` per admission** (on `hospital_admissions.encounter_id`). New orders from the admission file are new items on that open inpatient prescription when one exists (append), else a new inpatient prescription (`IssuePrescription` with `kind=inpatient`). Appending needs a small Clinic action `AppendPrescriptionItems` (validated by the SAME `PrescriptionRules` item rules, `assertEditable()`, only kind inpatient, fires `PrescriptionUpdated`) — `UpdatePrescription` replaces items and must not be used for this.
2. **Financial/stock = `hospital_med_charging`** (Hospital module), one row per inpatient prescription item: `prescription_item_id` unique, `company_id`, `admission_id`, `product_id` nullable, `warehouse_id` nullable, `unit_price` decimal(12,3) nullable, `charge_per_dose` bool default false, `service_id` nullable (override of the 'medication' item service), timestamps. Not on `hospital_med_admins` (per order, not per dose).
3. **Decoupling (the real bug):** on a `given` dose — **charge** iff `charge_per_dose && unit_price > 0`; **issue stock** iff `product_id && warehouse_id` — independently. design.md calls this «decoupled from collection»; the code gate is actually `charge_per_dose` (see Code reality). Product default = `prescription_items.drug_id` (products mode) or `clinic_drugs.product_id` (formulary item linked to a product); warehouse default = new setting `hospital.pharmacy_warehouse_id` (integer, nullable; read via `HospitalSettings::for()` with null default). No product or no warehouse ⇒ no stock movement, never an error.
4. **Doses stay in `hospital_med_admins`** + new `prescription_item_id` nullable (indexed) + new unique `(prescription_item_id, scheduled_at)` named `hosp_mar_item_slot`. `order_id` becomes nullable (new doses on the new path have no `hospital_med_orders` row once the flag is on). Keep `hosp_mar_slot` (NULL `order_id` rows don't collide in MySQL).
5. **One-release dual path behind `hospital.mar_on_prescriptions`** (boolean, default **true** for this release — the owner tests the new path; `false` = legacy free-text MedOrder path unchanged). With the flag on: `POST admissions/{a}/med-orders` writes prescription items (+charging) and ALSO a shadow `hospital_med_orders` row (with `prescription_item_id` set) so old readers/reports keep working one release; `administer` writes admins with both `prescription_item_id` and the shadow `order_id`. Report the setting definition row; you own the reader edit in `HospitalSettings.php`.
6. **Vocabulary:** add `case Q4h = 'q4h'` to `Clinic\Enums\MedicationFrequency` (`timesPerDay` 6, `defaultTimes` `['02:00','06:00','10:00','14:00','18:00','22:00']`, label ar/en in `Modules/Clinic/lang/{ar,en}/clinic.php` where the other frequency labels live). Legacy Hospital codes map **only** in the migration and the FE mirror: `bid→bd`, `tid→tds`, `qid→qds`, `hs→qhs`, others identical. `NursingService::FREQ`/`ROUTES` stop being the source — read `MedicationFrequency::defaultTimes()` / `MedicationRoute::values()` (keep the constants as `@deprecated` aliases one release).
7. **Catalog drug becomes mandatory for NEW inpatient orders** (PrescriptionRules — products mode `drug_id`, formulary mode `clinic_drug_id`). This is the owner's acceptance («كاتالوج حقيقي بدل نص»). Migrated legacy rows keep `drug_id=null` + `drug_name=drug` (the migration writes rows directly, bypassing the rules).
8. **Discharge Rx = `prescriptions kind=discharge`** on the admission encounter, issued from the discharge-order flow with the same component (`[kind]="'discharge'"`), `dispense_status=pending` (the patient may buy it at the POS pharmacy — P6), items `item_status=active`, `starts_at = discharge_ordered_at ?? now`, `ends_at` = starts_at + duration days when the duration is a number of days (parse `duration` int or leave null). Discharge does **not** stop it. At `AdmissionService::discharge()` every still-**active inpatient** item of that admission is set `completed` (`stopped_at` = now, `stopped_by` = user) so the MAR closes and «current meds» shows only the discharge Rx.
9. **Slots** come from `prescription_items.times` (+ `prn`, `starts_at`, `ends_at`, `item_status`); state rules unchanged (copy the existing `mar()` logic exactly). `item_status != active` ⇒ future slots `stopped`.
10. Stop from the MAR = `StopPrescriptionItem` (Clinic). Hospital `POST med-orders/{order}/stop` stays: with the flag on it resolves the shadow order → item and calls the Clinic action.
11. **Billing ref** of a charged dose = `['prescription_item', $item->id]` (new rows); legacy rows keep `['hospital_med', $order->id]`. Do not rename old refs.

## Shared-file rules
- **Orchestrator-owned — REPORT, never edit:** `Modules/Hospital/routes/api.php`, `Modules/Clinic/routes/api.php`, `Modules/Core/database/seeders/RolePermissionSeeder.php`, `StandardRolesSeeder.php`, `Modules/Hospital/database/seeders/HospitalSettingDefinitionSeeder.php`, `Modules/Clinic/database/seeders/ClinicSettingDefinitionSeeder.php`, `config/moonstack.php`, `docs/moonstack/CHANGELOG.md` (give 2 lines EN + AR).
- **Hot files you must NOT edit:** `Modules/Clinic/app/Actions/IssuePrescription.php`, `UpdatePrescription.php`, `Support/PrescriptionRules.php`, `Support/PrescriptionItemBuilder.php` (P1 — call them, don't change them; if a rule blocks you, STOP and report), `Modules/Clinic/app/Models/Encounter.php`, `HistoryService.php`, `Services/ObservationService.php`, `QueueController.php` (D1/D4), `visit-shell/**`, `consultation.component.*` (S1), `features/clinic/encounter/vitals-panel/**`.
- **P2 owns while it runs:** everything under `Modules/Hospital/app/**` it needs (NursingService, Folio only if unavoidable, AdmissionService discharge hooks, NursingController, MedOrder/MedAdmin models, new MedCharging model, HospitalSettings), `Modules/Clinic/app/Enums/MedicationFrequency.php` (Q4h only), new Clinic action `AppendPrescriptionItems.php`, `Modules/Clinic/app/Providers/EventServiceProvider.php` (add mappings only, if you add a listener). P2b: `features/hospital/**`, `features/clinic/encounter/prescription/prescription.component.*` (only the `kind` input + kind-dependent bits, see P2b).
- FE i18n: targeted key additions under `HOSPITAL.MAR.*` / `HOSPITAL.NURSE.*` / `CLINIC_RX_V2.KIND.*`; do NOT bump `I18N_VERSION`.

---

## P2a — BE (migrations `230000`–`230049`)

### Do
1. **`Modules/Hospital/database/migrations/2026_11_03_230000_create_hospital_med_charging_table.php`** — table per Decision 2 (guarded `hasTable`), idx (company_id, admission_id), unique `prescription_item_id` (`hosp_med_charging_item_uq`).
2. **`…230001_add_prescription_item_id_to_hospital_med_tables.php`** — `hospital_med_orders.prescription_item_id` nullable indexed (the bridge column) · `hospital_med_admins.prescription_item_id` nullable indexed + unique `(prescription_item_id, scheduled_at)` `hosp_mar_item_slot` · `hospital_med_admins.order_id` → nullable (`->change()`, guarded). All `hasColumn`-guarded.
3. **`…230002_migrate_hospital_med_orders_to_prescriptions.php`** (data, idempotent, chunked, per company): for each admission that has `hospital_med_orders` rows AND a non-null `encounter_id` (skip others, log the count): find-or-create ONE `prescriptions` row `kind=inpatient`, `admission_id`, `encounter_id`, `patient_id`/`doctor_id`/`branch_id` from the encounter, `status=issued`, `dispense_status=not_applicable` (hospital floor stock, not a pharmacy sale), `issued_at` = min(starts_at), `prescription_number` = `SequenceService::generateNext(companyId, module 'clinic', entity 'prescription', branchId)` (same call `IssuePrescription` makes — numbers stay unique; if SequenceService is unusable inside a migration, use `'MAR-'.$admissionId` and say so). Per order without `prescription_item_id`: insert `prescription_items` (`drug_name`=drug, `drug_id`=product_id, `dose`, `route` (map unknown → 'other'), `frequency_code` (legacy map, Decision 6), `frequency`=the human label, `times`, `prn`, `starts_at`, `ends_at`, `item_status` = active|stopped (stopped → also `stopped_at/by`), `sentence` = `trim(drug.' '.dose)` so the item satisfies «sentence OR structured»), set `hospital_med_orders.prescription_item_id`, insert `hospital_med_charging` (product_id, warehouse_id, unit_price, charge_per_dose). Then `UPDATE hospital_med_admins a JOIN hospital_med_orders o ON o.id=a.order_id SET a.prescription_item_id=o.prescription_item_id WHERE a.prescription_item_id IS NULL` (write it portable for SQLite tests: subquery update). Re-running creates nothing. `down()`: no-op.
4. **Models:** `Hospital/app/Models/MedCharging.php` (table `hospital_med_charging`, TenantAware like neighbours, `item()` belongsTo `Clinic\Models\PrescriptionItem`); `MedAdmin` + `prescription_item_id` fillable + `item()`; `MedOrder` + `prescription_item_id` fillable.
5. **Clinic action `Modules/Clinic/app/Actions/AppendPrescriptionItems.php`**: `handle(Prescription $rx, array $items, ?int $userId): Collection` — `$rx->encounter->assertEditable()`, kind must be inpatient and not cancelled (422 `clinic::rx.errors.append_not_inpatient` — add the lang key ar/en), validate each item with `Validator::make(['items' => $items], Arr::only(PrescriptionRules::rules($companyId), [every 'items' / 'items.*' key]), PrescriptionRules::messages())` (read the rules array to pick the exact keys), create with `PrescriptionItemBuilder::createAll`-equivalent per item (read the builder; if `createAll` only takes a Prescription + array and creates all, call it with the new items only), dispatch `PrescriptionUpdated` after commit.
6. **`MedicationFrequency`**: add `Q4h` (Decision 6) — keep every existing case/value unchanged.
7. **`NursingService`** (read it whole first):
   - `order(Admission, array $d, User)`: flag on ⇒ `inBed`; resolve the admission encounter; find the open inpatient Rx of the admission (`kind=inpatient`, not cancelled, `admission_id`) → `AppendPrescriptionItems`, else `IssuePrescription::handle($encounter, ['kind'=>'inpatient','items'=>[...]])`; the item payload is the Clinic item shape (`drug_id`|`clinic_drug_id`, `sentence` or dose/frequency_code/duration, `route`, `times`, `prn`, `starts_at`, `ends_at`, `instructions`=notes; `stat` ⇒ times=[now H:i], ends_at=endOfDay as today). Note `PrescriptionRules` L51-54: «structured» means dose **+ frequency + duration**; an inpatient order usually has dose + frequency_code and no duration — when the request has no `sentence` and no `duration`, build `sentence = trim("$dose ".frequencyLabel." ".routeLabel)` so the item validates (never send `'—'`). `IssuePrescription.php:87` always writes `dispense_status=pending` (hot file) — right after issuing an inpatient Rx set it to `not_applicable` (floor stock is not a pharmacy sale; P6's worklist also filters by kind). Then upsert `hospital_med_charging` (product/warehouse defaults per Decision 3; `unit_price`, `charge_per_dose` from the request) and write the shadow `hospital_med_orders` row (drug = item `drug_name`, `prescription_item_id`). Return a unified array/resource (see 9). Flag off ⇒ the current code path untouched.
   - `mar(Admission, date)`: flag on ⇒ build from the admission's inpatient `prescription_items` (+ charging + admins by `prescription_item_id`); the per-order JSON keeps the **same keys the FE reads today** (`id`, `drug`, `dose`, `route`, `frequency`, `times`, `prn`, `status`, `slots[]`, `prn_given[]`, `charge_per_dose`, `unit_price` — read the current return shape and the FE html first) plus `prescription_item_id`, `prescription_id`, `drug_id`, `clinic_drug_id`, `sentence`, `item_status`, `product_id`, `warehouse_id`. `id` = the shadow order id when it exists (so old FE calls keep working), else null.
   - `administer(...)`: accept either a `MedOrder` (legacy) or a `PrescriptionItem` (new) — add `administerItem(PrescriptionItem $item, ?string $slot, string $status, ?string $notes, User $user): MedAdmin`; the legacy method resolves its order's `prescription_item_id` and delegates when the flag is on. Same rules (in bed, active, slot required unless prn, `slot_done`). Then **Decision 3** with two independent `if`s; the charge description = `drug_name + dose` like today; `Folio::charge(..., ref: ['prescription_item', $item->id], unitPrice: $charging->unit_price)`; stock `ConsumptionService::issue($a, [['product_id'=>…, 'warehouse_id'=>…, 'quantity'=>1]], 'جرعة '.$drugName, $userId)`. A `stock_failed` 422 must roll the dose back (keep it in the same transaction — today the issue happens inside `administer`'s transaction; keep that).
   - `stop(...)`: flag on ⇒ `StopPrescriptionItem` (then mirror `status=stopped` on the shadow order).
   - `station()`: due/missed counts from the new `mar()`; nothing else changes.
8. **`NursingController`**: `order` validation adds `drug_id`, `clinic_drug_id`, `sentence`, `frequency_code` (in `MedicationFrequency::values()`), `route` in `MedicationRoute::values()`, `duration`, `instructions`; when the flag is on `drug` becomes optional (the drug name snapshot comes from the catalog) and `frequency` optional (frequency_code or sentence). New method `administerItem(Request, int $item)` + `stopItem(Request, int $item)` resolving the item through `company_id` and its prescription's `admission_id` (404 for another company / an item that is not inpatient).
9. **Discharge:** `AdmissionService::discharge()` — after the bill checks, before `markDone`: complete every active inpatient item of the admission (Decision 8). `AdmissionService::orderDischarge()` unchanged (the FE issues the discharge Rx through the Clinic endpoint `POST clinic/encounters/{e}/prescriptions` with `kind=discharge`; verify `PrescriptionRules` accepts `kind=discharge` — it lists the enum — and that `IssuePrescription` keeps an explicit kind). `AdmissionReader` (read it): expose `discharge_prescriptions` (ids + numbers + item count) on the admission payload so the discharge tab can show/print them.
10. **Settings (REPORT the definition rows, read with code defaults):** `hospital.mar_on_prescriptions` (boolean, default true, «MAR on the prescription» / «أدوية الإقامة على الروشتة»), `hospital.pharmacy_warehouse_id` (integer nullable, «Ward stock warehouse for doses» / «مخزن صرف الجرعات»). Add both to `HospitalSettings::for()`.
11. **Routes to REPORT** (Hospital file, next to L146-148, same `$nurse` middleware, names): `POST prescription-items/{item}/administer` → `NursingController@administerItem` (`nursing.items.administer`), `POST prescription-items/{item}/stop` → `@stopItem` (`nursing.items.stop`, `permission:hospital.clinical.record|hospital.nursing.record`). No Clinic route changes. Permissions: none new (`clinic.prescriptions.inpatient` exists); REPORT that nurses issuing inpatient orders via the Hospital endpoint need nothing in Clinic because the Hospital route permission gates them.
12. **Lang:** `Modules/Hospital/lang/{ar,en}/*.php` — find the file holding `not_in_bed`/`slot_done`; add `item_not_inpatient`, `no_admission_encounter`.

### Tests — `Modules/Hospital/tests/Feature/P2InpatientMedsTest.php` (prefix helpers `p2_`)
Order from the Hospital endpoint creates ONE inpatient Rx on the admission encounter and appends the second order to it; products mode without `drug_id` → 422; MAR slots come from `prescription_items.times` (bd → 09:00/21:00) and `q4h` gives 6 slots; given dose with `charge_per_dose=false` + product + warehouse → stock issued, **no** folio line (the bug); with `charge_per_dose=true` + `unit_price` → folio line with ref `prescription_item` AND stock; no warehouse → no issue, no error; double administer same slot → `slot_done`; PRN dose without slot ok; stop via Hospital route stops the item (and the shadow order); discharge completes active inpatient items but leaves a `kind=discharge` Rx active; migration `230002` on a seeded legacy order + 2 admins creates the Rx/item/charging and links both admins, second run creates nothing, an admission without encounter is skipped; flag off → legacy path writes only `hospital_med_orders`. Run also `HospitalNursingTest.php`, `HospitalAdmissionTest.php`, `Af2AdmissionEncounterKindTest.php`, `Modules/Clinic/tests/Feature/P1PrescriptionV2Test.php`, `IssuePrescriptionTest.php` — keep green (if an old Hospital test asserts the old charge-gated stock behaviour, update it to Decision 3 and say so).

### Out of scope (P2a)
POS dispensing (P6), «current meds» reader (D2a), NEWS2/vitals (D4), the shell (S3), dropping `hospital_med_orders` (next release), changing `hospital_nursing_notes`.

---

## P2b — FE (no migrations)

### Do
1. **`app-clinic-prescription`** (`features/clinic/encounter/prescription/prescription.component.ts/.html`, re-read it — P1-FE just rewrote it): add `@Input() kind: 'outpatient' | 'inpatient' | 'discharge' = 'outpatient'` sent in the create payload (`kind`), and for `inpatient`: show per-row «الأوقات» chips (prefilled from `frequency_code` defaults — fetch nothing new; mirror `MedicationFrequency::defaultTimes()` in one constant next to the component's existing frequency list), a PRN toggle, start date/time, and the stop action per item (`POST clinic/prescriptions/{p}/items/{i}/stop` — the service may already have it from P1-FE; add if missing); hide the spouse/subject line for `inpatient` and `discharge`. Keep the edit additive — no restyle of the outpatient form.
2. **Admission file**: the nursing tab's «أمر دواء جديد» opens a dialog hosting `<app-clinic-prescription [encounterId]="a.encounter_id" [patientId]="a.patient_id" [kind]="'inpatient'">` instead of the free-text order form when the admission payload says `mar_on_prescriptions` (expose the setting via the existing `settings` meta — `LocationController` meta already returns `HospitalSettings::for()`); after save, the panel reloads the MAR. Per item a small «تسعير/مخزن» popover (charge per dose, unit price, warehouse — editable by `hospital.billing.manage|hospital.admissions.manage` only) writing through a new endpoint you REPORT if you need one (preferred: include those three fields in the Hospital `order` request — P2a §8 already accepts them; for edits after creation report `PATCH prescription-items/{item}/charging` and implement it in P2a's controller if you are the same agent, else list it as not done).
3. **Nursing panel MAR** (`features/hospital/nursing/nursing-panel.component.*`): read the new MAR shape (same keys + `prescription_item_id`, `sentence`, `item_status`); when `prescription_item_id` is set, give/stop call the new `prescription-items/{item}/administer|stop` endpoints (`hospital.service.ts` L177-189 — add `administerItem`, `stopItem`); show `sentence` under the drug name; FREQS/ROUTES mirrors → read `MedicationFrequency`/`MedicationRoute` value lists (bd/tds/qds/qhs/q4h…) — keep the legacy codes only for displaying migrated rows.
4. **Discharge**: in the discharge-order dialog (admission-file html L267-279) add a «روشتة الخروج» section hosting `<app-clinic-prescription [encounterId] [patientId] [kind]="'discharge'">`; the discharge tab lists the discharge prescriptions with a print button (P1's `GET clinic/prescriptions/{p}/pdf`).
5. **Fable consult (mandatory, ONE):** screenshot the current nursing tab (MAR + order form) and the discharge dialog, send a Fable agent (`model: "fable"`) one focused question: layout of the inpatient Rx row (times chips + PRN + start) inside the admission file, and where the charging popover and the discharge-Rx section sit. Apply; don't iterate.
6. i18n targeted keys; `npx ng build --base-href /app/` green; no deploy.

### Out of scope (P2b)
The visit-shell inpatient context (S3), POS, the nursing station screen beyond the counts it already reads.

## Report (both WPs)
Files, test output (pass counts), route lines (verbatim, with names/middleware), setting definition rows, the CHANGELOG 2 lines (EN + AR), legacy rows skipped by the migration (count + reason), anything not done, any hot file you needed but did not touch.
