@tailor-platform/erp-kit 0.5.1 → 0.6.0

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 (251) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.mjs +103 -23
  3. package/package.json +1 -1
  4. package/skills/erp-kit-app-5-impl-backend/SKILL.md +7 -4
  5. package/skills/erp-kit-app-7-impl-review/SKILL.md +1 -1
  6. package/skills/erp-kit-module-6-impl-review/SKILL.md +39 -17
  7. package/src/commands/generate-doc.ts +1 -1
  8. package/src/commands/lib/discovery.test.ts +13 -3
  9. package/src/commands/lib/discovery.ts +10 -2
  10. package/src/commands/lib/paths.ts +4 -2
  11. package/src/commands/lib/sync-check-tests.test.ts +84 -6
  12. package/src/commands/lib/sync-check-tests.ts +63 -3
  13. package/src/commands/sync-check.ts +7 -3
  14. package/src/generator/generate-app-code.ts +51 -16
  15. package/src/generator/generate-stubs.ts +4 -0
  16. package/src/generator/stub-templates.test.ts +11 -0
  17. package/src/generator/stub-templates.ts +22 -1
  18. package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
  19. package/src/modules/inventory/docs/features/scrap-management.md +39 -1
  20. package/src/modules/manufacturing/README.md +63 -0
  21. package/src/modules/manufacturing/command/.gitkeep +0 -0
  22. package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
  23. package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
  24. package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
  25. package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
  26. package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
  27. package/src/modules/manufacturing/command/activateRouting.ts +92 -0
  28. package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
  29. package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
  30. package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
  31. package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
  32. package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
  33. package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
  34. package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
  35. package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
  36. package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
  37. package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
  38. package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
  39. package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
  40. package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
  41. package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
  42. package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
  43. package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
  44. package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
  45. package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
  46. package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
  47. package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
  48. package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
  49. package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
  50. package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
  51. package/src/modules/manufacturing/command/createRouting.ts +128 -0
  52. package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
  53. package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
  54. package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
  55. package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
  56. package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
  57. package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
  58. package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
  59. package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
  60. package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
  61. package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
  62. package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
  63. package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
  64. package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
  65. package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
  66. package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
  67. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
  68. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
  69. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
  70. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
  71. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
  72. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
  73. package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
  74. package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
  75. package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
  76. package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
  77. package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
  78. package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
  79. package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
  80. package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
  81. package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
  82. package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
  83. package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
  84. package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
  85. package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
  86. package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
  87. package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
  88. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
  89. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
  90. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
  91. package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
  92. package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
  93. package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
  94. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
  95. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
  96. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
  97. package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
  98. package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
  99. package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
  100. package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
  101. package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
  102. package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
  103. package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
  104. package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
  105. package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
  106. package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
  107. package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
  108. package/src/modules/manufacturing/command/updateRouting.ts +124 -0
  109. package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
  110. package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
  111. package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
  112. package/src/modules/manufacturing/db/.gitkeep +0 -0
  113. package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
  114. package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
  115. package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
  116. package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
  117. package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
  118. package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
  119. package/src/modules/manufacturing/db/productionOrder.ts +83 -0
  120. package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
  121. package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
  122. package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
  123. package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
  124. package/src/modules/manufacturing/db/routing.ts +63 -0
  125. package/src/modules/manufacturing/db/routingOperation.ts +57 -0
  126. package/src/modules/manufacturing/db/workCenter.ts +87 -0
  127. package/src/modules/manufacturing/db/workOrder.ts +65 -0
  128. package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
  129. package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
  130. package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
  131. package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
  132. package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
  133. package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
  134. package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
  135. package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
  136. package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
  137. package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
  138. package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
  139. package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
  140. package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
  141. package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
  142. package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
  143. package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
  144. package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
  145. package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
  146. package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
  147. package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
  148. package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
  149. package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
  150. package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
  151. package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
  152. package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
  153. package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
  154. package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
  155. package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
  156. package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
  157. package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
  158. package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
  159. package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
  160. package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
  161. package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
  162. package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
  163. package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
  164. package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
  165. package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
  166. package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
  167. package/src/modules/manufacturing/docs/models/Routing.md +58 -0
  168. package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
  169. package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
  170. package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
  171. package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
  172. package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
  173. package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
  174. package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
  175. package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
  176. package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
  177. package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
  178. package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
  179. package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
  180. package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
  181. package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
  182. package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
  183. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
  184. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
  185. package/src/modules/manufacturing/executor/.gitkeep +0 -0
  186. package/src/modules/manufacturing/generated/enums.ts +113 -0
  187. package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
  188. package/src/modules/manufacturing/index.ts +2 -0
  189. package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
  190. package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
  191. package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
  192. package/src/modules/manufacturing/lib/types.ts +111 -0
  193. package/src/modules/manufacturing/module.ts +226 -0
  194. package/src/modules/manufacturing/permissions.ts +3 -0
  195. package/src/modules/manufacturing/query/.gitkeep +0 -0
  196. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
  197. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
  198. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
  199. package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
  200. package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
  201. package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
  202. package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
  203. package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
  204. package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
  205. package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
  206. package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
  207. package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
  208. package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
  209. package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
  210. package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
  211. package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
  212. package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
  213. package/src/modules/manufacturing/query/getRouting.ts +34 -0
  214. package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
  215. package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
  216. package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
  217. package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
  218. package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
  219. package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
  220. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
  221. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
  222. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
  223. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
  224. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
  225. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
  226. package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
  227. package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
  228. package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
  229. package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
  230. package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
  231. package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
  232. package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
  233. package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
  234. package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
  235. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
  236. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
  237. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
  238. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
  239. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
  240. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
  241. package/src/modules/manufacturing/seed/index.ts +19 -0
  242. package/src/modules/manufacturing/tailor.config.ts +13 -0
  243. package/src/modules/manufacturing/tailor.d.ts +13 -0
  244. package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
  245. package/src/modules/manufacturing/testing/fixtures.ts +402 -0
  246. package/templates/scaffold/app/backend/package.json +9 -2
  247. package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
  248. package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
  249. package/templates/scaffold/app/backend/tsconfig.json +9 -2
  250. package/templates/scaffold/app/backend/vitest.config.ts +35 -0
  251. package/templates/scaffold/app/frontend/package.json +2 -2
