@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.

Files changed (201) hide show
  1. package/bin/naisys-erp +2 -0
  2. package/client-dist/android-chrome-192x192.png +0 -0
  3. package/client-dist/android-chrome-512x512.png +0 -0
  4. package/client-dist/apple-touch-icon.png +0 -0
  5. package/client-dist/assets/index-45dVo30p.css +1 -0
  6. package/client-dist/assets/index-Dffms7F_.js +168 -0
  7. package/client-dist/assets/naisys-logo-CzoPnn5I.webp +0 -0
  8. package/client-dist/favicon.ico +0 -0
  9. package/client-dist/index.html +42 -0
  10. package/client-dist/site.webmanifest +22 -0
  11. package/dist/api-reference.d.ts +10 -0
  12. package/dist/api-reference.js +101 -0
  13. package/dist/audit.d.ts +5 -0
  14. package/dist/audit.js +14 -0
  15. package/dist/auth-middleware.d.ts +18 -0
  16. package/dist/auth-middleware.js +203 -0
  17. package/dist/dbConfig.d.ts +5 -0
  18. package/dist/dbConfig.js +10 -0
  19. package/dist/erpDb.d.ts +10 -0
  20. package/dist/erpDb.js +34 -0
  21. package/dist/erpServer.d.ts +10 -0
  22. package/dist/erpServer.js +321 -0
  23. package/dist/error-handler.d.ts +7 -0
  24. package/dist/error-handler.js +17 -0
  25. package/dist/generated/prisma/client.d.ts +154 -0
  26. package/dist/generated/prisma/client.js +35 -0
  27. package/dist/generated/prisma/commonInputTypes.d.ts +637 -0
  28. package/dist/generated/prisma/commonInputTypes.js +11 -0
  29. package/dist/generated/prisma/enums.d.ts +59 -0
  30. package/dist/generated/prisma/enums.js +60 -0
  31. package/dist/generated/prisma/internal/class.d.ts +406 -0
  32. package/dist/generated/prisma/internal/class.js +50 -0
  33. package/dist/generated/prisma/internal/prismaNamespace.d.ts +2722 -0
  34. package/dist/generated/prisma/internal/prismaNamespace.js +366 -0
  35. package/dist/generated/prisma/models/Attachment.d.ts +1455 -0
  36. package/dist/generated/prisma/models/Attachment.js +2 -0
  37. package/dist/generated/prisma/models/AuditLog.d.ts +1359 -0
  38. package/dist/generated/prisma/models/AuditLog.js +2 -0
  39. package/dist/generated/prisma/models/Field.d.ts +1880 -0
  40. package/dist/generated/prisma/models/Field.js +2 -0
  41. package/dist/generated/prisma/models/FieldAttachment.d.ts +1245 -0
  42. package/dist/generated/prisma/models/FieldAttachment.js +2 -0
  43. package/dist/generated/prisma/models/FieldRecord.d.ts +1625 -0
  44. package/dist/generated/prisma/models/FieldRecord.js +2 -0
  45. package/dist/generated/prisma/models/FieldSet.d.ts +1577 -0
  46. package/dist/generated/prisma/models/FieldSet.js +2 -0
  47. package/dist/generated/prisma/models/FieldValue.d.ts +1908 -0
  48. package/dist/generated/prisma/models/FieldValue.js +2 -0
  49. package/dist/generated/prisma/models/Item.d.ts +1858 -0
  50. package/dist/generated/prisma/models/Item.js +2 -0
  51. package/dist/generated/prisma/models/ItemInstance.d.ts +1987 -0
  52. package/dist/generated/prisma/models/ItemInstance.js +2 -0
  53. package/dist/generated/prisma/models/LaborTicket.d.ts +1867 -0
  54. package/dist/generated/prisma/models/LaborTicket.js +2 -0
  55. package/dist/generated/prisma/models/Operation.d.ts +2578 -0
  56. package/dist/generated/prisma/models/Operation.js +2 -0
  57. package/dist/generated/prisma/models/OperationDependency.d.ts +1434 -0
  58. package/dist/generated/prisma/models/OperationDependency.js +2 -0
  59. package/dist/generated/prisma/models/OperationFieldRef.d.ts +1539 -0
  60. package/dist/generated/prisma/models/OperationFieldRef.js +2 -0
  61. package/dist/generated/prisma/models/OperationRun.d.ts +2563 -0
  62. package/dist/generated/prisma/models/OperationRun.js +2 -0
  63. package/dist/generated/prisma/models/OperationRunComment.d.ts +1366 -0
  64. package/dist/generated/prisma/models/OperationRunComment.js +2 -0
  65. package/dist/generated/prisma/models/Order.d.ts +1931 -0
  66. package/dist/generated/prisma/models/Order.js +2 -0
  67. package/dist/generated/prisma/models/OrderRevision.d.ts +1962 -0
  68. package/dist/generated/prisma/models/OrderRevision.js +2 -0
  69. package/dist/generated/prisma/models/OrderRun.d.ts +2310 -0
  70. package/dist/generated/prisma/models/OrderRun.js +2 -0
  71. package/dist/generated/prisma/models/SchemaVersion.d.ts +985 -0
  72. package/dist/generated/prisma/models/SchemaVersion.js +2 -0
  73. package/dist/generated/prisma/models/Session.d.ts +1213 -0
  74. package/dist/generated/prisma/models/Session.js +2 -0
  75. package/dist/generated/prisma/models/Step.d.ts +2180 -0
  76. package/dist/generated/prisma/models/Step.js +2 -0
  77. package/dist/generated/prisma/models/StepRun.d.ts +1963 -0
  78. package/dist/generated/prisma/models/StepRun.js +2 -0
  79. package/dist/generated/prisma/models/User.d.ts +11819 -0
  80. package/dist/generated/prisma/models/User.js +2 -0
  81. package/dist/generated/prisma/models/UserPermission.d.ts +1348 -0
  82. package/dist/generated/prisma/models/UserPermission.js +2 -0
  83. package/dist/generated/prisma/models/WorkCenter.d.ts +1657 -0
  84. package/dist/generated/prisma/models/WorkCenter.js +2 -0
  85. package/dist/generated/prisma/models/WorkCenterUser.d.ts +1390 -0
  86. package/dist/generated/prisma/models/WorkCenterUser.js +2 -0
  87. package/dist/generated/prisma/models.d.ts +28 -0
  88. package/dist/generated/prisma/models.js +2 -0
  89. package/dist/hateoas.d.ts +7 -0
  90. package/dist/hateoas.js +61 -0
  91. package/dist/route-helpers.d.ts +318 -0
  92. package/dist/route-helpers.js +220 -0
  93. package/dist/routes/admin.d.ts +3 -0
  94. package/dist/routes/admin.js +147 -0
  95. package/dist/routes/audit.d.ts +3 -0
  96. package/dist/routes/audit.js +36 -0
  97. package/dist/routes/auth.d.ts +3 -0
  98. package/dist/routes/auth.js +112 -0
  99. package/dist/routes/dispatch.d.ts +3 -0
  100. package/dist/routes/dispatch.js +174 -0
  101. package/dist/routes/inventory.d.ts +3 -0
  102. package/dist/routes/inventory.js +70 -0
  103. package/dist/routes/item-fields.d.ts +3 -0
  104. package/dist/routes/item-fields.js +220 -0
  105. package/dist/routes/item-instances.d.ts +3 -0
  106. package/dist/routes/item-instances.js +426 -0
  107. package/dist/routes/items.d.ts +3 -0
  108. package/dist/routes/items.js +252 -0
  109. package/dist/routes/labor-tickets.d.ts +3 -0
  110. package/dist/routes/labor-tickets.js +268 -0
  111. package/dist/routes/operation-dependencies.d.ts +3 -0
  112. package/dist/routes/operation-dependencies.js +170 -0
  113. package/dist/routes/operation-field-refs.d.ts +3 -0
  114. package/dist/routes/operation-field-refs.js +263 -0
  115. package/dist/routes/operation-run-comments.d.ts +3 -0
  116. package/dist/routes/operation-run-comments.js +108 -0
  117. package/dist/routes/operation-run-transitions.d.ts +3 -0
  118. package/dist/routes/operation-run-transitions.js +249 -0
  119. package/dist/routes/operation-runs.d.ts +112 -0
  120. package/dist/routes/operation-runs.js +299 -0
  121. package/dist/routes/operations.d.ts +3 -0
  122. package/dist/routes/operations.js +283 -0
  123. package/dist/routes/order-revision-transitions.d.ts +3 -0
  124. package/dist/routes/order-revision-transitions.js +86 -0
  125. package/dist/routes/order-revisions.d.ts +51 -0
  126. package/dist/routes/order-revisions.js +327 -0
  127. package/dist/routes/order-run-transitions.d.ts +3 -0
  128. package/dist/routes/order-run-transitions.js +215 -0
  129. package/dist/routes/order-runs.d.ts +58 -0
  130. package/dist/routes/order-runs.js +335 -0
  131. package/dist/routes/orders.d.ts +3 -0
  132. package/dist/routes/orders.js +262 -0
  133. package/dist/routes/root.d.ts +3 -0
  134. package/dist/routes/root.js +123 -0
  135. package/dist/routes/schemas.d.ts +3 -0
  136. package/dist/routes/schemas.js +31 -0
  137. package/dist/routes/step-field-attachments.d.ts +3 -0
  138. package/dist/routes/step-field-attachments.js +231 -0
  139. package/dist/routes/step-fields.d.ts +100 -0
  140. package/dist/routes/step-fields.js +315 -0
  141. package/dist/routes/step-run-fields.d.ts +3 -0
  142. package/dist/routes/step-run-fields.js +438 -0
  143. package/dist/routes/step-run-transitions.d.ts +3 -0
  144. package/dist/routes/step-run-transitions.js +113 -0
  145. package/dist/routes/step-runs.d.ts +332 -0
  146. package/dist/routes/step-runs.js +324 -0
  147. package/dist/routes/steps.d.ts +3 -0
  148. package/dist/routes/steps.js +283 -0
  149. package/dist/routes/user-permissions.d.ts +3 -0
  150. package/dist/routes/user-permissions.js +100 -0
  151. package/dist/routes/users.d.ts +57 -0
  152. package/dist/routes/users.js +381 -0
  153. package/dist/routes/work-centers.d.ts +3 -0
  154. package/dist/routes/work-centers.js +280 -0
  155. package/dist/schema-registry.d.ts +3 -0
  156. package/dist/schema-registry.js +45 -0
  157. package/dist/services/attachment-service.d.ts +33 -0
  158. package/dist/services/attachment-service.js +118 -0
  159. package/dist/services/field-ref-service.d.ts +96 -0
  160. package/dist/services/field-ref-service.js +74 -0
  161. package/dist/services/field-service.d.ts +49 -0
  162. package/dist/services/field-service.js +114 -0
  163. package/dist/services/field-value-service.d.ts +61 -0
  164. package/dist/services/field-value-service.js +256 -0
  165. package/dist/services/item-instance-service.d.ts +152 -0
  166. package/dist/services/item-instance-service.js +155 -0
  167. package/dist/services/item-service.d.ts +47 -0
  168. package/dist/services/item-service.js +56 -0
  169. package/dist/services/labor-ticket-service.d.ts +40 -0
  170. package/dist/services/labor-ticket-service.js +148 -0
  171. package/dist/services/log-file-service.d.ts +4 -0
  172. package/dist/services/log-file-service.js +11 -0
  173. package/dist/services/operation-dependency-service.d.ts +33 -0
  174. package/dist/services/operation-dependency-service.js +30 -0
  175. package/dist/services/operation-run-comment-service.d.ts +17 -0
  176. package/dist/services/operation-run-comment-service.js +26 -0
  177. package/dist/services/operation-run-service.d.ts +126 -0
  178. package/dist/services/operation-run-service.js +347 -0
  179. package/dist/services/operation-service.d.ts +47 -0
  180. package/dist/services/operation-service.js +132 -0
  181. package/dist/services/order-revision-service.d.ts +53 -0
  182. package/dist/services/order-revision-service.js +264 -0
  183. package/dist/services/order-run-service.d.ts +138 -0
  184. package/dist/services/order-run-service.js +356 -0
  185. package/dist/services/order-service.d.ts +15 -0
  186. package/dist/services/order-service.js +68 -0
  187. package/dist/services/revision-diff-service.d.ts +3 -0
  188. package/dist/services/revision-diff-service.js +194 -0
  189. package/dist/services/step-run-service.d.ts +172 -0
  190. package/dist/services/step-run-service.js +106 -0
  191. package/dist/services/step-service.d.ts +104 -0
  192. package/dist/services/step-service.js +89 -0
  193. package/dist/services/user-service.d.ts +185 -0
  194. package/dist/services/user-service.js +132 -0
  195. package/dist/services/work-center-service.d.ts +29 -0
  196. package/dist/services/work-center-service.js +106 -0
  197. package/dist/supervisorAuth.d.ts +3 -0
  198. package/dist/supervisorAuth.js +16 -0
  199. package/dist/userService.d.ts +20 -0
  200. package/dist/userService.js +118 -0
  201. package/package.json +69 -0
