@naisys/erp 3.0.0-beta.6

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.
Files changed (150) 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.js +101 -0
  12. package/dist/audit.js +14 -0
  13. package/dist/auth-middleware.js +203 -0
  14. package/dist/dbConfig.js +10 -0
  15. package/dist/erpDb.js +34 -0
  16. package/dist/erpServer.js +321 -0
  17. package/dist/error-handler.js +17 -0
  18. package/dist/generated/prisma/client.js +35 -0
  19. package/dist/generated/prisma/commonInputTypes.js +11 -0
  20. package/dist/generated/prisma/enums.js +60 -0
  21. package/dist/generated/prisma/internal/class.js +50 -0
  22. package/dist/generated/prisma/internal/prismaNamespace.js +366 -0
  23. package/dist/generated/prisma/models/Attachment.js +2 -0
  24. package/dist/generated/prisma/models/AuditLog.js +2 -0
  25. package/dist/generated/prisma/models/Field.js +2 -0
  26. package/dist/generated/prisma/models/FieldAttachment.js +2 -0
  27. package/dist/generated/prisma/models/FieldRecord.js +2 -0
  28. package/dist/generated/prisma/models/FieldSet.js +2 -0
  29. package/dist/generated/prisma/models/FieldValue.js +2 -0
  30. package/dist/generated/prisma/models/Item.js +2 -0
  31. package/dist/generated/prisma/models/ItemInstance.js +2 -0
  32. package/dist/generated/prisma/models/LaborTicket.js +2 -0
  33. package/dist/generated/prisma/models/Operation.js +2 -0
  34. package/dist/generated/prisma/models/OperationDependency.js +2 -0
  35. package/dist/generated/prisma/models/OperationFieldRef.js +2 -0
  36. package/dist/generated/prisma/models/OperationRun.js +2 -0
  37. package/dist/generated/prisma/models/OperationRunComment.js +2 -0
  38. package/dist/generated/prisma/models/Order.js +2 -0
  39. package/dist/generated/prisma/models/OrderRevision.js +2 -0
  40. package/dist/generated/prisma/models/OrderRun.js +2 -0
  41. package/dist/generated/prisma/models/SchemaVersion.js +2 -0
  42. package/dist/generated/prisma/models/Session.js +2 -0
  43. package/dist/generated/prisma/models/Step.js +2 -0
  44. package/dist/generated/prisma/models/StepRun.js +2 -0
  45. package/dist/generated/prisma/models/User.js +2 -0
  46. package/dist/generated/prisma/models/UserPermission.js +2 -0
  47. package/dist/generated/prisma/models/WorkCenter.js +2 -0
  48. package/dist/generated/prisma/models/WorkCenterUser.js +2 -0
  49. package/dist/generated/prisma/models.js +2 -0
  50. package/dist/hateoas.js +61 -0
  51. package/dist/route-helpers.js +220 -0
  52. package/dist/routes/admin.js +147 -0
  53. package/dist/routes/audit.js +36 -0
  54. package/dist/routes/auth.js +112 -0
  55. package/dist/routes/dispatch.js +174 -0
  56. package/dist/routes/inventory.js +70 -0
  57. package/dist/routes/item-fields.js +220 -0
  58. package/dist/routes/item-instances.js +426 -0
  59. package/dist/routes/items.js +252 -0
  60. package/dist/routes/labor-tickets.js +268 -0
  61. package/dist/routes/operation-dependencies.js +170 -0
  62. package/dist/routes/operation-field-refs.js +263 -0
  63. package/dist/routes/operation-run-comments.js +108 -0
  64. package/dist/routes/operation-run-transitions.js +249 -0
  65. package/dist/routes/operation-runs.js +299 -0
  66. package/dist/routes/operations.js +283 -0
  67. package/dist/routes/order-revision-transitions.js +86 -0
  68. package/dist/routes/order-revisions.js +327 -0
  69. package/dist/routes/order-run-transitions.js +215 -0
  70. package/dist/routes/order-runs.js +335 -0
  71. package/dist/routes/orders.js +262 -0
  72. package/dist/routes/root.js +123 -0
  73. package/dist/routes/schemas.js +31 -0
  74. package/dist/routes/step-field-attachments.js +231 -0
  75. package/dist/routes/step-fields.js +315 -0
  76. package/dist/routes/step-run-fields.js +438 -0
  77. package/dist/routes/step-run-transitions.js +113 -0
  78. package/dist/routes/step-runs.js +324 -0
  79. package/dist/routes/steps.js +283 -0
  80. package/dist/routes/user-permissions.js +100 -0
  81. package/dist/routes/users.js +381 -0
  82. package/dist/routes/work-centers.js +280 -0
  83. package/dist/schema-registry.js +45 -0
  84. package/dist/services/attachment-service.js +118 -0
  85. package/dist/services/field-ref-service.js +74 -0
  86. package/dist/services/field-service.js +114 -0
  87. package/dist/services/field-value-service.js +256 -0
  88. package/dist/services/item-instance-service.js +155 -0
  89. package/dist/services/item-service.js +56 -0
  90. package/dist/services/labor-ticket-service.js +148 -0
  91. package/dist/services/log-file-service.js +11 -0
  92. package/dist/services/operation-dependency-service.js +30 -0
  93. package/dist/services/operation-run-comment-service.js +26 -0
  94. package/dist/services/operation-run-service.js +347 -0
  95. package/dist/services/operation-service.js +132 -0
  96. package/dist/services/order-revision-service.js +264 -0
  97. package/dist/services/order-run-service.js +356 -0
  98. package/dist/services/order-service.js +68 -0
  99. package/dist/services/revision-diff-service.js +194 -0
  100. package/dist/services/step-run-service.js +106 -0
  101. package/dist/services/step-service.js +89 -0
  102. package/dist/services/user-service.js +132 -0
  103. package/dist/services/work-center-service.js +106 -0
  104. package/dist/supervisorAuth.js +16 -0
  105. package/dist/userService.js +118 -0
  106. package/package.json +75 -0
  107. package/prisma/migrations/20260212170352_init/migration.sql +125 -0
  108. package/prisma/migrations/20260308000000_multi_session/migration.sql +23 -0
  109. package/prisma/migrations/20260309000000_add_user_api_key/migration.sql +5 -0
  110. package/prisma/migrations/20260309010000_add_plan_operations/migration.sql +21 -0
  111. package/prisma/migrations/20260309020000_rename_exec_orders_to_order_runs/migration.sql +13 -0
  112. package/prisma/migrations/20260310000000_rename_plan_order_revs_to_order_revisions/migration.sql +22 -0
  113. package/prisma/migrations/20260310100000_rename_plan_orders_to_orders/migration.sql +23 -0
  114. package/prisma/migrations/20260310200000_rename_order_no_to_run_no/migration.sql +3 -0
  115. package/prisma/migrations/20260312000000_add_user_permissions/migration.sql +16 -0
  116. package/prisma/migrations/20260313000000_rename_plan_operations_to_operations/migration.sql +2 -0
  117. package/prisma/migrations/20260313100000_add_steps/migration.sql +20 -0
  118. package/prisma/migrations/20260314000000_add_step_fields/migration.sql +22 -0
  119. package/prisma/migrations/20260315000000_add_operation_runs/migration.sql +24 -0
  120. package/prisma/migrations/20260315100000_add_step_runs/migration.sql +40 -0
  121. package/prisma/migrations/20260316000000_drop_order_name/migration.sql +12 -0
  122. package/prisma/migrations/20260317000000_add_attachments/migration.sql +28 -0
  123. package/prisma/migrations/20260317000000_add_items/migration.sql +21 -0
  124. package/prisma/migrations/20260317100000_add_order_item_id/migration.sql +8 -0
  125. package/prisma/migrations/20260318000000_add_labor_tickets/migration.sql +27 -0
  126. package/prisma/migrations/20260319000000_add_operation_dependencies/migration.sql +17 -0
  127. package/prisma/migrations/20260320000000_step_field_is_array/migration.sql +5 -0
  128. package/prisma/migrations/20260320100000_rename_is_array_to_multi_value/migration.sql +2 -0
  129. package/prisma/migrations/20260320200000_add_field_types/migration.sql +2 -0
  130. package/prisma/migrations/20260321000000_add_multi_set/migration.sql +13 -0
  131. package/prisma/migrations/20260322000000_add_field_sets/migration.sql +90 -0
  132. package/prisma/migrations/20260323000000_add_item_instances/migration.sql +18 -0
  133. package/prisma/migrations/20260324000000_add_field_records/migration.sql +77 -0
  134. package/prisma/migrations/20260325000000_add_run_costs/migration.sql +3 -0
  135. package/prisma/migrations/20260326000000_add_comments/migration.sql +16 -0
  136. package/prisma/migrations/20260327000000_move_assigned_to_op_run/migration.sql +3 -0
  137. package/prisma/migrations/20260328000000_add_step_completion_note/migration.sql +2 -0
  138. package/prisma/migrations/20260328000000_add_step_title/migration.sql +2 -0
  139. package/prisma/migrations/20260329000000_rename_notes_to_release_note/migration.sql +2 -0
  140. package/prisma/migrations/20260329000000_simplify_order_run_dates/migration.sql +5 -0
  141. package/prisma/migrations/20260330000000_add_operation_run_completion_note/migration.sql +2 -0
  142. package/prisma/migrations/20260331000000_add_work_centers/migration.sql +30 -0
  143. package/prisma/migrations/20260401000000_fix_field_values_column_shift/migration.sql +26 -0
  144. package/prisma/migrations/20260402000000_rename_completion_note_to_status_note/migration.sql +5 -0
  145. package/prisma/migrations/20260403000000_add_operation_field_refs/migration.sql +16 -0
  146. package/prisma/migrations/20260404000000_rename_multi_value_to_is_array/migration.sql +2 -0
  147. package/prisma/migrations/20260404100000_add_attachment_public_id/migration.sql +8 -0
  148. package/prisma/migrations/migration_lock.toml +3 -0
  149. package/prisma/schema.prisma +595 -0
  150. package/prisma.config.ts +18 -0
