@naisys/erp 3.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @naisys/erp might be problematic. Click here for more details.
- package/bin/naisys-erp +2 -0
- package/client-dist/android-chrome-192x192.png +0 -0
- package/client-dist/android-chrome-512x512.png +0 -0
- package/client-dist/apple-touch-icon.png +0 -0
- package/client-dist/assets/index-45dVo30p.css +1 -0
- package/client-dist/assets/index-Dffms7F_.js +168 -0
- package/client-dist/assets/naisys-logo-CzoPnn5I.webp +0 -0
- package/client-dist/favicon.ico +0 -0
- package/client-dist/index.html +42 -0
- package/client-dist/site.webmanifest +22 -0
- package/dist/api-reference.d.ts +10 -0
- package/dist/api-reference.js +101 -0
- package/dist/audit.d.ts +5 -0
- package/dist/audit.js +14 -0
- package/dist/auth-middleware.d.ts +18 -0
- package/dist/auth-middleware.js +203 -0
- package/dist/dbConfig.d.ts +5 -0
- package/dist/dbConfig.js +10 -0
- package/dist/erpDb.d.ts +10 -0
- package/dist/erpDb.js +34 -0
- package/dist/erpServer.d.ts +10 -0
- package/dist/erpServer.js +321 -0
- package/dist/error-handler.d.ts +7 -0
- package/dist/error-handler.js +17 -0
- package/dist/generated/prisma/client.d.ts +154 -0
- package/dist/generated/prisma/client.js +35 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +637 -0
- package/dist/generated/prisma/commonInputTypes.js +11 -0
- package/dist/generated/prisma/enums.d.ts +59 -0
- package/dist/generated/prisma/enums.js +60 -0
- package/dist/generated/prisma/internal/class.d.ts +406 -0
- package/dist/generated/prisma/internal/class.js +50 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +2722 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +366 -0
- package/dist/generated/prisma/models/Attachment.d.ts +1455 -0
- package/dist/generated/prisma/models/Attachment.js +2 -0
- package/dist/generated/prisma/models/AuditLog.d.ts +1359 -0
- package/dist/generated/prisma/models/AuditLog.js +2 -0
- package/dist/generated/prisma/models/Field.d.ts +1880 -0
- package/dist/generated/prisma/models/Field.js +2 -0
- package/dist/generated/prisma/models/FieldAttachment.d.ts +1245 -0
- package/dist/generated/prisma/models/FieldAttachment.js +2 -0
- package/dist/generated/prisma/models/FieldRecord.d.ts +1625 -0
- package/dist/generated/prisma/models/FieldRecord.js +2 -0
- package/dist/generated/prisma/models/FieldSet.d.ts +1577 -0
- package/dist/generated/prisma/models/FieldSet.js +2 -0
- package/dist/generated/prisma/models/FieldValue.d.ts +1908 -0
- package/dist/generated/prisma/models/FieldValue.js +2 -0
- package/dist/generated/prisma/models/Item.d.ts +1858 -0
- package/dist/generated/prisma/models/Item.js +2 -0
- package/dist/generated/prisma/models/ItemInstance.d.ts +1987 -0
- package/dist/generated/prisma/models/ItemInstance.js +2 -0
- package/dist/generated/prisma/models/LaborTicket.d.ts +1867 -0
- package/dist/generated/prisma/models/LaborTicket.js +2 -0
- package/dist/generated/prisma/models/Operation.d.ts +2578 -0
- package/dist/generated/prisma/models/Operation.js +2 -0
- package/dist/generated/prisma/models/OperationDependency.d.ts +1434 -0
- package/dist/generated/prisma/models/OperationDependency.js +2 -0
- package/dist/generated/prisma/models/OperationFieldRef.d.ts +1539 -0
- package/dist/generated/prisma/models/OperationFieldRef.js +2 -0
- package/dist/generated/prisma/models/OperationRun.d.ts +2563 -0
- package/dist/generated/prisma/models/OperationRun.js +2 -0
- package/dist/generated/prisma/models/OperationRunComment.d.ts +1366 -0
- package/dist/generated/prisma/models/OperationRunComment.js +2 -0
- package/dist/generated/prisma/models/Order.d.ts +1931 -0
- package/dist/generated/prisma/models/Order.js +2 -0
- package/dist/generated/prisma/models/OrderRevision.d.ts +1962 -0
- package/dist/generated/prisma/models/OrderRevision.js +2 -0
- package/dist/generated/prisma/models/OrderRun.d.ts +2310 -0
- package/dist/generated/prisma/models/OrderRun.js +2 -0
- package/dist/generated/prisma/models/SchemaVersion.d.ts +985 -0
- package/dist/generated/prisma/models/SchemaVersion.js +2 -0
- package/dist/generated/prisma/models/Session.d.ts +1213 -0
- package/dist/generated/prisma/models/Session.js +2 -0
- package/dist/generated/prisma/models/Step.d.ts +2180 -0
- package/dist/generated/prisma/models/Step.js +2 -0
- package/dist/generated/prisma/models/StepRun.d.ts +1963 -0
- package/dist/generated/prisma/models/StepRun.js +2 -0
- package/dist/generated/prisma/models/User.d.ts +11819 -0
- package/dist/generated/prisma/models/User.js +2 -0
- package/dist/generated/prisma/models/UserPermission.d.ts +1348 -0
- package/dist/generated/prisma/models/UserPermission.js +2 -0
- package/dist/generated/prisma/models/WorkCenter.d.ts +1657 -0
- package/dist/generated/prisma/models/WorkCenter.js +2 -0
- package/dist/generated/prisma/models/WorkCenterUser.d.ts +1390 -0
- package/dist/generated/prisma/models/WorkCenterUser.js +2 -0
- package/dist/generated/prisma/models.d.ts +28 -0
- package/dist/generated/prisma/models.js +2 -0
- package/dist/hateoas.d.ts +7 -0
- package/dist/hateoas.js +61 -0
- package/dist/route-helpers.d.ts +318 -0
- package/dist/route-helpers.js +220 -0
- package/dist/routes/admin.d.ts +3 -0
- package/dist/routes/admin.js +147 -0
- package/dist/routes/audit.d.ts +3 -0
- package/dist/routes/audit.js +36 -0
- package/dist/routes/auth.d.ts +3 -0
- package/dist/routes/auth.js +112 -0
- package/dist/routes/dispatch.d.ts +3 -0
- package/dist/routes/dispatch.js +174 -0
- package/dist/routes/inventory.d.ts +3 -0
- package/dist/routes/inventory.js +70 -0
- package/dist/routes/item-fields.d.ts +3 -0
- package/dist/routes/item-fields.js +220 -0
- package/dist/routes/item-instances.d.ts +3 -0
- package/dist/routes/item-instances.js +426 -0
- package/dist/routes/items.d.ts +3 -0
- package/dist/routes/items.js +252 -0
- package/dist/routes/labor-tickets.d.ts +3 -0
- package/dist/routes/labor-tickets.js +268 -0
- package/dist/routes/operation-dependencies.d.ts +3 -0
- package/dist/routes/operation-dependencies.js +170 -0
- package/dist/routes/operation-field-refs.d.ts +3 -0
- package/dist/routes/operation-field-refs.js +263 -0
- package/dist/routes/operation-run-comments.d.ts +3 -0
- package/dist/routes/operation-run-comments.js +108 -0
- package/dist/routes/operation-run-transitions.d.ts +3 -0
- package/dist/routes/operation-run-transitions.js +249 -0
- package/dist/routes/operation-runs.d.ts +112 -0
- package/dist/routes/operation-runs.js +299 -0
- package/dist/routes/operations.d.ts +3 -0
- package/dist/routes/operations.js +283 -0
- package/dist/routes/order-revision-transitions.d.ts +3 -0
- package/dist/routes/order-revision-transitions.js +86 -0
- package/dist/routes/order-revisions.d.ts +51 -0
- package/dist/routes/order-revisions.js +327 -0
- package/dist/routes/order-run-transitions.d.ts +3 -0
- package/dist/routes/order-run-transitions.js +215 -0
- package/dist/routes/order-runs.d.ts +58 -0
- package/dist/routes/order-runs.js +335 -0
- package/dist/routes/orders.d.ts +3 -0
- package/dist/routes/orders.js +262 -0
- package/dist/routes/root.d.ts +3 -0
- package/dist/routes/root.js +123 -0
- package/dist/routes/schemas.d.ts +3 -0
- package/dist/routes/schemas.js +31 -0
- package/dist/routes/step-field-attachments.d.ts +3 -0
- package/dist/routes/step-field-attachments.js +231 -0
- package/dist/routes/step-fields.d.ts +100 -0
- package/dist/routes/step-fields.js +315 -0
- package/dist/routes/step-run-fields.d.ts +3 -0
- package/dist/routes/step-run-fields.js +438 -0
- package/dist/routes/step-run-transitions.d.ts +3 -0
- package/dist/routes/step-run-transitions.js +113 -0
- package/dist/routes/step-runs.d.ts +332 -0
- package/dist/routes/step-runs.js +324 -0
- package/dist/routes/steps.d.ts +3 -0
- package/dist/routes/steps.js +283 -0
- package/dist/routes/user-permissions.d.ts +3 -0
- package/dist/routes/user-permissions.js +100 -0
- package/dist/routes/users.d.ts +57 -0
- package/dist/routes/users.js +381 -0
- package/dist/routes/work-centers.d.ts +3 -0
- package/dist/routes/work-centers.js +280 -0
- package/dist/schema-registry.d.ts +3 -0
- package/dist/schema-registry.js +45 -0
- package/dist/services/attachment-service.d.ts +33 -0
- package/dist/services/attachment-service.js +118 -0
- package/dist/services/field-ref-service.d.ts +96 -0
- package/dist/services/field-ref-service.js +74 -0
- package/dist/services/field-service.d.ts +49 -0
- package/dist/services/field-service.js +114 -0
- package/dist/services/field-value-service.d.ts +61 -0
- package/dist/services/field-value-service.js +256 -0
- package/dist/services/item-instance-service.d.ts +152 -0
- package/dist/services/item-instance-service.js +155 -0
- package/dist/services/item-service.d.ts +47 -0
- package/dist/services/item-service.js +56 -0
- package/dist/services/labor-ticket-service.d.ts +40 -0
- package/dist/services/labor-ticket-service.js +148 -0
- package/dist/services/log-file-service.d.ts +4 -0
- package/dist/services/log-file-service.js +11 -0
- package/dist/services/operation-dependency-service.d.ts +33 -0
- package/dist/services/operation-dependency-service.js +30 -0
- package/dist/services/operation-run-comment-service.d.ts +17 -0
- package/dist/services/operation-run-comment-service.js +26 -0
- package/dist/services/operation-run-service.d.ts +126 -0
- package/dist/services/operation-run-service.js +347 -0
- package/dist/services/operation-service.d.ts +47 -0
- package/dist/services/operation-service.js +132 -0
- package/dist/services/order-revision-service.d.ts +53 -0
- package/dist/services/order-revision-service.js +264 -0
- package/dist/services/order-run-service.d.ts +138 -0
- package/dist/services/order-run-service.js +356 -0
- package/dist/services/order-service.d.ts +15 -0
- package/dist/services/order-service.js +68 -0
- package/dist/services/revision-diff-service.d.ts +3 -0
- package/dist/services/revision-diff-service.js +194 -0
- package/dist/services/step-run-service.d.ts +172 -0
- package/dist/services/step-run-service.js +106 -0
- package/dist/services/step-service.d.ts +104 -0
- package/dist/services/step-service.js +89 -0
- package/dist/services/user-service.d.ts +185 -0
- package/dist/services/user-service.js +132 -0
- package/dist/services/work-center-service.d.ts +29 -0
- package/dist/services/work-center-service.js +106 -0
- package/dist/supervisorAuth.d.ts +3 -0
- package/dist/supervisorAuth.js +16 -0
- package/dist/userService.d.ts +20 -0
- package/dist/userService.js +118 -0
- package/package.json +69 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { ErrorResponseSchema, OperationRunStatus, OperationRunTransitionSlimSchema, OrderRunStatus, TransitionNoteSchema, } from "@naisys/erp-shared";
|
|
2
|
+
import { requirePermission } from "../auth-middleware.js";
|
|
3
|
+
import { conflict, notFound, unprocessable } from "../error-handler.js";
|
|
4
|
+
import { checkOrderRunStarted, checkWorkCenterAccess, mutationResult, resolveOpRun, } from "../route-helpers.js";
|
|
5
|
+
import { clockIn, clockOutAllForOpRun, isUserClockedIn, sumLaborTicketCosts, } from "../services/labor-ticket-service.js";
|
|
6
|
+
import { checkPredecessorsComplete, checkStepsComplete, reblockSuccessors, transitionStatus, unblockSuccessors, validateStatusFor, } from "../services/operation-run-service.js";
|
|
7
|
+
import { transitionStatus as transitionOrderRunStatus } from "../services/order-run-service.js";
|
|
8
|
+
import { formatOpRunTransition, SeqNoParamsSchema } from "./operation-runs.js";
|
|
9
|
+
export default function operationRunTransitionRoutes(fastify) {
|
|
10
|
+
const app = fastify.withTypeProvider();
|
|
11
|
+
// START (pending → in_progress)
|
|
12
|
+
app.post("/:seqNo/start", {
|
|
13
|
+
schema: {
|
|
14
|
+
description: "Start an operation run (pending → in_progress)",
|
|
15
|
+
tags: ["Operation Runs"],
|
|
16
|
+
params: SeqNoParamsSchema,
|
|
17
|
+
body: TransitionNoteSchema,
|
|
18
|
+
response: {
|
|
19
|
+
200: OperationRunTransitionSlimSchema,
|
|
20
|
+
404: ErrorResponseSchema,
|
|
21
|
+
409: ErrorResponseSchema,
|
|
22
|
+
422: ErrorResponseSchema,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
preHandler: requirePermission("order_executor"),
|
|
26
|
+
handler: async (request, reply) => {
|
|
27
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
28
|
+
const { note } = request.body;
|
|
29
|
+
const userId = request.erpUser.id;
|
|
30
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
31
|
+
if (!resolved)
|
|
32
|
+
return notFound(reply, `Operation run not found`);
|
|
33
|
+
const wcErr = await checkWorkCenterAccess(resolved.opRun.operationId, request.erpUser);
|
|
34
|
+
if (wcErr)
|
|
35
|
+
return conflict(reply, wcErr);
|
|
36
|
+
// Auto-start the order run if it's still in released status
|
|
37
|
+
if (resolved.run.status === OrderRunStatus.released) {
|
|
38
|
+
await transitionOrderRunStatus(resolved.run.id, "start", OrderRunStatus.released, OrderRunStatus.started, userId);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
42
|
+
if (orderErr)
|
|
43
|
+
return conflict(reply, orderErr);
|
|
44
|
+
}
|
|
45
|
+
const statusErr = validateStatusFor("start", resolved.opRun.status, [
|
|
46
|
+
OperationRunStatus.pending,
|
|
47
|
+
]);
|
|
48
|
+
if (statusErr)
|
|
49
|
+
return conflict(reply, statusErr);
|
|
50
|
+
const priorErr = await checkPredecessorsComplete(resolved.run.id, resolved.opRun.operationId);
|
|
51
|
+
if (priorErr)
|
|
52
|
+
return unprocessable(reply, priorErr);
|
|
53
|
+
const opRun = await transitionStatus(resolved.opRun.id, "start", OperationRunStatus.pending, OperationRunStatus.in_progress, userId, { assignedToId: userId, statusNote: note ?? null });
|
|
54
|
+
await clockIn(resolved.opRun.id, userId, userId);
|
|
55
|
+
const full = await formatOpRunTransition(orderKey, runNo, request.erpUser, opRun);
|
|
56
|
+
return mutationResult(request, reply, full, {
|
|
57
|
+
status: opRun.status,
|
|
58
|
+
_actions: full._actions,
|
|
59
|
+
});
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
// COMPLETE (in_progress → completed)
|
|
63
|
+
app.post("/:seqNo/complete", {
|
|
64
|
+
schema: {
|
|
65
|
+
description: "Complete an operation run (in_progress → completed)",
|
|
66
|
+
tags: ["Operation Runs"],
|
|
67
|
+
params: SeqNoParamsSchema,
|
|
68
|
+
body: TransitionNoteSchema,
|
|
69
|
+
response: {
|
|
70
|
+
200: OperationRunTransitionSlimSchema,
|
|
71
|
+
404: ErrorResponseSchema,
|
|
72
|
+
409: ErrorResponseSchema,
|
|
73
|
+
422: ErrorResponseSchema,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
preHandler: requirePermission("order_executor"),
|
|
77
|
+
handler: async (request, reply) => {
|
|
78
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
79
|
+
const { note } = request.body;
|
|
80
|
+
const userId = request.erpUser.id;
|
|
81
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
82
|
+
if (!resolved)
|
|
83
|
+
return notFound(reply, `Operation run not found`);
|
|
84
|
+
const wcErr = await checkWorkCenterAccess(resolved.opRun.operationId, request.erpUser);
|
|
85
|
+
if (wcErr)
|
|
86
|
+
return conflict(reply, wcErr);
|
|
87
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
88
|
+
if (orderErr)
|
|
89
|
+
return conflict(reply, orderErr);
|
|
90
|
+
const statusErr = validateStatusFor("complete", resolved.opRun.status, [
|
|
91
|
+
OperationRunStatus.in_progress,
|
|
92
|
+
]);
|
|
93
|
+
if (statusErr)
|
|
94
|
+
return conflict(reply, statusErr);
|
|
95
|
+
const clockedIn = await isUserClockedIn(resolved.opRun.id, userId);
|
|
96
|
+
if (!clockedIn)
|
|
97
|
+
return conflict(reply, `You must be clocked in to complete an operation`);
|
|
98
|
+
const stepsErr = await checkStepsComplete(resolved.opRun.id);
|
|
99
|
+
if (stepsErr)
|
|
100
|
+
return unprocessable(reply, stepsErr);
|
|
101
|
+
await clockOutAllForOpRun(resolved.opRun.id, userId);
|
|
102
|
+
const cost = await sumLaborTicketCosts(resolved.opRun.id);
|
|
103
|
+
const opRun = await transitionStatus(resolved.opRun.id, "complete", OperationRunStatus.in_progress, OperationRunStatus.completed, userId, {
|
|
104
|
+
completedAt: new Date(),
|
|
105
|
+
cost,
|
|
106
|
+
statusNote: note ?? null,
|
|
107
|
+
});
|
|
108
|
+
await unblockSuccessors(resolved.run.id, resolved.opRun.operationId, userId);
|
|
109
|
+
const full = await formatOpRunTransition(orderKey, runNo, request.erpUser, opRun);
|
|
110
|
+
return mutationResult(request, reply, full, {
|
|
111
|
+
status: opRun.status,
|
|
112
|
+
_actions: full._actions,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
// SKIP (pending → skipped)
|
|
117
|
+
app.post("/:seqNo/skip", {
|
|
118
|
+
schema: {
|
|
119
|
+
description: "Skip an operation run (pending → skipped)",
|
|
120
|
+
tags: ["Operation Runs"],
|
|
121
|
+
params: SeqNoParamsSchema,
|
|
122
|
+
body: TransitionNoteSchema,
|
|
123
|
+
response: {
|
|
124
|
+
200: OperationRunTransitionSlimSchema,
|
|
125
|
+
404: ErrorResponseSchema,
|
|
126
|
+
409: ErrorResponseSchema,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
preHandler: requirePermission("order_manager"),
|
|
130
|
+
handler: async (request, reply) => {
|
|
131
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
132
|
+
const { note } = request.body;
|
|
133
|
+
const userId = request.erpUser.id;
|
|
134
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
135
|
+
if (!resolved)
|
|
136
|
+
return notFound(reply, `Operation run not found`);
|
|
137
|
+
const wcErr = await checkWorkCenterAccess(resolved.opRun.operationId, request.erpUser);
|
|
138
|
+
if (wcErr)
|
|
139
|
+
return conflict(reply, wcErr);
|
|
140
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
141
|
+
if (orderErr)
|
|
142
|
+
return conflict(reply, orderErr);
|
|
143
|
+
const statusErr = validateStatusFor("skip", resolved.opRun.status, [
|
|
144
|
+
OperationRunStatus.blocked,
|
|
145
|
+
OperationRunStatus.pending,
|
|
146
|
+
]);
|
|
147
|
+
if (statusErr)
|
|
148
|
+
return conflict(reply, statusErr);
|
|
149
|
+
const cost = await sumLaborTicketCosts(resolved.opRun.id);
|
|
150
|
+
const opRun = await transitionStatus(resolved.opRun.id, "skip", resolved.opRun.status, OperationRunStatus.skipped, userId, { ...(cost > 0 ? { cost } : undefined), statusNote: note ?? null });
|
|
151
|
+
await unblockSuccessors(resolved.run.id, resolved.opRun.operationId, userId);
|
|
152
|
+
const full = await formatOpRunTransition(orderKey, runNo, request.erpUser, opRun);
|
|
153
|
+
return mutationResult(request, reply, full, {
|
|
154
|
+
status: opRun.status,
|
|
155
|
+
_actions: full._actions,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
// FAIL (in_progress → failed)
|
|
160
|
+
app.post("/:seqNo/fail", {
|
|
161
|
+
schema: {
|
|
162
|
+
description: "Fail an operation run (in_progress → failed)",
|
|
163
|
+
tags: ["Operation Runs"],
|
|
164
|
+
params: SeqNoParamsSchema,
|
|
165
|
+
body: TransitionNoteSchema,
|
|
166
|
+
response: {
|
|
167
|
+
200: OperationRunTransitionSlimSchema,
|
|
168
|
+
404: ErrorResponseSchema,
|
|
169
|
+
409: ErrorResponseSchema,
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
preHandler: requirePermission("order_manager"),
|
|
173
|
+
handler: async (request, reply) => {
|
|
174
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
175
|
+
const { note } = request.body;
|
|
176
|
+
const userId = request.erpUser.id;
|
|
177
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
178
|
+
if (!resolved)
|
|
179
|
+
return notFound(reply, `Operation run not found`);
|
|
180
|
+
const wcErr = await checkWorkCenterAccess(resolved.opRun.operationId, request.erpUser);
|
|
181
|
+
if (wcErr)
|
|
182
|
+
return conflict(reply, wcErr);
|
|
183
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
184
|
+
if (orderErr)
|
|
185
|
+
return conflict(reply, orderErr);
|
|
186
|
+
const statusErr = validateStatusFor("fail", resolved.opRun.status, [
|
|
187
|
+
OperationRunStatus.in_progress,
|
|
188
|
+
]);
|
|
189
|
+
if (statusErr)
|
|
190
|
+
return conflict(reply, statusErr);
|
|
191
|
+
await clockOutAllForOpRun(resolved.opRun.id, userId);
|
|
192
|
+
const cost = await sumLaborTicketCosts(resolved.opRun.id);
|
|
193
|
+
const opRun = await transitionStatus(resolved.opRun.id, "fail", OperationRunStatus.in_progress, OperationRunStatus.failed, userId, { ...(cost > 0 ? { cost } : undefined), statusNote: note ?? null });
|
|
194
|
+
const full = await formatOpRunTransition(orderKey, runNo, request.erpUser, opRun);
|
|
195
|
+
return mutationResult(request, reply, full, {
|
|
196
|
+
status: opRun.status,
|
|
197
|
+
_actions: full._actions,
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
// REOPEN (completed/skipped/failed → in_progress/pending)
|
|
202
|
+
app.post("/:seqNo/reopen", {
|
|
203
|
+
schema: {
|
|
204
|
+
description: "Reopen an operation run (completed → in_progress)",
|
|
205
|
+
tags: ["Operation Runs"],
|
|
206
|
+
params: SeqNoParamsSchema,
|
|
207
|
+
body: TransitionNoteSchema,
|
|
208
|
+
response: {
|
|
209
|
+
200: OperationRunTransitionSlimSchema,
|
|
210
|
+
404: ErrorResponseSchema,
|
|
211
|
+
409: ErrorResponseSchema,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
preHandler: requirePermission("order_manager"),
|
|
215
|
+
handler: async (request, reply) => {
|
|
216
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
217
|
+
const { note } = request.body;
|
|
218
|
+
const userId = request.erpUser.id;
|
|
219
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
220
|
+
if (!resolved)
|
|
221
|
+
return notFound(reply, `Operation run not found`);
|
|
222
|
+
const wcErr = await checkWorkCenterAccess(resolved.opRun.operationId, request.erpUser);
|
|
223
|
+
if (wcErr)
|
|
224
|
+
return conflict(reply, wcErr);
|
|
225
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
226
|
+
if (orderErr)
|
|
227
|
+
return conflict(reply, orderErr);
|
|
228
|
+
const statusErr = validateStatusFor("reopen", resolved.opRun.status, [
|
|
229
|
+
OperationRunStatus.completed,
|
|
230
|
+
OperationRunStatus.skipped,
|
|
231
|
+
OperationRunStatus.failed,
|
|
232
|
+
]);
|
|
233
|
+
if (statusErr)
|
|
234
|
+
return conflict(reply, statusErr);
|
|
235
|
+
const reopenTo = resolved.opRun.status === OperationRunStatus.skipped
|
|
236
|
+
? OperationRunStatus.pending
|
|
237
|
+
: OperationRunStatus.in_progress;
|
|
238
|
+
const opRun = await transitionStatus(resolved.opRun.id, "reopen", resolved.opRun.status, reopenTo, userId, { completedAt: null, statusNote: note ?? null });
|
|
239
|
+
// Re-block successor ops that are still pending
|
|
240
|
+
await reblockSuccessors(resolved.run.id, resolved.opRun.operationId, userId);
|
|
241
|
+
const full = await formatOpRunTransition(orderKey, runNo, request.erpUser, opRun);
|
|
242
|
+
return mutationResult(request, reply, full, {
|
|
243
|
+
status: opRun.status,
|
|
244
|
+
_actions: full._actions,
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=operation-run-transitions.js.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import type { ErpUser } from "../auth-middleware.js";
|
|
4
|
+
import { type OpRunWithOp } from "../services/operation-run-service.js";
|
|
5
|
+
export declare const SeqNoParamsSchema: z.ZodObject<{
|
|
6
|
+
orderKey: z.ZodString;
|
|
7
|
+
runNo: z.ZodCoercedNumber<unknown>;
|
|
8
|
+
seqNo: z.ZodCoercedNumber<unknown>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export declare function formatOpRun(orderKey: string, runNo: number, user: ErpUser | undefined, opRun: OpRunWithOp): Promise<{
|
|
11
|
+
_links: {
|
|
12
|
+
rel: string;
|
|
13
|
+
href: string;
|
|
14
|
+
method?: string | undefined;
|
|
15
|
+
title?: string | undefined;
|
|
16
|
+
schema?: string | undefined;
|
|
17
|
+
}[];
|
|
18
|
+
_actions: {
|
|
19
|
+
rel: string;
|
|
20
|
+
href: string;
|
|
21
|
+
method: string;
|
|
22
|
+
title?: string | undefined;
|
|
23
|
+
schema?: string | undefined;
|
|
24
|
+
body?: Record<string, unknown> | undefined;
|
|
25
|
+
alternateEncoding?: {
|
|
26
|
+
contentType: string;
|
|
27
|
+
fileFields: string[];
|
|
28
|
+
description?: string | undefined;
|
|
29
|
+
} | undefined;
|
|
30
|
+
disabled?: boolean | undefined;
|
|
31
|
+
disabledReason?: string | string[] | undefined;
|
|
32
|
+
}[];
|
|
33
|
+
createdAt: string;
|
|
34
|
+
createdBy: string;
|
|
35
|
+
updatedAt: string;
|
|
36
|
+
updatedBy: string;
|
|
37
|
+
fieldRefSummary?: {
|
|
38
|
+
seqNo: number;
|
|
39
|
+
title: string;
|
|
40
|
+
sourceOpSeqNo: number;
|
|
41
|
+
sourceOpTitle: string;
|
|
42
|
+
sourceStepSeqNo: number;
|
|
43
|
+
sourceStepTitle: string;
|
|
44
|
+
multiSet: boolean;
|
|
45
|
+
fieldValues: {
|
|
46
|
+
fieldId: number;
|
|
47
|
+
fieldSeqNo: number;
|
|
48
|
+
label: string;
|
|
49
|
+
type: string;
|
|
50
|
+
valueFormat: string;
|
|
51
|
+
required: boolean;
|
|
52
|
+
setIndex: number;
|
|
53
|
+
value: string | string[];
|
|
54
|
+
validation: {
|
|
55
|
+
valid: boolean;
|
|
56
|
+
error?: string | undefined;
|
|
57
|
+
};
|
|
58
|
+
attachments?: {
|
|
59
|
+
id: string;
|
|
60
|
+
filename: string;
|
|
61
|
+
fileSize: number;
|
|
62
|
+
downloadHref?: string | undefined;
|
|
63
|
+
}[] | undefined;
|
|
64
|
+
}[];
|
|
65
|
+
}[] | undefined;
|
|
66
|
+
id: number;
|
|
67
|
+
orderRunId: number;
|
|
68
|
+
operationId: number;
|
|
69
|
+
seqNo: number;
|
|
70
|
+
title: string;
|
|
71
|
+
description: string;
|
|
72
|
+
workCenterKey: string | null;
|
|
73
|
+
status: import("../generated/prisma/enums.js").OperationRunStatus;
|
|
74
|
+
assignedTo: string | null;
|
|
75
|
+
cost: number | null;
|
|
76
|
+
note: string | null;
|
|
77
|
+
completedAt: string | null;
|
|
78
|
+
stepSummary: {
|
|
79
|
+
seqNo: number;
|
|
80
|
+
title: string;
|
|
81
|
+
completed: boolean;
|
|
82
|
+
}[];
|
|
83
|
+
}>;
|
|
84
|
+
export declare function formatOpRunTransition(orderKey: string, runNo: number, user: ErpUser | undefined, opRun: OpRunWithOp): Promise<{
|
|
85
|
+
_actions: {
|
|
86
|
+
rel: string;
|
|
87
|
+
href: string;
|
|
88
|
+
method: string;
|
|
89
|
+
title?: string | undefined;
|
|
90
|
+
schema?: string | undefined;
|
|
91
|
+
body?: Record<string, unknown> | undefined;
|
|
92
|
+
alternateEncoding?: {
|
|
93
|
+
contentType: string;
|
|
94
|
+
fileFields: string[];
|
|
95
|
+
description?: string | undefined;
|
|
96
|
+
} | undefined;
|
|
97
|
+
disabled?: boolean | undefined;
|
|
98
|
+
disabledReason?: string | string[] | undefined;
|
|
99
|
+
}[];
|
|
100
|
+
createdAt: string;
|
|
101
|
+
createdBy: string;
|
|
102
|
+
updatedAt: string;
|
|
103
|
+
updatedBy: string;
|
|
104
|
+
id: number;
|
|
105
|
+
status: import("../generated/prisma/enums.js").OperationRunStatus;
|
|
106
|
+
assignedTo: string | null;
|
|
107
|
+
cost: number | null;
|
|
108
|
+
note: string | null;
|
|
109
|
+
completedAt: string | null;
|
|
110
|
+
}>;
|
|
111
|
+
export default function operationRunRoutes(fastify: FastifyInstance): void;
|
|
112
|
+
//# sourceMappingURL=operation-runs.d.ts.map
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { ErrorResponseSchema, MutateResponseSchema, OperationRunListResponseSchema, OperationRunSchema, OperationRunStatus, UpdateOperationRunSchema, } from "@naisys/erp-shared";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { hasPermission, requirePermission } from "../auth-middleware.js";
|
|
4
|
+
import { conflict, notFound } from "../error-handler.js";
|
|
5
|
+
import { API_PREFIX, selfLink } from "../hateoas.js";
|
|
6
|
+
import { checkOrderRunStarted, checkWorkCenterAccess, childItemLinks, formatAuditFields, formatDate, resolveActions, resolveOpRun, resolveOrderRun, useFullSerializer, wantsFullResponse, } from "../route-helpers.js";
|
|
7
|
+
import { checkStepsComplete, getOpRun, getOpRunFieldRefSummary, getOpRunStepSummary, listOpRuns, updateOpRun, validateStatusFor, } from "../services/operation-run-service.js";
|
|
8
|
+
function opRunResource(orderKey, runNo) {
|
|
9
|
+
return `orders/${orderKey}/runs/${runNo}/ops`;
|
|
10
|
+
}
|
|
11
|
+
async function opRunItemActions(orderKey, runNo, seqNo, opRunId, operationId, status, user) {
|
|
12
|
+
const href = `${API_PREFIX}/${opRunResource(orderKey, runNo)}/${seqNo}`;
|
|
13
|
+
const isExecutor = hasPermission(user, "order_executor");
|
|
14
|
+
const stepsErr = isExecutor && status === OperationRunStatus.in_progress
|
|
15
|
+
? await checkStepsComplete(opRunId)
|
|
16
|
+
: null;
|
|
17
|
+
const wcErr = user ? await checkWorkCenterAccess(operationId, user) : null;
|
|
18
|
+
return resolveActions([
|
|
19
|
+
{
|
|
20
|
+
rel: "assign",
|
|
21
|
+
method: "PUT",
|
|
22
|
+
title: "Assign",
|
|
23
|
+
schema: `${API_PREFIX}/schemas/UpdateOperationRun`,
|
|
24
|
+
body: { assignedToId: 0 },
|
|
25
|
+
permission: "order_manager",
|
|
26
|
+
statuses: [
|
|
27
|
+
OperationRunStatus.blocked,
|
|
28
|
+
OperationRunStatus.pending,
|
|
29
|
+
OperationRunStatus.in_progress,
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
rel: "add-comment",
|
|
34
|
+
path: "/comments",
|
|
35
|
+
method: "POST",
|
|
36
|
+
title: "Add Comment",
|
|
37
|
+
schema: `${API_PREFIX}/schemas/CreateOperationRunComment`,
|
|
38
|
+
body: { body: "" },
|
|
39
|
+
permission: "order_executor",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
rel: "start",
|
|
43
|
+
path: "/start",
|
|
44
|
+
method: "POST",
|
|
45
|
+
title: "Start",
|
|
46
|
+
permission: "order_executor",
|
|
47
|
+
statuses: [OperationRunStatus.blocked, OperationRunStatus.pending],
|
|
48
|
+
disabledWhen: (ctx) => wcErr ??
|
|
49
|
+
(ctx.status === OperationRunStatus.blocked
|
|
50
|
+
? "Operation is blocked by incomplete predecessors"
|
|
51
|
+
: null),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
rel: "update",
|
|
55
|
+
method: "PUT",
|
|
56
|
+
title: "Update",
|
|
57
|
+
schema: `${API_PREFIX}/schemas/UpdateOperationRun`,
|
|
58
|
+
body: { assignedToId: 0 },
|
|
59
|
+
permission: "order_executor",
|
|
60
|
+
statuses: [OperationRunStatus.pending, OperationRunStatus.in_progress],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
rel: "complete",
|
|
64
|
+
path: "/complete",
|
|
65
|
+
method: "POST",
|
|
66
|
+
title: "Complete",
|
|
67
|
+
schema: `${API_PREFIX}/schemas/CompleteOperationRun`,
|
|
68
|
+
body: { note: "" },
|
|
69
|
+
permission: "order_executor",
|
|
70
|
+
statuses: [OperationRunStatus.in_progress],
|
|
71
|
+
disabledWhen: () => wcErr ?? stepsErr,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
rel: "skip",
|
|
75
|
+
path: "/skip",
|
|
76
|
+
method: "POST",
|
|
77
|
+
title: "Skip",
|
|
78
|
+
permission: "order_manager",
|
|
79
|
+
statuses: [OperationRunStatus.blocked, OperationRunStatus.pending],
|
|
80
|
+
disabledWhen: () => wcErr,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
rel: "fail",
|
|
84
|
+
path: "/fail",
|
|
85
|
+
method: "POST",
|
|
86
|
+
title: "Fail",
|
|
87
|
+
permission: "order_manager",
|
|
88
|
+
statuses: [OperationRunStatus.in_progress],
|
|
89
|
+
disabledWhen: () => wcErr,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
rel: "reopen",
|
|
93
|
+
path: "/reopen",
|
|
94
|
+
method: "POST",
|
|
95
|
+
title: "Reopen",
|
|
96
|
+
permission: "order_manager",
|
|
97
|
+
statuses: [
|
|
98
|
+
OperationRunStatus.completed,
|
|
99
|
+
OperationRunStatus.skipped,
|
|
100
|
+
OperationRunStatus.failed,
|
|
101
|
+
],
|
|
102
|
+
disabledWhen: () => wcErr,
|
|
103
|
+
},
|
|
104
|
+
], href, { status, user });
|
|
105
|
+
}
|
|
106
|
+
const RunNoParamsSchema = z.object({
|
|
107
|
+
orderKey: z.string(),
|
|
108
|
+
runNo: z.coerce.number().int(),
|
|
109
|
+
});
|
|
110
|
+
export const SeqNoParamsSchema = z.object({
|
|
111
|
+
orderKey: z.string(),
|
|
112
|
+
runNo: z.coerce.number().int(),
|
|
113
|
+
seqNo: z.coerce.number().int(),
|
|
114
|
+
});
|
|
115
|
+
export async function formatOpRun(orderKey, runNo, user, opRun) {
|
|
116
|
+
const seqNo = opRun.operation.seqNo;
|
|
117
|
+
const [stepSummaryRows, fieldRefSummary] = await Promise.all([
|
|
118
|
+
getOpRunStepSummary(opRun.id),
|
|
119
|
+
getOpRunFieldRefSummary(opRun.operationId, opRun.orderRunId, orderKey, runNo),
|
|
120
|
+
]);
|
|
121
|
+
return {
|
|
122
|
+
id: opRun.id,
|
|
123
|
+
orderRunId: opRun.orderRunId,
|
|
124
|
+
operationId: opRun.operationId,
|
|
125
|
+
seqNo,
|
|
126
|
+
title: opRun.operation.title,
|
|
127
|
+
description: opRun.operation.description,
|
|
128
|
+
workCenterKey: opRun.operation.workCenter?.key ?? null,
|
|
129
|
+
status: opRun.status,
|
|
130
|
+
assignedTo: opRun.assignedTo?.username ?? null,
|
|
131
|
+
cost: opRun.cost,
|
|
132
|
+
note: opRun.statusNote ?? null,
|
|
133
|
+
completedAt: formatDate(opRun.completedAt),
|
|
134
|
+
stepSummary: stepSummaryRows.map((sr) => ({
|
|
135
|
+
seqNo: sr.step.seqNo,
|
|
136
|
+
title: sr.step.title,
|
|
137
|
+
completed: sr.completed,
|
|
138
|
+
})),
|
|
139
|
+
...(fieldRefSummary.length > 0
|
|
140
|
+
? {
|
|
141
|
+
fieldRefSummary,
|
|
142
|
+
}
|
|
143
|
+
: {}),
|
|
144
|
+
...formatAuditFields(opRun),
|
|
145
|
+
_links: [
|
|
146
|
+
...childItemLinks("/" + opRunResource(orderKey, runNo), seqNo, "Operation Runs", "/orders/" + orderKey + "/runs/" + runNo, "Order Run", "OperationRun", "run"),
|
|
147
|
+
{
|
|
148
|
+
rel: "steps",
|
|
149
|
+
href: `${API_PREFIX}/${opRunResource(orderKey, runNo)}/${seqNo}/steps`,
|
|
150
|
+
title: "Step Runs",
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
rel: "labor",
|
|
154
|
+
href: `${API_PREFIX}/${opRunResource(orderKey, runNo)}/${seqNo}/labor`,
|
|
155
|
+
title: "Labor Tickets",
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
rel: "comments",
|
|
159
|
+
href: `${API_PREFIX}/${opRunResource(orderKey, runNo)}/${seqNo}/comments`,
|
|
160
|
+
title: "Comments",
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
_actions: await opRunItemActions(orderKey, runNo, seqNo, opRun.id, opRun.operationId, opRun.status, user),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export async function formatOpRunTransition(orderKey, runNo, user, opRun) {
|
|
167
|
+
const seqNo = opRun.operation.seqNo;
|
|
168
|
+
return {
|
|
169
|
+
id: opRun.id,
|
|
170
|
+
status: opRun.status,
|
|
171
|
+
assignedTo: opRun.assignedTo?.username ?? null,
|
|
172
|
+
cost: opRun.cost,
|
|
173
|
+
note: opRun.statusNote ?? null,
|
|
174
|
+
completedAt: formatDate(opRun.completedAt),
|
|
175
|
+
...formatAuditFields(opRun),
|
|
176
|
+
_actions: await opRunItemActions(orderKey, runNo, seqNo, opRun.id, opRun.operationId, opRun.status, user),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function formatListOpRun(opRun) {
|
|
180
|
+
const seqNo = opRun.operation.seqNo;
|
|
181
|
+
return {
|
|
182
|
+
id: opRun.id,
|
|
183
|
+
orderRunId: opRun.orderRunId,
|
|
184
|
+
operationId: opRun.operationId,
|
|
185
|
+
seqNo,
|
|
186
|
+
title: opRun.operation.title,
|
|
187
|
+
description: opRun.operation.description,
|
|
188
|
+
workCenterKey: opRun.operation.workCenter?.key ?? null,
|
|
189
|
+
status: opRun.status,
|
|
190
|
+
assignedTo: opRun.assignedTo?.username ?? null,
|
|
191
|
+
cost: opRun.cost,
|
|
192
|
+
note: opRun.statusNote ?? null,
|
|
193
|
+
completedAt: formatDate(opRun.completedAt),
|
|
194
|
+
...formatAuditFields(opRun),
|
|
195
|
+
stepCount: opRun._count.stepRuns,
|
|
196
|
+
predecessors: opRun.operation.predecessors.map((d) => ({
|
|
197
|
+
seqNo: d.predecessor.seqNo,
|
|
198
|
+
title: d.predecessor.title,
|
|
199
|
+
})),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export default function operationRunRoutes(fastify) {
|
|
203
|
+
const app = fastify.withTypeProvider();
|
|
204
|
+
// LIST
|
|
205
|
+
app.get("/", {
|
|
206
|
+
schema: {
|
|
207
|
+
description: "List operation runs for an order run",
|
|
208
|
+
tags: ["Operation Runs"],
|
|
209
|
+
params: RunNoParamsSchema,
|
|
210
|
+
response: {
|
|
211
|
+
200: OperationRunListResponseSchema,
|
|
212
|
+
404: ErrorResponseSchema,
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
handler: async (request, reply) => {
|
|
216
|
+
const { orderKey, runNo } = request.params;
|
|
217
|
+
const resolved = await resolveOrderRun(orderKey, runNo);
|
|
218
|
+
if (!resolved) {
|
|
219
|
+
return notFound(reply, `Order run not found`);
|
|
220
|
+
}
|
|
221
|
+
const items = await listOpRuns(resolved.run.id);
|
|
222
|
+
return {
|
|
223
|
+
items: items.map((opRun) => formatListOpRun(opRun)),
|
|
224
|
+
total: items.length,
|
|
225
|
+
_links: [selfLink(`/${opRunResource(orderKey, runNo)}`)],
|
|
226
|
+
_linkTemplates: [
|
|
227
|
+
{
|
|
228
|
+
rel: "item",
|
|
229
|
+
hrefTemplate: `${API_PREFIX}/orders/${orderKey}/runs/${runNo}/ops/{seqNo}`,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
// GET by seqNo
|
|
236
|
+
app.get("/:seqNo", {
|
|
237
|
+
schema: {
|
|
238
|
+
description: "Get a single operation run by operation sequence number",
|
|
239
|
+
tags: ["Operation Runs"],
|
|
240
|
+
params: SeqNoParamsSchema,
|
|
241
|
+
response: {
|
|
242
|
+
200: OperationRunSchema,
|
|
243
|
+
404: ErrorResponseSchema,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
handler: async (request, reply) => {
|
|
247
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
248
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
249
|
+
if (!resolved) {
|
|
250
|
+
return notFound(reply, `Operation run not found`);
|
|
251
|
+
}
|
|
252
|
+
const opRun = await getOpRun(resolved.opRun.id);
|
|
253
|
+
if (!opRun) {
|
|
254
|
+
return notFound(reply, `Operation run not found`);
|
|
255
|
+
}
|
|
256
|
+
return formatOpRun(orderKey, runNo, request.erpUser, opRun);
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
// UPDATE (pending/in_progress only)
|
|
260
|
+
app.put("/:seqNo", {
|
|
261
|
+
schema: {
|
|
262
|
+
description: "Update an operation run (pending or in_progress status only)",
|
|
263
|
+
tags: ["Operation Runs"],
|
|
264
|
+
params: SeqNoParamsSchema,
|
|
265
|
+
body: UpdateOperationRunSchema,
|
|
266
|
+
response: {
|
|
267
|
+
200: MutateResponseSchema,
|
|
268
|
+
404: ErrorResponseSchema,
|
|
269
|
+
409: ErrorResponseSchema,
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
preHandler: requirePermission("order_executor"),
|
|
273
|
+
handler: async (request, reply) => {
|
|
274
|
+
const { orderKey, runNo, seqNo } = request.params;
|
|
275
|
+
const userId = request.erpUser.id;
|
|
276
|
+
const resolved = await resolveOpRun(orderKey, runNo, seqNo);
|
|
277
|
+
if (!resolved)
|
|
278
|
+
return notFound(reply, `Operation run not found`);
|
|
279
|
+
const orderErr = checkOrderRunStarted(resolved.run.status);
|
|
280
|
+
if (orderErr)
|
|
281
|
+
return conflict(reply, orderErr);
|
|
282
|
+
const statusErr = validateStatusFor("update", resolved.opRun.status, [
|
|
283
|
+
OperationRunStatus.pending,
|
|
284
|
+
OperationRunStatus.in_progress,
|
|
285
|
+
]);
|
|
286
|
+
if (statusErr)
|
|
287
|
+
return conflict(reply, statusErr);
|
|
288
|
+
const opRun = await updateOpRun(resolved.opRun.id, request.body, userId);
|
|
289
|
+
if (wantsFullResponse(request)) {
|
|
290
|
+
useFullSerializer(reply);
|
|
291
|
+
return formatOpRun(orderKey, runNo, request.erpUser, opRun);
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
_actions: await opRunItemActions(orderKey, runNo, seqNo, opRun.id, opRun.operationId, opRun.status, request.erpUser),
|
|
295
|
+
};
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=operation-runs.js.map
|