Notify requester distinctly when receiver cancels their claimed order (Task #67)
Context - After Task #64 receivers can cancel an order they have already claimed. - Previously the requester's cancellation notification body was the same regardless of who cancelled (admin vs receiver), giving them no signal that their order had been claimed and then dropped. Changes - artifacts/api-server/src/routes/service-orders.ts (PATCH /orders/:id/status, cancel branch): - When the cancellation is initiated by the order's currently assigned receiver (existing.assignedTo === userId && next === "cancelled"), the requester's notification body now includes the bilingual phrase "استلم طلبك ولكن تم إلغاؤه لاحقاً" / "Your order was claimed but later cancelled by the receiver", appended to the service name for context. - Admin / non-assignee cancellations keep the existing service-name body so the two cases remain distinguishable in the requester's notifications list. - The notification is created via the existing notifyUser() helper, which persists to notifications and emits notification_created over Socket.IO, so the bell badge updates in real time. - artifacts/api-server/tests/service-orders.test.mjs: - Added "requester gets a distinct notification when the receiver cancels a claimed order" covering the full flow: place → confirm-receipt → cancel, then asserts the owner has a cancellation notification whose body matches the new bilingual receiver-cancel copy. Verification - All 7 tests in service-orders.test.mjs pass locally against the running API. Notes / deviations - Locale JSON files (ar.json/en.json) were inspected but not modified: notification copy is stored server-side as titleAr/titleEn/bodyAr/bodyEn on the notifications row; the client just renders those strings directly. Replit-Task-Id: 5aa480a9-c6e0-4a06-8919-b2b6784d8a98
This commit is contained in:
@@ -348,12 +348,19 @@ router.patch(
|
||||
cancelled: ["تم إلغاء طلبك", "Your order was cancelled"],
|
||||
};
|
||||
const [titleAr, titleEn] = titleMap[next];
|
||||
let bodyAr = enriched.service.nameAr;
|
||||
let bodyEn = enriched.service.nameEn;
|
||||
// Distinguish receiver-initiated cancel from admin-initiated cancel
|
||||
if (next === "cancelled" && existing.assignedTo === userId) {
|
||||
bodyAr = `${enriched.service.nameAr} — استلم طلبك ولكن تم إلغاؤه لاحقاً`;
|
||||
bodyEn = `${enriched.service.nameEn} — Your order was claimed but later cancelled by the receiver`;
|
||||
}
|
||||
await notifyUser(
|
||||
enriched.userId,
|
||||
titleAr,
|
||||
titleEn,
|
||||
enriched.service.nameAr,
|
||||
enriched.service.nameEn,
|
||||
bodyAr,
|
||||
bodyEn,
|
||||
orderId,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,6 +259,44 @@ test("status transitions matrix: receiver flows + owner/admin cancel rules", asy
|
||||
assert.equal((await res.json()).status, "cancelled");
|
||||
});
|
||||
|
||||
test("requester gets a distinct notification when the receiver cancels a claimed order", async () => {
|
||||
const owner = await createUser("ownN", "user");
|
||||
const recv = await createUser("recvN", "order_receiver");
|
||||
const co = await login(owner.username);
|
||||
const cr = await login(recv.username);
|
||||
|
||||
let res = await call(co, "POST", "/orders", { serviceId });
|
||||
assert.equal(res.status, 201);
|
||||
const order = await res.json();
|
||||
createdOrderIds.push(order.id);
|
||||
|
||||
// Receiver claims, then cancels
|
||||
res = await call(cr, "PATCH", `/orders/${order.id}/confirm-receipt`);
|
||||
assert.equal(res.status, 200);
|
||||
res = await call(cr, "PATCH", `/orders/${order.id}/status`, {
|
||||
status: "cancelled",
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
|
||||
// Owner should have a cancellation notification with the receiver-cancel body
|
||||
const { rows } = await pool.query(
|
||||
`SELECT title_ar, title_en, body_ar, body_en
|
||||
FROM notifications
|
||||
WHERE user_id = $1 AND related_type = 'order' AND related_id = $2
|
||||
ORDER BY id DESC`,
|
||||
[owner.id, order.id],
|
||||
);
|
||||
const cancelNotif = rows.find(
|
||||
(n) => n.title_en === "Your order was cancelled",
|
||||
);
|
||||
assert.ok(cancelNotif, "expected a cancellation notification for the owner");
|
||||
assert.match(
|
||||
cancelNotif.body_en,
|
||||
/claimed but later cancelled by the receiver/,
|
||||
);
|
||||
assert.match(cancelNotif.body_ar, /استلم طلبك ولكن تم إلغاؤه لاحقاً/);
|
||||
});
|
||||
|
||||
test("unauthenticated cannot place order", async () => {
|
||||
const res = await fetch(`${API_BASE}/api/orders`, {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user