feat(payment): prefill amount field with remaining balance and cap wallet prefill
This commit is contained in:
@@ -181,7 +181,7 @@ function ContractCard({ contract, item }: { contract: TenantInsurance; item: Ser
|
|||||||
{!draft.covered && !isLoading && (
|
{!draft.covered && !isLoading && (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '12px 14px' }}>
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '12px 14px' }}>
|
||||||
<span style={{ fontSize: 12, color: 'var(--text-3)' }}>این خدمت تحت این بیمه پوشش ندارد</span>
|
<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 ? '...' : 'ذخیره'}
|
{saveMut.isPending ? '...' : 'ذخیره'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -71,7 +71,8 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
|
|||||||
|
|
||||||
const payMut = useMutation({
|
const payMut = useMutation({
|
||||||
mutationFn: (body: object) => api.post(`/api/v1/session/${sessionUuid}/payments`, body),
|
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),
|
onError: (e: Error) => toast.error(e.message),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -128,6 +129,17 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
|
|||||||
: Math.max(0, finalPrice - discountRials);
|
: Math.max(0, finalPrice - discountRials);
|
||||||
const debt = session.remaining_rials ?? session.patient_debt_rials ?? 0;
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* هزینه سرویس */}
|
{/* هزینه سرویس */}
|
||||||
@@ -264,7 +276,7 @@ export default function PaymentStep({ recordUuid, session, walletBalance, onCont
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
aria-expanded={open}
|
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' }}
|
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>
|
<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');
|
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 () => {
|
it('applies and removes settlement discount (PATCH /session/{uuid})', async () => {
|
||||||
mockGets([session()]);
|
mockGets([session()]);
|
||||||
patch.mockResolvedValue({ success: true, data: session() });
|
patch.mockResolvedValue({ success: true, data: session() });
|
||||||
|
|||||||
Reference in New Issue
Block a user