feat: port payment management tab from tauri to admin dashboard
Add per-clinic payment methods (bank accounts + POS/card-reader devices)
under the "مدیریت پرداخت" settings tab at /admin/my-financial, ported from
clinic-pro-tauri's mock-only PaymentManagement tab into a real persisted
feature. These records are referenceable (by uuid) from patient invoices to
record which method a service payment was made with.
Backend (new src/PaymentMethod domain):
- BankAccount + Pos entities, repositories, PaymentMethodService (validation,
ownership scoping, create/update/toggle logic).
- Thin PaymentMethodController exposing /api/v1/my/payment-methods/{bank-accounts,pos}
(GET/POST/PUT + PATCH .../status), guarded to clinic/doctor/secretary/admin.
- Migration for bank_accounts + pos_devices tables.
- Functional tests (success + validation/404/403 + empty boundaries).
- docs/api/payment-method.md.
Frontend:
- Replace MyFinancialPage content with the payment-management UI (two tabs,
tables, add/edit modals, status toggle) using the admin design system.
- usePaymentMethods hook (TanStack Query) + presentational components.
- Update page test to cover tabs, data, empty state and the add modal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# Payment Methods API
|
||||
|
||||
> **Prefix:** `/api/v1/my/payment-methods`
|
||||
|
||||
Per-clinic payment methods managed from the settings screen (`/admin/my-financial`,
|
||||
tab "مدیریت پرداخت"). Two resources: **bank accounts** and **POS (card reader) devices**.
|
||||
Records are stored so a patient invoice can later reference which account/device a
|
||||
service payment was made to.
|
||||
|
||||
All endpoints are scoped to the acting user — a clinic never sees another's records.
|
||||
|
||||
**Permission:** authenticated user with one of `ROLE_CLINIC`, `ROLE_DOCTOR`,
|
||||
`ROLE_SECRETARY`, `ROLE_ADMIN` (otherwise `403 ERR_FORBIDDEN_001`).
|
||||
|
||||
---
|
||||
|
||||
## Bank accounts
|
||||
|
||||
### GET `/api/v1/my/payment-methods/bank-accounts`
|
||||
|
||||
List the current clinic's bank accounts (newest first).
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "b1e0...-...",
|
||||
"bank_name": "ملی",
|
||||
"card_number": "6037991234567890",
|
||||
"account_number": "0101234567890",
|
||||
"shaba_number": "IR820540102680020817909002",
|
||||
"is_active": true,
|
||||
"created_at": 1752566400
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Empty list returns `"data": []`.
|
||||
|
||||
---
|
||||
|
||||
### POST `/api/v1/my/payment-methods/bank-accounts`
|
||||
|
||||
Create a bank account.
|
||||
|
||||
#### Body
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `bank_name` | string | ✅ | Bank name |
|
||||
| `account_number` | string | ✅ | Account number |
|
||||
| `card_number` | string | ❌ | Card number |
|
||||
| `shaba_number` | string | ❌ | IBAN / SHABA |
|
||||
|
||||
#### Response `201`
|
||||
Single created record (same shape as list item).
|
||||
|
||||
#### Errors
|
||||
- `422 ERR_VALIDATION_001` — `bank_name` or `account_number` missing (`field` set).
|
||||
|
||||
---
|
||||
|
||||
### PUT `/api/v1/my/payment-methods/bank-accounts/{uuid}`
|
||||
|
||||
Update a bank account. Any subset of the create fields may be sent; only provided
|
||||
keys change. Empty `bank_name`/`account_number` → `422`.
|
||||
|
||||
#### Response `200`
|
||||
Updated record.
|
||||
|
||||
#### Errors
|
||||
- `404 ERR_NOT_FOUND_001` — uuid unknown or owned by another clinic.
|
||||
- `422 ERR_VALIDATION_001` — provided `bank_name`/`account_number` empty.
|
||||
|
||||
---
|
||||
|
||||
### PATCH `/api/v1/my/payment-methods/bank-accounts/{uuid}/status`
|
||||
|
||||
Toggle `is_active` (active ⇄ inactive). No body.
|
||||
|
||||
#### Response `200`
|
||||
Record with flipped `is_active`.
|
||||
|
||||
#### Errors
|
||||
- `404 ERR_NOT_FOUND_001` — uuid unknown or not owned.
|
||||
|
||||
---
|
||||
|
||||
## POS devices
|
||||
|
||||
### GET `/api/v1/my/payment-methods/pos`
|
||||
|
||||
List the current clinic's card reader devices (newest first).
|
||||
|
||||
#### Response `200`
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"uuid": "c2f1...-...",
|
||||
"bank_name": "ملت",
|
||||
"serial_number": "SN-98765",
|
||||
"terminal_number": "123456",
|
||||
"account_number": null,
|
||||
"is_active": true,
|
||||
"created_at": 1752566400
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST `/api/v1/my/payment-methods/pos`
|
||||
|
||||
Create a POS device.
|
||||
|
||||
#### Body
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `bank_name` | string | ✅ | Bank name |
|
||||
| `terminal_number` | string | ✅ | Terminal number |
|
||||
| `serial_number` | string | ❌ | Device serial number |
|
||||
| `account_number` | string | ❌ | Linked account number |
|
||||
|
||||
#### Response `201`
|
||||
Single created record.
|
||||
|
||||
#### Errors
|
||||
- `422 ERR_VALIDATION_001` — `bank_name` or `terminal_number` missing (`field` set).
|
||||
|
||||
---
|
||||
|
||||
### PUT `/api/v1/my/payment-methods/pos/{uuid}`
|
||||
|
||||
Update a POS device. Partial update; empty `bank_name`/`terminal_number` → `422`.
|
||||
|
||||
#### Response `200`
|
||||
Updated record.
|
||||
|
||||
#### Errors
|
||||
- `404 ERR_NOT_FOUND_001` — uuid unknown or not owned.
|
||||
- `422 ERR_VALIDATION_001` — provided `bank_name`/`terminal_number` empty.
|
||||
|
||||
---
|
||||
|
||||
### PATCH `/api/v1/my/payment-methods/pos/{uuid}/status`
|
||||
|
||||
Toggle `is_active`. No body.
|
||||
|
||||
#### Response `200`
|
||||
Record with flipped `is_active`.
|
||||
|
||||
#### Errors
|
||||
- `404 ERR_NOT_FOUND_001` — uuid unknown or not owned.
|
||||
Reference in New Issue
Block a user