An appointment can now carry the insurance it is billed with: the service kind
(outpatient/inpatient) and the basic insurance. Confirming it no longer hands the
whole amount to the patient — the visit is split through BillingCalculator with the
coverage percent of that service kind, and the choice travels to the encounter and
the invoice built from it.
The enabled service kinds are a tenant-wide setting (all of that tenant's
insurances share it), so a tenant covering only one kind is never asked which one:
the panel resolves it the same way the server does.
- add tenant_service_category_settings + TenantServiceCategoryService, exposed on
the existing insurance-pricing endpoint (service_categories,
default_service_category); at least one kind must stay enabled
- add appointments.insurance_service_category / insurance_base_id with
AppointmentInsuranceService validating them against the tenant's own settings
and active contracts (basic only), accepted by PATCH and by confirm
- snapshot the kind on patient_sessions and invoices; the visit's coverage rule is
resolved per kind (services keep using their own ServiceItem.service_category)
- lib/insuranceShares becomes the single client-side mirror of BillingCalculator,
shared by the confirm modal, the appointment edit page and the session form
- surface the selection: confirm modal (with live shares), turns timeline chip,
appointment edit page, patient record service card and invoice summary
- the session form shows the insurance block whenever the tenant has an active
contract and prefills the patient's own insurance, so it can be changed
- fix: the confirm modal showed a zero visit price when the appointment had none —
it now falls back to the tenant's free-visit price like the server
- fix: useServiceCategories read one level too shallow, so Persian labels never
arrived and raw enum keys leaked into the contract summary
- fix: BlogsPage test asserted the public blogs endpoint after the page moved to
the admin one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Added support for 'partial' payment status in MyPaymentsPage and related components.
- Updated API responses to include 'paid_rials' and 'summary' for invoices.
- Introduced InvoicePaymentStatus service to derive payment status based on actual payments.
- Enhanced tests to cover new payment scenarios including partial payments and payment methods.
- Updated documentation to reflect changes in payment status and API responses.
- Introduced `tracking_number` field in the `claims` table to store the insurance tracking number.
- Created `claim_status_logs` table to maintain a history of status changes for claims, including who made the change and when.
- Implemented `ClaimStatusLog` entity and repository for managing status log entries.
- Updated `ClaimService` to log transitions and handle tracking numbers during claim submissions.
- Added new API endpoint for fetching claims by patient, including detailed claim history and status logs.
- Enhanced frontend with a new `ClaimPatientDetailPage` to display claims and their status history.
- Added tests to ensure correct aggregation of claims and proper handling of status transitions.
Port two nobat724 Figma screens into the admin SPA for the doctor/clinic
tenant panel:
- node 1 — لیست پرداختها (/admin/my-payments): per-patient payment summary
(invoice count, paid, remaining, derived status paid/unsettled/unpaid),
filters by national code / status / Jalali date range, pagination.
- node 2 — پرداختهای ثبتشده (/admin/my-payments/:patientUuid): a patient's
recorded invoices with patient header, service title, total, status badge,
and an expandable per-invoice item breakdown.
Backend (App\Billing):
- InvoiceRepository::patientPaymentSummary/countPatientPaymentSummary — DQL
aggregation grouped by patient record (arbitrary join Invoice→PatientRecord
→User), draft/void excluded, derived-status HAVING filters.
- InvoiceRepository::invoicesForPatient/count + InvoiceService methods that
shape rows and derive status.
- BillingController: GET /api/v1/my/billing/patient-payments and
GET /api/v1/my/billing/patients/{patientUuid}/invoices (thin, resolveEntity,
tenant-scoped, 403/404). Invoice::getIssuedAt / InvoiceItem::getTitle added.
- docs/api/billing.md documents both endpoints.
Frontend: useMyPayments hooks, MyPaymentsPage, MyPaymentDetailPage, routes in
App.tsx (doctor/secretary/clinic, blockClinicScope) and a sidebar entry.
Persian strings hardcoded per existing admin convention (no i18n infra).
Tests: tests/Billing/PatientPaymentsTest.php (8), useMyPayments + both page
tests (11). Note: pre-existing LoginPage.test failures are unrelated (proven
by stashing this change).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>