@@ -0,0 +1,170 @@
1
+ import { CreateOperationDependencySchema, CreateResponseSchema, ErrorResponseSchema, OperationDependencyListResponseSchema, RevisionStatus, } 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 { mutationResult, resolveRevision } from "../route-helpers.js";
7
+ import { createDependency, deleteDependency, listDependencies, } from "../services/operation-dependency-service.js";
8
+ import { findExisting } from "../services/operation-service.js";
9
+ const ParamsSchema = z.object({
10
+ orderKey: z.string(),
11
+ revNo: z.coerce.number().int(),
12
+ seqNo: z.coerce.number().int(),
13
+ });
14
+ const DepParamsSchema = z.object({
15
+ orderKey: z.string(),
16
+ revNo: z.coerce.number().int(),
17
+ seqNo: z.coerce.number().int(),
18
+ predecessorSeqNo: z.coerce.number().int(),
19
+ });
20
+ function depBasePath(orderKey, revNo, seqNo) {
21
+ return `/orders/${orderKey}/revs/${revNo}/ops/${seqNo}/deps`;
22
+ }
23
+ function formatDependency(dep) {
24
+ return {
25
+ id: dep.id,
26
+ predecessorSeqNo: dep.predecessor.seqNo,
27
+ predecessorTitle: dep.predecessor.title,
28
+ createdAt: dep.createdAt.toISOString(),
29
+ createdBy: dep.createdBy.username,
30
+ };
31
+ }
32
+ function depActionTemplates(basePath, revStatus, user) {
33
+ if (!hasPermission(user, "order_planner") ||
34
+ revStatus !== RevisionStatus.draft)
35
+ return [];
36
+ return [
37
+ {
38
+ rel: "deleteDependency",
39
+ hrefTemplate: `${API_PREFIX}${basePath}/{predecessorSeqNo}`,
40
+ method: "DELETE",
41
+ title: "Remove Dependency",
42
+ },
43
+ ];
44
+ }
45
+ export default function operationDependencyRoutes(fastify) {
46
+ const app = fastify.withTypeProvider();
47
+ // LIST dependencies (predecessors) for an operation
48
+ app.get("/", {
49
+ schema: {
50
+ description: "List predecessor dependencies for an operation",
51
+ tags: ["Operation Dependencies"],
52
+ params: ParamsSchema,
53
+ response: {
54
+ 200: OperationDependencyListResponseSchema,
55
+ 404: ErrorResponseSchema,
56
+ },
57
+ },
58
+ handler: async (request, reply) => {
59
+ const { orderKey, revNo, seqNo } = request.params;
60
+ const resolved = await resolveRevision(orderKey, revNo);
61
+ if (!resolved) {
62
+ return notFound(reply, "Revision not found");
63
+ }
64
+ const operation = await findExisting(resolved.rev.id, seqNo);
65
+ if (!operation) {
66
+ return notFound(reply, `Operation ${seqNo} not found`);
67
+ }
68
+ const items = await listDependencies(operation.id);
69
+ const user = request.erpUser;
70
+ const base = depBasePath(orderKey, revNo, seqNo);
71
+ return {
72
+ items: items.map((dep) => formatDependency(dep)),
73
+ total: items.length,
74
+ _links: [selfLink(base)],
75
+ _actions: hasPermission(user, "order_planner") &&
76
+ resolved.rev.status === RevisionStatus.draft
77
+ ? [
78
+ {
79
+ rel: "create",
80
+ href: `${API_PREFIX}${base}`,
81
+ method: "POST",
82
+ title: "Add Dependency",
83
+ schema: `${API_PREFIX}/schemas/CreateOperationDependency`,
84
+ },
85
+ ]
86
+ : [],
87
+ _actionTemplates: depActionTemplates(base, resolved.rev.status, user),
88
+ };
89
+ },
90
+ });
91
+ // CREATE a dependency
92
+ app.post("/", {
93
+ schema: {
94
+ description: "Add a predecessor dependency to an operation",
95
+ tags: ["Operation Dependencies"],
96
+ params: ParamsSchema,
97
+ body: CreateOperationDependencySchema,
98
+ response: {
99
+ 201: CreateResponseSchema,
100
+ 404: ErrorResponseSchema,
101
+ 409: ErrorResponseSchema,
102
+ },
103
+ },
104
+ preHandler: requirePermission("order_planner"),
105
+ handler: async (request, reply) => {
106
+ const { orderKey, revNo, seqNo } = request.params;
107
+ const { predecessorSeqNo } = request.body;
108
+ const userId = request.erpUser.id;
109
+ const resolved = await resolveRevision(orderKey, revNo);
110
+ if (!resolved) {
111
+ return notFound(reply, "Revision not found");
112
+ }
113
+ if (resolved.rev.status !== RevisionStatus.draft) {
114
+ return conflict(reply, `Cannot modify dependencies on a ${resolved.rev.status} revision`);
115
+ }
116
+ const successor = await findExisting(resolved.rev.id, seqNo);
117
+ if (!successor) {
118
+ return notFound(reply, `Operation ${seqNo} not found`);
119
+ }
120
+ const predecessor = await findExisting(resolved.rev.id, predecessorSeqNo);
121
+ if (!predecessor) {
122
+ return notFound(reply, `Predecessor operation ${predecessorSeqNo} not found`);
123
+ }
124
+ if (successor.id === predecessor.id) {
125
+ return conflict(reply, "An operation cannot depend on itself");
126
+ }
127
+ const dep = await createDependency(successor.id, predecessor.id, userId);
128
+ const full = formatDependency(dep);
129
+ reply.status(201);
130
+ return mutationResult(request, reply, full, {
131
+ id: full.id,
132
+ });
133
+ },
134
+ });
135
+ // DELETE a dependency
136
+ app.delete("/:predecessorSeqNo", {
137
+ schema: {
138
+ description: "Remove a predecessor dependency from an operation",
139
+ tags: ["Operation Dependencies"],
140
+ params: DepParamsSchema,
141
+ response: {
142
+ 204: z.void(),
143
+ 404: ErrorResponseSchema,
144
+ 409: ErrorResponseSchema,
145
+ },
146
+ },
147
+ preHandler: requirePermission("order_planner"),
148
+ handler: async (request, reply) => {
149
+ const { orderKey, revNo, seqNo, predecessorSeqNo } = request.params;
150
+ const resolved = await resolveRevision(orderKey, revNo);
151
+ if (!resolved) {
152
+ return notFound(reply, "Revision not found");
153
+ }
154
+ if (resolved.rev.status !== RevisionStatus.draft) {
155
+ return conflict(reply, `Cannot modify dependencies on a ${resolved.rev.status} revision`);
156
+ }
157
+ const successor = await findExisting(resolved.rev.id, seqNo);
158
+ if (!successor) {
159
+ return notFound(reply, `Operation ${seqNo} not found`);
160
+ }
161
+ const predecessor = await findExisting(resolved.rev.id, predecessorSeqNo);
162
+ if (!predecessor) {
163
+ return notFound(reply, `Predecessor operation ${predecessorSeqNo} not found`);
164
+ }
165
+ await deleteDependency(successor.id, predecessor.id);
166
+ reply.status(204);
167
+ },
168
+ });
169
+ }
170
+ //# sourceMappingURL=operation-dependencies.js.map
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ export default function operationFieldRefRoutes(fastify: FastifyInstance): void;
3
+ //# sourceMappingURL=operation-field-refs.d.ts.map
@@ -0,0 +1,263 @@
1
+ import { CreateFieldRefSchema, ErrorResponseSchema, FieldRefListResponseSchema, RevisionStatus, SeqNoCreateResponseSchema, } from "@naisys/erp-shared";
2
+ import { z } from "zod/v4";
3
+ import { requirePermission } from "../auth-middleware.js";
4
+ import erpDb from "../erpDb.js";
5
+ import { conflict, notFound } from "../error-handler.js";
6
+ import { API_PREFIX, selfLink } from "../hateoas.js";
7
+ import { calcNextSeqNo, childItemLinks, mutationResult, resolveActions, resolveOperation, } from "../route-helpers.js";
8
+ import { checkDuplicateSource, createFieldRef, deleteFieldRef, findExistingFieldRef, listFieldRefs, } from "../services/field-ref-service.js";
9
+ const ParamsSchema = z.object({
10
+ orderKey: z.string(),
11
+ revNo: z.coerce.number().int(),
12
+ seqNo: z.coerce.number().int(),
13
+ });
14
+ const RefParamsSchema = z.object({
15
+ orderKey: z.string(),
16
+ revNo: z.coerce.number().int(),
17
+ seqNo: z.coerce.number().int(),
18
+ refSeqNo: z.coerce.number().int(),
19
+ });
20
+ function basePath(orderKey, revNo, seqNo) {
21
+ return `/orders/${orderKey}/revs/${revNo}/ops/${seqNo}/field-refs`;
22
+ }
23
+ function formatFieldRef(orderKey, revNo, seqNo, revStatus, user, ref) {
24
+ const base = basePath(orderKey, revNo, seqNo);
25
+ return {
26
+ id: ref.id,
27
+ seqNo: ref.seqNo,
28
+ title: ref.title,
29
+ sourceOpSeqNo: ref.sourceStep.operation.seqNo,
30
+ sourceOpTitle: ref.sourceStep.operation.title,
31
+ sourceStepSeqNo: ref.sourceStep.seqNo,
32
+ sourceStepTitle: ref.sourceStep.title,
33
+ fields: (ref.sourceStep.fieldSet?.fields ?? []).map((f) => ({
34
+ seqNo: f.seqNo,
35
+ label: f.label,
36
+ type: f.type,
37
+ })),
38
+ createdAt: ref.createdAt.toISOString(),
39
+ createdBy: ref.createdBy.username,
40
+ _links: childItemLinks(base, ref.seqNo, "Field Refs", `/orders/${orderKey}/revs/${revNo}/ops/${seqNo}`, "Operation", "FieldRef"),
41
+ _actions: deleteAction(`${API_PREFIX}${base}/${ref.seqNo}`, revStatus, user),
42
+ };
43
+ }
44
+ function deleteAction(href, revStatus, user) {
45
+ return resolveActions([
46
+ {
47
+ rel: "delete",
48
+ method: "DELETE",
49
+ title: "Remove Reference",
50
+ permission: "order_planner",
51
+ statuses: [RevisionStatus.draft],
52
+ hideWithoutPermission: true,
53
+ },
54
+ ], href, { status: revStatus, user });
55
+ }
56
+ const draftCreateDef = {
57
+ rel: "create",
58
+ method: "POST",
59
+ title: "Add Field Reference",
60
+ schema: `${API_PREFIX}/schemas/CreateFieldRef`,
61
+ permission: "order_planner",
62
+ disabledWhen: (ctx) => ctx.status !== RevisionStatus.draft
63
+ ? "Can only add field references in draft revisions"
64
+ : null,
65
+ };
66
+ // Schema for the /available response
67
+ const AvailableStepSchema = z.object({
68
+ opSeqNo: z.number(),
69
+ opTitle: z.string(),
70
+ stepSeqNo: z.number(),
71
+ stepTitle: z.string(),
72
+ stepId: z.number(),
73
+ fieldCount: z.number(),
74
+ });
75
+ const AvailableStepsResponseSchema = z.object({
76
+ items: z.array(AvailableStepSchema),
77
+ });
78
+ export default function operationFieldRefRoutes(fastify) {
79
+ const app = fastify.withTypeProvider();
80
+ // LIST available steps with fields (for the "add reference" dialog)
81
+ app.get("/available", {
82
+ schema: {
83
+ description: "List steps with fields in the same revision that can be referenced",
84
+ tags: ["Operation Field Refs"],
85
+ params: ParamsSchema,
86
+ response: {
87
+ 200: AvailableStepsResponseSchema,
88
+ 404: ErrorResponseSchema,
89
+ },
90
+ },
91
+ handler: async (request, reply) => {
92
+ const { orderKey, revNo, seqNo } = request.params;
93
+ const resolved = await resolveOperation(orderKey, revNo, seqNo);
94
+ if (!resolved) {
95
+ return notFound(reply, "Operation not found");
96
+ }
97
+ // Find all steps in this revision that have fields
98
+ const steps = await erpDb.step.findMany({
99
+ where: {
100
+ operation: { orderRevId: resolved.rev.id },
101
+ fieldSetId: { not: null },
102
+ fieldSet: { fields: { some: {} } },
103
+ },
104
+ select: {
105
+ id: true,
106
+ seqNo: true,
107
+ title: true,
108
+ operation: { select: { seqNo: true, title: true } },
109
+ fieldSet: { select: { _count: { select: { fields: true } } } },
110
+ },
111
+ orderBy: [{ operation: { seqNo: "asc" } }, { seqNo: "asc" }],
112
+ });
113
+ // Exclude steps already referenced by this operation
114
+ const existingRefs = await erpDb.operationFieldRef.findMany({
115
+ where: { operationId: resolved.operation.id },
116
+ select: { sourceStepId: true },
117
+ });
118
+ const refSet = new Set(existingRefs.map((r) => r.sourceStepId));
119
+ return {
120
+ items: steps
121
+ .filter((s) => !refSet.has(s.id))
122
+ .map((s) => ({
123
+ opSeqNo: s.operation.seqNo,
124
+ opTitle: s.operation.title,
125
+ stepSeqNo: s.seqNo,
126
+ stepTitle: s.title,
127
+ stepId: s.id,
128
+ fieldCount: s.fieldSet?._count.fields ?? 0,
129
+ })),
130
+ };
131
+ },
132
+ });
133
+ // LIST
134
+ app.get("/", {
135
+ schema: {
136
+ description: "List field references for an operation",
137
+ tags: ["Operation Field Refs"],
138
+ params: ParamsSchema,
139
+ response: {
140
+ 200: FieldRefListResponseSchema,
141
+ 404: ErrorResponseSchema,
142
+ },
143
+ },
144
+ handler: async (request, reply) => {
145
+ const { orderKey, revNo, seqNo } = request.params;
146
+ const resolved = await resolveOperation(orderKey, revNo, seqNo);
147
+ if (!resolved) {
148
+ return notFound(reply, "Operation not found");
149
+ }
150
+ const items = await listFieldRefs(resolved.operation.id);
151
+ const maxSeq = items.length > 0 ? items[items.length - 1].seqNo : 0;
152
+ const base = basePath(orderKey, revNo, seqNo);
153
+ return {
154
+ items: items.map((ref) => {
155
+ const { _links, ...rest } = formatFieldRef(orderKey, revNo, seqNo, resolved.rev.status, request.erpUser, ref);
156
+ return rest;
157
+ }),
158
+ total: items.length,
159
+ nextSeqNo: calcNextSeqNo(maxSeq),
160
+ _links: [selfLink(base)],
161
+ _linkTemplates: [
162
+ {
163
+ rel: "item",
164
+ hrefTemplate: `${API_PREFIX}${base}/{seqNo}`,
165
+ },
166
+ ],
167
+ _actions: resolveActions([draftCreateDef], `${API_PREFIX}${base}`, {
168
+ status: resolved.rev.status,
169
+ user: request.erpUser,
170
+ }),
171
+ };
172
+ },
173
+ });
174
+ // CREATE
175
+ app.post("/", {
176
+ schema: {
177
+ description: "Add a field reference to an operation (draft revision only)",
178
+ tags: ["Operation Field Refs"],
179
+ params: ParamsSchema,
180
+ body: CreateFieldRefSchema,
181
+ response: {
182
+ 201: SeqNoCreateResponseSchema,
183
+ 404: ErrorResponseSchema,
184
+ 409: ErrorResponseSchema,
185
+ },
186
+ },
187
+ preHandler: requirePermission("order_planner"),
188
+ handler: async (request, reply) => {
189
+ const { orderKey, revNo, seqNo } = request.params;
190
+ const { seqNo: requestedSeqNo, title, sourceOpSeqNo, sourceStepSeqNo, } = request.body;
191
+ const userId = request.erpUser.id;
192
+ const resolved = await resolveOperation(orderKey, revNo, seqNo);
193
+ if (!resolved) {
194
+ return notFound(reply, "Operation not found");
195
+ }
196
+ if (resolved.rev.status !== RevisionStatus.draft) {
197
+ return conflict(reply, `Cannot add field references to a ${resolved.rev.status} revision`);
198
+ }
199
+ // Resolve source step
200
+ const sourceOp = await erpDb.operation.findFirst({
201
+ where: { orderRevId: resolved.rev.id, seqNo: sourceOpSeqNo },
202
+ });
203
+ if (!sourceOp) {
204
+ return notFound(reply, `Source operation ${sourceOpSeqNo} not found`);
205
+ }
206
+ const sourceStep = await erpDb.step.findFirst({
207
+ where: { operationId: sourceOp.id, seqNo: sourceStepSeqNo },
208
+ select: { id: true, fieldSetId: true },
209
+ });
210
+ if (!sourceStep) {
211
+ return notFound(reply, `Source step ${sourceStepSeqNo} not found in operation ${sourceOpSeqNo}`);
212
+ }
213
+ if (!sourceStep.fieldSetId) {
214
+ return conflict(reply, "Source step has no fields");
215
+ }
216
+ // Check for duplicate
217
+ const dup = await checkDuplicateSource(resolved.operation.id, sourceStep.id);
218
+ if (dup) {
219
+ return conflict(reply, "This step is already referenced");
220
+ }
221
+ const ref = await createFieldRef(resolved.operation.id, requestedSeqNo, title, sourceStep.id, userId);
222
+ const full = formatFieldRef(orderKey, revNo, seqNo, resolved.rev.status, request.erpUser, ref);
223
+ reply.status(201);
224
+ return mutationResult(request, reply, full, {
225
+ id: full.id,
226
+ seqNo: full.seqNo,
227
+ _links: full._links,
228
+ _actions: full._actions,
229
+ });
230
+ },
231
+ });
232
+ // DELETE
233
+ app.delete("/:refSeqNo", {
234
+ schema: {
235
+ description: "Remove a field reference from an operation (draft revision only)",
236
+ tags: ["Operation Field Refs"],
237
+ params: RefParamsSchema,
238
+ response: {
239
+ 204: z.void(),
240
+ 404: ErrorResponseSchema,
241
+ 409: ErrorResponseSchema,
242
+ },
243
+ },
244
+ preHandler: requirePermission("order_planner"),
245
+ handler: async (request, reply) => {
246
+ const { orderKey, revNo, seqNo, refSeqNo } = request.params;
247
+ const resolved = await resolveOperation(orderKey, revNo, seqNo);
248
+ if (!resolved) {
249
+ return notFound(reply, "Operation not found");
250
+ }
251
+ if (resolved.rev.status !== RevisionStatus.draft) {
252
+ return conflict(reply, `Cannot remove field references from a ${resolved.rev.status} revision`);
253
+ }
254
+ const existing = await findExistingFieldRef(resolved.operation.id, refSeqNo);
255
+ if (!existing) {
256
+ return notFound(reply, `Field reference ${refSeqNo} not found`);
257
+ }
258
+ await deleteFieldRef(existing.id);
259
+ reply.status(204);
260
+ },
261
+ });
262
+ }
263
+ //# sourceMappingURL=operation-field-refs.js.map
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ export default function operationRunCommentRoutes(fastify: FastifyInstance): void;
3
+ //# sourceMappingURL=operation-run-comments.d.ts.map
@@ -0,0 +1,108 @@
1
+ import { CreateOperationRunCommentSchema, CreateResponseSchema, ErrorResponseSchema, OperationRunCommentListResponseSchema, OperationRunCommentType, } from "@naisys/erp-shared";
2
+ import { z } from "zod/v4";
3
+ import { requirePermission } from "../auth-middleware.js";
4
+ import { notFound } from "../error-handler.js";
5
+ import { API_PREFIX, selfLink } from "../hateoas.js";
6
+ import { mutationResult, resolveActions, resolveOpRun, } from "../route-helpers.js";
7
+ import { createComment, listComments, } from "../services/operation-run-comment-service.js";
8
+ function commentResource(orderKey, runNo, seqNo) {
9
+ return `orders/${orderKey}/runs/${runNo}/ops/${seqNo}/comments`;
10
+ }
11
+ function commentListActions(orderKey, runNo, seqNo, user) {
12
+ return resolveActions([
13
+ {
14
+ rel: "create",
15
+ method: "POST",
16
+ title: "Add Comment",
17
+ schema: `${API_PREFIX}/schemas/CreateOperationRunComment`,
18
+ permission: "order_executor",
19
+ },
20
+ ], `${API_PREFIX}/${commentResource(orderKey, runNo, seqNo)}`, { user });
21
+ }
22
+ function formatComment(orderKey, runNo, seqNo, comment) {
23
+ return {
24
+ id: comment.id,
25
+ operationRunId: comment.operationRunId,
26
+ type: comment.type,
27
+ body: comment.body,
28
+ createdAt: comment.createdAt.toISOString(),
29
+ createdBy: comment.createdBy.username,
30
+ _links: [
31
+ selfLink(`/${commentResource(orderKey, runNo, seqNo)}/${comment.id}`),
32
+ ],
33
+ };
34
+ }
35
+ const CommentParamsSchema = z.object({
36
+ orderKey: z.string(),
37
+ runNo: z.coerce.number().int(),
38
+ seqNo: z.coerce.number().int(),
39
+ });
40
+ export default function operationRunCommentRoutes(fastify) {
41
+ const app = fastify.withTypeProvider();
42
+ // LIST
43
+ app.get("/", {
44
+ schema: {
45
+ description: "List comments for an operation run",
46
+ tags: ["Operation Run Comments"],
47
+ params: CommentParamsSchema,
48
+ response: {
49
+ 200: OperationRunCommentListResponseSchema,
50
+ 404: ErrorResponseSchema,
51
+ },
52
+ },
53
+ handler: async (request, reply) => {
54
+ const { orderKey, runNo, seqNo } = request.params;
55
+ const resolved = await resolveOpRun(orderKey, runNo, seqNo);
56
+ if (!resolved) {
57
+ return notFound(reply, "Operation run not found");
58
+ }
59
+ const items = await listComments(resolved.opRun.id);
60
+ return {
61
+ items: items.map((c) => {
62
+ const { _links, ...rest } = formatComment(orderKey, runNo, seqNo, c);
63
+ return rest;
64
+ }),
65
+ total: items.length,
66
+ _links: [selfLink(`/${commentResource(orderKey, runNo, seqNo)}`)],
67
+ _linkTemplates: [
68
+ {
69
+ rel: "item",
70
+ hrefTemplate: `${API_PREFIX}/${commentResource(orderKey, runNo, seqNo)}/{id}`,
71
+ },
72
+ ],
73
+ _actions: commentListActions(orderKey, runNo, seqNo, request.erpUser),
74
+ };
75
+ },
76
+ });
77
+ // CREATE
78
+ app.post("/", {
79
+ schema: {
80
+ description: "Add a comment to an operation run",
81
+ tags: ["Operation Run Comments"],
82
+ params: CommentParamsSchema,
83
+ body: CreateOperationRunCommentSchema,
84
+ response: {
85
+ 201: CreateResponseSchema,
86
+ 404: ErrorResponseSchema,
87
+ },
88
+ },
89
+ preHandler: requirePermission("order_executor"),
90
+ handler: async (request, reply) => {
91
+ const { orderKey, runNo, seqNo } = request.params;
92
+ const { type, body } = request.body;
93
+ const userId = request.erpUser.id;
94
+ const resolved = await resolveOpRun(orderKey, runNo, seqNo);
95
+ if (!resolved) {
96
+ return notFound(reply, "Operation run not found");
97
+ }
98
+ const comment = await createComment(resolved.opRun.id, type ?? OperationRunCommentType.note, body, userId);
99
+ const full = formatComment(orderKey, runNo, seqNo, comment);
100
+ reply.status(201);
101
+ return mutationResult(request, reply, full, {
102
+ id: full.id,
103
+ _links: full._links,
104
+ });
105
+ },
106
+ });
107
+ }
108
+ //# sourceMappingURL=operation-run-comments.js.map
@@ -0,0 +1,3 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ export default function operationRunTransitionRoutes(fastify: FastifyInstance): void;
3
+ //# sourceMappingURL=operation-run-transitions.d.ts.map