feat(patients): phase C — patient financial tabs (payments, wallet, transactions)
The generic wallet/payment endpoints are bound to #[CurrentUser] (the
requester's own money), so they cannot serve a record owner viewing a patient's
finances. Add three record-owner-gated read endpoints on PatientController that
reuse the existing repositories:
GET /patient/{uuid}/payments — paginated gateway payments (?status)
GET /patient/{uuid}/wallet — balance + 10 recent transactions
GET /patient/{uuid}/wallet/transactions — full paginated ledger
Wire the previously-placeholder "پرداختها" and "کیف پول" tabs on the patient
detail page: payments via the shared TabList, wallet via a new WalletTab (balance
card + credit/debit ledger). PatientFinancialsTest covers the happy path, the
credit−debit balance, and ownership scoping (404 for a different owner).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,13 @@ beforeEach(() => {
|
||||
user_mobile: '09120000000', user_national_code: '1234567890',
|
||||
profile: { gender: 'female', referral_source: 'اینستاگرام', address: 'یزد', description: 'یادداشت' },
|
||||
} });
|
||||
if (url === '/api/v1/patient/r1/payments') return Promise.resolve({ success: true, data: [
|
||||
{ uuid: 'p1', amount_rials: 250000, status: 'success', gateway: 'mellat', created_at: 1700000000 },
|
||||
], meta: { totalRecords: 1 } });
|
||||
if (url === '/api/v1/patient/r1/wallet') return Promise.resolve({ success: true, data: {
|
||||
balance_rials: 300000,
|
||||
recent_transactions: [{ uuid: 't1', amount_rials: 500000, type: 'credit', description: 'شارژ', balance_after: 300000, created_at: 1700000000 }],
|
||||
} });
|
||||
return Promise.resolve({ success: true, data: [] });
|
||||
});
|
||||
});
|
||||
@@ -83,6 +90,22 @@ describe('PatientDetailPage (پرونده تبدار)', () => {
|
||||
expect(link).toHaveAttribute('href', '/admin/patients/r1/session/new');
|
||||
});
|
||||
|
||||
it('lists patient payments with status label on the payments tab', async () => {
|
||||
renderDetail();
|
||||
await screen.findByText('ساغر صابری');
|
||||
fireEvent.click(screen.getByText('پرداختها'));
|
||||
expect(await screen.findByText('موفق')).toBeInTheDocument();
|
||||
expect(screen.getByText(/mellat/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows wallet balance and a transaction on the wallet tab', async () => {
|
||||
renderDetail();
|
||||
await screen.findByText('ساغر صابری');
|
||||
fireEvent.click(screen.getByText('کیف پول'));
|
||||
expect(await screen.findByText('موجودی کیف پول')).toBeInTheDocument();
|
||||
expect(await screen.findByText('شارژ')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the messages tab with a send box', async () => {
|
||||
renderDetail();
|
||||
await screen.findByText('ساغر صابری');
|
||||
|
||||
Reference in New Issue
Block a user