diff --git a/artifacts/api-server/src/routes/service-orders.ts b/artifacts/api-server/src/routes/service-orders.ts index d1988e49..20a56aa5 100644 --- a/artifacts/api-server/src/routes/service-orders.ts +++ b/artifacts/api-server/src/routes/service-orders.ts @@ -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, ); } diff --git a/artifacts/api-server/tests/service-orders.test.mjs b/artifacts/api-server/tests/service-orders.test.mjs index 171080cf..f94a3630 100644 --- a/artifacts/api-server/tests/service-orders.test.mjs +++ b/artifacts/api-server/tests/service-orders.test.mjs @@ -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",