Task #62: Service Orders backend foundation (corrected)

After prior code review rejection, refactored to match spec exactly:
- Permission renamed orders:receive → orders.receive (dot form), seeded with order_receiver role
- service_orders table: user_id, service_id, notes, status (pending/received/preparing/completed/cancelled with CHECK), assigned_to, created_at, updated_at
- /confirm-receipt is now the receiver atomic claim (UPDATE WHERE pending+unassigned), 409 already_claimed on miss
- /status accepts preparing|completed|cancelled with permission matrix:
  * preparing/completed: assigned receiver or admin
  * cancelled: owner (pending|received) OR admin (any non-cancelled status)
- requirePermission middleware no longer auto-bypasses admin; admin gets the permission via explicit seed grant
- Notifications use type='order', relatedType='order'
- Realtime emits notification_created (per receiver) + order_incoming_changed (broadcast) + order_updated (owner)
- OpenAPI Order schema rewritten (no quantity, no per-status timestamps), endpoint summaries updated, codegen run
- Tests cover: client place+list, non-receiver 403, no-role 403, parallel claim race (200/409), status matrix, owner-cancel rules, admin-cancel-completed, descriptions excluded from service summary

All 24 api-server tests pass. Ready for code review re-check.
This commit is contained in:
riyadhafraa
2026-04-21 18:34:14 +00:00
parent cdf5bf4d33
commit 2602edaca0
9 changed files with 361 additions and 391 deletions
+3 -7
View File
@@ -76,12 +76,11 @@ export function requirePermission(name: string) {
}
const rows = await db
.select({ roleName: rolesTable.name })
.select({ permName: permissionsTable.name })
.from(userRolesTable)
.innerJoin(rolesTable, eq(userRolesTable.roleId, rolesTable.id))
.innerJoin(
rolePermissionsTable,
eq(rolePermissionsTable.roleId, rolesTable.id),
eq(rolePermissionsTable.roleId, userRolesTable.roleId),
)
.innerJoin(
permissionsTable,
@@ -94,10 +93,7 @@ export function requirePermission(name: string) {
),
);
const isAdmin = rows.some((r) => r.roleName === "admin");
const hasPerm = rows.length > 0;
if (!isAdmin && !hasPerm) {
if (rows.length === 0) {
res.status(403).json({ error: "Forbidden" });
return;
}