feat: enhance payment status handling and summary in MyPaymentsPage
- Added support for 'partial' payment status in MyPaymentsPage and related components. - Updated API responses to include 'paid_rials' and 'summary' for invoices. - Introduced InvoicePaymentStatus service to derive payment status based on actual payments. - Enhanced tests to cover new payment scenarios including partial payments and payment methods. - Updated documentation to reflect changes in payment status and API responses.
This commit is contained in:
@@ -14,12 +14,20 @@ const get = api.get as ReturnType<typeof vi.fn>;
|
||||
const PAYLOAD = {
|
||||
patient: { uuid: 'abc', name: 'دنیا خلیلی', national_code: '1744023654' },
|
||||
data: [
|
||||
{ uuid: 'i1', number: 12345, issued_at: 1717000000, total_rials: 2350000, status: 'paid',
|
||||
service_title: 'روکش دندان',
|
||||
items: [{ uuid: 'it1', title: 'روکش دندان', quantity: 1, total_rials: 2350000, patient_rials: 2350000 }] },
|
||||
{ uuid: 'i2', number: 52614, issued_at: 1718000000, total_rials: 6000000, status: 'unsettled',
|
||||
service_title: 'طرح لبخند', items: [] },
|
||||
{ uuid: 'i1', number: 12345, issued_at: 1717000000, total_rials: 2350000, patient_rials: 2350000,
|
||||
paid_rials: 2350000, status: 'paid', service_title: 'روکش دندان',
|
||||
items: [{ uuid: 'it1', title: 'روکش دندان', quantity: 1, total_rials: 2350000, patient_rials: 2350000 }],
|
||||
payments: [
|
||||
{ method: 'cash', amount_rials: 350000, paid_at: 1717000000, created_by_name: 'منشی' },
|
||||
{ method: 'pos', amount_rials: 2000000, paid_at: 1717000500, created_by_name: null },
|
||||
] },
|
||||
{ uuid: 'i2', number: 52614, issued_at: 1718000000, total_rials: 6000000, patient_rials: 6000000,
|
||||
paid_rials: 0, status: 'unsettled', service_title: 'طرح لبخند', items: [], payments: [] },
|
||||
{ uuid: 'i3', number: 52615, issued_at: 1719000000, total_rials: 1000000, patient_rials: 1000000,
|
||||
paid_rials: 400000, status: 'partial', service_title: 'جرمگیری', items: [],
|
||||
payments: [{ method: 'wallet', amount_rials: 400000, paid_at: 1719000000, created_by_name: null }] },
|
||||
],
|
||||
summary: { total_rials: 8350000, paid_rials: 2350000, unsettled_rials: 6000000, invoices_count: 2 },
|
||||
meta: { totalRecords: 2, totalPages: 1, currentPage: 1 },
|
||||
};
|
||||
|
||||
@@ -40,31 +48,62 @@ beforeEach(() => {
|
||||
describe('MyPaymentDetailPage (پرداختهای ثبتشده)', () => {
|
||||
it('renders the patient header and invoice rows', async () => {
|
||||
renderPage();
|
||||
expect(await screen.findByText('دنیا خلیلی')).toBeInTheDocument();
|
||||
// the name shows twice: breadcrumb + identity card
|
||||
expect(await screen.findAllByText('دنیا خلیلی')).toHaveLength(2);
|
||||
expect(screen.getByText(/1744023654/)).toBeInTheDocument();
|
||||
expect(screen.getByText('روکش دندان')).toBeInTheDocument();
|
||||
expect(screen.getByText('طرح لبخند')).toBeInTheDocument();
|
||||
expect(screen.getByText('پرداخت شده')).toBeInTheDocument();
|
||||
expect(screen.getByText('تسویه نشده')).toBeInTheDocument();
|
||||
expect(screen.getByText('پرداخت ناقص')).toBeInTheDocument();
|
||||
expect(screen.getByText(/پرداختشده:/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the summary stat cards', async () => {
|
||||
renderPage();
|
||||
expect(await screen.findByText('مجموع صورتحسابها')).toBeInTheDocument();
|
||||
expect(screen.getByText('پرداختشده')).toBeInTheDocument();
|
||||
expect(screen.getByText('تسویهنشده')).toBeInTheDocument();
|
||||
expect(screen.getByText('تعداد صورتحساب')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('fetches invoices for the patient uuid from the route', async () => {
|
||||
renderPage();
|
||||
await screen.findByText('دنیا خلیلی');
|
||||
await screen.findAllByText('دنیا خلیلی');
|
||||
expect(get.mock.calls[0][0]).toContain('/api/v1/my/billing/patients/abc/invoices');
|
||||
});
|
||||
|
||||
it('expands a row to show its line items on بیشتر', async () => {
|
||||
it('expands a row to show its line items and payment methods on بیشتر', async () => {
|
||||
renderPage();
|
||||
await screen.findByText('روکش دندان');
|
||||
fireEvent.click(screen.getAllByRole('button', { name: /بیشتر/ })[0]);
|
||||
|
||||
// the item breakdown appears (title repeated inside the expanded sub-row)
|
||||
expect(await screen.findAllByText('روکش دندان')).toHaveLength(2);
|
||||
// and each payment shows its method in Persian
|
||||
expect(screen.getByText('روشهای پرداخت')).toBeInTheDocument();
|
||||
expect(screen.getByText('نقدی')).toBeInTheDocument();
|
||||
expect(screen.getByText('کارتخوان')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('says so when an invoice has no recorded payment', async () => {
|
||||
renderPage();
|
||||
await screen.findByText('طرح لبخند');
|
||||
fireEvent.click(screen.getAllByRole('button', { name: /بیشتر/ })[1]);
|
||||
expect(await screen.findByText('پرداختی ثبت نشده است.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows an empty state when the patient has no invoices', async () => {
|
||||
get.mockResolvedValue({ success: true, data: { patient: PAYLOAD.patient, data: [], meta: { totalRecords: 0, totalPages: 0, currentPage: 1 } } });
|
||||
get.mockResolvedValue({
|
||||
success: true,
|
||||
data: {
|
||||
patient: PAYLOAD.patient,
|
||||
data: [],
|
||||
summary: { total_rials: 0, paid_rials: 0, unsettled_rials: 0, invoices_count: 0 },
|
||||
meta: { totalRecords: 0, totalPages: 0, currentPage: 1 },
|
||||
},
|
||||
});
|
||||
renderPage();
|
||||
expect(await screen.findByText('صورتحسابی ثبت نشده است')).toBeInTheDocument();
|
||||
expect(await screen.findByText('صورتحسابی ثبت نشده است.')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user