@@ -0,0 +1,70 @@
1
+ import { InventoryListQuerySchema, InventoryListResponseSchema, } from "@naisys/erp-shared";
2
+ import erpDb from "../erpDb.js";
3
+ import { API_PREFIX, paginationLinks } from "../hateoas.js";
4
+ export default function inventoryRoutes(fastify) {
5
+ const app = fastify.withTypeProvider();
6
+ app.get("/", {
7
+ schema: {
8
+ description: "List all item instances across all items (inventory view)",
9
+ tags: ["Inventory"],
10
+ querystring: InventoryListQuerySchema,
11
+ response: {
12
+ 200: InventoryListResponseSchema,
13
+ },
14
+ },
15
+ handler: async (request) => {
16
+ const { page, pageSize, search } = request.query;
17
+ const where = {};
18
+ if (search) {
19
+ where.OR = [
20
+ { key: { contains: search } },
21
+ { item: { key: { contains: search } } },
22
+ ];
23
+ }
24
+ const [instances, total] = await Promise.all([
25
+ erpDb.itemInstance.findMany({
26
+ where,
27
+ include: {
28
+ item: { select: { key: true } },
29
+ orderRun: {
30
+ select: {
31
+ runNo: true,
32
+ order: { select: { key: true } },
33
+ },
34
+ },
35
+ },
36
+ skip: (page - 1) * pageSize,
37
+ take: pageSize,
38
+ orderBy: { createdAt: "desc" },
39
+ }),
40
+ erpDb.itemInstance.count({ where }),
41
+ ]);
42
+ return {
43
+ items: instances.map((inst) => ({
44
+ id: inst.id,
45
+ itemKey: inst.item.key,
46
+ key: inst.key,
47
+ quantity: inst.quantity,
48
+ orderKey: inst.orderRun?.order.key ?? null,
49
+ orderRunNo: inst.orderRun?.runNo ?? null,
50
+ createdAt: inst.createdAt.toISOString(),
51
+ })),
52
+ total,
53
+ page,
54
+ pageSize,
55
+ _links: paginationLinks("inventory", page, pageSize, total, {
56
+ search,
57
+ }),
58
+ _actionTemplates: [
59
+ {
60
+ rel: "viewInstance",
61
+ hrefTemplate: `${API_PREFIX}/items/{itemKey}/instances/{id}`,
62
+ method: "GET",
63
+ title: "View Instance",
64
+ },
65
+ ],
66
+ };
67
+ },
68
+ });
69
+ }
70
+ //# sourceMappingURL=inventory.js.map
@@ -0,0 +1,220 @@
1
+ import { CreateFieldSchema, ErrorResponseSchema, FieldListResponseSchema, FieldSchema, MutateResponseSchema, SeqNoCreateResponseSchema, UpdateFieldSchema, } from "@naisys/erp-shared";
2
+ import { z } from "zod/v4";
3
+ import { hasPermission, requirePermission } from "../auth-middleware.js";
4
+ import erpDb from "../erpDb.js";
5
+ import { notFound } from "../error-handler.js";
6
+ import { API_PREFIX, selfLink } from "../hateoas.js";
7
+ import { calcNextSeqNo, childItemLinks, formatAuditFields, mutationResult, } from "../route-helpers.js";
8
+ import { createField, deleteField, ensureFieldSet, findExistingField, getField, listFields, updateField, } from "../services/field-service.js";
9
+ import { findExisting as findExistingItem } from "../services/item-service.js";
10
+ const ParamsSchema = z.object({ key: z.string() });
11
+ const FieldParamsSchema = z.object({
12
+ key: z.string(),
13
+ fieldSeqNo: z.coerce.number().int(),
14
+ });
15
+ function fieldBasePath(key) {
16
+ return `/items/${key}/fields`;
17
+ }
18
+ function formatField(key, user, field) {
19
+ const base = fieldBasePath(key);
20
+ return {
21
+ id: field.id,
22
+ fieldSetId: field.fieldSetId,
23
+ seqNo: field.seqNo,
24
+ label: field.label,
25
+ type: field.type,
26
+ isArray: field.isArray,
27
+ required: field.required,
28
+ ...formatAuditFields(field),
29
+ _links: childItemLinks(base, field.seqNo, "Fields", `/items/${key}`, "Item", "Field"),
30
+ _actions: hasPermission(user, "item_manager")
31
+ ? [
32
+ {
33
+ rel: "update",
34
+ href: `${API_PREFIX}${base}/${field.seqNo}`,
35
+ method: "PUT",
36
+ title: "Update",
37
+ schema: `${API_PREFIX}/schemas/UpdateField`,
38
+ },
39
+ {
40
+ rel: "delete",
41
+ href: `${API_PREFIX}${base}/${field.seqNo}`,
42
+ method: "DELETE",
43
+ title: "Delete",
44
+ },
45
+ ]
46
+ : [],
47
+ };
48
+ }
49
+ export default function itemFieldRoutes(fastify) {
50
+ const app = fastify.withTypeProvider();
51
+ // LIST
52
+ app.get("/", {
53
+ schema: {
54
+ description: "List fields for an item",
55
+ tags: ["Item Fields"],
56
+ params: ParamsSchema,
57
+ response: {
58
+ 200: FieldListResponseSchema,
59
+ 404: ErrorResponseSchema,
60
+ },
61
+ },
62
+ handler: async (request, reply) => {
63
+ const { key } = request.params;
64
+ const item = await findExistingItem(key);
65
+ if (!item)
66
+ return notFound(reply, `Item '${key}' not found`);
67
+ const fields = item.fieldSetId ? await listFields(item.fieldSetId) : [];
68
+ const maxSeq = fields.length > 0 ? fields[fields.length - 1].seqNo : 0;
69
+ const base = fieldBasePath(key);
70
+ return {
71
+ items: fields.map((f) => {
72
+ const { _links, ...rest } = formatField(key, request.erpUser, f);
73
+ return rest;
74
+ }),
75
+ total: fields.length,
76
+ nextSeqNo: calcNextSeqNo(maxSeq),
77
+ _links: [selfLink(base)],
78
+ _linkTemplates: [
79
+ {
80
+ rel: "item",
81
+ hrefTemplate: `${API_PREFIX}${base}/{seqNo}`,
82
+ },
83
+ ],
84
+ _actions: hasPermission(request.erpUser, "item_manager")
85
+ ? [
86
+ {
87
+ rel: "create",
88
+ href: `${API_PREFIX}${base}`,
89
+ method: "POST",
90
+ title: "Add Field",
91
+ schema: `${API_PREFIX}/schemas/CreateField`,
92
+ },
93
+ ]
94
+ : [],
95
+ };
96
+ },
97
+ });
98
+ // CREATE
99
+ app.post("/", {
100
+ schema: {
101
+ description: "Create a field for an item",
102
+ tags: ["Item Fields"],
103
+ params: ParamsSchema,
104
+ body: CreateFieldSchema,
105
+ response: {
106
+ 201: SeqNoCreateResponseSchema,
107
+ 404: ErrorResponseSchema,
108
+ },
109
+ },
110
+ preHandler: requirePermission("item_manager"),
111
+ handler: async (request, reply) => {
112
+ const { key } = request.params;
113
+ const { seqNo: requestedSeqNo, label, type, isArray, required, } = request.body;
114
+ const userId = request.erpUser.id;
115
+ const item = await findExistingItem(key);
116
+ if (!item)
117
+ return notFound(reply, `Item '${key}' not found`);
118
+ let fieldSetId = item.fieldSetId;
119
+ if (!fieldSetId) {
120
+ fieldSetId = await ensureFieldSet(null, userId);
121
+ await erpDb.item.update({
122
+ where: { key },
123
+ data: { fieldSetId },
124
+ });
125
+ }
126
+ const field = await createField(fieldSetId, { seqNo: requestedSeqNo, label, type, isArray, required }, userId);
127
+ const full = formatField(key, request.erpUser, field);
128
+ reply.status(201);
129
+ return mutationResult(request, reply, full, {
130
+ id: full.id,
131
+ seqNo: full.seqNo,
132
+ _links: full._links,
133
+ _actions: full._actions,
134
+ });
135
+ },
136
+ });
137
+ // GET
138
+ app.get("/:fieldSeqNo", {
139
+ schema: {
140
+ description: "Get an item field",
141
+ tags: ["Item Fields"],
142
+ params: FieldParamsSchema,
143
+ response: {
144
+ 200: FieldSchema,
145
+ 404: ErrorResponseSchema,
146
+ },
147
+ },
148
+ handler: async (request, reply) => {
149
+ const { key, fieldSeqNo } = request.params;
150
+ const item = await findExistingItem(key);
151
+ if (!item)
152
+ return notFound(reply, `Item '${key}' not found`);
153
+ if (!item.fieldSetId)
154
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
155
+ const field = await getField(item.fieldSetId, fieldSeqNo);
156
+ if (!field)
157
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
158
+ return formatField(key, request.erpUser, field);
159
+ },
160
+ });
161
+ // UPDATE
162
+ app.put("/:fieldSeqNo", {
163
+ schema: {
164
+ description: "Update an item field",
165
+ tags: ["Item Fields"],
166
+ params: FieldParamsSchema,
167
+ body: UpdateFieldSchema,
168
+ response: {
169
+ 200: MutateResponseSchema,
170
+ 404: ErrorResponseSchema,
171
+ },
172
+ },
173
+ preHandler: requirePermission("item_manager"),
174
+ handler: async (request, reply) => {
175
+ const { key, fieldSeqNo } = request.params;
176
+ const { label, type, isArray, required, seqNo: newSeqNo } = request.body;
177
+ const userId = request.erpUser.id;
178
+ const item = await findExistingItem(key);
179
+ if (!item)
180
+ return notFound(reply, `Item '${key}' not found`);
181
+ if (!item.fieldSetId)
182
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
183
+ const existing = await findExistingField(item.fieldSetId, fieldSeqNo);
184
+ if (!existing)
185
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
186
+ const field = await updateField(existing.id, { label, type, isArray, required, seqNo: newSeqNo }, userId);
187
+ const full = formatField(key, request.erpUser, field);
188
+ return mutationResult(request, reply, full, {
189
+ _actions: full._actions,
190
+ });
191
+ },
192
+ });
193
+ // DELETE
194
+ app.delete("/:fieldSeqNo", {
195
+ schema: {
196
+ description: "Delete an item field",
197
+ tags: ["Item Fields"],
198
+ params: FieldParamsSchema,
199
+ response: {
200
+ 204: z.void(),
201
+ 404: ErrorResponseSchema,
202
+ },
203
+ },
204
+ preHandler: requirePermission("item_manager"),
205
+ handler: async (request, reply) => {
206
+ const { key, fieldSeqNo } = request.params;
207
+ const item = await findExistingItem(key);
208
+ if (!item)
209
+ return notFound(reply, `Item '${key}' not found`);
210
+ if (!item.fieldSetId)
211
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
212
+ const existing = await findExistingField(item.fieldSetId, fieldSeqNo);
213
+ if (!existing)
214
+ return notFound(reply, `Field ${fieldSeqNo} not found`);
215
+ await deleteField(existing.id);
216
+ reply.status(204);
217
+ },
218
+ });
219
+ }
220
+ //# sourceMappingURL=item-fields.js.map