delete and undo
This commit is contained in:
@@ -339,6 +339,74 @@ async function run(port) {
|
||||
});
|
||||
assert(moved.status === 200 && moved.json.data.currentDealerId === dealerB.json.data.id, 'transfer failed');
|
||||
|
||||
const dealerUndo = await request(port, 'POST', '/api/stocks/CH-RED/stage/undo', {
|
||||
token: dealerToken,
|
||||
});
|
||||
assert(dealerUndo.status === 403, 'dealer should not undo a stage');
|
||||
|
||||
const undonePayment = await request(port, 'POST', '/api/stocks/CH-RED/stage/undo', {
|
||||
token: repToken,
|
||||
});
|
||||
assert(
|
||||
undonePayment.status === 200 &&
|
||||
undonePayment.json.data.currentStage === 'documentsProcessed' &&
|
||||
undonePayment.json.data.paymentType == null &&
|
||||
undonePayment.json.data.leaseProviderType == null &&
|
||||
undonePayment.json.data.financeCompanyName == null &&
|
||||
!undonePayment.json.data.stageLogDates.paymentApproved,
|
||||
`undo payment stage failed ${JSON.stringify(undonePayment.json)}`
|
||||
);
|
||||
const undoneDocs = await request(port, 'POST', '/api/stocks/CH-RED/stage/undo', {
|
||||
token: repToken,
|
||||
});
|
||||
assert(
|
||||
undoneDocs.status === 200 && undoneDocs.json.data.currentStage === 'orderedByCustomer',
|
||||
'undo documents stage failed'
|
||||
);
|
||||
const undoneOrder = await request(port, 'POST', '/api/stocks/CH-RED/stage/undo', {
|
||||
token: repToken,
|
||||
});
|
||||
assert(
|
||||
undoneOrder.status === 200 && undoneOrder.json.data.currentStage === 'received',
|
||||
'undo ordered stage failed'
|
||||
);
|
||||
const undoReceived = await request(port, 'POST', '/api/stocks/CH-RED/stage/undo', {
|
||||
token: repToken,
|
||||
});
|
||||
assert(undoReceived.status === 400, 'received stage should not be undoable');
|
||||
|
||||
const undonePlate = await request(port, 'POST', '/api/stocks/CH-GREEN/stage/undo', {
|
||||
token: adminToken,
|
||||
});
|
||||
assert(
|
||||
undonePlate.status === 200 &&
|
||||
undonePlate.json.data.currentStage === 'rmvRegistration' &&
|
||||
undonePlate.json.data.plateNumber == null &&
|
||||
!undonePlate.json.data.stageLogDates.plateReceived,
|
||||
`undo plate stage failed ${JSON.stringify(undonePlate.json)}`
|
||||
);
|
||||
|
||||
await addStock('CH-OUT', 'EN-OUT', 1, dealerC.json.data.id);
|
||||
const repUndoOutside = await request(port, 'POST', '/api/stocks/CH-OUT/stage/undo', {
|
||||
token: repToken,
|
||||
});
|
||||
assert(repUndoOutside.status === 403, 'sales rep should not undo stock outside assignment');
|
||||
const repDeleteOutside = await request(port, 'DELETE', '/api/stocks/CH-OUT', { token: repToken });
|
||||
assert(repDeleteOutside.status === 403, 'sales rep should not delete stock outside assignment');
|
||||
const dealerDelete = await request(port, 'DELETE', '/api/stocks/CH-YELLOW', { token: dealerToken });
|
||||
assert(dealerDelete.status === 403, 'dealer should not delete a bike');
|
||||
const deleted = await request(port, 'DELETE', '/api/stocks/CH-YELLOW', { token: repToken });
|
||||
assert(deleted.status === 200, `stock delete failed ${deleted.status} ${JSON.stringify(deleted.json)}`);
|
||||
const stockGone = await prisma.stock.findUnique({ where: { chassisNumber: 'CH-YELLOW' } });
|
||||
const transferGone = await prisma.stockTransfer.findFirst({ where: { stockId: moved.json.data.id } });
|
||||
assert(!stockGone && !transferGone, 'deleted bike and its transfers should be gone');
|
||||
const missingStock = await request(port, 'DELETE', '/api/stocks/CH-YELLOW', { token: adminToken });
|
||||
assert(missingStock.status === 404, 'deleting a missing bike should be 404');
|
||||
const missingUndo = await request(port, 'POST', '/api/stocks/NO-SUCH/stage/undo', {
|
||||
token: adminToken,
|
||||
});
|
||||
assert(missingUndo.status === 404, 'undo of a missing bike should be 404');
|
||||
|
||||
const eventBad = await request(port, 'POST', '/api/events', {
|
||||
token: dealerToken,
|
||||
body: {
|
||||
|
||||
@@ -166,6 +166,73 @@ router.patch(
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:chassisNumber/stage/undo',
|
||||
requireRole('salesRep', 'superAdmin'),
|
||||
asyncHandler(async (req, res) => {
|
||||
const chassisNumber = req.params.chassisNumber;
|
||||
const stock = await prisma.stock.findUnique({
|
||||
where: { chassisNumber },
|
||||
include: stockInclude,
|
||||
});
|
||||
if (!stock) throw httpError(404, 'Stock not found');
|
||||
if (!canAccessDealer(req.user, stock.currentDealerId)) {
|
||||
throw httpError(403, 'Forbidden');
|
||||
}
|
||||
|
||||
const logs = [...stock.stageLogs].sort((a, b) => {
|
||||
const diff = new Date(a.loggedAt).getTime() - new Date(b.loggedAt).getTime();
|
||||
if (diff !== 0) return diff;
|
||||
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
||||
});
|
||||
const latest = logs[logs.length - 1];
|
||||
if (!latest || logs.length < 2 || stock.currentStage === 'received' || latest.stage === 'received') {
|
||||
throw httpError(400, 'Nothing to undo');
|
||||
}
|
||||
if (latest.stage !== stock.currentStage) {
|
||||
throw httpError(400, 'Stage log does not match the current stage');
|
||||
}
|
||||
|
||||
const previous = logs[logs.length - 2];
|
||||
const data = { currentStage: previous.stage };
|
||||
if (latest.stage === 'paymentApproved') {
|
||||
data.paymentType = null;
|
||||
data.leaseProviderType = null;
|
||||
data.financeCompanyName = null;
|
||||
}
|
||||
if (latest.stage === 'plateReceived') {
|
||||
data.plateNumber = null;
|
||||
}
|
||||
|
||||
const updated = await prisma.$transaction(async (tx) => {
|
||||
await tx.stageLog.delete({ where: { id: latest.id } });
|
||||
return tx.stock.update({
|
||||
where: { id: stock.id },
|
||||
data,
|
||||
include: stockInclude,
|
||||
});
|
||||
});
|
||||
|
||||
return res.json({ success: true, data: serializeStock(updated) });
|
||||
})
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/:chassisNumber',
|
||||
requireRole('salesRep', 'superAdmin'),
|
||||
asyncHandler(async (req, res) => {
|
||||
const chassisNumber = req.params.chassisNumber;
|
||||
const stock = await prisma.stock.findUnique({ where: { chassisNumber } });
|
||||
if (!stock) throw httpError(404, 'Stock not found');
|
||||
if (!canAccessDealer(req.user, stock.currentDealerId)) {
|
||||
throw httpError(403, 'Forbidden');
|
||||
}
|
||||
|
||||
await prisma.stock.delete({ where: { id: stock.id } });
|
||||
return res.json({ success: true });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:chassisNumber/transfer',
|
||||
requireRole('salesRep', 'superAdmin'),
|
||||
|
||||
Reference in New Issue
Block a user