feat(payment): prefill amount field with remaining balance and cap wallet prefill

This commit is contained in:
hamed
2026-07-19 09:52:11 +03:30
parent 7e4e4b9157
commit 7ebc04fc3f
3 changed files with 33 additions and 3 deletions
@@ -181,7 +181,7 @@ function ContractCard({ contract, item }: { contract: TenantInsurance; item: Ser
{!draft.covered && !isLoading && (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '12px 14px' }}>
<span style={{ fontSize: 12, color: 'var(--text-3)' }}>این خدمت تحت این بیمه پوشش ندارد</span>
<button className="btn sm" disabled={saveMut.isPending} onClick={() => saveMut.mutate()}>
<button className="btn primary sm" disabled={saveMut.isPending} onClick={() => saveMut.mutate()}>
{saveMut.isPending ? '...' : 'ذخیره'}
</button>
</div>
@@ -71,7 +71,8 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
const payMut = useMutation({
mutationFn: (body: object) => api.post(`/api/v1/session/${sessionUuid}/payments`, body),
onSuccess: () => { invalidate(); setAmount(0); toast.success('پرداخت ثبت شد'); },
// بعد از ثبت، آکاردئون بسته می‌شود تا باز شدن بعدی مانده‌ی به‌روزشده را پیش‌فرض بگذارد.
onSuccess: () => { invalidate(); setAmount(0); setExpanded(null); toast.success('پرداخت ثبت شد'); },
onError: (e: Error) => toast.error(e.message),
});
@@ -128,6 +129,17 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
: Math.max(0, finalPrice - discountRials);
const debt = session.remaining_rials ?? session.patient_debt_rials ?? 0;
/**
* مبلغ پیش‌فرض هنگام باز شدن هر روش پرداخت = مانده‌ی فعلی (تومان).
* چون `debt` از `remaining_rials` سرور می‌آید، بعد از هر پرداخت جزئی خودش کم می‌شود؛
* پس در پرداخت چندروشه، روش بعدی باقی‌مانده‌ی درست را پیش‌فرض می‌گیرد.
* برای کیف پول سقفِ موجودی هم اعمال می‌شود.
*/
const defaultAmountFor = (method: string) => {
const cap = method === 'wallet' ? Math.min(debt, walletBalance) : debt;
return rialToToman(Math.max(0, cap));
};
return (
<>
{/* هزینه سرویس */}
@@ -264,7 +276,7 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
<button
type="button"
aria-expanded={open}
onClick={() => { setExpanded(open ? null : m.key); setAmount(0); }}
onClick={() => { setExpanded(open ? null : m.key); setAmount(open ? 0 : defaultAmountFor(m.key)); }}
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', padding: '14px 4px', background: 'transparent', border: 'none', cursor: 'pointer' }}
>
<span className="dark:text-[#D7D8ED]" style={{ fontSize: 14, fontWeight: 600, color: '#111827' }}>{m.label}</span>
@@ -97,6 +97,24 @@ describe('SessionPaymentPage (تکمیل پرداخت مراجعه)', () => {
expect(typeof (body as any).paid_at).toBe('number');
});
it('prefills the amount field with the remaining balance when a method is opened', async () => {
// مانده ۲,۳۰۰,۰۰۰ ریال → ۲۳۰,۰۰۰ تومان در فیلد مبلغ
mockGets([session({ remaining_rials: 2_300_000 })]);
renderPage();
fireEvent.click(await screen.findByText('پرداخت نقدی'));
expect(screen.getByPlaceholderText('مبلغ (تومان)')).toHaveValue('230,000');
});
it('caps the wallet prefill at the wallet balance', async () => {
// موجودی کیف پول ۳۰۰,۰۰۰ ریال < مانده ۲,۳۰۰,۰۰۰ ریال → ۳۰,۰۰۰ تومان
mockGets([session({ remaining_rials: 2_300_000 })]);
renderPage();
fireEvent.click(await screen.findByText('پرداخت از طریق کیف پول'));
expect(screen.getByPlaceholderText('مبلغ (تومان)')).toHaveValue('30,000');
});
it('applies and removes settlement discount (PATCH /session/{uuid})', async () => {
mockGets([session()]);
patch.mockResolvedValue({ success: true, data: session() });