@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,256 @@
1
+ import { formatFileSize } from "@naisys/common";
2
+ import { FieldType, } from "@naisys/erp-shared";
3
+ import erpDb from "../erpDb.js";
4
+ // --- Lookups ---
5
+ export async function findStepRunWithField(id, opRunId, fieldSeqNo) {
6
+ const stepRun = await erpDb.stepRun.findUnique({
7
+ where: { id },
8
+ include: {
9
+ step: {
10
+ select: {
11
+ multiSet: true,
12
+ fieldSet: {
13
+ select: {
14
+ fields: {
15
+ where: { seqNo: fieldSeqNo },
16
+ select: {
17
+ id: true,
18
+ fieldSetId: true,
19
+ seqNo: true,
20
+ label: true,
21
+ type: true,
22
+ isArray: true,
23
+ required: true,
24
+ },
25
+ },
26
+ },
27
+ },
28
+ },
29
+ },
30
+ },
31
+ });
32
+ if (!stepRun || stepRun.operationRunId !== opRunId)
33
+ return null;
34
+ return stepRun;
35
+ }
36
+ // --- Serialization ---
37
+ /**
38
+ * Serialize a field value for DB storage.
39
+ * - Scalar (string): stored as-is
40
+ * - Array (string[]): stored as JSON array string
41
+ */
42
+ export function serializeFieldValue(value) {
43
+ if (Array.isArray(value)) {
44
+ return JSON.stringify(value);
45
+ }
46
+ return value;
47
+ }
48
+ /**
49
+ * Deserialize a DB-stored value back to the API shape.
50
+ * - isArray fields: parse JSON array, falling back to comma-split for legacy data
51
+ * - Scalar fields: return as-is
52
+ */
53
+ export function deserializeFieldValue(dbValue, isArray) {
54
+ if (!isArray)
55
+ return dbValue;
56
+ if (!dbValue)
57
+ return [];
58
+ // Try JSON array first (new format)
59
+ if (dbValue.startsWith("[")) {
60
+ try {
61
+ const parsed = JSON.parse(dbValue);
62
+ if (Array.isArray(parsed))
63
+ return parsed;
64
+ }
65
+ catch {
66
+ // fall through to legacy
67
+ }
68
+ }
69
+ // Legacy: comma-separated string — migrate on read
70
+ return dbValue.split(",").map((v) => v.trim());
71
+ }
72
+ // --- Validation ---
73
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
74
+ const DATETIME_RE = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}(:\d{2})?$/;
75
+ function validateSingleValue(type, value) {
76
+ const v = value.trim();
77
+ if (!v)
78
+ return null;
79
+ switch (type) {
80
+ case FieldType.number:
81
+ if (isNaN(Number(v)))
82
+ return "Must be a number";
83
+ break;
84
+ case FieldType.date:
85
+ if (!DATE_RE.test(v) || isNaN(Date.parse(v)))
86
+ return "Must be a valid date (YYYY-MM-DD)";
87
+ break;
88
+ case FieldType.datetime:
89
+ if (!DATETIME_RE.test(v) || isNaN(Date.parse(v)))
90
+ return "Must be a valid date/time (YYYY-MM-DDTHH:mm)";
91
+ break;
92
+ case FieldType.yesNo:
93
+ if (v !== "Yes" && v !== "No")
94
+ return 'Must be "Yes" or "No"';
95
+ break;
96
+ case FieldType.checkbox:
97
+ if (v !== "checked")
98
+ return "Invalid checkbox value";
99
+ break;
100
+ }
101
+ return null;
102
+ }
103
+ /**
104
+ * Check that the value shape matches the field's isArray flag.
105
+ * Returns an error string if mismatched, or null if OK.
106
+ */
107
+ export function checkFieldValueShape(label, type, isArray, value) {
108
+ if (isArray && !Array.isArray(value)) {
109
+ return `Field "${label}" is an array field (type: ${type}[]) — value must be a JSON array, e.g. ["value1", "value2"]`;
110
+ }
111
+ if (!isArray && Array.isArray(value)) {
112
+ return `Field "${label}" is not an array field — value must be a string, not an array`;
113
+ }
114
+ return null;
115
+ }
116
+ export function validateFieldValue(type, isArray, required, value) {
117
+ const shapeErr = checkFieldValueShape("field", type, isArray, value);
118
+ if (shapeErr)
119
+ return { valid: false, error: shapeErr };
120
+ if (isArray) {
121
+ const items = value;
122
+ if (required && items.every((v) => !v.trim())) {
123
+ return { valid: false, error: "Required" };
124
+ }
125
+ for (let i = 0; i < items.length; i++) {
126
+ const err = validateSingleValue(type, items[i]);
127
+ if (err) {
128
+ return { valid: false, error: `Item ${i + 1}: ${err}` };
129
+ }
130
+ }
131
+ }
132
+ else {
133
+ const v = value;
134
+ if (required && !v.trim()) {
135
+ return { valid: false, error: "Required" };
136
+ }
137
+ const err = validateSingleValue(type, v);
138
+ if (err) {
139
+ return { valid: false, error: err };
140
+ }
141
+ }
142
+ return { valid: true };
143
+ }
144
+ function fieldValueKey(fieldId, setIndex) {
145
+ return `${fieldId}_${setIndex}`;
146
+ }
147
+ export function validateCompletionFields(existing) {
148
+ const existingFieldValues = existing.fieldRecord?.fieldValues ?? [];
149
+ // Build a map of field definitions keyed by id for isArray lookup
150
+ const fieldDefs = new Map((existing.step.fieldSet?.fields ?? []).map((f) => [f.id, f]));
151
+ const storedMap = new Map(existingFieldValues.map((fv) => {
152
+ const def = fieldDefs.get(fv.fieldId);
153
+ return [
154
+ fieldValueKey(fv.fieldId, fv.setIndex),
155
+ deserializeFieldValue(fv.value, def?.isArray ?? false),
156
+ ];
157
+ }));
158
+ // Determine how many sets exist
159
+ const allSetIndexes = new Set();
160
+ for (const fv of existingFieldValues)
161
+ allSetIndexes.add(fv.setIndex);
162
+ if (allSetIndexes.size === 0)
163
+ allSetIndexes.add(0);
164
+ const errors = [];
165
+ for (const si of [...allSetIndexes].sort((a, b) => a - b)) {
166
+ for (const field of existing.step.fieldSet?.fields ?? []) {
167
+ const key = fieldValueKey(field.id, si);
168
+ const value = storedMap.get(key) ?? "";
169
+ const result = validateFieldValue(field.type, field.isArray, field.required, value);
170
+ if (!result.valid) {
171
+ const prefix = existing.step.multiSet ? `Set ${si + 1} / ` : "";
172
+ errors.push(`${prefix}${field.label}: ${result.error}`);
173
+ }
174
+ }
175
+ }
176
+ if (errors.length > 0) {
177
+ return `Cannot complete step:\n${errors.join("\n")}`;
178
+ }
179
+ return null;
180
+ }
181
+ // --- Attachment value helpers ---
182
+ export function formatAttachmentLabel(filename, fileSize) {
183
+ return `${filename} (${formatFileSize(fileSize)})`;
184
+ }
185
+ /**
186
+ * Query current attachments for a field value and rebuild the stored value
187
+ * to reflect them. Returns the new API-shape value.
188
+ */
189
+ export async function rebuildAttachmentFieldValue(fieldRecordId, fieldId, setIndex, isArray, userId) {
190
+ const fieldValue = await erpDb.fieldValue.findUnique({
191
+ where: {
192
+ fieldRecordId_fieldId_setIndex: { fieldRecordId, fieldId, setIndex },
193
+ },
194
+ include: {
195
+ fieldAttachments: {
196
+ include: {
197
+ attachment: { select: { filename: true, fileSize: true } },
198
+ },
199
+ },
200
+ },
201
+ });
202
+ const labels = (fieldValue?.fieldAttachments ?? []).map((fa) => formatAttachmentLabel(fa.attachment.filename, fa.attachment.fileSize));
203
+ const value = labels.length === 0 ? (isArray ? [] : "") : isArray ? labels : labels[0];
204
+ await upsertFieldValue(fieldRecordId, fieldId, setIndex, value, userId);
205
+ return value;
206
+ }
207
+ /**
208
+ * Clear an attachment field: delete all FieldAttachment links and set value to empty.
209
+ */
210
+ export async function clearAttachmentFieldValue(fieldRecordId, fieldId, setIndex, userId) {
211
+ const fieldValue = await erpDb.fieldValue.findUnique({
212
+ where: {
213
+ fieldRecordId_fieldId_setIndex: { fieldRecordId, fieldId, setIndex },
214
+ },
215
+ select: { id: true },
216
+ });
217
+ if (fieldValue) {
218
+ await erpDb.fieldAttachment.deleteMany({
219
+ where: { fieldValueId: fieldValue.id },
220
+ });
221
+ }
222
+ const empty = "";
223
+ await upsertFieldValue(fieldRecordId, fieldId, setIndex, empty, userId);
224
+ }
225
+ // --- Mutations ---
226
+ export async function upsertFieldValue(fieldRecordId, fieldId, setIndex, value, userId) {
227
+ const dbValue = serializeFieldValue(value);
228
+ await erpDb.fieldValue.upsert({
229
+ where: {
230
+ fieldRecordId_fieldId_setIndex: { fieldRecordId, fieldId, setIndex },
231
+ },
232
+ create: {
233
+ fieldRecordId,
234
+ fieldId,
235
+ setIndex,
236
+ value: dbValue,
237
+ createdById: userId,
238
+ updatedById: userId,
239
+ },
240
+ update: {
241
+ value: dbValue,
242
+ updatedById: userId,
243
+ },
244
+ });
245
+ }
246
+ export async function deleteFieldValueSet(fieldRecordId, setIndex) {
247
+ await erpDb.$transaction(async (erpTx) => {
248
+ // Delete all field values for this set
249
+ await erpTx.fieldValue.deleteMany({
250
+ where: { fieldRecordId, setIndex },
251
+ });
252
+ // Re-index higher sets to fill the gap
253
+ await erpTx.$executeRawUnsafe(`UPDATE field_values SET set_index = set_index - 1 WHERE field_record_id = ? AND set_index > ?`, fieldRecordId, setIndex);
254
+ });
255
+ }
256
+ //# sourceMappingURL=field-value-service.js.map
@@ -0,0 +1,152 @@
1
+ import type { ItemInstanceModel } from "../generated/prisma/models/ItemInstance.js";
2
+ import { type WithAuditUsers } from "../route-helpers.js";
3
+ export declare const includeItemInstanceRelations: {
4
+ readonly item: {
5
+ readonly select: {
6
+ readonly key: true;
7
+ readonly fieldSet: {
8
+ readonly select: {
9
+ readonly id: true;
10
+ readonly fields: {
11
+ readonly select: {
12
+ readonly id: true;
13
+ readonly seqNo: true;
14
+ readonly label: true;
15
+ readonly type: true;
16
+ readonly isArray: true;
17
+ readonly required: true;
18
+ };
19
+ readonly orderBy: {
20
+ readonly seqNo: "asc";
21
+ };
22
+ };
23
+ };
24
+ };
25
+ };
26
+ };
27
+ readonly orderRun: {
28
+ readonly select: {
29
+ readonly runNo: true;
30
+ readonly order: {
31
+ readonly select: {
32
+ readonly key: true;
33
+ };
34
+ };
35
+ };
36
+ };
37
+ readonly fieldRecord: {
38
+ readonly include: {
39
+ readonly fieldValues: {
40
+ readonly select: {
41
+ readonly id: true;
42
+ readonly fieldId: true;
43
+ readonly setIndex: true;
44
+ readonly value: true;
45
+ readonly fieldAttachments: {
46
+ readonly select: {
47
+ readonly attachment: {
48
+ readonly select: {
49
+ readonly publicId: true;
50
+ readonly filename: true;
51
+ readonly fileSize: true;
52
+ };
53
+ };
54
+ };
55
+ };
56
+ };
57
+ readonly orderBy: {
58
+ readonly setIndex: "asc";
59
+ };
60
+ };
61
+ };
62
+ };
63
+ readonly createdBy: {
64
+ readonly select: {
65
+ readonly username: true;
66
+ };
67
+ };
68
+ readonly updatedBy: {
69
+ readonly select: {
70
+ readonly username: true;
71
+ };
72
+ };
73
+ };
74
+ type FieldDef = {
75
+ id: number;
76
+ seqNo: number;
77
+ label: string;
78
+ type: string;
79
+ isArray: boolean;
80
+ required: boolean;
81
+ };
82
+ export type ItemInstanceWithRelations = ItemInstanceModel & WithAuditUsers & {
83
+ item: {
84
+ key: string;
85
+ fieldSet: {
86
+ id: number;
87
+ fields: FieldDef[];
88
+ } | null;
89
+ };
90
+ orderRun: {
91
+ runNo: number;
92
+ order: {
93
+ key: string;
94
+ };
95
+ } | null;
96
+ fieldRecordId: number | null;
97
+ fieldRecord: {
98
+ id: number;
99
+ fieldValues: {
100
+ id: number;
101
+ fieldId: number;
102
+ setIndex: number;
103
+ value: string;
104
+ fieldAttachments: {
105
+ attachment: {
106
+ publicId: string;
107
+ filename: string;
108
+ fileSize: number;
109
+ };
110
+ }[];
111
+ }[];
112
+ } | null;
113
+ };
114
+ export declare function listItemInstances(where: Record<string, unknown>, page: number, pageSize: number): Promise<[ItemInstanceWithRelations[], number]>;
115
+ export declare function findItemInstance(id: number): Promise<ItemInstanceWithRelations | null>;
116
+ export declare function findItemInstanceByItemAndKey(itemId: number, key: string): Promise<ItemInstanceWithRelations | null>;
117
+ export declare function findItemInstanceWithField(id: number, fieldSeqNo: number): Promise<({
118
+ item: {
119
+ fieldSet: {
120
+ id: number;
121
+ fields: {
122
+ required: boolean;
123
+ id: number;
124
+ seqNo: number;
125
+ type: import("../generated/prisma/enums.js").FieldType;
126
+ label: string;
127
+ isArray: boolean;
128
+ }[];
129
+ } | null;
130
+ };
131
+ } & {
132
+ createdAt: Date;
133
+ id: number;
134
+ updatedAt: Date;
135
+ key: string;
136
+ quantity: number | null;
137
+ orderRunId: number | null;
138
+ createdById: number;
139
+ updatedById: number;
140
+ itemId: number;
141
+ fieldRecordId: number | null;
142
+ }) | null>;
143
+ export declare function createItemInstance(itemId: number, key: string, quantity: number | null | undefined, orderRunId: number | null | undefined, userId: number): Promise<ItemInstanceWithRelations>;
144
+ export declare function updateItemInstance(id: number, data: Record<string, unknown>, userId: number): Promise<ItemInstanceWithRelations>;
145
+ export declare function deleteItemInstance(id: number): Promise<void>;
146
+ /**
147
+ * Get or create a FieldRecord for an ItemInstance, linking it back.
148
+ * Returns the fieldRecordId, or null if the item has no fieldSet.
149
+ */
150
+ export declare function ensureItemInstanceFieldRecord(instanceId: number, userId: number): Promise<number | null>;
151
+ export {};
152
+ //# sourceMappingURL=item-instance-service.d.ts.map
@@ -0,0 +1,155 @@
1
+ import erpDb from "../erpDb.js";
2
+ import { includeUsers } from "../route-helpers.js";
3
+ // --- Prisma include & result type ---
4
+ export const includeItemInstanceRelations = {
5
+ ...includeUsers,
6
+ item: {
7
+ select: {
8
+ key: true,
9
+ fieldSet: {
10
+ select: {
11
+ id: true,
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
+ orderRun: {
28
+ select: {
29
+ runNo: true,
30
+ order: { select: { key: true } },
31
+ },
32
+ },
33
+ fieldRecord: {
34
+ include: {
35
+ fieldValues: {
36
+ select: {
37
+ id: true,
38
+ fieldId: true,
39
+ setIndex: true,
40
+ value: true,
41
+ fieldAttachments: {
42
+ select: {
43
+ attachment: {
44
+ select: { publicId: true, filename: true, fileSize: true },
45
+ },
46
+ },
47
+ },
48
+ },
49
+ orderBy: { setIndex: "asc" },
50
+ },
51
+ },
52
+ },
53
+ };
54
+ // --- Lookups ---
55
+ export async function listItemInstances(where, page, pageSize) {
56
+ return Promise.all([
57
+ erpDb.itemInstance.findMany({
58
+ where,
59
+ include: includeItemInstanceRelations,
60
+ skip: (page - 1) * pageSize,
61
+ take: pageSize,
62
+ orderBy: { createdAt: "desc" },
63
+ }),
64
+ erpDb.itemInstance.count({ where }),
65
+ ]);
66
+ }
67
+ export async function findItemInstance(id) {
68
+ return erpDb.itemInstance.findUnique({
69
+ where: { id },
70
+ include: includeItemInstanceRelations,
71
+ });
72
+ }
73
+ export async function findItemInstanceByItemAndKey(itemId, key) {
74
+ return erpDb.itemInstance.findUnique({
75
+ where: { itemId_key: { itemId, key } },
76
+ include: includeItemInstanceRelations,
77
+ });
78
+ }
79
+ export async function findItemInstanceWithField(id, fieldSeqNo) {
80
+ return erpDb.itemInstance.findUnique({
81
+ where: { id },
82
+ include: {
83
+ item: {
84
+ select: {
85
+ fieldSet: {
86
+ select: {
87
+ id: true,
88
+ fields: {
89
+ where: { seqNo: fieldSeqNo },
90
+ select: {
91
+ id: true,
92
+ seqNo: true,
93
+ label: true,
94
+ type: true,
95
+ isArray: true,
96
+ required: true,
97
+ },
98
+ },
99
+ },
100
+ },
101
+ },
102
+ },
103
+ },
104
+ });
105
+ }
106
+ // --- Mutations ---
107
+ export async function createItemInstance(itemId, key, quantity, orderRunId, userId) {
108
+ return erpDb.itemInstance.create({
109
+ data: {
110
+ itemId,
111
+ key,
112
+ quantity: quantity ?? null,
113
+ orderRunId: orderRunId ?? null,
114
+ createdById: userId,
115
+ updatedById: userId,
116
+ },
117
+ include: includeItemInstanceRelations,
118
+ });
119
+ }
120
+ export async function updateItemInstance(id, data, userId) {
121
+ return erpDb.itemInstance.update({
122
+ where: { id },
123
+ data: { ...data, updatedById: userId },
124
+ include: includeItemInstanceRelations,
125
+ });
126
+ }
127
+ export async function deleteItemInstance(id) {
128
+ await erpDb.itemInstance.delete({ where: { id } });
129
+ }
130
+ /**
131
+ * Get or create a FieldRecord for an ItemInstance, linking it back.
132
+ * Returns the fieldRecordId, or null if the item has no fieldSet.
133
+ */
134
+ export async function ensureItemInstanceFieldRecord(instanceId, userId) {
135
+ const inst = await erpDb.itemInstance.findUniqueOrThrow({
136
+ where: { id: instanceId },
137
+ select: {
138
+ fieldRecordId: true,
139
+ item: { select: { fieldSetId: true } },
140
+ },
141
+ });
142
+ if (inst.fieldRecordId)
143
+ return inst.fieldRecordId;
144
+ if (!inst.item.fieldSetId)
145
+ return null;
146
+ const fr = await erpDb.fieldRecord.create({
147
+ data: { fieldSetId: inst.item.fieldSetId, createdById: userId },
148
+ });
149
+ await erpDb.itemInstance.update({
150
+ where: { id: instanceId },
151
+ data: { fieldRecordId: fr.id },
152
+ });
153
+ return fr.id;
154
+ }
155
+ //# sourceMappingURL=item-instance-service.js.map
@@ -0,0 +1,47 @@
1
+ import type { ItemModel } from "../generated/prisma/models/Item.js";
2
+ import { type WithAuditUsers } from "../route-helpers.js";
3
+ import type { FieldWithUsers } from "./field-service.js";
4
+ export declare const includeUsersAndFieldSet: {
5
+ readonly fieldSet: {
6
+ readonly include: {
7
+ readonly fields: {
8
+ readonly include: {
9
+ readonly createdBy: {
10
+ readonly select: {
11
+ readonly username: true;
12
+ };
13
+ };
14
+ readonly updatedBy: {
15
+ readonly select: {
16
+ readonly username: true;
17
+ };
18
+ };
19
+ };
20
+ readonly orderBy: {
21
+ readonly seqNo: "asc";
22
+ };
23
+ };
24
+ };
25
+ };
26
+ readonly createdBy: {
27
+ readonly select: {
28
+ readonly username: true;
29
+ };
30
+ };
31
+ readonly updatedBy: {
32
+ readonly select: {
33
+ readonly username: true;
34
+ };
35
+ };
36
+ };
37
+ export type ItemWithUsers = ItemModel & WithAuditUsers & {
38
+ fieldSet: {
39
+ fields: FieldWithUsers[];
40
+ } | null;
41
+ };
42
+ export declare function listItems(where: Record<string, unknown>, page: number, pageSize: number): Promise<[ItemWithUsers[], number]>;
43
+ export declare function findExisting(key: string): Promise<ItemWithUsers | null>;
44
+ export declare function createItem(key: string, description: string | undefined, userId: number): Promise<ItemWithUsers>;
45
+ export declare function updateItem(key: string, data: Record<string, unknown>, userId: number): Promise<ItemWithUsers>;
46
+ export declare function deleteItem(key: string): Promise<void>;
47
+ //# sourceMappingURL=item-service.d.ts.map
@@ -0,0 +1,56 @@
1
+ import erpDb from "../erpDb.js";
2
+ import { includeUsers } from "../route-helpers.js";
3
+ // --- Prisma include & result type ---
4
+ export const includeUsersAndFieldSet = {
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 listItems(where, page, pageSize) {
17
+ return Promise.all([
18
+ erpDb.item.findMany({
19
+ where,
20
+ include: includeUsersAndFieldSet,
21
+ skip: (page - 1) * pageSize,
22
+ take: pageSize,
23
+ orderBy: { createdAt: "desc" },
24
+ }),
25
+ erpDb.item.count({ where }),
26
+ ]);
27
+ }
28
+ export async function findExisting(key) {
29
+ return erpDb.item.findUnique({
30
+ where: { key },
31
+ include: includeUsersAndFieldSet,
32
+ });
33
+ }
34
+ // --- Mutations ---
35
+ export async function createItem(key, description, userId) {
36
+ return erpDb.item.create({
37
+ data: {
38
+ key,
39
+ description,
40
+ createdById: userId,
41
+ updatedById: userId,
42
+ },
43
+ include: includeUsersAndFieldSet,
44
+ });
45
+ }
46
+ export async function updateItem(key, data, userId) {
47
+ return erpDb.item.update({
48
+ where: { key },
49
+ data: { ...data, updatedById: userId },
50
+ include: includeUsersAndFieldSet,
51
+ });
52
+ }
53
+ export async function deleteItem(key) {
54
+ await erpDb.item.delete({ where: { key } });
55
+ }
56
+ //# sourceMappingURL=item-service.js.map