@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,160 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ ProductionItemNotFoundError,
6
+ ProductionItemNotManufacturableError,
7
+ InvalidPlannedQuantityError,
8
+ InvalidSiteScopeError,
9
+ CrossScopeMasterReferenceError,
10
+ } from "../lib/errors.generated";
11
+ import { baseDraftProductionOrder, baseActiveBom, baseActiveRouting } from "../testing/fixtures";
12
+ import { run } from "./createProductionOrder";
13
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
14
+
15
+ describe("createProductionOrder", () => {
16
+ const ctx: CommandContext = {
17
+ actorId: "test-actor",
18
+ permissions: ["manufacturing:createProductionOrder"],
19
+ };
20
+
21
+ const validInput = {
22
+ orderedItemId: "item-1",
23
+ companyId: "company-1",
24
+ siteId: "site-1",
25
+ plannedQuantity: 100,
26
+ plannedStartDate: new Date("2024-03-01T00:00:00.000Z"),
27
+ plannedEndDate: new Date("2024-03-15T00:00:00.000Z"),
28
+ };
29
+
30
+ it("creates a draft production order for a manufacturable item", async () => {
31
+ const { db, spies } = createMockDb<Transaction>();
32
+ const createdOrder = {
33
+ ...baseDraftProductionOrder,
34
+ id: "new-order-id",
35
+ };
36
+
37
+ // item lookup
38
+ spies.select.mockReturnValueOnce({ id: "item-1" });
39
+ // site lookup
40
+ spies.select.mockReturnValueOnce({ id: "site-1" });
41
+ // insert
42
+ spies.insert.mockReturnValue(createdOrder);
43
+
44
+ const result = await run(db, validInput, ctx);
45
+
46
+ expect(result.ok).toBe(true);
47
+ if (result.ok) {
48
+ expect(result.value.productionOrder.status).toBe("DRAFT");
49
+ }
50
+ expect(spies.insert).toHaveBeenCalled();
51
+ });
52
+
53
+ it("returns error when the produced item does not exist", async () => {
54
+ const { db, spies } = createMockDb<Transaction>();
55
+
56
+ // item lookup returns undefined
57
+ spies.select.mockReturnValueOnce(undefined);
58
+
59
+ const result = await run(db, validInput, ctx);
60
+
61
+ expect(result.ok).toBe(false);
62
+ if (!result.ok) {
63
+ expect(result.error).toBeInstanceOf(ProductionItemNotFoundError);
64
+ }
65
+ });
66
+
67
+ it("returns error when the produced item is not manufacturable", async () => {
68
+ const { db, spies } = createMockDb<Transaction>();
69
+
70
+ // item exists but not manufacturable
71
+ spies.select.mockReturnValueOnce({ id: "item-1", isManufacturable: false });
72
+
73
+ const result = await run(db, validInput, ctx);
74
+
75
+ expect(result.ok).toBe(false);
76
+ if (!result.ok) {
77
+ expect(result.error).toBeInstanceOf(ProductionItemNotManufacturableError);
78
+ }
79
+ });
80
+
81
+ it("returns error when planned quantity is not positive", async () => {
82
+ const { db } = createMockDb<Transaction>();
83
+
84
+ const result = await run(db, { ...validInput, plannedQuantity: 0 }, ctx);
85
+
86
+ expect(result.ok).toBe(false);
87
+ if (!result.ok) {
88
+ expect(result.error).toBeInstanceOf(InvalidPlannedQuantityError);
89
+ }
90
+ });
91
+
92
+ it("returns error when site scope is inconsistent with company", async () => {
93
+ const { db } = createMockDb<Transaction>();
94
+
95
+ const result = await run(db, { ...validInput, siteId: "" }, ctx);
96
+
97
+ expect(result.ok).toBe(false);
98
+ if (!result.ok) {
99
+ expect(result.error).toBeInstanceOf(InvalidSiteScopeError);
100
+ }
101
+ });
102
+
103
+ it("returns error when site does not exist", async () => {
104
+ const { db, spies } = createMockDb<Transaction>();
105
+
106
+ // item lookup
107
+ spies.select.mockReturnValueOnce({ id: "item-1" });
108
+ // site lookup returns undefined
109
+ spies.select.mockReturnValueOnce(undefined);
110
+
111
+ const result = await run(db, validInput, ctx);
112
+
113
+ expect(result.ok).toBe(false);
114
+ if (!result.ok) {
115
+ expect(result.error).toBeInstanceOf(InvalidSiteScopeError);
116
+ }
117
+ });
118
+
119
+ it("returns error when an optional BOM reference is outside scope", async () => {
120
+ const { db, spies } = createMockDb<Transaction>();
121
+ const crossScopeBom = { ...baseActiveBom, companyId: "other-company" };
122
+
123
+ // item lookup
124
+ spies.select.mockReturnValueOnce({ id: "item-1" });
125
+ // site lookup
126
+ spies.select.mockReturnValueOnce({ id: "site-1" });
127
+ // bom lookup
128
+ spies.select.mockReturnValueOnce(crossScopeBom);
129
+
130
+ const result = await run(db, { ...validInput, selectedBomVersionId: crossScopeBom.id }, ctx);
131
+
132
+ expect(result.ok).toBe(false);
133
+ if (!result.ok) {
134
+ expect(result.error).toBeInstanceOf(CrossScopeMasterReferenceError);
135
+ }
136
+ });
137
+
138
+ it("returns error when an optional routing reference is outside scope", async () => {
139
+ const { db, spies } = createMockDb<Transaction>();
140
+ const crossScopeRouting = { ...baseActiveRouting, companyId: "other-company" };
141
+
142
+ // item lookup
143
+ spies.select.mockReturnValueOnce({ id: "item-1" });
144
+ // site lookup
145
+ spies.select.mockReturnValueOnce({ id: "site-1" });
146
+ // routing lookup
147
+ spies.select.mockReturnValueOnce(crossScopeRouting);
148
+
149
+ const result = await run(
150
+ db,
151
+ { ...validInput, selectedRoutingRevisionId: crossScopeRouting.id },
152
+ ctx,
153
+ );
154
+
155
+ expect(result.ok).toBe(false);
156
+ if (!result.ok) {
157
+ expect(result.error).toBeInstanceOf(CrossScopeMasterReferenceError);
158
+ }
159
+ });
160
+ });
@@ -0,0 +1,129 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ ProductionItemNotFoundError,
4
+ ProductionItemNotManufacturableError,
5
+ InvalidPlannedQuantityError,
6
+ InvalidSiteScopeError,
7
+ CrossScopeMasterReferenceError,
8
+ } from "../lib/errors.generated";
9
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
10
+
11
+ export interface CreateProductionOrderInput {
12
+ orderedItemId: string;
13
+ companyId: string;
14
+ siteId: string;
15
+ plannedQuantity: number;
16
+ plannedStartDate: Date;
17
+ plannedEndDate: Date;
18
+ selectedBomVersionId?: string | null;
19
+ selectedRoutingRevisionId?: string | null;
20
+ }
21
+
22
+ /**
23
+ * Function: createProductionOrder
24
+ *
25
+ * Creates a draft manufacturing request with produced item, quantity, site,
26
+ * and planned dates. Records planner intent without freezing BOM, routing,
27
+ * work orders, or cost assumptions.
28
+ */
29
+ export async function run<CF extends Record<string, unknown>>(
30
+ db: Transaction,
31
+ input: CreateProductionOrderInput & CF,
32
+ _ctx: CommandContext,
33
+ ) {
34
+ const {
35
+ orderedItemId,
36
+ companyId,
37
+ siteId,
38
+ plannedQuantity,
39
+ plannedStartDate,
40
+ plannedEndDate,
41
+ selectedBomVersionId,
42
+ selectedRoutingRevisionId,
43
+ ...customFields
44
+ } = input;
45
+
46
+ // 1. Validate planned quantity > 0
47
+ if (plannedQuantity <= 0) {
48
+ return err(new InvalidPlannedQuantityError(orderedItemId));
49
+ }
50
+
51
+ // 2. Validate scope
52
+ if (!companyId || !siteId) {
53
+ return err(new InvalidSiteScopeError(orderedItemId));
54
+ }
55
+
56
+ // 3. Validate produced item exists
57
+ const item = await db
58
+ .selectFrom("Item")
59
+ .selectAll()
60
+ .where("id", "=", orderedItemId)
61
+ .executeTakeFirst();
62
+
63
+ if (!item) {
64
+ return err(new ProductionItemNotFoundError(orderedItemId));
65
+ }
66
+
67
+ // 4. Validate item is manufacturable
68
+ if (item && "isManufacturable" in item && item.isManufacturable === false) {
69
+ return err(new ProductionItemNotManufacturableError(orderedItemId));
70
+ }
71
+
72
+ // 5. Validate site belongs to company scope
73
+ const site = await db.selectFrom("Site").selectAll().where("id", "=", siteId).executeTakeFirst();
74
+
75
+ if (!site) {
76
+ return err(new InvalidSiteScopeError(orderedItemId));
77
+ }
78
+
79
+ // 5. Validate optional BOM reference scope
80
+ if (selectedBomVersionId) {
81
+ const bom = await db
82
+ .selectFrom("BillOfMaterial")
83
+ .selectAll()
84
+ .where("id", "=", selectedBomVersionId)
85
+ .executeTakeFirst();
86
+
87
+ if (bom && (bom.companyId !== companyId || (bom.siteId != null && bom.siteId !== siteId))) {
88
+ return err(new CrossScopeMasterReferenceError(orderedItemId));
89
+ }
90
+ }
91
+
92
+ // 6. Validate optional routing reference scope
93
+ if (selectedRoutingRevisionId) {
94
+ const routing = await db
95
+ .selectFrom("Routing")
96
+ .selectAll()
97
+ .where("id", "=", selectedRoutingRevisionId)
98
+ .executeTakeFirst();
99
+
100
+ if (
101
+ routing &&
102
+ (routing.companyId !== companyId || (routing.siteId != null && routing.siteId !== siteId))
103
+ ) {
104
+ return err(new CrossScopeMasterReferenceError(orderedItemId));
105
+ }
106
+ }
107
+
108
+ // 7. Create draft production order
109
+ const productionOrder = await db
110
+ .insertInto("ProductionOrder")
111
+ .values({
112
+ ...(customFields as Record<string, unknown>),
113
+ orderedItemId,
114
+ companyId,
115
+ siteId,
116
+ plannedQuantity,
117
+ plannedStartDate,
118
+ plannedEndDate,
119
+ selectedBomVersionId: selectedBomVersionId ?? null,
120
+ selectedRoutingRevisionId: selectedRoutingRevisionId ?? null,
121
+ status: "DRAFT",
122
+ createdAt: new Date(),
123
+ updatedAt: null,
124
+ })
125
+ .returningAll()
126
+ .executeTakeFirst();
127
+
128
+ return ok({ productionOrder: productionOrder! });
129
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./createRouting";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const createRouting = defineCommand(permissions.createRouting, run);
@@ -0,0 +1,168 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ RoutingItemNotFoundError,
6
+ OperationRequiredError,
7
+ DuplicateOperationSequenceError,
8
+ InvalidStandardTimeError,
9
+ WorkCenterNotFoundError,
10
+ CrossCompanyWorkCenterError,
11
+ } from "../lib/errors.generated";
12
+ import { baseDraftRouting, baseActiveWorkCenter } from "../testing/fixtures";
13
+ import { run } from "./createRouting";
14
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
15
+
16
+ describe("createRouting", () => {
17
+ const ctx: CommandContext = {
18
+ actorId: "test-actor",
19
+ permissions: ["manufacturing:createRouting"],
20
+ };
21
+
22
+ const validOperations = [
23
+ {
24
+ sequenceNumber: 10,
25
+ operationDescription: "Assembly step",
26
+ workCenterId: "work-center-2",
27
+ standardSetupTime: 30,
28
+ standardRunTime: 60,
29
+ operatorInstructions: null,
30
+ },
31
+ {
32
+ sequenceNumber: 20,
33
+ operationDescription: "Quality check",
34
+ workCenterId: "work-center-2",
35
+ standardSetupTime: 10,
36
+ standardRunTime: 15,
37
+ operatorInstructions: null,
38
+ },
39
+ ];
40
+
41
+ const validInput = {
42
+ parentItemId: "item-1",
43
+ companyId: "company-1",
44
+ siteId: null,
45
+ revisionNumber: "R1",
46
+ operations: validOperations,
47
+ };
48
+
49
+ it("creates a draft routing with ordered operations", async () => {
50
+ const { db, spies } = createMockDb<Transaction>();
51
+
52
+ // select: Item lookup
53
+ spies.select.mockReturnValueOnce({ id: "item-1" });
54
+ // select: WorkCenter lookup for op 1
55
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter);
56
+ // select: WorkCenter lookup for op 2
57
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter);
58
+ // insert: Routing
59
+ spies.insert.mockReturnValue(baseDraftRouting);
60
+
61
+ const result = await run(db, validInput, ctx);
62
+
63
+ expect(result.ok).toBe(true);
64
+ if (result.ok) {
65
+ expect(result.value.routing.status).toBe("DRAFT");
66
+ expect(result.value.routing.parentItemId).toBe("item-1");
67
+ }
68
+ expect(spies.insert).toHaveBeenCalled();
69
+ });
70
+
71
+ it("returns error when no operations are provided", async () => {
72
+ const { db, spies } = createMockDb<Transaction>();
73
+
74
+ // select: Item lookup
75
+ spies.select.mockReturnValueOnce({ id: "item-1" });
76
+
77
+ const result = await run(db, { ...validInput, operations: [] }, ctx);
78
+
79
+ expect(result.ok).toBe(false);
80
+ if (!result.ok) {
81
+ expect(result.error).toBeInstanceOf(OperationRequiredError);
82
+ }
83
+ });
84
+
85
+ it("returns error when two operations share the same sequence", async () => {
86
+ const { db, spies } = createMockDb<Transaction>();
87
+
88
+ // select: Item lookup
89
+ spies.select.mockReturnValueOnce({ id: "item-1" });
90
+
91
+ const duplicateOps = [
92
+ { ...validOperations[0], sequenceNumber: 10 },
93
+ { ...validOperations[1], sequenceNumber: 10 },
94
+ ];
95
+
96
+ const result = await run(db, { ...validInput, operations: duplicateOps }, ctx);
97
+
98
+ expect(result.ok).toBe(false);
99
+ if (!result.ok) {
100
+ expect(result.error).toBeInstanceOf(DuplicateOperationSequenceError);
101
+ }
102
+ });
103
+
104
+ it("returns error when a standard time is negative", async () => {
105
+ const { db, spies } = createMockDb<Transaction>();
106
+
107
+ // select: Item lookup
108
+ spies.select.mockReturnValueOnce({ id: "item-1" });
109
+
110
+ const badOps = [{ ...validOperations[0], standardSetupTime: -5 }];
111
+
112
+ const result = await run(db, { ...validInput, operations: badOps }, ctx);
113
+
114
+ expect(result.ok).toBe(false);
115
+ if (!result.ok) {
116
+ expect(result.error).toBeInstanceOf(InvalidStandardTimeError);
117
+ }
118
+ });
119
+
120
+ it("returns error when a referenced work center does not exist", async () => {
121
+ const { db, spies } = createMockDb<Transaction>();
122
+
123
+ // select: Item lookup
124
+ spies.select.mockReturnValueOnce({ id: "item-1" });
125
+ // select: WorkCenter lookup - not found
126
+ spies.select.mockReturnValueOnce(undefined);
127
+
128
+ const result = await run(db, validInput, ctx);
129
+
130
+ expect(result.ok).toBe(false);
131
+ if (!result.ok) {
132
+ expect(result.error).toBeInstanceOf(WorkCenterNotFoundError);
133
+ }
134
+ });
135
+
136
+ it("returns error when a work center is outside the routing company", async () => {
137
+ const { db, spies } = createMockDb<Transaction>();
138
+
139
+ // select: Item lookup
140
+ spies.select.mockReturnValueOnce({ id: "item-1" });
141
+ // select: WorkCenter lookup - different company
142
+ spies.select.mockReturnValueOnce({
143
+ ...baseActiveWorkCenter,
144
+ companyId: "other-company",
145
+ });
146
+
147
+ const result = await run(db, validInput, ctx);
148
+
149
+ expect(result.ok).toBe(false);
150
+ if (!result.ok) {
151
+ expect(result.error).toBeInstanceOf(CrossCompanyWorkCenterError);
152
+ }
153
+ });
154
+
155
+ it("returns error when parent item does not exist", async () => {
156
+ const { db, spies } = createMockDb<Transaction>();
157
+
158
+ // select: Item lookup - not found
159
+ spies.select.mockReturnValueOnce(undefined);
160
+
161
+ const result = await run(db, validInput, ctx);
162
+
163
+ expect(result.ok).toBe(false);
164
+ if (!result.ok) {
165
+ expect(result.error).toBeInstanceOf(RoutingItemNotFoundError);
166
+ }
167
+ });
168
+ });
@@ -0,0 +1,128 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ RoutingItemNotFoundError,
4
+ OperationRequiredError,
5
+ DuplicateOperationSequenceError,
6
+ InvalidStandardTimeError,
7
+ WorkCenterNotFoundError,
8
+ CrossCompanyWorkCenterError,
9
+ } from "../lib/errors.generated";
10
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
11
+
12
+ export interface CreateRoutingOperationInput {
13
+ sequenceNumber: number;
14
+ operationDescription?: string | null;
15
+ workCenterId: string;
16
+ standardSetupTime: number;
17
+ standardRunTime: number;
18
+ operatorInstructions?: string | null;
19
+ }
20
+
21
+ export interface CreateRoutingInput {
22
+ parentItemId: string;
23
+ companyId: string;
24
+ siteId?: string | null;
25
+ revisionNumber?: string | null;
26
+ operations: CreateRoutingOperationInput[];
27
+ }
28
+
29
+ /**
30
+ * Function: createRouting
31
+ *
32
+ * Creates a new routing in DRAFT status with ordered operations.
33
+ * Validates parent item existence, operation sequence uniqueness,
34
+ * standard times, and work center references.
35
+ */
36
+ export async function run<CF extends Record<string, unknown>>(
37
+ db: Transaction,
38
+ input: CreateRoutingInput & CF,
39
+ _ctx: CommandContext,
40
+ ) {
41
+ const { parentItemId, companyId, siteId, revisionNumber, operations, ...customFields } = input;
42
+
43
+ // 1. Validate parent item exists
44
+ const parentItem = await db
45
+ .selectFrom("Item")
46
+ .selectAll()
47
+ .where("id", "=", parentItemId)
48
+ .executeTakeFirst();
49
+
50
+ if (!parentItem) {
51
+ return err(new RoutingItemNotFoundError(parentItemId));
52
+ }
53
+
54
+ // 2. Validate at least one operation
55
+ if (!operations || operations.length === 0) {
56
+ return err(new OperationRequiredError(parentItemId));
57
+ }
58
+
59
+ // 3. Validate unique sequence numbers
60
+ const seqNumbers = operations.map((op) => op.sequenceNumber);
61
+ const uniqueSeqs = new Set(seqNumbers);
62
+ if (uniqueSeqs.size !== seqNumbers.length) {
63
+ return err(new DuplicateOperationSequenceError(parentItemId));
64
+ }
65
+
66
+ // 4. Validate standard times >= 0
67
+ for (const op of operations) {
68
+ if (op.standardSetupTime < 0 || op.standardRunTime < 0) {
69
+ return err(new InvalidStandardTimeError(parentItemId));
70
+ }
71
+ }
72
+
73
+ // 5. Validate work centers exist and belong to same company
74
+ for (const op of operations) {
75
+ const workCenter = await db
76
+ .selectFrom("WorkCenter")
77
+ .selectAll()
78
+ .where("id", "=", op.workCenterId)
79
+ .executeTakeFirst();
80
+
81
+ if (!workCenter) {
82
+ return err(new WorkCenterNotFoundError(op.workCenterId));
83
+ }
84
+
85
+ if (workCenter.companyId !== companyId) {
86
+ return err(new CrossCompanyWorkCenterError(op.workCenterId));
87
+ }
88
+ }
89
+
90
+ // 6. Create routing in DRAFT status
91
+ const routing = await db
92
+ .insertInto("Routing")
93
+ .values({
94
+ ...(customFields as Record<string, unknown>),
95
+ parentItemId,
96
+ companyId,
97
+ siteId: siteId ?? null,
98
+ revisionNumber: revisionNumber ?? null,
99
+ status: "DRAFT",
100
+ createdAt: new Date(),
101
+ updatedAt: null,
102
+ })
103
+ .returningAll()
104
+ .executeTakeFirst();
105
+
106
+ // 7. Create routing operations
107
+ const createdOperations = [];
108
+ for (const op of operations) {
109
+ const operation = await db
110
+ .insertInto("RoutingOperation")
111
+ .values({
112
+ routingId: routing!.id,
113
+ sequenceNumber: op.sequenceNumber,
114
+ operationDescription: op.operationDescription ?? null,
115
+ workCenterId: op.workCenterId,
116
+ standardSetupTime: op.standardSetupTime,
117
+ standardRunTime: op.standardRunTime,
118
+ operatorInstructions: op.operatorInstructions ?? null,
119
+ createdAt: new Date(),
120
+ updatedAt: null,
121
+ })
122
+ .returningAll()
123
+ .executeTakeFirst();
124
+ createdOperations.push(operation!);
125
+ }
126
+
127
+ return ok({ routing: routing!, operations: createdOperations });
128
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./createWorkCenter";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const createWorkCenter = defineCommand(permissions.createWorkCenter, run);