@tailor-platform/erp-kit 0.5.0 → 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 (262) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cli.mjs +139 -35
  3. package/package.json +1 -1
  4. package/skills/erp-kit-app-5-impl-backend/SKILL.md +10 -5
  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/init-module.test.ts +17 -3
  9. package/src/commands/init-module.ts +0 -12
  10. package/src/commands/lib/discovery.test.ts +13 -3
  11. package/src/commands/lib/discovery.ts +10 -2
  12. package/src/commands/lib/paths.ts +4 -2
  13. package/src/commands/lib/sync-check-tests.test.ts +84 -6
  14. package/src/commands/lib/sync-check-tests.ts +63 -3
  15. package/src/commands/sync-check.ts +7 -3
  16. package/src/generator/generate-app-code.ts +51 -16
  17. package/src/generator/generate-code-boilerplate.test.ts +9 -1
  18. package/src/generator/generate-stubs.ts +4 -0
  19. package/src/generator/scaffold.ts +6 -2
  20. package/src/generator/stub-templates.test.ts +11 -0
  21. package/src/generator/stub-templates.ts +22 -1
  22. package/src/mdschema.ts +39 -3
  23. package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
  24. package/src/modules/inventory/docs/features/scrap-management.md +39 -1
  25. package/src/modules/manufacturing/README.md +63 -0
  26. package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
  27. package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
  28. package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
  29. package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
  30. package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
  31. package/src/modules/manufacturing/command/activateRouting.ts +92 -0
  32. package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
  33. package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
  34. package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
  35. package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
  36. package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
  37. package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
  38. package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
  39. package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
  40. package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
  41. package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
  42. package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
  43. package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
  44. package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
  45. package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
  46. package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
  47. package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
  48. package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
  49. package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
  50. package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
  51. package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
  52. package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
  53. package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
  54. package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
  55. package/src/modules/manufacturing/command/createRouting.ts +128 -0
  56. package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
  57. package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
  58. package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
  59. package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
  60. package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
  61. package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
  62. package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
  63. package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
  64. package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
  65. package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
  66. package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
  67. package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
  68. package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
  69. package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
  70. package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
  71. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
  72. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
  73. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
  74. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
  75. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
  76. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
  77. package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
  78. package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
  79. package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
  80. package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
  81. package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
  82. package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
  83. package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
  84. package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
  85. package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
  86. package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
  87. package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
  88. package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
  89. package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
  90. package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
  91. package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
  92. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
  93. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
  94. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
  95. package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
  96. package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
  97. package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
  98. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
  99. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
  100. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
  101. package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
  102. package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
  103. package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
  104. package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
  105. package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
  106. package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
  107. package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
  108. package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
  109. package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
  110. package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
  111. package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
  112. package/src/modules/manufacturing/command/updateRouting.ts +124 -0
  113. package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
  114. package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
  115. package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
  116. package/src/modules/manufacturing/db/.gitkeep +0 -0
  117. package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
  118. package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
  119. package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
  120. package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
  121. package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
  122. package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
  123. package/src/modules/manufacturing/db/productionOrder.ts +83 -0
  124. package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
  125. package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
  126. package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
  127. package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
  128. package/src/modules/manufacturing/db/routing.ts +63 -0
  129. package/src/modules/manufacturing/db/routingOperation.ts +57 -0
  130. package/src/modules/manufacturing/db/workCenter.ts +87 -0
  131. package/src/modules/manufacturing/db/workOrder.ts +65 -0
  132. package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
  133. package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
  134. package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
  135. package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
  136. package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
  137. package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
  138. package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
  139. package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
  140. package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
  141. package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
  142. package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
  143. package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
  144. package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
  145. package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
  146. package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
  147. package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
  148. package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
  149. package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
  150. package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
  151. package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
  152. package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
  153. package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
  154. package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
  155. package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
  156. package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
  157. package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
  158. package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
  159. package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
  160. package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
  161. package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
  162. package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
  163. package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
  164. package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
  165. package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
  166. package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
  167. package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
  168. package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
  169. package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
  170. package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
  171. package/src/modules/manufacturing/docs/models/Routing.md +58 -0
  172. package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
  173. package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
  174. package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
  175. package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
  176. package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
  177. package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
  178. package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
  179. package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
  180. package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
  181. package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
  182. package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
  183. package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
  184. package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
  185. package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
  186. package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
  187. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
  188. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
  189. package/src/modules/manufacturing/executor/.gitkeep +0 -0
  190. package/src/modules/manufacturing/generated/enums.ts +113 -0
  191. package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
  192. package/src/modules/manufacturing/index.ts +2 -0
  193. package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
  194. package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
  195. package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
  196. package/src/modules/manufacturing/lib/types.ts +111 -0
  197. package/src/modules/manufacturing/module.ts +226 -0
  198. package/src/modules/manufacturing/permissions.ts +3 -0
  199. package/src/modules/manufacturing/query/.gitkeep +0 -0
  200. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
  201. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
  202. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
  203. package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
  204. package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
  205. package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
  206. package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
  207. package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
  208. package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
  209. package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
  210. package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
  211. package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
  212. package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
  213. package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
  214. package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
  215. package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
  216. package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
  217. package/src/modules/manufacturing/query/getRouting.ts +34 -0
  218. package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
  219. package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
  220. package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
  221. package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
  222. package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
  223. package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
  224. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
  225. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
  226. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
  227. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
  228. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
  229. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
  230. package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
  231. package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
  232. package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
  233. package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
  234. package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
  235. package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
  236. package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
  237. package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
  238. package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
  239. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
  240. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
  241. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
  242. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
  243. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
  244. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
  245. package/src/modules/manufacturing/seed/index.ts +19 -0
  246. package/src/modules/manufacturing/tailor.config.ts +13 -0
  247. package/src/modules/manufacturing/tailor.d.ts +13 -0
  248. package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
  249. package/src/modules/manufacturing/testing/fixtures.ts +402 -0
  250. package/templates/scaffold/app/backend/package.json +9 -2
  251. package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
  252. package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
  253. package/templates/scaffold/app/backend/tsconfig.json +9 -2
  254. package/templates/scaffold/app/backend/vitest.config.ts +35 -0
  255. package/templates/scaffold/app/frontend/package.json +2 -2
  256. package/templates/scaffold/module/__dot__gitignore +3 -0
  257. package/templates/scaffold/module/eslint.config.js +31 -0
  258. package/templates/scaffold/module/generated/kysely-tailordb.ts +3 -0
  259. package/templates/scaffold/module/lib/types.ts +1 -6
  260. package/templates/scaffold/module/package.json +26 -0
  261. package/templates/scaffold/module/tsconfig.json +16 -0
  262. /package/{templates/scaffold/module/generated → src/modules/manufacturing/command}/.gitkeep +0 -0
