@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,139 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ ProductionOrderNotFoundError,
4
+ CostSummaryNotFoundError,
5
+ CostSummaryNotCollectingError,
6
+ MissingInventoryIssueReferenceError,
7
+ MissingActualUnitCostError,
8
+ MissingActualExtendedCostError,
9
+ MissingPostingDateError,
10
+ IssueOutcomeNotFinalError,
11
+ DuplicateIssueOutcomeError,
12
+ } from "../lib/errors.generated";
13
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
14
+
15
+ export interface RecordInventoryIssueOutcomeInput {
16
+ productionOrderId: string;
17
+ inventoryIssueReference: string;
18
+ itemReference: string;
19
+ issuedQuantity: number;
20
+ unitOfMeasure: string;
21
+ actualUnitCost: number;
22
+ actualExtendedCost: number;
23
+ currency: string;
24
+ valuationMethod: string;
25
+ postingDate: string;
26
+ siteReference: string;
27
+ isFinalValuation: boolean;
28
+ }
29
+
30
+ /**
31
+ * Function: recordInventoryIssueOutcome
32
+ *
33
+ * Applies one inventory-owned valuation outcome to the manufacturing cost
34
+ * summary for a production order. It is the only valid path for actual
35
+ * material cost to enter manufacturing.
36
+ */
37
+ export async function run<CF extends Record<string, unknown>>(
38
+ db: Transaction,
39
+ input: RecordInventoryIssueOutcomeInput & CF,
40
+ _ctx: CommandContext,
41
+ ) {
42
+ const {
43
+ productionOrderId,
44
+ inventoryIssueReference,
45
+ actualUnitCost,
46
+ actualExtendedCost,
47
+ postingDate,
48
+ isFinalValuation,
49
+ ...customFields
50
+ } = input;
51
+ void customFields;
52
+
53
+ // 1. Validate required payload fields
54
+ if (!inventoryIssueReference) {
55
+ return err(new MissingInventoryIssueReferenceError(productionOrderId));
56
+ }
57
+ if (actualUnitCost == null) {
58
+ return err(new MissingActualUnitCostError(productionOrderId));
59
+ }
60
+ if (actualExtendedCost == null) {
61
+ return err(new MissingActualExtendedCostError(productionOrderId));
62
+ }
63
+ if (!postingDate) {
64
+ return err(new MissingPostingDateError(productionOrderId));
65
+ }
66
+
67
+ // 2. Resolve production order
68
+ const productionOrder = await db
69
+ .selectFrom("ProductionOrder")
70
+ .selectAll()
71
+ .where("id", "=", productionOrderId)
72
+ .forUpdate()
73
+ .executeTakeFirst();
74
+
75
+ if (!productionOrder) {
76
+ return err(new ProductionOrderNotFoundError(productionOrderId));
77
+ }
78
+
79
+ // 3. Resolve cost summary
80
+ const costSummary = await db
81
+ .selectFrom("ManufacturingCostSummary")
82
+ .selectAll()
83
+ .where("productionOrderId", "=", productionOrderId)
84
+ .forUpdate()
85
+ .executeTakeFirst();
86
+
87
+ if (!costSummary) {
88
+ return err(new CostSummaryNotFoundError(productionOrderId));
89
+ }
90
+
91
+ // 4. Summary must be COLLECTING
92
+ if (costSummary.status !== "COLLECTING") {
93
+ return err(new CostSummaryNotCollectingError(productionOrderId));
94
+ }
95
+
96
+ // 5. Inventory valuation must be final
97
+ if (!isFinalValuation) {
98
+ return err(new IssueOutcomeNotFinalError(productionOrderId));
99
+ }
100
+
101
+ // 6. Check for duplicate application
102
+ const existingCostLine = await db
103
+ .selectFrom("ManufacturingCostLine")
104
+ .selectAll()
105
+ .where("costSummaryId", "=", costSummary.id)
106
+ .where("costType", "=", `MATERIAL:${inventoryIssueReference}`)
107
+ .executeTakeFirst();
108
+
109
+ if (existingCostLine) {
110
+ return err(new DuplicateIssueOutcomeError(productionOrderId));
111
+ }
112
+
113
+ // 7. Create a manufacturing cost line for this issue outcome
114
+ await db
115
+ .insertInto("ManufacturingCostLine")
116
+ .values({
117
+ costSummaryId: costSummary.id,
118
+ costType: `MATERIAL:${inventoryIssueReference}`,
119
+ plannedAmount: 0,
120
+ actualAmount: actualExtendedCost,
121
+ createdAt: new Date(),
122
+ updatedAt: null,
123
+ })
124
+ .returningAll()
125
+ .executeTakeFirst();
126
+
127
+ // 8. Update the cost summary's actual material cost
128
+ const updatedSummary = await db
129
+ .updateTable("ManufacturingCostSummary")
130
+ .set({
131
+ actualMaterialCost: costSummary.actualMaterialCost + actualExtendedCost,
132
+ updatedAt: new Date(),
133
+ })
134
+ .where("id", "=", costSummary.id)
135
+ .returningAll()
136
+ .executeTakeFirst();
137
+
138
+ return ok({ costSummary: updatedSummary! });
139
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./recordManufacturingCostSettlementAcknowledgment";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const recordManufacturingCostSettlementAcknowledgment = defineCommand(permissions.recordManufacturingCostSettlementAcknowledgment, run);
@@ -0,0 +1,120 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ CostSummaryNotFoundError,
6
+ CostSummaryNotSettlableError,
7
+ AcknowledgmentMismatchError,
8
+ AcknowledgmentRejectedOrSupersededError,
9
+ AcknowledgmentSourceInvalidError,
10
+ } from "../lib/errors.generated";
11
+ import { baseReviewedCostSummary, basePendingReviewCostSummary } from "../testing/fixtures";
12
+ import { run } from "./recordManufacturingCostSettlementAcknowledgment";
13
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
14
+
15
+ describe("recordManufacturingCostSettlementAcknowledgment", () => {
16
+ const ctx: CommandContext = {
17
+ actorId: "test-actor",
18
+ permissions: ["manufacturing:recordManufacturingCostSettlementAcknowledgment"],
19
+ };
20
+
21
+ const validInput = {
22
+ costSummaryId: "cost-summary-3",
23
+ productionOrderId: "production-order-2",
24
+ currencyCode: "USD",
25
+ settlementReference: "SETTLE-2024-001",
26
+ settlementDate: "2024-04-15",
27
+ acknowledgedDate: "2024-04-15",
28
+ acknowledgmentStatus: "ACCEPTED",
29
+ acknowledgmentSource: "downstream-accounting-system",
30
+ };
31
+
32
+ it("records a valid downstream settlement acknowledgment and settles the summary", async () => {
33
+ const { db, spies } = createMockDb<Transaction>();
34
+
35
+ spies.select.mockReturnValueOnce(baseReviewedCostSummary);
36
+
37
+ const settledSummary = {
38
+ ...baseReviewedCostSummary,
39
+ status: "SETTLED",
40
+ };
41
+ spies.insert.mockReturnValue({});
42
+ spies.update.mockReturnValue(settledSummary);
43
+
44
+ const result = await run(db, validInput, ctx);
45
+
46
+ expect(result.ok).toBe(true);
47
+ if (result.ok) {
48
+ expect(result.value.costSummary.status).toBe("SETTLED");
49
+ }
50
+ expect(spies.insert).toHaveBeenCalled();
51
+ expect(spies.update).toHaveBeenCalled();
52
+ });
53
+
54
+ it("returns error when the summary does not exist", async () => {
55
+ const { db, spies } = createMockDb<Transaction>();
56
+
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(CostSummaryNotFoundError);
64
+ }
65
+ });
66
+
67
+ it("returns error when the summary is not variance reviewed", async () => {
68
+ const { db, spies } = createMockDb<Transaction>();
69
+
70
+ spies.select.mockReturnValueOnce(basePendingReviewCostSummary); // PENDING_VARIANCE_REVIEW
71
+
72
+ const result = await run(db, validInput, ctx);
73
+
74
+ expect(result.ok).toBe(false);
75
+ if (!result.ok) {
76
+ expect(result.error).toBeInstanceOf(CostSummaryNotSettlableError);
77
+ }
78
+ });
79
+
80
+ it("returns error when the payload does not match the reviewed handoff", async () => {
81
+ const { db, spies } = createMockDb<Transaction>();
82
+
83
+ spies.select.mockReturnValueOnce(baseReviewedCostSummary);
84
+
85
+ const result = await run(
86
+ db,
87
+ { ...validInput, currencyCode: "EUR" }, // mismatch
88
+ ctx,
89
+ );
90
+
91
+ expect(result.ok).toBe(false);
92
+ if (!result.ok) {
93
+ expect(result.error).toBeInstanceOf(AcknowledgmentMismatchError);
94
+ }
95
+ });
96
+
97
+ it("returns error when downstream accounting rejects, reverses, or supersedes the reviewed handoff", async () => {
98
+ const { db, spies } = createMockDb<Transaction>();
99
+
100
+ spies.select.mockReturnValueOnce(baseReviewedCostSummary);
101
+
102
+ const result = await run(db, { ...validInput, acknowledgmentStatus: "REJECTED" }, ctx);
103
+
104
+ expect(result.ok).toBe(false);
105
+ if (!result.ok) {
106
+ expect(result.error).toBeInstanceOf(AcknowledgmentRejectedOrSupersededError);
107
+ }
108
+ });
109
+
110
+ it("returns error when the acknowledgment source is invalid", async () => {
111
+ const { db } = createMockDb<Transaction>();
112
+
113
+ const result = await run(db, { ...validInput, acknowledgmentSource: null }, ctx);
114
+
115
+ expect(result.ok).toBe(false);
116
+ if (!result.ok) {
117
+ expect(result.error).toBeInstanceOf(AcknowledgmentSourceInvalidError);
118
+ }
119
+ });
120
+ });
@@ -0,0 +1,110 @@
1
+ import type { Transaction } from "../generated/kysely-tailordb";
2
+ import {
3
+ CostSummaryNotFoundError,
4
+ CostSummaryNotSettlableError,
5
+ AcknowledgmentMismatchError,
6
+ AcknowledgmentRejectedOrSupersededError,
7
+ AcknowledgmentSourceInvalidError,
8
+ } from "../lib/errors.generated";
9
+ import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
10
+
11
+ export interface RecordManufacturingCostSettlementAcknowledgmentInput {
12
+ costSummaryId: string;
13
+ productionOrderId: string;
14
+ currencyCode: string;
15
+ settlementReference: string;
16
+ settlementDate: string;
17
+ acknowledgedDate?: string | null;
18
+ acknowledgmentStatus?: string | null;
19
+ acknowledgmentSource?: string | null;
20
+ }
21
+
22
+ /**
23
+ * Function: recordManufacturingCostSettlementAcknowledgment
24
+ *
25
+ * Persists the downstream accounting acknowledgment that a reviewed
26
+ * manufacturing cost handoff has been consumed by settlement processing.
27
+ * It is the only path that moves a reviewed summary to SETTLED.
28
+ */
29
+ export async function run<CF extends Record<string, unknown>>(
30
+ db: Transaction,
31
+ input: RecordManufacturingCostSettlementAcknowledgmentInput & CF,
32
+ _ctx: CommandContext,
33
+ ) {
34
+ const {
35
+ costSummaryId,
36
+ productionOrderId,
37
+ currencyCode,
38
+ settlementReference,
39
+ settlementDate,
40
+ acknowledgedDate,
41
+ acknowledgmentStatus,
42
+ acknowledgmentSource,
43
+ } = input;
44
+
45
+ // 1. Validate acknowledgment source
46
+ if (!acknowledgmentSource) {
47
+ return err(new AcknowledgmentSourceInvalidError(costSummaryId));
48
+ }
49
+
50
+ // 2. Resolve cost summary
51
+ const costSummary = await db
52
+ .selectFrom("ManufacturingCostSummary")
53
+ .selectAll()
54
+ .where("id", "=", costSummaryId)
55
+ .forUpdate()
56
+ .executeTakeFirst();
57
+
58
+ if (!costSummary) {
59
+ return err(new CostSummaryNotFoundError(costSummaryId));
60
+ }
61
+
62
+ // 3. Summary must be VARIANCE_REVIEWED
63
+ if (costSummary.status !== "VARIANCE_REVIEWED") {
64
+ return err(new CostSummaryNotSettlableError(costSummaryId));
65
+ }
66
+
67
+ // 4. Validate payload matches the reviewed summary's identity and currency
68
+ if (
69
+ costSummary.productionOrderId !== productionOrderId ||
70
+ costSummary.currencyCode !== currencyCode
71
+ ) {
72
+ return err(new AcknowledgmentMismatchError(costSummaryId));
73
+ }
74
+
75
+ // 5. Check that downstream accounting accepted (not rejected/reversed/superseded)
76
+ if (
77
+ acknowledgmentStatus === "REJECTED" ||
78
+ acknowledgmentStatus === "REVERSED" ||
79
+ acknowledgmentStatus === "SUPERSEDED"
80
+ ) {
81
+ return err(new AcknowledgmentRejectedOrSupersededError(costSummaryId));
82
+ }
83
+
84
+ // 6. Create settlement record
85
+ await db
86
+ .insertInto("ManufacturingCostSettlementRecord")
87
+ .values({
88
+ costSummaryId: costSummary.id,
89
+ settlementDate: new Date(settlementDate),
90
+ settlementReference,
91
+ acknowledgedDate: acknowledgedDate ? new Date(acknowledgedDate) : new Date(),
92
+ createdAt: new Date(),
93
+ updatedAt: null,
94
+ })
95
+ .returningAll()
96
+ .executeTakeFirst();
97
+
98
+ // 7. Update summary to SETTLED
99
+ const updatedSummary = await db
100
+ .updateTable("ManufacturingCostSummary")
101
+ .set({
102
+ status: "SETTLED",
103
+ updatedAt: new Date(),
104
+ })
105
+ .where("id", "=", costSummary.id)
106
+ .returningAll()
107
+ .executeTakeFirst();
108
+
109
+ return ok({ costSummary: updatedSummary! });
110
+ }
@@ -0,0 +1,6 @@
1
+ // @generated — do not edit
2
+ import { permissions } from "../lib/permissions.generated";
3
+ import { run } from "./releaseProductionOrder";
4
+ import { defineCommand } from "@tailor-platform/erp-kit/module";
5
+
6
+ export const releaseProductionOrder = defineCommand(permissions.releaseProductionOrder, run);
@@ -0,0 +1,220 @@
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
+ ProductionOrderNotReleasableError,
7
+ BomNotResolvedError,
8
+ RoutingNotResolvedError,
9
+ CrossCompanyMasterReferenceError,
10
+ CrossSiteMasterReferenceError,
11
+ PlannedMaterialCostUnavailableError,
12
+ } from "../lib/errors.generated";
13
+ import {
14
+ baseDraftProductionOrder,
15
+ baseReleasedProductionOrder,
16
+ baseActiveBom,
17
+ baseActiveRouting,
18
+ baseBomLine,
19
+ baseRoutingOperation,
20
+ baseActiveWorkCenter,
21
+ } from "../testing/fixtures";
22
+ import { run } from "./releaseProductionOrder";
23
+ import type { CommandContext } from "@tailor-platform/erp-kit/module";
24
+
25
+ const baseReleaseValuation = {
26
+ id: "item-2",
27
+ valuationMethod: "AVCO",
28
+ costPerUnit: 10,
29
+ currencyCode: "USD",
30
+ valuationReference: "valuation-1",
31
+ };
32
+
33
+ describe("releaseProductionOrder", () => {
34
+ const ctx: CommandContext = {
35
+ actorId: "test-actor",
36
+ permissions: ["manufacturing:releaseProductionOrder"],
37
+ };
38
+
39
+ it("releases a draft order with valid BOM and routing snapshots", async () => {
40
+ const { db, spies } = createMockDb<Transaction>();
41
+ const released = { ...baseDraftProductionOrder, status: "RELEASED" as const };
42
+
43
+ // order lookup
44
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
45
+ // bom resolution (no selectedBomVersionId, so default resolution)
46
+ spies.select.mockReturnValueOnce(baseActiveBom);
47
+ // routing resolution (no selectedRoutingRevisionId, so default resolution)
48
+ spies.select.mockReturnValueOnce(baseActiveRouting);
49
+ // BOM lines query
50
+ spies.select.mockReturnValueOnce([baseBomLine]);
51
+ // material cost valuation for bomLine itemId
52
+ spies.select.mockReturnValueOnce(baseReleaseValuation);
53
+ // routing operations query
54
+ spies.select.mockReturnValueOnce([baseRoutingOperation]);
55
+ // work center lookup for operation
56
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter);
57
+ // inserts: bom snapshot, routing snapshot, cost baseline, material requirement, work order, cost summary
58
+ spies.insert.mockReturnValue({});
59
+ // update: order status
60
+ spies.update.mockReturnValue(released);
61
+
62
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
63
+
64
+ expect(result.ok).toBe(true);
65
+ if (result.ok) {
66
+ expect(result.value.productionOrder.status).toBe("RELEASED");
67
+ }
68
+ const snapshotValues = spies.values.mock.calls.find(
69
+ ([values]) =>
70
+ typeof values === "object" &&
71
+ values !== null &&
72
+ "snapshotData" in (values as Record<string, unknown>),
73
+ )?.[0] as Record<string, unknown> | undefined;
74
+ expect(snapshotValues?.snapshotData).not.toBeNull();
75
+ });
76
+
77
+ it("returns error when the order does not exist", async () => {
78
+ const { db, spies } = createMockDb<Transaction>();
79
+ spies.select.mockReturnValueOnce(undefined);
80
+
81
+ const result = await run(db, { id: "nonexistent" }, ctx);
82
+
83
+ expect(result.ok).toBe(false);
84
+ if (!result.ok) {
85
+ expect(result.error).toBeInstanceOf(ProductionOrderNotFoundError);
86
+ }
87
+ });
88
+
89
+ it("returns error when the order is not in DRAFT", async () => {
90
+ const { db, spies } = createMockDb<Transaction>();
91
+ spies.select.mockReturnValueOnce(baseReleasedProductionOrder);
92
+
93
+ const result = await run(db, { id: baseReleasedProductionOrder.id }, ctx);
94
+
95
+ expect(result.ok).toBe(false);
96
+ if (!result.ok) {
97
+ expect(result.error).toBeInstanceOf(ProductionOrderNotReleasableError);
98
+ }
99
+ });
100
+
101
+ it("returns error when no active BOM can be resolved", async () => {
102
+ const { db, spies } = createMockDb<Transaction>();
103
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
104
+ // bom resolution returns nothing
105
+ spies.select.mockReturnValueOnce(undefined);
106
+
107
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
108
+
109
+ expect(result.ok).toBe(false);
110
+ if (!result.ok) {
111
+ expect(result.error).toBeInstanceOf(BomNotResolvedError);
112
+ }
113
+ });
114
+
115
+ it("returns error when no active routing can be resolved", async () => {
116
+ const { db, spies } = createMockDb<Transaction>();
117
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
118
+ // bom resolution
119
+ spies.select.mockReturnValueOnce(baseActiveBom);
120
+ // routing resolution returns nothing
121
+ spies.select.mockReturnValueOnce(undefined);
122
+
123
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
124
+
125
+ expect(result.ok).toBe(false);
126
+ if (!result.ok) {
127
+ expect(result.error).toBeInstanceOf(RoutingNotResolvedError);
128
+ }
129
+ });
130
+
131
+ it("returns error when BOM or routing belongs to another company", async () => {
132
+ const { db, spies } = createMockDb<Transaction>();
133
+ const crossCompanyBom = { ...baseActiveBom, companyId: "other-company" };
134
+
135
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
136
+ // bom resolution
137
+ spies.select.mockReturnValueOnce(crossCompanyBom);
138
+
139
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
140
+
141
+ expect(result.ok).toBe(false);
142
+ if (!result.ok) {
143
+ expect(result.error).toBeInstanceOf(CrossCompanyMasterReferenceError);
144
+ }
145
+ });
146
+
147
+ it("returns error when BOM or routing belongs to another site in the same company", async () => {
148
+ const { db, spies } = createMockDb<Transaction>();
149
+ const crossSiteBom = { ...baseActiveBom, companyId: "company-1", siteId: "other-site" };
150
+
151
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
152
+ // bom resolution
153
+ spies.select.mockReturnValueOnce(crossSiteBom);
154
+
155
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
156
+
157
+ expect(result.ok).toBe(false);
158
+ if (!result.ok) {
159
+ expect(result.error).toBeInstanceOf(CrossSiteMasterReferenceError);
160
+ }
161
+ });
162
+
163
+ it("returns error when inventory valuation for a required component is unavailable", async () => {
164
+ const { db, spies } = createMockDb<Transaction>();
165
+
166
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
167
+ // bom resolution
168
+ spies.select.mockReturnValueOnce(baseActiveBom);
169
+ // routing resolution
170
+ spies.select.mockReturnValueOnce(baseActiveRouting);
171
+ // BOM lines query
172
+ spies.select.mockReturnValueOnce([baseBomLine]);
173
+ // material cost valuation returns nothing
174
+ spies.select.mockReturnValueOnce(undefined);
175
+
176
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
177
+
178
+ expect(result.ok).toBe(false);
179
+ if (!result.ok) {
180
+ expect(result.error).toBeInstanceOf(PlannedMaterialCostUnavailableError);
181
+ }
182
+ });
183
+
184
+ it("creates work orders and a collecting cost summary at release", async () => {
185
+ const { db, spies } = createMockDb<Transaction>();
186
+ const released = {
187
+ ...baseDraftProductionOrder,
188
+ status: "RELEASED" as const,
189
+ selectedBomVersionId: baseActiveBom.id,
190
+ selectedRoutingRevisionId: baseActiveRouting.id,
191
+ };
192
+
193
+ // order lookup
194
+ spies.select.mockReturnValueOnce(baseDraftProductionOrder);
195
+ // bom resolution
196
+ spies.select.mockReturnValueOnce(baseActiveBom);
197
+ // routing resolution
198
+ spies.select.mockReturnValueOnce(baseActiveRouting);
199
+ // BOM lines query
200
+ spies.select.mockReturnValueOnce([baseBomLine]);
201
+ // material cost valuation
202
+ spies.select.mockReturnValueOnce(baseReleaseValuation);
203
+ // routing operations query
204
+ spies.select.mockReturnValueOnce([baseRoutingOperation]);
205
+ // work center lookup
206
+ spies.select.mockReturnValueOnce(baseActiveWorkCenter);
207
+ // inserts
208
+ spies.insert.mockReturnValue({});
209
+ // update
210
+ spies.update.mockReturnValue(released);
211
+
212
+ const result = await run(db, { id: baseDraftProductionOrder.id }, ctx);
213
+
214
+ expect(result.ok).toBe(true);
215
+ // Verify inserts were called: bom snapshot + routing snapshot + cost baseline +
216
+ // material requirements + work orders + cost summary
217
+ expect(spies.insert).toHaveBeenCalled();
218
+ expect(spies.update).toHaveBeenCalled();
219
+ });
220
+ });