# W1-CL — Booking receipt · pay-before-consult gate · doctor free-visit quota (full-stack, Clinic)

Migration timestamp range: `2026_11_25_100001…100099`. Read `_COMMON.md` first. Look at `design/client-msg-1.png` (the client's current thermal booking receipt).

## 1. Booking receipt on «إضافة الزيارة» (client: «عايز الإيصال ده مع إضافة الزيارة»)
Today: FE `features/clinic/booking/create-visit.component.ts` `printCollectionReceipt()` (~:2776) prints only after collect-now (~:2973, setting `clinic.booking.print_receipt`, default on); body in `features/clinic/services/clinic-receipt-print.service.ts:84-151` (patient name, receipt no, time, lines, paid, remaining); shell/header/footer/paper in `services/clinic-print-shell.service.ts:175-195` (`clinic.print.header_text`/`footer_text`/`paper_size` a4|a5 only).
Build a **booking receipt** that prints whenever a visit/booking is saved (paid, partly paid or unpaid / sent to cashier), when `clinic.booking.print_receipt` is on:
- Header: clinic/company name + logo (existing shell), contact phones line («التواصل على الأرقام الآتية …») — new setting `clinic.print.contact_phones` (string).
- Patient code (MRN) + name (table like the photo), booking date (created), clinic/doctor, visit type (first/follow-up/… the consultation service/visit type label), appointment day name + date + time, paid / remaining (المدفوع / المتبقي).
- Nutrition departments only (clinical_type = nutrition): «الوزن السابق» (latest recorded weight, if any) + an empty «الوزن الحالي (  )» box.
- Instructions block: new multi-line setting `clinic.booking.receipt_instructions` (each line a bullet; default Arabic text from the photo: no food 4h before the scale; arrive 45–60 min before; no-show voids the booking and is non-refundable; on cancel the analysis-scale fee is deducted). Setting `clinic.booking.receipt_note` (the bold line above the table, default «برجاء عدم تناول الطعام قبل الميزان بـ4 ساعات على الأقل») — or fold into instructions; your call, keep it simple.
- Barcode of the patient code (CODE128). Reuse the barcode code the LIS module already uses in FE (grep JsBarcode / bwip / barcode in src/app) — no new npm dependency unless none exists.
- Paper: add `80mm` to `clinic.print.paper_size` choices AND a separate `clinic.booking.receipt_paper` (default `80mm`) used by this receipt and the collection receipt. 80mm CSS must fit 72mm printable width, @page size 80mm auto, margins ~3mm.
- A «طباعة إيصال الحجز» reprint action where reception sees the appointment (booking list/queue row menu) — find the existing appointment actions menu.
- In-place editing: on the printed-preview / or next to the print action provide a small «✎ نص الإيصال» popover (reception admin with settings perm) editing phones + instructions directly — owner wants settings editable in place. If a print-settings screen exists (clinic settings registry `features/clinic/settings/clinic-settings.registry.ts`), register the new keys there too.
BE: if the appointment resource lacks anything (MRN, appointment time, visit type label, last weight), extend the appointment/booking response minimally (Clinic `AppointmentResource`) — last weight from Nutrition measurements (BodyComposition/weights; find the service the nutrition board uses).

## 2. Block starting the consultation without payment unless a manager approves (client: «ممنوع بدء الكشف عند الدكتور بدون دفع إلا بموافقة من المدير»)
Today: `Clinic/app/Services/EncounterService.php:66 open()` and `:117 ensureForAppointment()` check status/doctor/department only; `EncounterController::store/begin` (~:69, :126) no payment check; `AppointmentResource.php:100-112` exposes `payment_status`. LIS precedent: `lis.require_payment_before_sample`.
- Setting `clinic.encounter.require_payment_before_start` (bool, default **false**; the orchestrator enables it on dev).
- When on: opening/starting an encounter for an appointment whose patient-payable outstanding > 0 (use the existing outstanding helper — NOT ledger balance; see `ClinicLedger outstandingFor()`; insured payer share and zero-price/free/package-covered visits are not "unpaid") → 422 with code `payment_required` and a clear Arabic message. Walk-in encounters without an order → allowed.
- Manager override: new permission `clinic.encounters.approve_unpaid_start` (grant to admin/clinic manager roles + bridge migration). Endpoint `POST clinic/appointments/{id}/approve-unpaid-start {reason}` stores `unpaid_start_approved_by/at/reason` on the appointment (migration) + activity log. Once approved, the gate passes. Also: a user who themselves holds the permission (e.g. doctor-owner) gets an inline «بدء بموافقة المدير» confirm with reason on the 422 instead of a dead end.
- FE: doctor queue / visit open path shows the block nicely (badge «غير مدفوع» + message «لازم الدفع أو موافقة المدير»), reception/manager queue row gets «موافقة على البدء بدون دفع» (reason dialog) when the setting is on and the visit is unpaid. Find the doctor worklist/queue components (features/clinic/queue or doctor-queue) and the visit shell/consultation open path (they call encounters begin/store).
- Also guard `SyncAppointmentStatus`/check-in paths only if they start the encounter.

## 3. Doctor free-visit quota (client: «إمكانية أن الطبيب له عدد 2 كشف مجانا مثلا شهرياً»)
- Doctor-level field `free_visits_per_month` (int, default 0 = none) — on the clinic doctor profile (find where doctor clinic settings live: clinic_doctors / doctor schedule profile). FE: editable on that doctor screen.
- Create-visit: a «كشف مجاني» toggle next to the discount box (~create-visit.component.ts:847, 2695-2737) visible when the chosen doctor has quota > 0; shows «متبقي X من N هذا الشهر». When on: the consultation line becomes 100% discount with reason `free_visit`, and the appointment is flagged `is_free_visit` (migration).
- BE validates on booking (BookAppointment / CreateVisit actions): count of this doctor's non-cancelled `is_free_visit` appointments in the calendar month of the appointment date (company timezone) < quota, else 422 «الكشوفات المجانية للطبيب خلصت الشهر ده». Cancelling releases the slot (count excludes cancelled).
- Endpoint for remaining: include `free_visits_remaining` in the doctor list the booking uses, or `GET clinic/doctors/{id}/free-visits?date=`.
- A free visit passes the payment gate (outstanding 0).

## Acceptance
- Saving a visit prints the 80mm booking receipt with all fields above; reprint from the booking row; phones/instructions editable in place.
- Setting on + unpaid → doctor can't start; manager approves with reason → doctor can start; paid/free/insured-covered → no block. Setting off → unchanged behaviour.
- Doctor with quota 2: third free visit in the same month is refused; cancelling one frees a slot.
- Tests (Pest): payment gate on/off/approved/free; approve endpoint permission; quota count/month boundary/cancel; booking resource fields.
- CHANGELOG: orchestrator writes it; give a one-line summary per capability in your report.

Out of scope: loyalty redemption on booking (W2-LOY), cashier screens, nutrition plan print.