@@ -0,0 +1,113 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ WorkCenterNotFoundError,
6
+ WorkCenterNotDeactivatableError,
7
+ WorkCenterInUseError,
8
+ } from "../lib/errors.generated";
9
+ import { baseActiveWorkCenter, baseDraftWorkCenter } from "../testing/fixtures";
10
+ import { run } from "./deactivateWorkCenter";
11
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
12
+
13
+ describe("deactivateWorkCenter", () => {
14
+ const ctx: CommandContext = {
15
+ actorId: "test-actor",
16
+ permissions: ["manufacturing:deactivateWorkCenter"],
17
+ };
18
+
19
+ it("deactivates an active work center with no blocking dependencies", async () => {
20
+ const { db, spies } = createMockDb<Transaction>();
21
+ const deactivated = { ...baseActiveWorkCenter, status: "INACTIVE" as const };
22
+
23
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter); // fetch work center
24
+ spies.select.mockReturnValueOnce(undefined); // no active routing dependency
25
+ spies.select.mockReturnValueOnce(undefined); // no open work orders
26
+ spies.update.mockReturnValue(deactivated);
27
+
28
+ const result = await run(db, { id: baseActiveWorkCenter.id }, ctx);
29
+
30
+ expect(result.ok).toBe(true);
31
+ if (result.ok) {
32
+ expect(result.value.workCenter.status).toBe("INACTIVE");
33
+ }
34
+ expect(spies.update).toHaveBeenCalled();
35
+ });
36
+
37
+ it("returns error when the work center does not exist", async () => {
38
+ const { db, spies } = createMockDb<Transaction>();
39
+ spies.select.mockReturnValueOnce(undefined);
40
+
41
+ const result = await run(db, { id: "nonexistent" }, ctx);
42
+
43
+ expect(result.ok).toBe(false);
44
+ if (!result.ok) {
45
+ expect(result.error).toBeInstanceOf(WorkCenterNotFoundError);
46
+ }
47
+ });
48
+
49
+ it("returns error when the work center is not active", async () => {
50
+ const { db, spies } = createMockDb<Transaction>();
51
+ spies.select.mockReturnValueOnce(baseDraftWorkCenter);
52
+
53
+ const result = await run(db, { id: baseDraftWorkCenter.id }, ctx);
54
+
55
+ expect(result.ok).toBe(false);
56
+ if (!result.ok) {
57
+ expect(result.error).toBeInstanceOf(WorkCenterNotDeactivatableError);
58
+ }
59
+ });
60
+
61
+ it("returns error when open routing or released-work dependencies block deactivation", async () => {
62
+ const { db, spies } = createMockDb<Transaction>();
63
+
64
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter); // fetch work center
65
+ spies.select.mockReturnValueOnce({ id: "operation-1" }); // active routing dependency found
66
+
67
+ const result = await run(db, { id: baseActiveWorkCenter.id }, ctx);
68
+
69
+ expect(result.ok).toBe(false);
70
+ if (!result.ok) {
71
+ expect(result.error).toBeInstanceOf(WorkCenterInUseError);
72
+ }
73
+ });
74
+
75
+ it("returns error when open work orders block deactivation", async () => {
76
+ const { db, spies } = createMockDb<Transaction>();
77
+
78
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter); // fetch work center
79
+ spies.select.mockReturnValueOnce(undefined); // no active routing dependency
80
+ spies.select.mockReturnValueOnce({ id: "work-order-1" }); // open work order found
81
+
82
+ const result = await run(db, { id: baseActiveWorkCenter.id }, ctx);
83
+
84
+ expect(result.ok).toBe(false);
85
+ if (!result.ok) {
86
+ expect(result.error).toBeInstanceOf(WorkCenterInUseError);
87
+ }
88
+ });
89
+
90
+ it("preserves historical work-order and cost data after deactivation", async () => {
91
+ const { db, spies } = createMockDb<Transaction>();
92
+ const deactivated = {
93
+ ...baseActiveWorkCenter,
94
+ status: "INACTIVE" as const,
95
+ updatedAt: new Date(),
96
+ };
97
+
98
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter);
99
+ spies.select.mockReturnValueOnce(undefined); // no active routing dependency
100
+ spies.select.mockReturnValueOnce(undefined); // no open work orders
101
+ spies.update.mockReturnValue(deactivated);
102
+
103
+ const result = await run(db, { id: baseActiveWorkCenter.id }, ctx);
104
+
105
+ expect(result.ok).toBe(true);
106
+ if (result.ok) {
107
+ // The work center is deactivated but its identity and historical fields are preserved
108
+ expect(result.value.workCenter.id).toBe(baseActiveWorkCenter.id);
109
+ expect(result.value.workCenter.code).toBe(baseActiveWorkCenter.code);
110
+ expect(result.value.workCenter.status).toBe("INACTIVE");
111
+ }
112
+ });
113
+ });
@@ -0,0 +1,85 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ WorkCenterNotFoundError,
4
+ WorkCenterNotDeactivatableError,
5
+ WorkCenterInUseError,
6
+ } from "../lib/errors.generated";
7
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
8
+
9
+ export interface DeactivateWorkCenterInput {
10
+ id: string;
11
+ from?: string[];
12
+ }
13
+
14
+ /**
15
+ * Function: deactivateWorkCenter
16
+ *
17
+ * Removes a work center from future routing and release selection.
18
+ * Preserves historical work-order and cost records while blocking
19
+ * new execution use.
20
+ */
21
+ export async function run<CF extends Record<string, unknown>>(
22
+ db: Transaction,
23
+ input: DeactivateWorkCenterInput & CF,
24
+ _ctx: CommandContext,
25
+ ) {
26
+ const { id, from, ...customFields } = input;
27
+ void customFields;
28
+
29
+ const allowedStatuses = from ?? ["ACTIVE"];
30
+
31
+ // 1. Fetch work center with lock
32
+ const workCenter = await db
33
+ .selectFrom("WorkCenter")
34
+ .selectAll()
35
+ .where("id", "=", id)
36
+ .forUpdate()
37
+ .executeTakeFirst();
38
+
39
+ if (!workCenter) {
40
+ return err(new WorkCenterNotFoundError(id));
41
+ }
42
+
43
+ // 2. Validate status is deactivatable
44
+ if (!allowedStatuses.includes(workCenter.status)) {
45
+ return err(new WorkCenterNotDeactivatableError(workCenter.code));
46
+ }
47
+
48
+ // 3. Check for blocking dependencies — active routings that reference this work center
49
+ const activeRoutingDependency = await db
50
+ .selectFrom("RoutingOperation")
51
+ .innerJoin("Routing", "Routing.id", "RoutingOperation.routingId")
52
+ .select("RoutingOperation.id")
53
+ .where("RoutingOperation.workCenterId", "=", id)
54
+ .where("Routing.status", "=", "ACTIVE")
55
+ .executeTakeFirst();
56
+
57
+ if (activeRoutingDependency) {
58
+ return err(new WorkCenterInUseError(workCenter.code));
59
+ }
60
+
61
+ // 4. Check for open work orders that still reference this work center
62
+ const openWorkOrder = await db
63
+ .selectFrom("WorkOrder")
64
+ .select("WorkOrder.id")
65
+ .where("WorkOrder.workCenterId", "=", id)
66
+ .where("WorkOrder.status", "in", ["PENDING", "IN_PROGRESS", "PAUSED"])
67
+ .executeTakeFirst();
68
+
69
+ if (openWorkOrder) {
70
+ return err(new WorkCenterInUseError(workCenter.code));
71
+ }
72
+
73
+ // 5. Set status to INACTIVE
74
+ const updatedWorkCenter = await db
75
+ .updateTable("WorkCenter")
76
+ .set({
77
+ status: "INACTIVE",
78
+ updatedAt: new Date(),
79
+ })
80
+ .where("id", "=", id)
81
+ .returningAll()
82
+ .executeTakeFirst();
83
+
84
+ return ok({ workCenter: updatedWorkCenter! });
85
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./pauseWorkOrder";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const pauseWorkOrder = defineCommand(permissions.pauseWorkOrder, run);
@@ -0,0 +1,118 @@
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
+ WorkOrderNotPausableError,
7
+ PauseReasonRequiredError,
8
+ } from "../lib/errors.generated";
9
+ import { baseInProgressWorkOrder, basePendingWorkOrder } from "../testing/fixtures";
10
+ import { run } from "./pauseWorkOrder";
11
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
12
+
13
+ describe("pauseWorkOrder", () => {
14
+ const ctx: CommandContext = {
15
+ actorId: "test-actor",
16
+ permissions: ["manufacturing:pauseWorkOrder"],
17
+ };
18
+
19
+ it("pauses an in-progress work order", async () => {
20
+ const { db, spies } = createMockDb<Transaction>();
21
+ const pausedWorkOrder = {
22
+ ...baseInProgressWorkOrder,
23
+ status: "PAUSED" as const,
24
+ pauseReason: "Machine maintenance",
25
+ };
26
+
27
+ // work order lookup
28
+ spies.select.mockReturnValueOnce(baseInProgressWorkOrder);
29
+ // update work order
30
+ spies.update.mockReturnValueOnce(pausedWorkOrder);
31
+ // insert execution event
32
+ spies.insert.mockReturnValueOnce(undefined);
33
+
34
+ const result = await run(
35
+ db,
36
+ { id: baseInProgressWorkOrder.id, pauseReason: "Machine maintenance" },
37
+ ctx,
38
+ );
39
+
40
+ expect(result.ok).toBe(true);
41
+ if (result.ok) {
42
+ expect(result.value.workOrder.status).toBe("PAUSED");
43
+ expect(result.value.workOrder.pauseReason).toBe("Machine maintenance");
44
+ }
45
+ expect(spies.update).toHaveBeenCalled();
46
+ expect(spies.insert).toHaveBeenCalled();
47
+ });
48
+
49
+ it("returns error when the work order does not exist", async () => {
50
+ const { db, spies } = createMockDb<Transaction>();
51
+ spies.select.mockReturnValueOnce(undefined);
52
+
53
+ const result = await run(db, { id: "nonexistent", pauseReason: "Some reason" }, ctx);
54
+
55
+ expect(result.ok).toBe(false);
56
+ if (!result.ok) {
57
+ expect(result.error).toBeInstanceOf(WorkOrderNotFoundError);
58
+ }
59
+ });
60
+
61
+ it("returns error when the work order is not in progress", async () => {
62
+ const { db, spies } = createMockDb<Transaction>();
63
+ spies.select.mockReturnValueOnce(basePendingWorkOrder);
64
+
65
+ const result = await run(db, { id: basePendingWorkOrder.id, pauseReason: "Some reason" }, ctx);
66
+
67
+ expect(result.ok).toBe(false);
68
+ if (!result.ok) {
69
+ expect(result.error).toBeInstanceOf(WorkOrderNotPausableError);
70
+ }
71
+ });
72
+
73
+ it("returns error when no pause reason is provided", async () => {
74
+ const { db } = createMockDb<Transaction>();
75
+
76
+ const result = await run(db, { id: baseInProgressWorkOrder.id, pauseReason: "" }, ctx);
77
+
78
+ expect(result.ok).toBe(false);
79
+ if (!result.ok) {
80
+ expect(result.error).toBeInstanceOf(PauseReasonRequiredError);
81
+ }
82
+ });
83
+
84
+ it("preserves prior execution quantities and time after pause", async () => {
85
+ const { db, spies } = createMockDb<Transaction>();
86
+ const pausedWorkOrder = {
87
+ ...baseInProgressWorkOrder,
88
+ status: "PAUSED" as const,
89
+ pauseReason: "Break time",
90
+ completedQuantity: baseInProgressWorkOrder.completedQuantity,
91
+ scrapQuantity: baseInProgressWorkOrder.scrapQuantity,
92
+ actualSetupTime: baseInProgressWorkOrder.actualSetupTime,
93
+ actualRunTime: baseInProgressWorkOrder.actualRunTime,
94
+ };
95
+
96
+ // work order lookup
97
+ spies.select.mockReturnValueOnce(baseInProgressWorkOrder);
98
+ // update work order
99
+ spies.update.mockReturnValueOnce(pausedWorkOrder);
100
+ // insert execution event
101
+ spies.insert.mockReturnValueOnce(undefined);
102
+
103
+ const result = await run(
104
+ db,
105
+ { id: baseInProgressWorkOrder.id, pauseReason: "Break time" },
106
+ ctx,
107
+ );
108
+
109
+ expect(result.ok).toBe(true);
110
+ if (result.ok) {
111
+ expect(result.value.workOrder.completedQuantity).toBe(
112
+ baseInProgressWorkOrder.completedQuantity,
113
+ );
114
+ expect(result.value.workOrder.actualSetupTime).toBe(baseInProgressWorkOrder.actualSetupTime);
115
+ expect(result.value.workOrder.actualRunTime).toBe(baseInProgressWorkOrder.actualRunTime);
116
+ }
117
+ });
118
+ });
@@ -0,0 +1,82 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ WorkOrderNotFoundError,
4
+ WorkOrderNotPausableError,
5
+ PauseReasonRequiredError,
6
+ } from "../lib/errors.generated";
7
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
8
+
9
+ export interface PauseWorkOrderInput {
10
+ id: string;
11
+ pauseReason: string;
12
+ }
13
+
14
+ /**
15
+ * Function: pauseWorkOrder
16
+ *
17
+ * Temporarily halts an in-progress work order and records the reason for
18
+ * the interruption. Preserves accumulated execution history so the work
19
+ * order can later resume.
20
+ */
21
+ export async function run<CF extends Record<string, unknown>>(
22
+ db: Transaction,
23
+ input: PauseWorkOrderInput & CF,
24
+ _ctx: CommandContext,
25
+ ) {
26
+ const { id, pauseReason, ...customFields } = input;
27
+ void customFields;
28
+
29
+ // 1. Validate pause reason is provided
30
+ if (!pauseReason || pauseReason.trim() === "") {
31
+ return err(new PauseReasonRequiredError(id));
32
+ }
33
+
34
+ // 2. Fetch work order with lock
35
+ const workOrder = await db
36
+ .selectFrom("WorkOrder")
37
+ .selectAll()
38
+ .where("id", "=", id)
39
+ .forUpdate()
40
+ .executeTakeFirst();
41
+
42
+ if (!workOrder) {
43
+ return err(new WorkOrderNotFoundError(id));
44
+ }
45
+
46
+ // 3. Validate status is IN_PROGRESS
47
+ if (workOrder.status !== "IN_PROGRESS") {
48
+ return err(new WorkOrderNotPausableError(id));
49
+ }
50
+
51
+ // 4. Record pause event and set PAUSED
52
+ const now = new Date();
53
+
54
+ const updatedWorkOrder = await db
55
+ .updateTable("WorkOrder")
56
+ .set({
57
+ status: "PAUSED",
58
+ pauseReason,
59
+ updatedAt: now,
60
+ })
61
+ .where("id", "=", id)
62
+ .returningAll()
63
+ .executeTakeFirstOrThrow();
64
+
65
+ // 5. Create PAUSED execution event
66
+ await db
67
+ .insertInto("WorkOrderExecutionEvent")
68
+ .values({
69
+ workOrderId: id,
70
+ eventType: "PAUSED",
71
+ timestamp: now,
72
+ quantity: null,
73
+ timeValue: null,
74
+ scrapValue: null,
75
+ notes: pauseReason,
76
+ createdAt: now,
77
+ updatedAt: null,
78
+ })
79
+ .execute();
80
+
81
+ return ok({ workOrder: updatedWorkOrder });
82
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./recordInventoryIssueOutcome";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const recordInventoryIssueOutcome = defineCommand(permissions.recordInventoryIssueOutcome, run);
@@ -0,0 +1,183 @@
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
+ CostSummaryNotFoundError,
7
+ CostSummaryNotCollectingError,
8
+ MissingInventoryIssueReferenceError,
9
+ MissingActualUnitCostError,
10
+ MissingActualExtendedCostError,
11
+ MissingPostingDateError,
12
+ IssueOutcomeNotFinalError,
13
+ DuplicateIssueOutcomeError,
14
+ } from "../lib/errors.generated";
15
+ import {
16
+ baseReleasedProductionOrder,
17
+ baseCollectingCostSummary,
18
+ basePendingReviewCostSummary,
19
+ baseCostLine,
20
+ } from "../testing/fixtures";
21
+ import { run } from "./recordInventoryIssueOutcome";
22
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
23
+
24
+ describe("recordInventoryIssueOutcome", () => {
25
+ const ctx: CommandContext = {
26
+ actorId: "test-actor",
27
+ permissions: ["manufacturing:recordInventoryIssueOutcome"],
28
+ };
29
+
30
+ const validInput = {
31
+ productionOrderId: "production-order-2",
32
+ inventoryIssueReference: "INV-ISSUE-001",
33
+ itemReference: "item-2",
34
+ issuedQuantity: 200,
35
+ unitOfMeasure: "EA",
36
+ actualUnitCost: 10.5,
37
+ actualExtendedCost: 2100,
38
+ currency: "USD",
39
+ valuationMethod: "STANDARD",
40
+ postingDate: "2024-03-10",
41
+ siteReference: "site-1",
42
+ isFinalValuation: true,
43
+ };
44
+
45
+ it("applies a final-valued inventory issue outcome to a collecting summary", async () => {
46
+ const { db, spies } = createMockDb<Transaction>();
47
+
48
+ // select: production order, cost summary, existing cost line (duplicate check)
49
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
50
+ spies.select.mockReturnValueOnce(baseCollectingCostSummary);
51
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
52
+
53
+ const updatedSummary = {
54
+ ...baseCollectingCostSummary,
55
+ actualMaterialCost: 2100,
56
+ };
57
+ spies.insert.mockReturnValue({});
58
+ spies.update.mockReturnValue(updatedSummary);
59
+
60
+ const result = await run(db, validInput, ctx);
61
+
62
+ expect(result.ok).toBe(true);
63
+ if (result.ok) {
64
+ expect(result.value.costSummary).toEqual(updatedSummary);
65
+ }
66
+ expect(spies.insert).toHaveBeenCalled();
67
+ expect(spies.update).toHaveBeenCalled();
68
+ });
69
+
70
+ it("returns error when the production order does not exist", async () => {
71
+ const { db, spies } = createMockDb<Transaction>();
72
+
73
+ spies.select.mockReturnValueOnce(undefined); // no production order
74
+
75
+ const result = await run(db, validInput, ctx);
76
+
77
+ expect(result.ok).toBe(false);
78
+ if (!result.ok) {
79
+ expect(result.error).toBeInstanceOf(ProductionOrderNotFoundError);
80
+ }
81
+ });
82
+
83
+ it("returns error when the linked cost summary does not exist", async () => {
84
+ const { db, spies } = createMockDb<Transaction>();
85
+
86
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
87
+ spies.select.mockReturnValueOnce(undefined); // no cost summary
88
+
89
+ const result = await run(db, validInput, ctx);
90
+
91
+ expect(result.ok).toBe(false);
92
+ if (!result.ok) {
93
+ expect(result.error).toBeInstanceOf(CostSummaryNotFoundError);
94
+ }
95
+ });
96
+
97
+ it("returns error when the summary is not collecting", async () => {
98
+ const { db, spies } = createMockDb<Transaction>();
99
+
100
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
101
+ spies.select.mockReturnValueOnce(basePendingReviewCostSummary); // not COLLECTING
102
+
103
+ const result = await run(db, validInput, ctx);
104
+
105
+ expect(result.ok).toBe(false);
106
+ if (!result.ok) {
107
+ expect(result.error).toBeInstanceOf(CostSummaryNotCollectingError);
108
+ }
109
+ });
110
+
111
+ it("returns error when inventoryIssueReference is missing from the outcome payload", async () => {
112
+ const { db } = createMockDb<Transaction>();
113
+
114
+ const result = await run(db, { ...validInput, inventoryIssueReference: null as never }, ctx);
115
+
116
+ expect(result.ok).toBe(false);
117
+ if (!result.ok) {
118
+ expect(result.error).toBeInstanceOf(MissingInventoryIssueReferenceError);
119
+ }
120
+ });
121
+
122
+ it("returns error when actualUnitCost is missing from the outcome payload", async () => {
123
+ const { db } = createMockDb<Transaction>();
124
+
125
+ const result = await run(db, { ...validInput, actualUnitCost: null as never }, ctx);
126
+
127
+ expect(result.ok).toBe(false);
128
+ if (!result.ok) {
129
+ expect(result.error).toBeInstanceOf(MissingActualUnitCostError);
130
+ }
131
+ });
132
+
133
+ it("returns error when actualExtendedCost is missing from the outcome payload", async () => {
134
+ const { db } = createMockDb<Transaction>();
135
+
136
+ const result = await run(db, { ...validInput, actualExtendedCost: null as never }, ctx);
137
+
138
+ expect(result.ok).toBe(false);
139
+ if (!result.ok) {
140
+ expect(result.error).toBeInstanceOf(MissingActualExtendedCostError);
141
+ }
142
+ });
143
+
144
+ it("returns error when postingDate is missing from the outcome payload", async () => {
145
+ const { db } = createMockDb<Transaction>();
146
+
147
+ const result = await run(db, { ...validInput, postingDate: null as never }, ctx);
148
+
149
+ expect(result.ok).toBe(false);
150
+ if (!result.ok) {
151
+ expect(result.error).toBeInstanceOf(MissingPostingDateError);
152
+ }
153
+ });
154
+
155
+ it("returns error when the inventory issue outcome is not yet final", async () => {
156
+ const { db, spies } = createMockDb<Transaction>();
157
+
158
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
159
+ spies.select.mockReturnValueOnce(baseCollectingCostSummary);
160
+
161
+ const result = await run(db, { ...validInput, isFinalValuation: false }, ctx);
162
+
163
+ expect(result.ok).toBe(false);
164
+ if (!result.ok) {
165
+ expect(result.error).toBeInstanceOf(IssueOutcomeNotFinalError);
166
+ }
167
+ });
168
+
169
+ it("returns error when the same issue outcome is applied twice", async () => {
170
+ const { db, spies } = createMockDb<Transaction>();
171
+
172
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
173
+ spies.select.mockReturnValueOnce(baseCollectingCostSummary);
174
+ spies.select.mockReturnValueOnce(baseCostLine); // duplicate found
175
+
176
+ const result = await run(db, validInput, ctx);
177
+
178
+ expect(result.ok).toBe(false);
179
+ if (!result.ok) {
180
+ expect(result.error).toBeInstanceOf(DuplicateIssueOutcomeError);
181
+ }
182
+ });
183
+ });