> **Orchestrator update (2026-10-22):** `khaled.alshamaa/ar-php` is INSTALLED (commit 4ecd61d1f). Use `ArPHP\I18N\Arabic::utf8Glyphs()` in the shared print engine to shape Arabic before DomPDF renders (shape text nodes in the rendered HTML — mixed AR/EN lines must keep Latin/number runs intact; test with a mixed sentence). Also move the two existing clinical PDFs onto the engine in this WP: `Modules/Clinic/app/Services/PrescriptionPdfService.php` (P1) and `RadReportPdfService.php` (P3) — keep their public methods/signatures. The LIS LabReportPdfService stays as is (separate WP). Routes and permissions: REPORT them (orchestrator-owned).

# P5 — clinical documents (sick leave · medical report · certificate · discharge summary · UDS report) + the one print engine

Read `_COMMON.md` first. Spec: `../design.md` §4.4 (documents), §4.6 (print engine), §4.7, §7 step 9; `../design-gyn-integration.md` §2 rows `urodynamic_studies` / Summary generator and the P5 row («+ doc_type=uds_report|summary»); master-plan P5 row («إجازة مرضية برقم تتطبع من لوحة الخروج»). Migration range `2026_11_03_200000`–`200099`. Depends on: P0 ✅ (documents without encounter), P3 (creates `clinical_templates` — read `tasks/P3-executor-radiology.md` Do §2 and P3's migration in the 160000 range; if it has not landed, STOP and report). Parallel with SEC2: name your routes exactly as in Do §5 so SEC2's audit map covers them.

## ⛔ Precondition — Arabic shaping (read before anything else)
Both existing BE PDFs (`Modules/LIS/app/Services/LabReportPdfService.php` header comment, and P1's `Modules/Clinic/app/Services/PrescriptionPdfService.php`) state **DomPDF cannot shape/join Arabic**. A sick leave or medical report is an Arabic-first legal paper; unjoined glyphs are unacceptable. Check `composer.json` / `vendor/` for `khaled.alshamaa/ar-php` or `mpdf/mpdf`:
- If ar-php is installed → the engine pre-shapes every Arabic run with `\ArPHP\I18N\Arabic::utf8Glyphs()` inside ONE helper (`ClinicalPdfService::shape()`), applied to values, never to HTML markup.
- If mPDF is installed → use it as the renderer behind the same `ClinicalPdfService` interface.
- If neither → build everything below, but the engine's default output is the **HTML print view** (same Blade, served as `text/html` for the browser to print — the browser shapes Arabic) and `?format=pdf` returns DomPDF output labelled experimental. Report «needs composer require khaled.alshamaa/ar-php (orchestrator)» as the first line of your report. Do NOT run composer.
Setting `clinic.print.engine` = `browser` | `pdf` (default `browser`) decides what the FE opens — report its definition (orchestrator owns `ClinicSettingDefinitionSeeder`).

## Code reality
- `clinical_documents` (`2026_06_22_160000` + P0 `100003`): `encounter_id` nullable, unique `(company_id, ref_key)`, `doc_type` string(30), `title`, `body` longText, `data` json, `status` string(20) default `released`, `released_at`. Lab/rad report rows are written by `Modules\Clinic\Support\ClinicalDocumentWriter` (AF2) — do not change that writer's behaviour. Model `ClinicalDocument` fillable lacks every new column.
- Numbers: `Modules\Core\Services\SequenceService::generateUnique($companyId, 'clinic', $entity, 'clinical_documents', 'document_number', scopeToCompany: true)`. Missing sequences auto-create (`createDefaultSequence`, prefix from `defaultPrefix()` → `sick_leave`=`SL`, `medical_report`=`MR`, `certificate`=`CER`, `discharge_summary`=`DS`, `uds_report`=`UR`), yearly reset, 5 digits → `SL-2026-00001`.
- FE prints today (browser, `features/clinic/services/`): `clinic-print-shell.service.ts` (shell), `clinic-visit-summary-print.service.ts`, `clinic-prescription.service.ts` (print part), `clinic-order-print.service.ts` (lab/rad slips), `encounter/referral/referral-dialog.component.ts` (referral letter), `clinic-receipt-print.service.ts` (financial — out of scope). Finish panel = `features/clinic/encounter/consultation.component.html` lines ~677–734 («Discharge panel (م10 WP7)»: visit summary / discharge Rx / lab slip / rad slip / referral buttons).
- Three BE PDF paths are being built concurrently (P1 prescription PDF — done/landing, P3 rad report PDF, this WP). This WP owns the shared engine; re-pointing P1/P3 services onto it is a follow-up (they are other agents' files).

## Decisions already made (do not re-litigate)
- One entity: `clinical_documents` with new doc_types `sick_leave`, `medical_report`, `certificate`, `discharge_summary`, `uds_report` (+ `rx_print` reserved). Template kinds in `clinical_templates.kind`: `sick_leave`, `medical_report`, `certificate`, `discharge` (↔ doc_type `discharge_summary`), `uds_report` (add if P3's column is constrained).
- Lifecycle: `draft` → `issued` (number + `signed_at/by` assigned at issue, body snapshot frozen) → `voided` (reason; number kept, never reused). Drafts have no number and print with a «مسودة / DRAFT» watermark. Issued documents are immutable (edit = void + new).
- **Documents are NOT blocked by `Encounter::assertEditable()`** — a sick leave / report is legitimately issued after the visit is signed. Only a **cancelled** encounter refuses (422). Documents may also exist without an encounter (`POST patients/{p}/documents`).
- Template bodies are **user-authored text**: render with a strict allow-listed placeholder replacer (`{{patient.name}}`, `{{patient.age}}`, `{{patient.gender}}`, `{{patient.mrn}}`, `{{patient.national_id_masked}}` (last 4), `{{encounter.date}}`, `{{doctor.name}}`, `{{doctor.specialty}}`, `{{diagnoses}}` (primary + ICD), `{{from_date}}`, `{{to_date}}`, `{{days}}`, `{{today}}`, `{{fields.<key>}}`), values HTML-escaped, newlines → `<br>`. **Never** `Blade::render()` / `eval` a template body. Unknown tokens stay literal.
- Sick leave: `from_date ≤ to_date`, `days = diff+1`, max `clinic.documents.sick_leave_max_days` (default 30 — report definition), from_date not more than 7 days in the past unless `clinic.documents.backdate` permission.

## Do (BE)
1. Migration `2026_11_03_200000_extend_clinical_documents_for_authored_docs.php` (guarded): `document_number` string(30) nullable + unique `(company_id, document_number)` (name `clinical_docs_co_number_uq`), `template_id` nullable (no FK if `clinical_templates` missing), `language` string(2) default `ar`, `from_date`/`to_date` date nullable, `days` unsignedSmallInteger nullable, `signed_at` timestamp nullable, `signed_by` nullable, `voided_at`/`voided_by`/`void_reason` string(255) nullable, `doctor_id` nullable (lab_doctors), idx `(company_id, patient_id, doc_type)`. Do not touch the existing ref_key unique.
2. If P3's `clinical_templates.kind` is an enum/check: guarded migration `200001` widening it to include `uds_report`. Data migration `200002_seed_default_clinical_templates.php`: per company that has clinic data (mirror how other clinic data migrations pick companies), `updateOrCreate(company_id, kind, scope_ref_type NULL, name_en)` one default AR+EN template for sick_leave, medical_report, certificate, discharge, uds_report (fields json for uds_report = empty — G6 fills). Never overwrite a template the clinic edited (create-only on existing rows).
3. `ClinicalDocument` model: fillable + casts for the new columns, `DocType` enum (`Modules/Clinic/app/Enums/ClinicalDocType.php`, keep `lab_report|rad_report|note` values), relations template/doctor/signer; scope `authored()` (= the five types). Keep it on BaseModel (edits are logged).
4. Actions (`Modules/Clinic/app/Actions/Documents/`): `CreateClinicalDocument` (draft; from encounter or patient; prefill per type — sick_leave: diagnoses of the encounter; discharge_summary on an `encounters.kind=inpatient` encounter: admission dates/diagnosis/`admissions.discharge_summary` text + the encounter's `prescriptions.kind=discharge` items; medical_report: diagnoses + latest vitals via `ObservationService::latestFor` if D1 landed; uds_report: answers of `module_key='uds'` if any), `UpdateClinicalDocument` (draft only, 422 otherwise), `IssueClinicalDocument` (number via SequenceService inside the transaction, snapshot rendered body into `body`, field values into `data`, `status=issued`, `signed_*`, `released_at`; event `ClinicalDocumentIssued`), `VoidClinicalDocument` (`ClinicalDocumentVoided`). `Support/Documents/TemplateRenderer.php` (the allow-list replacer + unit tests).
5. Endpoints (new `ClinicalDocumentController`, `ClinicalTemplateController`, `ClinicalPrintController`) — report these exact lines for `Modules/Clinic/routes/api.php`, with route names:
   - `GET encounters/{encounter}/documents` (`clinic.documents.view`, `documents.index`), `POST encounters/{encounter}/documents` (`clinic.documents.create`, `documents.store`), `GET patients/{patient}/documents` (`clinic.documents.view`, `patients.documents.index`), `POST patients/{patient}/documents` (`clinic.documents.create`, `patients.documents.store`), `GET documents/{document}` (`documents.show`), `PUT documents/{document}` (`clinic.documents.create`, `documents.update`), `POST documents/{document}/issue` (`clinic.documents.issue`, `documents.issue`), `POST documents/{document}/void` (`clinic.documents.void`, `documents.void`), `GET documents/{document}/pdf` (`clinic.documents.print`, `documents.pdf`).
   - Templates CRUD: `GET/POST clinical-templates`, `PUT/DELETE clinical-templates/{template}` (`clinic.templates.manage`; GET also `clinic.documents.create`), filter `?kind=`.
   - Generic print: `GET print/{kind}/{id}` (`print.show`) with an internal kind→permission map: `document` → clinic.documents.print, `visit_summary` → clinic.encounters.view, `referral` → clinic.referrals.view, `lab_slip` → clinic.lab-orders.view, `rad_slip` → clinic.rad-orders.view. `?format=pdf|html&lang=ar|en`.
   Every handler is company-scoped and returns 404 for another company's id.
6. Print engine `Modules/Clinic/app/Services/Print/ClinicalPdfService.php`: `render(string $view, array $data, string $lang, ?int $doctorId): Response` (pdf or html per precondition), shared layout `Modules/Clinic/resources/views/print/layout.blade.php` (RTL/LTR by lang; header/footer text, logo, paper size from the existing `clinic.print.*` settings — read `PrescriptionPdfService` for the keys it uses; doctor signature image if the doctor has one — read how LIS stores `lab_doctors` signature (`LabDoctorController::uploadSignature`) and reuse it; QR/number line for issued docs; DRAFT watermark). Views `print/document.blade.php`, `print/visit-summary.blade.php`, `print/referral.blade.php`, `print/order-slip.blade.php` — content mirrors the FE print services listed above field-for-field (read each FE service and copy its sections).
7. Lang `Modules/Clinic/lang/{ar,en}/documents.php` (labels, errors, doc type names).
8. Permissions (report — do NOT edit the seeder): `clinic.documents.view`, `.create`, `.issue`, `.void`, `.print`, `clinic.documents.backdate`, `clinic.templates.manage`; presets: doctor = view/create/issue/print; reception = view/print; admin = templates.manage. SequenceService `ARABIC_ENTITY_NAMES` entries to report: `sick_leave` رقم الإجازة المرضية, `medical_report` رقم التقرير الطبي, `certificate` رقم الشهادة, `discharge_summary` رقم ملخص الخروج, `uds_report` رقم تقرير ديناميكية التبول.
9. Events `ClinicalDocumentIssued`, `ClinicalDocumentVoided` (no listeners).

## Do (FE)
- New `src/app/features/clinic/encounter/documents/`: `clinical-document-dialog.component` (`[encounterId] [patientId] [docType] [(visible)]`): type picker (5 chips), template picker (from `clinical-templates?kind=`), per-type fields (sick leave: من/إلى + days computed live + «سبب» from diagnoses; certificate: purpose; medical report: free sections), live preview (rendered by the BE `print/document/{id}?format=html` after save-draft), buttons «حفظ مسودة» · «إصدار» (confirm: «بعد الإصدار لا يمكن التعديل») · «طباعة». `clinical-documents-list.component` (patient/encounter documents with number, type, date, status chip, print, void with reason).
- `clinical-templates.component` under clinic settings (route `clinic/settings/templates` — ONE targeted route entry in `clinic.routes.ts`; one link in the clinic settings screen): list by kind, edit name/body AR+EN with a placeholder palette (click to insert `{{…}}`), active toggle.
- Host the dialog with **one line/button** in the finish panel of `consultation.component.html` («مستند طبي» next to the referral button) + the matching signal in `.ts` (keep the edit to a few lines — S1 is live on this component; re-read it right before editing). Do NOT edit `visit-shell/card-catalog.ts`: report the card def `{id:'documents', component: ClinicalDocumentsListComponent}` hookup for the shell's existing `documents` card.
- Old prints onto the engine: in `clinic-visit-summary-print.service.ts`, `clinic-order-print.service.ts` and the referral dialog print, when `clinic.print.engine === 'pdf'` open `GET /api/clinic/print/{kind}/{id}?format=pdf` (blob → new tab) instead of the browser shell; otherwise unchanged. Prescription print: same switch to P1's `prescriptions/{p}/pdf`. Receipts untouched.
- Service `features/clinic/services/clinic-documents.service.ts`. i18n under `CLINIC_DOCS.*` (ar + en) with small targeted edits; do NOT bump I18N_VERSION. `ng build --base-href /app/` green.
- **Fable consult (mandatory, ONE focused consult):** send a Fable agent (`model: "fable"`) a screenshot of the current finish panel + the existing referral dialog and ask once: the document dialog layout (type chips → template → fields → preview), the issued/draft/voided states, and the A4/A5 printed sick-leave layout (header, number, signature block). Apply; don't iterate.

## Tests
`Modules/Clinic/tests/Feature/P5ClinicalDocumentsTest.php` (prefix `p5_`): create sick-leave draft on an encounter (no number) → issue assigns `SL-<year>-00001`, second issue `…00002`, numbers per company independent; issued doc PUT → 422; void keeps number; to<from → 422; > max days → 422; signed encounter → document still allowed; cancelled encounter → 422; patient-level certificate without encounter; template placeholders rendered and an unknown/blade-like token (`{{ $x }}`, `@php`) stays literal and is escaped; discharge summary on an inpatient encounter prefills discharge Rx items; print endpoint 200 for html and pdf; other company's document 404; template CRUD respects `clinic.templates.manage`. `Modules/Clinic/tests/Unit/P5TemplateRendererTest.php`. Run also any existing `*ClinicalDocument*Test.php`, `*LabReport*Test.php`, `*RadReport*Test.php`, `Af2*Test.php` that touch clinical_documents — keep green. You cannot migrate MySQL (orchestrator does it); SQLite hides missing columns, so list in your report every new column a query/Resource reads, for the orchestrator's MySQL check.

## Files you must NOT edit (report instead)
`Modules/Clinic/routes/api.php`, `RolePermissionSeeder.php`, `ClinicSettingDefinitionSeeder.php`, `SequenceService.php`, `DefaultSequenceSeeder.php`, `PrescriptionPdfService.php` (P1), P3's rad PDF service + `clinical_templates` migration/model (P3 — you may ADD methods to the model only if P3 is committed; otherwise report), `ClinicalDocumentWriter.php` (AF2), `visit-shell/**`, HistoryService, CHANGELOG (give 2 lines EN + AR).

## Out of scope
Re-pointing P1/P3 PDFs onto the engine (follow-up WP), nutrition `print-context` and dental `window.print` (S2), hospital admission screen hosting of the dialog (S3), UDS form fields (G6), operative summary (G4), signed QR verification portal.

## Report
Files, test output, Arabic-shaping status (first line), route lines, permissions + presets, setting definitions (`clinic.print.engine`, `clinic.documents.sick_leave_max_days`), sequence names, the shell card hookup, anything not done.
