Hide self-notifications across order lifecycle
Task: Audit notification rules in service-orders.ts so users never
receive notifications about actions they themselves initiated. The
specific scenario: a user with the `orders.receive` permission who
cancels their own assigned order would notify themselves under the
old logic.
Approach:
- Added an optional `actorId` parameter to `notifyUser`. When the
notification recipient equals the actor, the function returns
early (no DB insert, no socket emit).
- Threaded `actorId` through the three notification call sites:
1. POST /orders — placing an order no longer notifies the placer
even if they also hold the receiver role.
2. PATCH /orders/:id/confirm-receipt — a receiver claiming their
own order no longer notifies themselves.
3. PATCH /orders/:id/status — replaced the narrow
`!initiatedByOwner` guard with the generic `actorId === userId`
check inside `notifyUser`. This now covers owner-cancel as
before AND owner-as-assignee transitions (preparing/completed/
cancelled) where the owner happens to also be the assignee.
Notes / deviations:
- Kept the existing distinguishing wording for receiver-initiated
cancels ("claimed but later cancelled by the receiver"); it's
simply suppressed when the receiver is also the owner.
- No schema or API contract changes; purely server-side notification
filtering. Typecheck passes.
- No follow-ups proposed: existing project tasks already cover
automated tests for order/cancel flow, re-order from history, and
an undo window for cancellations.
Replit-Task-Id: 65b6d89a-6882-42f8-adb7-6ac1a2560748
This commit is contained in:
@@ -98,7 +98,10 @@ async function notifyUser(
|
||||
bodyAr: string,
|
||||
bodyEn: string,
|
||||
orderId: number,
|
||||
actorId?: number,
|
||||
) {
|
||||
// Never notify users about actions they themselves initiated.
|
||||
if (actorId !== undefined && actorId === userId) return;
|
||||
const [notification] = await db
|
||||
.insert(notificationsTable)
|
||||
.values({
|
||||
@@ -163,6 +166,7 @@ router.post("/orders", requireAuth, async (req, res): Promise<void> => {
|
||||
service.nameAr,
|
||||
service.nameEn,
|
||||
order.id,
|
||||
userId,
|
||||
);
|
||||
}
|
||||
await broadcastIncomingChanged(receivers);
|
||||
@@ -252,6 +256,7 @@ router.patch(
|
||||
enriched.service.nameAr,
|
||||
enriched.service.nameEn,
|
||||
orderId,
|
||||
userId,
|
||||
);
|
||||
await emitToUser(enriched.userId, "order_updated", enriched);
|
||||
}
|
||||
@@ -339,9 +344,10 @@ router.patch(
|
||||
|
||||
const enriched = await loadOrder(orderId);
|
||||
|
||||
// Notify owner on every status change initiated by a receiver/admin (not by owner cancel)
|
||||
const initiatedByOwner = existing.userId === userId;
|
||||
if (!initiatedByOwner && enriched) {
|
||||
// Notify owner on every status change — notifyUser will skip if the
|
||||
// owner themselves initiated the action (e.g. owner-cancel, or an
|
||||
// assignee transition where the owner also holds the receiver role).
|
||||
if (enriched) {
|
||||
const titleMap: Record<string, [string, string]> = {
|
||||
preparing: ["طلبك قيد التحضير", "Your order is being prepared"],
|
||||
completed: ["تم إكمال طلبك", "Your order is completed"],
|
||||
@@ -362,6 +368,7 @@ router.patch(
|
||||
bodyAr,
|
||||
bodyEn,
|
||||
orderId,
|
||||
userId,
|
||||
);
|
||||
}
|
||||
if (enriched) await emitToUser(enriched.userId, "order_updated", enriched);
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 25 KiB |
Reference in New Issue
Block a user