updates on the reverse logistics
This commit is contained in:
@@ -22,6 +22,8 @@ jest.mock('@/api/doormile', () => ({
|
||||
cancelRto: jest.fn(),
|
||||
completeRto: jest.fn(),
|
||||
getReturns: jest.fn(),
|
||||
getReturnsSummary: jest.fn(),
|
||||
getConsignmentHistory: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/api/doormile/notify', () => ({
|
||||
@@ -54,6 +56,8 @@ beforeEach(() => {
|
||||
api.initiateRto.mockResolvedValue({ success: true, data: { started: true } });
|
||||
api.cancelRto.mockResolvedValue({ success: true, data: {} });
|
||||
api.completeRto.mockResolvedValue({ success: true, data: {} });
|
||||
api.getReturnsSummary.mockResolvedValue({ success: true, data: { total: 0, by_client: [], by_reason: [] } });
|
||||
api.getConsignmentHistory.mockResolvedValue({ success: true, data: [] });
|
||||
});
|
||||
|
||||
describe('which rows may start or close a return', () => {
|
||||
@@ -172,7 +176,73 @@ describe('Returns page', () => {
|
||||
wrap(<Returns />);
|
||||
await screen.findByText('DMX00000061');
|
||||
expect(screen.queryByRole('button', { name: 'Mark returned' })).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Actions')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: 'Re-attempt delivery' })).not.toBeInTheDocument();
|
||||
// A client may still read each parcel's history.
|
||||
expect(screen.getAllByRole('button', { name: 'History' })).toHaveLength(2);
|
||||
});
|
||||
|
||||
const SUMMARY = {
|
||||
success: true,
|
||||
data: {
|
||||
total: 8, delivered: 4, in_return: 1, in_return_now: 2, returned: 3, return_rate: 50, avg_return_days: 1.5,
|
||||
by_client: [
|
||||
{ tenantid: 1, tenantname: 'Acme Foods', total: 6, in_return: 1, returned: 3, return_rate: 66.7 },
|
||||
{ tenantid: 2, tenantname: 'Beta Co', total: 2, in_return: 0, returned: 0, return_rate: 0 },
|
||||
],
|
||||
by_reason: [
|
||||
{ reason: 'Receiver refused', count: 3 },
|
||||
{ reason: 'Address not found', count: 1 },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
it('summarises the return rate per client and per reason for the chosen period', async () => {
|
||||
api.getReturns.mockResolvedValue(RETURNS);
|
||||
api.getReturnsSummary.mockResolvedValue(SUMMARY);
|
||||
wrap(<Returns />);
|
||||
expect(await screen.findByText('Return rate by client')).toBeInTheDocument();
|
||||
expect(screen.getByText('50%')).toBeInTheDocument();
|
||||
expect(screen.getByText('66.7%')).toBeInTheDocument();
|
||||
expect(screen.getByText('1.5 d')).toBeInTheDocument();
|
||||
// "In return now" counts every open return, not only parcels created in the window.
|
||||
expect(screen.getByText('In return now').parentElement).toHaveTextContent('2');
|
||||
expect(screen.getByText('Address not found')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Based on 8 parcels/)).toBeInTheDocument();
|
||||
// Same window as the list.
|
||||
const listParams = api.getReturns.mock.calls[0][0];
|
||||
expect(api.getReturnsSummary.mock.calls[0][0]).toEqual({ from: listParams.from, to: listParams.to });
|
||||
});
|
||||
|
||||
it('does not show a client the per-client table, only its own reasons', async () => {
|
||||
mockAuth = { user: { email: 'ops@acme.test', role: 'manager', tenantid: 1 }, isClient: true };
|
||||
api.getReturns.mockResolvedValue(RETURNS);
|
||||
api.getReturnsSummary.mockResolvedValue(SUMMARY);
|
||||
wrap(<Returns />);
|
||||
expect(await screen.findByText('Why parcels came back')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Return rate by client')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("opens a parcel's timeline from History", async () => {
|
||||
api.getReturns.mockResolvedValue(RETURNS);
|
||||
api.getConsignmentHistory.mockResolvedValue({
|
||||
success: true,
|
||||
data: [
|
||||
{ historyid: 1, eventstatus: 'Out_for_Delivery', remarks: '', createdat: '2026-10-01T09:00:00+05:30' },
|
||||
{ historyid: 2, eventstatus: 'Delivery_Skipped', remarks: 'Customer unavailable', createdat: '2026-10-01T09:30:00+05:30' },
|
||||
{
|
||||
historyid: 3, eventstatus: 'RTO_Initiated', remarks: 'Receiver refused: gate closed',
|
||||
fromstatus: 'Out_for_Delivery', actorname: 'Ops Priya', createdat: '2026-10-01T10:00:00+05:30',
|
||||
},
|
||||
],
|
||||
});
|
||||
wrap(<Returns />);
|
||||
await screen.findByText('DMX00000061');
|
||||
fireEvent.click(screen.getAllByRole('button', { name: 'History' })[0]);
|
||||
expect(await screen.findByText('Return to sender started')).toBeInTheDocument();
|
||||
expect(api.getConsignmentHistory).toHaveBeenCalledWith(61);
|
||||
expect(screen.getByText('Delivery attempt failed')).toBeInTheDocument();
|
||||
expect(screen.getByText('Receiver refused: gate closed')).toBeInTheDocument();
|
||||
expect(screen.getByText(/by Ops Priya · was out for delivery/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('says plainly when the backend has no returns yet', async () => {
|
||||
|
||||
Reference in New Issue
Block a user