@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,194 @@
1
+ import erpDb from "../erpDb.js";
2
+ // --- Prisma deep include for full revision tree ---
3
+ const includeFullTree = {
4
+ operations: {
5
+ orderBy: { seqNo: "asc" },
6
+ include: {
7
+ steps: {
8
+ orderBy: { seqNo: "asc" },
9
+ include: {
10
+ fieldSet: {
11
+ include: { fields: { orderBy: { seqNo: "asc" } } },
12
+ },
13
+ },
14
+ },
15
+ predecessors: {
16
+ include: {
17
+ predecessor: { select: { seqNo: true, title: true } },
18
+ },
19
+ },
20
+ },
21
+ },
22
+ };
23
+ async function getRevisionTree(orderId, revNo) {
24
+ return erpDb.orderRevision.findFirst({
25
+ where: { orderId, revNo },
26
+ include: includeFullTree,
27
+ });
28
+ }
29
+ // --- Comparison helpers ---
30
+ function compareProps(pairs) {
31
+ const changes = [];
32
+ for (const [field, from, to] of pairs) {
33
+ if (from !== to) {
34
+ changes.push({
35
+ field,
36
+ from: from,
37
+ to: to,
38
+ });
39
+ }
40
+ }
41
+ return changes;
42
+ }
43
+ function diffFields(fromFields, toFields) {
44
+ const fromMap = new Map(fromFields.map((f) => [f.seqNo, f]));
45
+ const toMap = new Map(toFields.map((f) => [f.seqNo, f]));
46
+ const allSeqNos = new Set([...fromMap.keys(), ...toMap.keys()]);
47
+ const result = [];
48
+ for (const seqNo of [...allSeqNos].sort((a, b) => a - b)) {
49
+ const from = fromMap.get(seqNo);
50
+ const to = toMap.get(seqNo);
51
+ if (!from && to) {
52
+ result.push({ seqNo, label: to.label, status: "added" });
53
+ }
54
+ else if (from && !to) {
55
+ result.push({ seqNo, label: from.label, status: "removed" });
56
+ }
57
+ else if (from && to) {
58
+ const changes = compareProps([
59
+ ["label", from.label, to.label],
60
+ ["type", from.type, to.type],
61
+ ["isArray", from.isArray, to.isArray],
62
+ ["required", from.required, to.required],
63
+ ]);
64
+ result.push({
65
+ seqNo,
66
+ label: to.label,
67
+ status: changes.length > 0 ? "modified" : "unchanged",
68
+ ...(changes.length > 0 ? { changes } : {}),
69
+ });
70
+ }
71
+ }
72
+ return result;
73
+ }
74
+ function diffSteps(fromSteps, toSteps) {
75
+ const fromMap = new Map(fromSteps.map((s) => [s.seqNo, s]));
76
+ const toMap = new Map(toSteps.map((s) => [s.seqNo, s]));
77
+ const allSeqNos = new Set([...fromMap.keys(), ...toMap.keys()]);
78
+ const result = [];
79
+ for (const seqNo of [...allSeqNos].sort((a, b) => a - b)) {
80
+ const from = fromMap.get(seqNo);
81
+ const to = toMap.get(seqNo);
82
+ if (!from && to) {
83
+ result.push({ seqNo, title: to.title, status: "added" });
84
+ }
85
+ else if (from && !to) {
86
+ result.push({ seqNo, title: from.title, status: "removed" });
87
+ }
88
+ else if (from && to) {
89
+ const changes = compareProps([
90
+ ["title", from.title, to.title],
91
+ ["instructions", from.instructions, to.instructions],
92
+ ["multiSet", from.multiSet, to.multiSet],
93
+ ]);
94
+ const fields = diffFields(from.fieldSet?.fields ?? [], to.fieldSet?.fields ?? []);
95
+ const hasFieldChanges = fields.some((f) => f.status !== "unchanged");
96
+ const isModified = changes.length > 0 || hasFieldChanges;
97
+ result.push({
98
+ seqNo,
99
+ title: to.title,
100
+ status: isModified ? "modified" : "unchanged",
101
+ ...(changes.length > 0 ? { changes } : {}),
102
+ ...(hasFieldChanges ? { fields } : {}),
103
+ });
104
+ }
105
+ }
106
+ return result;
107
+ }
108
+ function diffDeps(fromDeps, toDeps) {
109
+ const fromSet = new Map(fromDeps.map((d) => [d.predecessor.seqNo, d.predecessor.title]));
110
+ const toSet = new Map(toDeps.map((d) => [d.predecessor.seqNo, d.predecessor.title]));
111
+ const allSeqNos = new Set([...fromSet.keys(), ...toSet.keys()]);
112
+ const result = [];
113
+ for (const seqNo of [...allSeqNos].sort((a, b) => a - b)) {
114
+ const fromTitle = fromSet.get(seqNo);
115
+ const toTitle = toSet.get(seqNo);
116
+ if (fromTitle === undefined && toTitle !== undefined) {
117
+ result.push({
118
+ predecessorSeqNo: seqNo,
119
+ predecessorTitle: toTitle,
120
+ status: "added",
121
+ });
122
+ }
123
+ else if (fromTitle !== undefined && toTitle === undefined) {
124
+ result.push({
125
+ predecessorSeqNo: seqNo,
126
+ predecessorTitle: fromTitle,
127
+ status: "removed",
128
+ });
129
+ }
130
+ else if (fromTitle !== undefined) {
131
+ result.push({
132
+ predecessorSeqNo: seqNo,
133
+ predecessorTitle: toTitle ?? fromTitle,
134
+ status: "unchanged",
135
+ });
136
+ }
137
+ }
138
+ return result;
139
+ }
140
+ function diffOperations(fromOps, toOps) {
141
+ const fromMap = new Map(fromOps.map((op) => [op.seqNo, op]));
142
+ const toMap = new Map(toOps.map((op) => [op.seqNo, op]));
143
+ const allSeqNos = new Set([...fromMap.keys(), ...toMap.keys()]);
144
+ const result = [];
145
+ for (const seqNo of [...allSeqNos].sort((a, b) => a - b)) {
146
+ const from = fromMap.get(seqNo);
147
+ const to = toMap.get(seqNo);
148
+ if (!from && to) {
149
+ result.push({ seqNo, title: to.title, status: "added" });
150
+ }
151
+ else if (from && !to) {
152
+ result.push({ seqNo, title: from.title, status: "removed" });
153
+ }
154
+ else if (from && to) {
155
+ const changes = compareProps([
156
+ ["title", from.title, to.title],
157
+ ["description", from.description, to.description],
158
+ ]);
159
+ const steps = diffSteps(from.steps, to.steps);
160
+ const deps = diffDeps(from.predecessors, to.predecessors);
161
+ const hasStepChanges = steps.some((s) => s.status !== "unchanged");
162
+ const hasDepChanges = deps.some((d) => d.status !== "unchanged");
163
+ const isModified = changes.length > 0 || hasStepChanges || hasDepChanges;
164
+ result.push({
165
+ seqNo,
166
+ title: to.title,
167
+ status: isModified ? "modified" : "unchanged",
168
+ ...(changes.length > 0 ? { changes } : {}),
169
+ ...(hasStepChanges ? { steps } : {}),
170
+ ...(hasDepChanges ? { dependencies: deps } : {}),
171
+ });
172
+ }
173
+ }
174
+ return result;
175
+ }
176
+ // --- Public API ---
177
+ export async function diffRevisions(orderId, fromRevNo, toRevNo) {
178
+ const [fromRev, toRev] = await Promise.all([
179
+ getRevisionTree(orderId, fromRevNo),
180
+ getRevisionTree(orderId, toRevNo),
181
+ ]);
182
+ if (!fromRev || !toRev)
183
+ return null;
184
+ const revisionChanges = compareProps([
185
+ ["description", fromRev.description, toRev.description],
186
+ ]);
187
+ return {
188
+ fromRevNo,
189
+ toRevNo,
190
+ revisionChanges,
191
+ operations: diffOperations(fromRev.operations, toRev.operations),
192
+ };
193
+ }
194
+ //# sourceMappingURL=revision-diff-service.js.map
@@ -0,0 +1,106 @@
1
+ import erpDb from "../erpDb.js";
2
+ // --- Prisma include & result type ---
3
+ export const includeStepRunWithFields = {
4
+ step: {
5
+ select: {
6
+ seqNo: true,
7
+ title: true,
8
+ instructions: true,
9
+ multiSet: true,
10
+ fieldSet: {
11
+ select: {
12
+ fields: {
13
+ select: {
14
+ id: true,
15
+ seqNo: true,
16
+ label: true,
17
+ type: true,
18
+ isArray: true,
19
+ required: true,
20
+ },
21
+ orderBy: { seqNo: "asc" },
22
+ },
23
+ },
24
+ },
25
+ },
26
+ },
27
+ fieldRecord: {
28
+ include: {
29
+ fieldValues: {
30
+ select: {
31
+ id: true,
32
+ fieldId: true,
33
+ setIndex: true,
34
+ value: true,
35
+ fieldAttachments: {
36
+ select: {
37
+ attachment: {
38
+ select: { publicId: true, filename: true, fileSize: true },
39
+ },
40
+ },
41
+ },
42
+ },
43
+ orderBy: { setIndex: "asc" },
44
+ },
45
+ },
46
+ },
47
+ createdBy: { select: { username: true } },
48
+ updatedBy: { select: { username: true } },
49
+ };
50
+ // --- Lightweight include (step metadata only, no field values) ---
51
+ export const includeStepRun = {
52
+ step: {
53
+ select: {
54
+ seqNo: true,
55
+ title: true,
56
+ instructions: true,
57
+ multiSet: true,
58
+ fieldSet: {
59
+ select: {
60
+ _count: { select: { fields: true } },
61
+ },
62
+ },
63
+ },
64
+ },
65
+ createdBy: { select: { username: true } },
66
+ updatedBy: { select: { username: true } },
67
+ };
68
+ // --- Lookups ---
69
+ export async function listStepRuns(opRunId) {
70
+ return erpDb.stepRun.findMany({
71
+ where: { operationRunId: opRunId },
72
+ include: includeStepRun,
73
+ orderBy: { step: { seqNo: "asc" } },
74
+ });
75
+ }
76
+ export async function listStepRunsWithFields(opRunId) {
77
+ return erpDb.stepRun.findMany({
78
+ where: { operationRunId: opRunId },
79
+ include: includeStepRunWithFields,
80
+ orderBy: { step: { seqNo: "asc" } },
81
+ });
82
+ }
83
+ export async function getStepRunWithFields(id) {
84
+ return erpDb.stepRun.findUnique({
85
+ where: { id },
86
+ include: includeStepRunWithFields,
87
+ });
88
+ }
89
+ // --- Mutations ---
90
+ export async function updateStepRun(id, completed, statusNote, userId) {
91
+ if (completed !== undefined) {
92
+ await erpDb.stepRun.update({
93
+ where: { id },
94
+ data: {
95
+ completed,
96
+ statusNote: statusNote ?? null,
97
+ updatedById: userId,
98
+ },
99
+ });
100
+ }
101
+ return erpDb.stepRun.findUniqueOrThrow({
102
+ where: { id },
103
+ include: includeStepRunWithFields,
104
+ });
105
+ }
106
+ //# sourceMappingURL=step-run-service.js.map
@@ -0,0 +1,89 @@
1
+ import erpDb from "../erpDb.js";
2
+ import { calcNextSeqNo, includeUsers, } from "../route-helpers.js";
3
+ // --- Prisma include & result type ---
4
+ export const includeUsersAndFields = {
5
+ ...includeUsers,
6
+ fieldSet: {
7
+ include: {
8
+ fields: {
9
+ include: includeUsers,
10
+ orderBy: { seqNo: "asc" },
11
+ },
12
+ },
13
+ },
14
+ };
15
+ // --- Lookups ---
16
+ export async function listSteps(operationId) {
17
+ return erpDb.step.findMany({
18
+ where: { operationId },
19
+ include: includeUsersAndFields,
20
+ orderBy: { seqNo: "asc" },
21
+ });
22
+ }
23
+ export async function getStep(operationId, seqNo) {
24
+ return erpDb.step.findFirst({
25
+ where: { operationId, seqNo },
26
+ include: includeUsersAndFields,
27
+ });
28
+ }
29
+ export async function findExisting(operationId, seqNo) {
30
+ return erpDb.step.findFirst({
31
+ where: { operationId, seqNo },
32
+ include: includeUsersAndFields,
33
+ });
34
+ }
35
+ // --- Mutations ---
36
+ export async function createSteps(operationId, items, userId) {
37
+ return erpDb.$transaction(async (erpTx) => {
38
+ const maxSeq = await erpTx.step.findFirst({
39
+ where: { operationId },
40
+ orderBy: { seqNo: "desc" },
41
+ select: { seqNo: true },
42
+ });
43
+ let nextSeqNo = calcNextSeqNo(maxSeq?.seqNo ?? 0);
44
+ const created = [];
45
+ for (const item of items) {
46
+ const seqNo = item.seqNo ?? nextSeqNo;
47
+ const step = await erpTx.step.create({
48
+ data: {
49
+ operationId,
50
+ seqNo,
51
+ title: item.title ?? "",
52
+ instructions: item.instructions ?? "",
53
+ multiSet: item.multiSet ?? false,
54
+ createdById: userId,
55
+ updatedById: userId,
56
+ },
57
+ include: includeUsersAndFields,
58
+ });
59
+ created.push(step);
60
+ if (!item.seqNo) {
61
+ nextSeqNo = calcNextSeqNo(seqNo);
62
+ }
63
+ }
64
+ return created;
65
+ });
66
+ }
67
+ export async function createStep(operationId, requestedSeqNo, title, instructions, multiSet, userId) {
68
+ const [step] = await createSteps(operationId, [{ seqNo: requestedSeqNo, title, instructions, multiSet }], userId);
69
+ return step;
70
+ }
71
+ export async function updateStep(id, data, userId) {
72
+ return erpDb.step.update({
73
+ where: { id },
74
+ data: {
75
+ ...(data.title !== undefined ? { title: data.title } : {}),
76
+ ...(data.instructions !== undefined
77
+ ? { instructions: data.instructions }
78
+ : {}),
79
+ ...(data.seqNo !== undefined ? { seqNo: data.seqNo } : {}),
80
+ ...(data.multiSet !== undefined ? { multiSet: data.multiSet } : {}),
81
+ updatedById: userId,
82
+ },
83
+ include: includeUsersAndFields,
84
+ });
85
+ }
86
+ export async function deleteStep(id) {
87
+ await erpDb.step.delete({ where: { id } });
88
+ }
89
+ //# sourceMappingURL=step-service.js.map
@@ -0,0 +1,132 @@
1
+ import { getAgentApiKeyByUuid, rotateAgentApiKeyByUuid, } from "@naisys/hub-database";
2
+ import bcrypt from "bcryptjs";
3
+ import { randomBytes, randomUUID } from "crypto";
4
+ import erpDb from "../erpDb.js";
5
+ import { isSupervisorAuth } from "../supervisorAuth.js";
6
+ // --- Prisma include & result type ---
7
+ export const includePermissions = {
8
+ permissions: true,
9
+ };
10
+ // --- Constants ---
11
+ const SALT_ROUNDS = 10;
12
+ // --- Lookups ---
13
+ export async function listUsers(options) {
14
+ const { page, pageSize, search } = options;
15
+ const where = search ? { username: { contains: search } } : {};
16
+ const [items, total] = await Promise.all([
17
+ erpDb.user.findMany({
18
+ where,
19
+ include: includePermissions,
20
+ orderBy: { createdAt: "desc" },
21
+ skip: (page - 1) * pageSize,
22
+ take: pageSize,
23
+ }),
24
+ erpDb.user.count({ where }),
25
+ ]);
26
+ return { items, total, pageSize };
27
+ }
28
+ export async function getUserByUsername(username) {
29
+ return erpDb.user.findUnique({
30
+ where: { username },
31
+ include: includePermissions,
32
+ });
33
+ }
34
+ export async function getUserById(id) {
35
+ return erpDb.user.findUnique({
36
+ where: { id },
37
+ include: includePermissions,
38
+ });
39
+ }
40
+ export async function getUserApiKey(id) {
41
+ const user = await erpDb.user.findUnique({
42
+ where: { id },
43
+ select: { isAgent: true, uuid: true, apiKey: true },
44
+ });
45
+ if (!user)
46
+ return null;
47
+ if (user.isAgent && isSupervisorAuth()) {
48
+ return getAgentApiKeyByUuid(user.uuid);
49
+ }
50
+ else {
51
+ return user.apiKey ?? null;
52
+ }
53
+ }
54
+ // --- Mutations ---
55
+ export async function getUserByUuid(uuid) {
56
+ return erpDb.user.findFirst({
57
+ where: { uuid },
58
+ include: includePermissions,
59
+ });
60
+ }
61
+ export async function createUserForAgent(username, uuid) {
62
+ return erpDb.user.create({
63
+ data: {
64
+ username,
65
+ uuid,
66
+ passwordHash: "",
67
+ isAgent: true,
68
+ },
69
+ include: includePermissions,
70
+ });
71
+ }
72
+ export async function createUserWithPassword(data) {
73
+ const passwordHash = await bcrypt.hash(data.password, SALT_ROUNDS);
74
+ const uuid = randomUUID();
75
+ return erpDb.user.create({
76
+ data: {
77
+ username: data.username,
78
+ uuid,
79
+ passwordHash,
80
+ isAgent: false,
81
+ apiKey: randomBytes(32).toString("hex"),
82
+ },
83
+ include: includePermissions,
84
+ });
85
+ }
86
+ export async function updateUser(id, data) {
87
+ const updateData = {};
88
+ if (data.username !== undefined) {
89
+ updateData.username = data.username;
90
+ }
91
+ if (data.password !== undefined) {
92
+ updateData.passwordHash = await bcrypt.hash(data.password, SALT_ROUNDS);
93
+ }
94
+ return erpDb.user.update({
95
+ where: { id },
96
+ data: updateData,
97
+ include: includePermissions,
98
+ });
99
+ }
100
+ export async function deleteUser(id) {
101
+ return erpDb.user.delete({ where: { id } });
102
+ }
103
+ export async function grantPermission(userId, permission, grantedBy) {
104
+ return erpDb.userPermission.create({
105
+ data: { userId, permission, grantedBy },
106
+ });
107
+ }
108
+ export async function revokePermission(userId, permission) {
109
+ return erpDb.userPermission.deleteMany({
110
+ where: { userId, permission },
111
+ });
112
+ }
113
+ export async function rotateUserApiKey(id) {
114
+ const newKey = randomBytes(32).toString("hex");
115
+ const user = await erpDb.user.findUnique({
116
+ where: { id },
117
+ select: { isAgent: true, uuid: true },
118
+ });
119
+ if (!user)
120
+ throw new Error("User not found");
121
+ if (user.isAgent && isSupervisorAuth()) {
122
+ await rotateAgentApiKeyByUuid(user.uuid, newKey);
123
+ }
124
+ else {
125
+ await erpDb.user.update({
126
+ where: { id },
127
+ data: { apiKey: newKey },
128
+ });
129
+ }
130
+ return newKey;
131
+ }
132
+ //# sourceMappingURL=user-service.js.map
@@ -0,0 +1,106 @@
1
+ import erpDb from "../erpDb.js";
2
+ import { includeUsers } from "../route-helpers.js";
3
+ // --- Prisma include & result type ---
4
+ const includeDetail = {
5
+ ...includeUsers,
6
+ userAssignments: {
7
+ include: {
8
+ user: { select: { id: true, username: true } },
9
+ createdBy: { select: { username: true } },
10
+ },
11
+ orderBy: { user: { username: "asc" } },
12
+ },
13
+ _count: { select: { userAssignments: true } },
14
+ };
15
+ // --- Lookups ---
16
+ export async function listWorkCenters(where, page, pageSize) {
17
+ return Promise.all([
18
+ erpDb.workCenter.findMany({
19
+ where,
20
+ include: includeDetail,
21
+ skip: (page - 1) * pageSize,
22
+ take: pageSize,
23
+ orderBy: { createdAt: "desc" },
24
+ }),
25
+ erpDb.workCenter.count({ where }),
26
+ ]);
27
+ }
28
+ export async function findExisting(key) {
29
+ return erpDb.workCenter.findUnique({
30
+ where: { key },
31
+ include: includeDetail,
32
+ });
33
+ }
34
+ // --- Mutations ---
35
+ export async function createWorkCenter(key, description, userId) {
36
+ return erpDb.workCenter.create({
37
+ data: {
38
+ key,
39
+ description,
40
+ createdById: userId,
41
+ updatedById: userId,
42
+ },
43
+ include: includeDetail,
44
+ });
45
+ }
46
+ export async function updateWorkCenter(key, data, userId) {
47
+ return erpDb.workCenter.update({
48
+ where: { key },
49
+ data: { ...data, updatedById: userId },
50
+ include: includeDetail,
51
+ });
52
+ }
53
+ export async function deleteWorkCenter(key) {
54
+ await erpDb.workCenter.delete({ where: { key } });
55
+ }
56
+ // --- User assignments ---
57
+ export async function assignUser(workCenterKey, username, createdById) {
58
+ const workCenter = await erpDb.workCenter.findUniqueOrThrow({
59
+ where: { key: workCenterKey },
60
+ });
61
+ const user = await erpDb.user.findUniqueOrThrow({
62
+ where: { username },
63
+ });
64
+ await erpDb.workCenterUser.create({
65
+ data: {
66
+ workCenterId: workCenter.id,
67
+ userId: user.id,
68
+ createdById,
69
+ },
70
+ });
71
+ return erpDb.workCenter.findUniqueOrThrow({
72
+ where: { key: workCenterKey },
73
+ include: includeDetail,
74
+ });
75
+ }
76
+ export async function removeUser(workCenterKey, username) {
77
+ const workCenter = await erpDb.workCenter.findUniqueOrThrow({
78
+ where: { key: workCenterKey },
79
+ });
80
+ const user = await erpDb.user.findUniqueOrThrow({
81
+ where: { username },
82
+ });
83
+ await erpDb.workCenterUser.delete({
84
+ where: {
85
+ workCenterId_userId: {
86
+ workCenterId: workCenter.id,
87
+ userId: user.id,
88
+ },
89
+ },
90
+ });
91
+ }
92
+ // --- Work center ID lookups for dispatch ---
93
+ export async function getUserWorkCenterIds(userId) {
94
+ const assignments = await erpDb.workCenterUser.findMany({
95
+ where: { userId },
96
+ select: { workCenterId: true },
97
+ });
98
+ return assignments.map((a) => a.workCenterId);
99
+ }
100
+ export async function findWorkCenterByKey(key) {
101
+ return erpDb.workCenter.findUnique({
102
+ where: { key },
103
+ select: { id: true },
104
+ });
105
+ }
106
+ //# sourceMappingURL=work-center-service.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Module-level flag indicating whether ERP should use the supervisor/hub
3
+ * databases for SSO authentication and agent API key lookups.
4
+ *
5
+ * Enabled by:
6
+ * - SUPERVISOR_AUTH=true environment variable
7
+ * - Supervisor calling `enableSupervisorAuth()` before registering the ERP plugin
8
+ */
9
+ let _enabled = false;
10
+ export function enableSupervisorAuth() {
11
+ _enabled = true;
12
+ }
13
+ export function isSupervisorAuth() {
14
+ return _enabled || process.env.SUPERVISOR_AUTH === "true";
15
+ }
16
+ //# sourceMappingURL=supervisorAuth.js.map