@@ -0,0 +1,185 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ ProductionOrderNotFoundError,
6
+ ProductionOrderNotReschedulableError,
7
+ ExecutionAlreadyStartedError,
8
+ InvalidDateRangeError,
9
+ } from "../lib/errors.generated";
10
+ import {
11
+ baseDraftProductionOrder,
12
+ baseReleasedProductionOrder,
13
+ basePendingWorkOrder,
14
+ baseInProgressWorkOrder,
15
+ } from "../testing/fixtures";
16
+ import { run } from "./rescheduleProductionOrder";
17
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
18
+
19
+ describe("rescheduleProductionOrder", () => {
20
+ const ctx: CommandContext = {
21
+ actorId: "test-actor",
22
+ permissions: ["manufacturing:rescheduleProductionOrder"],
23
+ };
24
+
25
+ const newStartDate = new Date("2024-04-01T00:00:00.000Z");
26
+ const newEndDate = new Date("2024-04-15T00:00:00.000Z");
27
+
28
+ it("reschedules a released order before execution starts", async () => {
29
+ const { db, spies } = createMockDb<Transaction>();
30
+ const rescheduled = {
31
+ ...baseReleasedProductionOrder,
32
+ plannedStartDate: newStartDate,
33
+ plannedEndDate: newEndDate,
34
+ };
35
+
36
+ // order lookup
37
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
38
+ // work orders - all pending
39
+ spies.select.mockReturnValueOnce([basePendingWorkOrder]);
40
+ // update
41
+ spies.update.mockReturnValue(rescheduled);
42
+
43
+ const result = await run(
44
+ db,
45
+ {
46
+ id: baseReleasedProductionOrder.id,
47
+ plannedStartDate: newStartDate,
48
+ plannedEndDate: newEndDate,
49
+ },
50
+ ctx,
51
+ );
52
+
53
+ expect(result.ok).toBe(true);
54
+ if (result.ok) {
55
+ expect(result.value.productionOrder.plannedStartDate).toBe(newStartDate);
56
+ expect(result.value.productionOrder.plannedEndDate).toBe(newEndDate);
57
+ }
58
+ expect(spies.update).toHaveBeenCalled();
59
+ });
60
+
61
+ it("returns error when the order does not exist", async () => {
62
+ const { db, spies } = createMockDb<Transaction>();
63
+ spies.select.mockReturnValueOnce(undefined);
64
+
65
+ const result = await run(
66
+ db,
67
+ { id: "nonexistent", plannedStartDate: newStartDate, plannedEndDate: newEndDate },
68
+ ctx,
69
+ );
70
+
71
+ expect(result.ok).toBe(false);
72
+ if (!result.ok) {
73
+ expect(result.error).toBeInstanceOf(ProductionOrderNotFoundError);
74
+ }
75
+ });
76
+
77
+ it("returns error when the order is not in RELEASED", async () => {
78
+ const { db, spies } = createMockDb<Transaction>();
79
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
80
+
81
+ const result = await run(
82
+ db,
83
+ {
84
+ id: baseDraftProductionOrder.id,
85
+ plannedStartDate: newStartDate,
86
+ plannedEndDate: newEndDate,
87
+ },
88
+ ctx,
89
+ );
90
+
91
+ expect(result.ok).toBe(false);
92
+ if (!result.ok) {
93
+ expect(result.error).toBeInstanceOf(ProductionOrderNotReschedulableError);
94
+ }
95
+ });
96
+
97
+ it("returns error when execution has already started", async () => {
98
+ const { db, spies } = createMockDb<Transaction>();
99
+
100
+ // order lookup
101
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
102
+ // work orders - one in progress
103
+ spies.select.mockReturnValueOnce([baseInProgressWorkOrder]);
104
+
105
+ const result = await run(
106
+ db,
107
+ {
108
+ id: baseReleasedProductionOrder.id,
109
+ plannedStartDate: newStartDate,
110
+ plannedEndDate: newEndDate,
111
+ },
112
+ ctx,
113
+ );
114
+
115
+ expect(result.ok).toBe(false);
116
+ if (!result.ok) {
117
+ expect(result.error).toBeInstanceOf(ExecutionAlreadyStartedError);
118
+ }
119
+ });
120
+
121
+ it("returns error when planned dates are invalid", async () => {
122
+ const { db, spies } = createMockDb<Transaction>();
123
+ const invalidStart = new Date("2024-04-15T00:00:00.000Z");
124
+ const invalidEnd = new Date("2024-04-01T00:00:00.000Z");
125
+
126
+ // order lookup
127
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
128
+ // work orders - all pending
129
+ spies.select.mockReturnValueOnce([basePendingWorkOrder]);
130
+
131
+ const result = await run(
132
+ db,
133
+ {
134
+ id: baseReleasedProductionOrder.id,
135
+ plannedStartDate: invalidStart,
136
+ plannedEndDate: invalidEnd,
137
+ },
138
+ ctx,
139
+ );
140
+
141
+ expect(result.ok).toBe(false);
142
+ if (!result.ok) {
143
+ expect(result.error).toBeInstanceOf(InvalidDateRangeError);
144
+ }
145
+ });
146
+
147
+ it("preserves release snapshots after rescheduling", async () => {
148
+ const { db, spies } = createMockDb<Transaction>();
149
+ const rescheduled = {
150
+ ...baseReleasedProductionOrder,
151
+ plannedStartDate: newStartDate,
152
+ plannedEndDate: newEndDate,
153
+ };
154
+
155
+ // order lookup
156
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
157
+ // work orders - all pending
158
+ spies.select.mockReturnValueOnce([basePendingWorkOrder]);
159
+ // update
160
+ spies.update.mockReturnValue(rescheduled);
161
+
162
+ const result = await run(
163
+ db,
164
+ {
165
+ id: baseReleasedProductionOrder.id,
166
+ plannedStartDate: newStartDate,
167
+ plannedEndDate: newEndDate,
168
+ },
169
+ ctx,
170
+ );
171
+
172
+ expect(result.ok).toBe(true);
173
+ if (result.ok) {
174
+ // Status remains RELEASED - snapshots are preserved
175
+ expect(result.value.productionOrder.selectedBomVersionId).toBe(
176
+ baseReleasedProductionOrder.selectedBomVersionId,
177
+ );
178
+ expect(result.value.productionOrder.selectedRoutingRevisionId).toBe(
179
+ baseReleasedProductionOrder.selectedRoutingRevisionId,
180
+ );
181
+ }
182
+ // No delete calls should have been made
183
+ expect(spies.delete).not.toHaveBeenCalled();
184
+ });
185
+ });
@@ -0,0 +1,95 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ ProductionOrderNotFoundError,
4
+ ProductionOrderNotReschedulableError,
5
+ ExecutionAlreadyStartedError,
6
+ InvalidDateRangeError,
7
+ } from "../lib/errors.generated";
8
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
9
+
10
+ export interface RescheduleProductionOrderInput {
11
+ id: string;
12
+ plannedStartDate: Date;
13
+ plannedEndDate: Date;
14
+ from?: string[];
15
+ }
16
+
17
+ /**
18
+ * Function: rescheduleProductionOrder
19
+ *
20
+ * Changes the planned execution dates on a released order before execution
21
+ * starts. Preserves the released snapshots and keeps the change auditable.
22
+ */
23
+ export async function run<CF extends Record<string, unknown>>(
24
+ db: Transaction,
25
+ input: RescheduleProductionOrderInput & CF,
26
+ _ctx: CommandContext,
27
+ ) {
28
+ const { id, plannedStartDate, plannedEndDate, from, ...customFields } = input;
29
+ void customFields;
30
+
31
+ const allowedStatuses = from ?? ["RELEASED"];
32
+
33
+ // 1. Fetch production order with lock
34
+ const order = await db
35
+ .selectFrom("ProductionOrder")
36
+ .selectAll()
37
+ .where("id", "=", id)
38
+ .forUpdate()
39
+ .executeTakeFirst();
40
+
41
+ if (!order) {
42
+ return err(new ProductionOrderNotFoundError(id));
43
+ }
44
+
45
+ // 2. Validate status is reschedulable
46
+ if (!allowedStatuses.includes(order.status)) {
47
+ return err(new ProductionOrderNotReschedulableError(id));
48
+ }
49
+
50
+ // 3. Check no work order has execution evidence
51
+ const workOrders = await db
52
+ .selectFrom("WorkOrder")
53
+ .selectAll()
54
+ .where("productionOrderId", "=", id)
55
+ .execute();
56
+
57
+ const hasExecution = workOrders.some(
58
+ (wo) =>
59
+ wo.status === "IN_PROGRESS" ||
60
+ wo.status === "PAUSED" ||
61
+ wo.status === "COMPLETE" ||
62
+ wo.completedQuantity > 0 ||
63
+ wo.actualStartDate != null,
64
+ );
65
+
66
+ if (hasExecution) {
67
+ return err(new ExecutionAlreadyStartedError(id));
68
+ }
69
+
70
+ // 4. Validate revised dates
71
+ if (!plannedStartDate || !plannedEndDate) {
72
+ return err(new InvalidDateRangeError(id));
73
+ }
74
+
75
+ const start = new Date(plannedStartDate);
76
+ const end = new Date(plannedEndDate);
77
+
78
+ if (isNaN(start.getTime()) || isNaN(end.getTime()) || start >= end) {
79
+ return err(new InvalidDateRangeError(id));
80
+ }
81
+
82
+ // 5. Persist scheduling update
83
+ const rescheduled = await db
84
+ .updateTable("ProductionOrder")
85
+ .set({
86
+ plannedStartDate,
87
+ plannedEndDate,
88
+ updatedAt: new Date(),
89
+ })
90
+ .where("id", "=", id)
91
+ .returningAll()
92
+ .executeTakeFirstOrThrow();
93
+
94
+ return ok({ productionOrder: rescheduled });
95
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./resumeWorkOrder";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const resumeWorkOrder = defineCommand(permissions.resumeWorkOrder, run);
@@ -0,0 +1,122 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ WorkOrderNotFoundError,
6
+ WorkOrderNotResumableError,
7
+ ParentOrderNotExecutableError,
8
+ } from "../lib/errors.generated";
9
+ import {
10
+ basePausedWorkOrder,
11
+ basePendingWorkOrder,
12
+ baseInProgressProductionOrder,
13
+ baseDraftProductionOrder,
14
+ } from "../testing/fixtures";
15
+ import { run } from "./resumeWorkOrder";
16
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
17
+
18
+ describe("resumeWorkOrder", () => {
19
+ const ctx: CommandContext = {
20
+ actorId: "test-actor",
21
+ permissions: ["manufacturing:resumeWorkOrder"],
22
+ };
23
+
24
+ it("resumes a paused work order", async () => {
25
+ const { db, spies } = createMockDb<Transaction>();
26
+ const resumedWorkOrder = {
27
+ ...basePausedWorkOrder,
28
+ status: "IN_PROGRESS" as const,
29
+ pauseReason: null,
30
+ };
31
+
32
+ // work order lookup
33
+ spies.select.mockReturnValueOnce(basePausedWorkOrder);
34
+ // parent production order lookup
35
+ spies.select.mockReturnValueOnce(baseInProgressProductionOrder);
36
+ // update work order
37
+ spies.update.mockReturnValueOnce(resumedWorkOrder);
38
+ // insert execution event
39
+ spies.insert.mockReturnValueOnce(undefined);
40
+
41
+ const result = await run(db, { id: basePausedWorkOrder.id }, ctx);
42
+
43
+ expect(result.ok).toBe(true);
44
+ if (result.ok) {
45
+ expect(result.value.workOrder.status).toBe("IN_PROGRESS");
46
+ expect(result.value.workOrder.pauseReason).toBeNull();
47
+ }
48
+ expect(spies.update).toHaveBeenCalled();
49
+ expect(spies.insert).toHaveBeenCalled();
50
+ });
51
+
52
+ it("returns error when the work order does not exist", async () => {
53
+ const { db, spies } = createMockDb<Transaction>();
54
+ spies.select.mockReturnValueOnce(undefined);
55
+
56
+ const result = await run(db, { id: "nonexistent" }, ctx);
57
+
58
+ expect(result.ok).toBe(false);
59
+ if (!result.ok) {
60
+ expect(result.error).toBeInstanceOf(WorkOrderNotFoundError);
61
+ }
62
+ });
63
+
64
+ it("returns error when the work order is not paused", async () => {
65
+ const { db, spies } = createMockDb<Transaction>();
66
+ spies.select.mockReturnValueOnce(basePendingWorkOrder);
67
+
68
+ const result = await run(db, { id: basePendingWorkOrder.id }, ctx);
69
+
70
+ expect(result.ok).toBe(false);
71
+ if (!result.ok) {
72
+ expect(result.error).toBeInstanceOf(WorkOrderNotResumableError);
73
+ }
74
+ });
75
+
76
+ it("returns error when the parent production order no longer allows execution", async () => {
77
+ const { db, spies } = createMockDb<Transaction>();
78
+
79
+ // work order lookup
80
+ spies.select.mockReturnValueOnce(basePausedWorkOrder);
81
+ // parent production order - DRAFT is not executable
82
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
83
+
84
+ const result = await run(db, { id: basePausedWorkOrder.id }, ctx);
85
+
86
+ expect(result.ok).toBe(false);
87
+ if (!result.ok) {
88
+ expect(result.error).toBeInstanceOf(ParentOrderNotExecutableError);
89
+ }
90
+ });
91
+
92
+ it("preserves prior execution history after resume", async () => {
93
+ const { db, spies } = createMockDb<Transaction>();
94
+ const resumedWorkOrder = {
95
+ ...basePausedWorkOrder,
96
+ status: "IN_PROGRESS" as const,
97
+ pauseReason: null,
98
+ completedQuantity: basePausedWorkOrder.completedQuantity,
99
+ scrapQuantity: basePausedWorkOrder.scrapQuantity,
100
+ actualSetupTime: basePausedWorkOrder.actualSetupTime,
101
+ actualRunTime: basePausedWorkOrder.actualRunTime,
102
+ };
103
+
104
+ // work order lookup
105
+ spies.select.mockReturnValueOnce(basePausedWorkOrder);
106
+ // parent production order lookup
107
+ spies.select.mockReturnValueOnce(baseInProgressProductionOrder);
108
+ // update work order
109
+ spies.update.mockReturnValueOnce(resumedWorkOrder);
110
+ // insert execution event
111
+ spies.insert.mockReturnValueOnce(undefined);
112
+
113
+ const result = await run(db, { id: basePausedWorkOrder.id }, ctx);
114
+
115
+ expect(result.ok).toBe(true);
116
+ if (result.ok) {
117
+ expect(result.value.workOrder.completedQuantity).toBe(basePausedWorkOrder.completedQuantity);
118
+ expect(result.value.workOrder.actualSetupTime).toBe(basePausedWorkOrder.actualSetupTime);
119
+ expect(result.value.workOrder.actualRunTime).toBe(basePausedWorkOrder.actualRunTime);
120
+ }
121
+ });
122
+ });
@@ -0,0 +1,94 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ WorkOrderNotFoundError,
4
+ WorkOrderNotResumableError,
5
+ ParentOrderNotExecutableError,
6
+ } from "../lib/errors.generated";
7
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
8
+
9
+ const EXECUTABLE_ORDER_STATUSES = ["RELEASED", "IN_PROGRESS"] as const;
10
+
11
+ export interface ResumeWorkOrderInput {
12
+ id: string;
13
+ }
14
+
15
+ /**
16
+ * Function: resumeWorkOrder
17
+ *
18
+ * Restarts a paused work order without losing the accumulated execution
19
+ * history recorded before the interruption. Clears the pause reason.
20
+ */
21
+ export async function run<CF extends Record<string, unknown>>(
22
+ db: Transaction,
23
+ input: ResumeWorkOrderInput & CF,
24
+ _ctx: CommandContext,
25
+ ) {
26
+ const { id, ...customFields } = input;
27
+ void customFields;
28
+
29
+ // 1. Fetch work order with lock
30
+ const workOrder = await db
31
+ .selectFrom("WorkOrder")
32
+ .selectAll()
33
+ .where("id", "=", id)
34
+ .forUpdate()
35
+ .executeTakeFirst();
36
+
37
+ if (!workOrder) {
38
+ return err(new WorkOrderNotFoundError(id));
39
+ }
40
+
41
+ // 2. Validate status is PAUSED
42
+ if (workOrder.status !== "PAUSED") {
43
+ return err(new WorkOrderNotResumableError(id));
44
+ }
45
+
46
+ // 3. Check parent production order is still execution-capable
47
+ const parentOrder = await db
48
+ .selectFrom("ProductionOrder")
49
+ .selectAll()
50
+ .where("id", "=", workOrder.productionOrderId)
51
+ .forUpdate()
52
+ .executeTakeFirst();
53
+
54
+ if (
55
+ !parentOrder ||
56
+ !EXECUTABLE_ORDER_STATUSES.includes(
57
+ parentOrder.status as (typeof EXECUTABLE_ORDER_STATUSES)[number],
58
+ )
59
+ ) {
60
+ return err(new ParentOrderNotExecutableError(id));
61
+ }
62
+
63
+ // 4. Record resume event and set IN_PROGRESS, clear pause reason
64
+ const now = new Date();
65
+
66
+ const updatedWorkOrder = await db
67
+ .updateTable("WorkOrder")
68
+ .set({
69
+ status: "IN_PROGRESS",
70
+ pauseReason: null,
71
+ updatedAt: now,
72
+ })
73
+ .where("id", "=", id)
74
+ .returningAll()
75
+ .executeTakeFirstOrThrow();
76
+
77
+ // 5. Create RESUMED execution event
78
+ await db
79
+ .insertInto("WorkOrderExecutionEvent")
80
+ .values({
81
+ workOrderId: id,
82
+ eventType: "RESUMED",
83
+ timestamp: now,
84
+ quantity: null,
85
+ timeValue: null,
86
+ scrapValue: null,
87
+ notes: null,
88
+ createdAt: now,
89
+ updatedAt: null,
90
+ })
91
+ .execute();
92
+
93
+ return ok({ workOrder: updatedWorkOrder });
94
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./reviewManufacturingCostSummary";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const reviewManufacturingCostSummary = defineCommand(permissions.reviewManufacturingCostSummary, run);