From e1a6a400340d1ab3145d850f2cde9936f72af7c1 Mon Sep 17 00:00:00 2001 From: Caesar Mukama Date: Wed, 24 Jun 2026 13:04:34 +0300 Subject: [PATCH] Feat: add work order reopen route Claude-Session: https://claude.ai/code/session_018SZfbTWwga6AnWp6HVY35a --- tests/unit/handlers/work.orders.handlers.test.js | 14 ++++++++++++++ tests/unit/routes/work.orders.routes.test.js | 1 + workers/lib/constants.js | 4 +++- .../lib/server/handlers/work.orders.handlers.js | 7 +++++++ workers/lib/server/routes/work.orders.routes.js | 7 +++++++ workers/lib/server/schemas/work.orders.schemas.js | 11 ++++++++++- 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/tests/unit/handlers/work.orders.handlers.test.js b/tests/unit/handlers/work.orders.handlers.test.js index de3215d..32989f7 100644 --- a/tests/unit/handlers/work.orders.handlers.test.js +++ b/tests/unit/handlers/work.orders.handlers.test.js @@ -261,6 +261,20 @@ test('handlers: cancelWorkOrder maps to updateThing with status=cancelled', asyn t.is(flow.lastPush.params[0].info.cancelReason, 'duplicate') }) +test('handlers: reopenWorkOrder maps to updateThing with status=open and clears closedAt', async (t) => { + const flow = buildSubmitFlow() + await handlers.reopenWorkOrder(flow.ctx, { + ...userMeta(), + params: { id: 'wo-1' }, + body: { reason: 'rework needed' } + }) + t.is(flow.lastPush.action, 'updateThing') + t.is(flow.lastPush.params[0].id, 'wo-1') + t.is(flow.lastPush.params[0].info.status, 'open') + t.is(flow.lastPush.params[0].info.closedAt, null, 'clears closedAt') + t.is(flow.lastPush.params[0].info.reopenReason, 'rework needed') +}) + test('handlers: assignWorkOrder maps to updateThing with assignedTo', async (t) => { const flow = buildSubmitFlow() await handlers.assignWorkOrder(flow.ctx, { diff --git a/tests/unit/routes/work.orders.routes.test.js b/tests/unit/routes/work.orders.routes.test.js index b25b723..56e49cf 100644 --- a/tests/unit/routes/work.orders.routes.test.js +++ b/tests/unit/routes/work.orders.routes.test.js @@ -26,6 +26,7 @@ test('work.orders.routes: registers every WO endpoint', (t) => { { method: HTTP_METHODS.PATCH, url: ENDPOINTS.WORK_ORDER_BY_ID }, { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_CLOSE }, { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_CANCEL }, + { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_REOPEN }, { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_ASSIGN }, { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_LOG }, { method: HTTP_METHODS.GET, url: ENDPOINTS.WORK_ORDER_EXPORT }, diff --git a/workers/lib/constants.js b/workers/lib/constants.js index 85f424b..3b410d9 100644 --- a/workers/lib/constants.js +++ b/workers/lib/constants.js @@ -209,6 +209,7 @@ const ENDPOINTS = { WORK_ORDER_ASSIGN: '/auth/work-orders/:id/assign', WORK_ORDER_CLOSE: '/auth/work-orders/:id/close', WORK_ORDER_CANCEL: '/auth/work-orders/:id/cancel', + WORK_ORDER_REOPEN: '/auth/work-orders/:id/reopen', // Spare Part endpoints SPARE_PARTS: '/auth/spare-parts', SPARE_PARTS_BATCH: '/auth/spare-parts/batch', @@ -267,7 +268,8 @@ const OPERATIONS = { WORK_ORDER_UPDATE: 'work_order.update', WORK_ORDER_CLOSE: 'work_order.close', WORK_ORDER_CANCEL: 'work_order.cancel', - WORK_ORDER_ASSIGN: 'work_order.assign' + WORK_ORDER_ASSIGN: 'work_order.assign', + WORK_ORDER_REOPEN: 'work_order.reopen' } const DEFAULTS = { diff --git a/workers/lib/server/handlers/work.orders.handlers.js b/workers/lib/server/handlers/work.orders.handlers.js index 7e2ace9..65170e3 100644 --- a/workers/lib/server/handlers/work.orders.handlers.js +++ b/workers/lib/server/handlers/work.orders.handlers.js @@ -179,6 +179,12 @@ async function cancelWorkOrder (ctx, req) { return submitWorkOrderAction(ctx, req, 'updateThing', { id: req.params.id, info }) } +async function reopenWorkOrder (ctx, req) { + const info = { status: 'open', closedAt: null } + if (req.body?.reason) info.reopenReason = req.body.reason + return submitWorkOrderAction(ctx, req, 'updateThing', { id: req.params.id, info }) +} + async function assignWorkOrder (ctx, req) { return submitWorkOrderAction(ctx, req, 'updateThing', { id: req.params.id, @@ -332,6 +338,7 @@ module.exports = { updateWorkOrder, closeWorkOrder, cancelWorkOrder, + reopenWorkOrder, assignWorkOrder, appendWorkLogEntry, getWorkOrderAudit, diff --git a/workers/lib/server/routes/work.orders.routes.js b/workers/lib/server/routes/work.orders.routes.js index 9f73541..c80b2f5 100644 --- a/workers/lib/server/routes/work.orders.routes.js +++ b/workers/lib/server/routes/work.orders.routes.js @@ -14,6 +14,7 @@ const { updateWorkOrder, closeWorkOrder, cancelWorkOrder, + reopenWorkOrder, assignWorkOrder, appendWorkLogEntry, getWorkOrderAudit, @@ -105,6 +106,12 @@ module.exports = (ctx) => [ schema: schemas.cancel, ...createAuthRoute(ctx, cancelWorkOrder, [AUTH_PERMISSIONS.WORK_ORDER]) }, + { + method: HTTP_METHODS.POST, + url: ENDPOINTS.WORK_ORDER_REOPEN, + schema: schemas.reopen, + ...createAuthRoute(ctx, reopenWorkOrder, [AUTH_PERMISSIONS.WORK_ORDER]) + }, { method: HTTP_METHODS.POST, url: ENDPOINTS.WORK_ORDER_ASSIGN, diff --git a/workers/lib/server/schemas/work.orders.schemas.js b/workers/lib/server/schemas/work.orders.schemas.js index 16d7e3a..63670f0 100644 --- a/workers/lib/server/schemas/work.orders.schemas.js +++ b/workers/lib/server/schemas/work.orders.schemas.js @@ -181,6 +181,15 @@ const cancel = { } } +const reopen = { + params: byId.params, + body: { + type: 'object', + additionalProperties: false, + properties: { reason: { type: 'string', minLength: 1, maxLength: 2000 } } + } +} + const assign = { params: byId.params, body: { @@ -238,4 +247,4 @@ const exportRma = { } } -module.exports = { create, createBatch, list, byId, update, close, cancel, assign, audit, log, export: exportRoute, exportRma } +module.exports = { create, createBatch, list, byId, update, close, cancel, reopen, assign, audit, log, export: exportRoute, exportRma }