feat: port tauri create-service payment flow to admin session settlement
Port the tauri /files/create-service page (payment mode) to the admin SPA
and back it with real multi-part session settlement:
Backend:
- New SessionPayment entity (session_payments table): partial payments
per session with method (wallet/pos/cash/card), amount, paid_at, actor
- PatientSession: settlement discount (percent/fixed), discount_rials,
paid_at, payments relation; remaining debt derived from
final - discount - paid total
- POST /api/v1/session/{uuid}/payments: register a partial payment;
wallet method debits the patient wallet; zero remaining marks paid
- PATCH /api/v1/session/{uuid}: accepts discount_type/discount_value
(null removes) and paid_at, backward compatible
- New error codes: ERR_SESSION_PAYMENT_INVALID/_EXCEEDS,
ERR_SESSION_DISCOUNT_INVALID
- Migration + 14 functional tests (partial/full/wallet/exceed/discount)
Frontend (admin):
- SessionPaymentPage: two-step stepper (پرداخت ← جزییات) ported from
tauri AddService payment mode — service cost, settlement discount
input, Jalali payment date, wallet balance, 4-method payment accordion,
paid-list box, details summary
- SessionStepper + stepper/payment icons ported verbatim from tauri SVGs
- «تکمیل پرداخت» on SessionServiceCard now navigates to the payment page
(replaces the small settle modal on PatientDetailPage)
- Routes for patients/ and my-patients/ variants; vitest coverage
- docs/api/patient.md updated
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -199,16 +199,18 @@ describe('PatientDetailPage (پرونده تبدار)', () => {
|
||||
await waitFor(() => expect(post).toHaveBeenCalledWith('/api/v1/patient/r1/wallet/charge', { amount_rials: 500000, payment_method: 'cash' }));
|
||||
});
|
||||
|
||||
it('settles a session from the wallet via the payment-method chooser', async () => {
|
||||
const patch = api.patch as ReturnType<typeof vi.fn>;
|
||||
patch.mockResolvedValue({ success: true, data: {} });
|
||||
renderDetail();
|
||||
it('navigates to the session payment page when «تکمیل پرداخت» is clicked', async () => {
|
||||
renderWithProviders(
|
||||
<Routes>
|
||||
<Route path="/admin/patients/:uuid" element={<PatientDetailPage />} />
|
||||
<Route path="/admin/patients/:recordUuid/session/:sessionUuid/pay" element={<div>صفحه تکمیل پرداخت</div>} />
|
||||
</Routes>,
|
||||
{ route: '/admin/patients/r1' },
|
||||
);
|
||||
await loaded();
|
||||
// تب سرویسها پیشفرض است؛ کارت پرداختنشده → «تکمیل پرداخت»
|
||||
// تب سرویسها پیشفرض است؛ کارت پرداختنشده → «تکمیل پرداخت» → صفحهی استپر پرداخت
|
||||
fireEvent.click(await screen.findByText('تکمیل پرداخت'));
|
||||
expect(await screen.findByText('روش پرداخت مراجعه')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole('button', { name: 'کیف پول بیمار' }));
|
||||
await waitFor(() => expect(patch).toHaveBeenCalledWith('/api/v1/session/s1', { payment_method: 'wallet' }));
|
||||
expect(await screen.findByText('صفحه تکمیل پرداخت')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the notes tab with a compose box and empty state', async () => {
|
||||
|
||||
Reference in New Issue
Block a user