- Updated InsuranceModal to include doctorUuid in the payload for insurance contracts.
- Enhanced TenantInsuranceContracts to allow selection of doctors and pass doctorUuid in API requests.
- Modified InsuranceController to handle doctorUuid for tenant insurance endpoints, ensuring contracts are stored per doctor.
- Updated API documentation to reflect the new optional doctor_uuid parameter for tenant insurance endpoints.
- Added tests to verify the functionality of per-doctor insurance contracts and ensure isolation of contracts between doctors.
- Implement PriceInput component tests to validate Persian and Arabic numeral handling, input formatting, and controlled behavior.
- Create ServiceDetailPage component with detailed service information, including pricing, insurance coverage, and editing capabilities.
- Add API tests for service item detail retrieval and coverage synchronization with insurance contracts.
- Ensure proper error handling and user feedback for service item retrieval and coverage management.
Rebuild the /admin/insurance-pricing contracts UI to the Figma "مدیریت بیمه"
design and inject the coverage/franchise/ceiling fields the design omitted.
Backend:
- Add contract-level `kind` column to TenantInsurance (basic|supplementary),
defaulting to the catalog type; migration Version20260715093358.
- POST/PATCH /billing/tenant-insurances now accept effective_from,
effective_to, kind; PATCH also toggles is_active without clobbering the
user-set effective_to (unlike DELETE/deactivate).
- List returns the latest version of every insurance (active + inactive) via
TenantInsuranceRepository::findLatestByTenant, for the فعال/غیرفعال toggle.
Frontend:
- New InsuranceModal (ui/Modal + SearchableSelect + PersianDateInput) with the
seven fields; submit "ثبت بیمه".
- TenantInsuranceContracts rebuilt: header + search box, desktop table
(ردیف/نام/کد/نوع/وضعیت/عملیات) and mobile cards, status toggle -> PATCH.
- utils: isoToUnix/unixToIso helpers for contract dates.
Tests: TenantInsuranceContractApiTest (create/edit/toggle/list, 5 cases),
InsuranceModal + TenantInsuranceContracts vitest suites, docs/api updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- M2 GET /insurance/{id}: was unguarded; now owner-or-admin (403 otherwise) —
stops reading another doctor's negotiated price by id enumeration.
- M3 GET /clinic-pro/doctor-address/{id}: add the same owner/admin check the
sibling PATCH/DELETE already had.
- M4 POST/PATCH /service-item: staff_uuid must belong to the caller's tenant
(entity_type/entity_id) → 422; stops binding another tenant's staff.
- M5 appointment-settings list endpoints (date-override/holidays/
available-locations): add the per-doctor ownership check the sibling
single-record endpoints already enforce.
Regressions (6 negative cases fail without the fixes):
DoctorInsuranceOwnershipTest, DoctorAddressOwnershipTest,
ServiceItemStaffOwnershipTest, AppointmentSettingsListOwnershipTest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verification pass found three tests only guarded correctness, not the fix's
behavior:
- H7: add repository white-box test asserting likes/replies come back as
initialised PersistentCollections (lazy without the fetch-join).
- H8: add a query-count test (constant vs coverage-row count) — without the
batch fetch the count grows ~1 per row.
- H5: add an end-to-end test hitting DELETE /api/v1/doctor and asserting the
insurance config is purged (the service unit test didn't cover the wiring).
All three now fail when their fix is reverted. Suite: 39 tests / 92 assertions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
listServiceCoverage called serviceItemRepo->find() once per coverage row (N+1).
Collect the ids and fetch them in one findBy(['id' => $ids]), then map by id.
Regression: tests/Insurance/ServiceCoverageNPlusOneTest (functional correctness —
every row resolves the right service_item_uuid).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tenant_insurances, entity_insurance_pricing and tenant_service_coverages
reference their owner through a polymorphic (entity_type, entity_id) pair, so no
database FK can cascade their cleanup. Hard-deleting a doctor (DoctorController)
or clinic (AdminApiController) left these rows orphaned.
Add TenantInsuranceCleanupService::purgeForEntity() and call it from both delete
paths — removes coverage (via owning tenant_insurance ids), then tenant
insurances, then pricing.
Residual (separate, lower-freq paths): deleting an insurance category or a
service_item still orphans rows that reference them by id — tracked under the
medium-tier soft-ref findings.
Regression: tests/Insurance/TenantInsuranceCleanupTest.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>