updates on the reverse logistics

This commit is contained in:
2026-10-07 13:00:43 +05:30
parent 0d5edf94bf
commit ff1b795292
7 changed files with 354 additions and 32 deletions

View File

@@ -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 () => {