feat(billing): patient payments list + patient invoices detail (doctor/clinic)
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>
This commit is contained in:
@@ -185,6 +185,55 @@
|
||||
```
|
||||
`debt = claimed - paid` (حداقل صفر).
|
||||
|
||||
## GET /api/v1/my/billing/patient-payments
|
||||
«لیست پرداختها» — یک ردیف بهازای هر بیمار با جمع صورتحسابهای همان tenant. صورتحسابهای `draft`/`void` نادیده گرفته میشوند.
|
||||
|
||||
**Query params:**
|
||||
|
||||
| param | توضیح |
|
||||
|-------|-------|
|
||||
| `national_code` | جستوجوی جزئی روی کد ملی بیمار (`LIKE`) |
|
||||
| `status` | `paid` (باقیمانده=۰) · `unpaid` (پرداختی=۰ و باقیمانده>۰) · `unsettled` (هر دو>۰) — روی جمعها اعمال میشود |
|
||||
| `from` / `to` | بازهی `issued_at` بر حسب ثانیهی Unix |
|
||||
| `page` / `limit` | صفحهبندی (پیشفرض ۱ / ۲۰، سقف ۱۰۰) |
|
||||
|
||||
**Response 200** (صفحهبندیشدهی مسطح):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{ "patient_uuid": "…", "patient_name": "دنیا خلیلی", "national_code": "1744023654",
|
||||
"invoice_count": 2, "paid_rials": 2350000, "remaining_rials": 500000, "status": "unsettled" }
|
||||
],
|
||||
"meta": { "totalRecords": 12, "totalPages": 1, "currentPage": 1 }
|
||||
}
|
||||
```
|
||||
> `paid_rials` = جمع سهم بیمار روی صورتحسابهای `paid`؛ `remaining_rials` = جمع سهم بیمار روی صورتحسابهای `finalized`. `status` سمت سرور از همین دو مشتق میشود.
|
||||
|
||||
**Errors:** `403` (`ERR_FORBIDDEN_001`) پروفایل tenant یافت نشد.
|
||||
|
||||
## GET /api/v1/my/billing/patients/{patientUuid}/invoices
|
||||
«پرداختهای ثبتشده» — سربرگ بیمار + فهرست صفحهبندیشدهی صورتحسابهای `finalized`/`paid` او (جدیدترین اول). فقط مالک رکورد (همان tenant) دسترسی دارد.
|
||||
|
||||
**Response 200:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"patient": { "uuid": "…", "name": "دنیا خلیلی", "national_code": "1744023654" },
|
||||
"data": [
|
||||
{ "uuid": "…", "number": 12345, "issued_at": 1717000000, "total_rials": 2350000,
|
||||
"status": "paid", "service_title": "روکش دندان",
|
||||
"items": [ { "uuid": "…", "title": "روکش دندان", "quantity": 1, "total_rials": 2350000, "patient_rials": 2350000 } ] }
|
||||
],
|
||||
"meta": { "totalRecords": 3, "totalPages": 1, "currentPage": 1 }
|
||||
}
|
||||
}
|
||||
```
|
||||
> `status` دو حالته: `paid` (پرداختشده) یا `unsettled` (تسویهنشده = `finalized`). `service_title` عنوان اولین آیتم است (+ «و موارد دیگر» اگر بیش از یک آیتم باشد).
|
||||
|
||||
**Errors:** `403` پروفایل tenant یافت نشد · `404` (`ERR_NOT_FOUND_001`) بیمار متعلق به این tenant نیست/یافت نشد.
|
||||
|
||||
---
|
||||
|
||||
## ارسال مطالبه (ClaimSubmitter)
|
||||
|
||||
Reference in New Issue
Block a user