@tailor-platform/erp-kit 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli.mjs +103 -23
  3. package/package.json +1 -1
  4. package/skills/erp-kit-app-5-impl-backend/SKILL.md +7 -4
  5. package/skills/erp-kit-app-7-impl-review/SKILL.md +1 -1
  6. package/skills/erp-kit-module-6-impl-review/SKILL.md +39 -17
  7. package/src/commands/generate-doc.ts +1 -1
  8. package/src/commands/lib/discovery.test.ts +13 -3
  9. package/src/commands/lib/discovery.ts +10 -2
  10. package/src/commands/lib/paths.ts +4 -2
  11. package/src/commands/lib/sync-check-tests.test.ts +84 -6
  12. package/src/commands/lib/sync-check-tests.ts +63 -3
  13. package/src/commands/sync-check.ts +7 -3
  14. package/src/generator/generate-app-code.ts +51 -16
  15. package/src/generator/generate-stubs.ts +4 -0
  16. package/src/generator/stub-templates.test.ts +11 -0
  17. package/src/generator/stub-templates.ts +22 -1
  18. package/src/modules/inventory/docs/features/inventory-adjustment.md +2 -1
  19. package/src/modules/inventory/docs/features/scrap-management.md +39 -1
  20. package/src/modules/manufacturing/README.md +63 -0
  21. package/src/modules/manufacturing/command/.gitkeep +0 -0
  22. package/src/modules/manufacturing/command/activateBillOfMaterial.generated.ts +6 -0
  23. package/src/modules/manufacturing/command/activateBillOfMaterial.test.ts +166 -0
  24. package/src/modules/manufacturing/command/activateBillOfMaterial.ts +173 -0
  25. package/src/modules/manufacturing/command/activateRouting.generated.ts +6 -0
  26. package/src/modules/manufacturing/command/activateRouting.test.ts +152 -0
  27. package/src/modules/manufacturing/command/activateRouting.ts +92 -0
  28. package/src/modules/manufacturing/command/activateWorkCenter.generated.ts +6 -0
  29. package/src/modules/manufacturing/command/activateWorkCenter.test.ts +135 -0
  30. package/src/modules/manufacturing/command/activateWorkCenter.ts +91 -0
  31. package/src/modules/manufacturing/command/cancelProductionOrder.generated.ts +6 -0
  32. package/src/modules/manufacturing/command/cancelProductionOrder.test.ts +151 -0
  33. package/src/modules/manufacturing/command/cancelProductionOrder.ts +114 -0
  34. package/src/modules/manufacturing/command/closeProductionOrder.generated.ts +6 -0
  35. package/src/modules/manufacturing/command/closeProductionOrder.test.ts +126 -0
  36. package/src/modules/manufacturing/command/closeProductionOrder.ts +87 -0
  37. package/src/modules/manufacturing/command/completeProductionOrder.generated.ts +6 -0
  38. package/src/modules/manufacturing/command/completeProductionOrder.test.ts +132 -0
  39. package/src/modules/manufacturing/command/completeProductionOrder.ts +97 -0
  40. package/src/modules/manufacturing/command/completeWorkOrder.generated.ts +6 -0
  41. package/src/modules/manufacturing/command/completeWorkOrder.test.ts +369 -0
  42. package/src/modules/manufacturing/command/completeWorkOrder.ts +212 -0
  43. package/src/modules/manufacturing/command/createBillOfMaterial.generated.ts +6 -0
  44. package/src/modules/manufacturing/command/createBillOfMaterial.test.ts +210 -0
  45. package/src/modules/manufacturing/command/createBillOfMaterial.ts +176 -0
  46. package/src/modules/manufacturing/command/createProductionOrder.generated.ts +6 -0
  47. package/src/modules/manufacturing/command/createProductionOrder.test.ts +160 -0
  48. package/src/modules/manufacturing/command/createProductionOrder.ts +129 -0
  49. package/src/modules/manufacturing/command/createRouting.generated.ts +6 -0
  50. package/src/modules/manufacturing/command/createRouting.test.ts +168 -0
  51. package/src/modules/manufacturing/command/createRouting.ts +128 -0
  52. package/src/modules/manufacturing/command/createWorkCenter.generated.ts +6 -0
  53. package/src/modules/manufacturing/command/createWorkCenter.test.ts +148 -0
  54. package/src/modules/manufacturing/command/createWorkCenter.ts +131 -0
  55. package/src/modules/manufacturing/command/deactivateBillOfMaterial.generated.ts +6 -0
  56. package/src/modules/manufacturing/command/deactivateBillOfMaterial.test.ts +103 -0
  57. package/src/modules/manufacturing/command/deactivateBillOfMaterial.ts +78 -0
  58. package/src/modules/manufacturing/command/deactivateRouting.generated.ts +6 -0
  59. package/src/modules/manufacturing/command/deactivateRouting.test.ts +112 -0
  60. package/src/modules/manufacturing/command/deactivateRouting.ts +76 -0
  61. package/src/modules/manufacturing/command/deactivateWorkCenter.generated.ts +6 -0
  62. package/src/modules/manufacturing/command/deactivateWorkCenter.test.ts +113 -0
  63. package/src/modules/manufacturing/command/deactivateWorkCenter.ts +85 -0
  64. package/src/modules/manufacturing/command/pauseWorkOrder.generated.ts +6 -0
  65. package/src/modules/manufacturing/command/pauseWorkOrder.test.ts +118 -0
  66. package/src/modules/manufacturing/command/pauseWorkOrder.ts +82 -0
  67. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.generated.ts +6 -0
  68. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +183 -0
  69. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +139 -0
  70. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.generated.ts +6 -0
  71. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +120 -0
  72. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +110 -0
  73. package/src/modules/manufacturing/command/releaseProductionOrder.generated.ts +6 -0
  74. package/src/modules/manufacturing/command/releaseProductionOrder.test.ts +220 -0
  75. package/src/modules/manufacturing/command/releaseProductionOrder.ts +450 -0
  76. package/src/modules/manufacturing/command/reopenProductionOrder.generated.ts +6 -0
  77. package/src/modules/manufacturing/command/reopenProductionOrder.test.ts +196 -0
  78. package/src/modules/manufacturing/command/reopenProductionOrder.ts +98 -0
  79. package/src/modules/manufacturing/command/reportWorkOrderProgress.generated.ts +6 -0
  80. package/src/modules/manufacturing/command/reportWorkOrderProgress.test.ts +204 -0
  81. package/src/modules/manufacturing/command/reportWorkOrderProgress.ts +129 -0
  82. package/src/modules/manufacturing/command/rescheduleProductionOrder.generated.ts +6 -0
  83. package/src/modules/manufacturing/command/rescheduleProductionOrder.test.ts +185 -0
  84. package/src/modules/manufacturing/command/rescheduleProductionOrder.ts +95 -0
  85. package/src/modules/manufacturing/command/resumeWorkOrder.generated.ts +6 -0
  86. package/src/modules/manufacturing/command/resumeWorkOrder.test.ts +122 -0
  87. package/src/modules/manufacturing/command/resumeWorkOrder.ts +94 -0
  88. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.generated.ts +6 -0
  89. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +231 -0
  90. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +137 -0
  91. package/src/modules/manufacturing/command/startWorkOrder.generated.ts +6 -0
  92. package/src/modules/manufacturing/command/startWorkOrder.test.ts +118 -0
  93. package/src/modules/manufacturing/command/startWorkOrder.ts +126 -0
  94. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.generated.ts +6 -0
  95. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.test.ts +153 -0
  96. package/src/modules/manufacturing/command/technicallyCompleteProductionOrder.ts +106 -0
  97. package/src/modules/manufacturing/command/unreleaseProductionOrder.generated.ts +6 -0
  98. package/src/modules/manufacturing/command/unreleaseProductionOrder.test.ts +140 -0
  99. package/src/modules/manufacturing/command/unreleaseProductionOrder.ts +131 -0
  100. package/src/modules/manufacturing/command/updateBillOfMaterial.generated.ts +6 -0
  101. package/src/modules/manufacturing/command/updateBillOfMaterial.test.ts +149 -0
  102. package/src/modules/manufacturing/command/updateBillOfMaterial.ts +174 -0
  103. package/src/modules/manufacturing/command/updateProductionOrder.generated.ts +6 -0
  104. package/src/modules/manufacturing/command/updateProductionOrder.test.ts +112 -0
  105. package/src/modules/manufacturing/command/updateProductionOrder.ts +145 -0
  106. package/src/modules/manufacturing/command/updateRouting.generated.ts +6 -0
  107. package/src/modules/manufacturing/command/updateRouting.test.ts +211 -0
  108. package/src/modules/manufacturing/command/updateRouting.ts +124 -0
  109. package/src/modules/manufacturing/command/updateWorkCenter.generated.ts +6 -0
  110. package/src/modules/manufacturing/command/updateWorkCenter.test.ts +152 -0
  111. package/src/modules/manufacturing/command/updateWorkCenter.ts +137 -0
  112. package/src/modules/manufacturing/db/.gitkeep +0 -0
  113. package/src/modules/manufacturing/db/billOfMaterial.ts +70 -0
  114. package/src/modules/manufacturing/db/billOfMaterialLine.ts +49 -0
  115. package/src/modules/manufacturing/db/costVarianceLine.ts +53 -0
  116. package/src/modules/manufacturing/db/manufacturingCostLine.ts +35 -0
  117. package/src/modules/manufacturing/db/manufacturingCostSettlementRecord.ts +39 -0
  118. package/src/modules/manufacturing/db/manufacturingCostSummary.ts +59 -0
  119. package/src/modules/manufacturing/db/productionOrder.ts +83 -0
  120. package/src/modules/manufacturing/db/productionOrderBomSnapshot.ts +44 -0
  121. package/src/modules/manufacturing/db/productionOrderCostBaseline.ts +44 -0
  122. package/src/modules/manufacturing/db/productionOrderMaterialRequirement.ts +57 -0
  123. package/src/modules/manufacturing/db/productionOrderRoutingSnapshot.ts +43 -0
  124. package/src/modules/manufacturing/db/routing.ts +63 -0
  125. package/src/modules/manufacturing/db/routingOperation.ts +57 -0
  126. package/src/modules/manufacturing/db/workCenter.ts +87 -0
  127. package/src/modules/manufacturing/db/workOrder.ts +65 -0
  128. package/src/modules/manufacturing/db/workOrderExecutionEvent.ts +54 -0
  129. package/src/modules/manufacturing/docs/commands/ActivateBillOfMaterial.md +50 -0
  130. package/src/modules/manufacturing/docs/commands/ActivateRouting.md +48 -0
  131. package/src/modules/manufacturing/docs/commands/ActivateWorkCenter.md +49 -0
  132. package/src/modules/manufacturing/docs/commands/CancelProductionOrder.md +48 -0
  133. package/src/modules/manufacturing/docs/commands/CloseProductionOrder.md +46 -0
  134. package/src/modules/manufacturing/docs/commands/CompleteProductionOrder.md +48 -0
  135. package/src/modules/manufacturing/docs/commands/CompleteWorkOrder.md +66 -0
  136. package/src/modules/manufacturing/docs/commands/CreateBillOfMaterial.md +54 -0
  137. package/src/modules/manufacturing/docs/commands/CreateProductionOrder.md +49 -0
  138. package/src/modules/manufacturing/docs/commands/CreateRouting.md +50 -0
  139. package/src/modules/manufacturing/docs/commands/CreateWorkCenter.md +51 -0
  140. package/src/modules/manufacturing/docs/commands/DeactivateBillOfMaterial.md +45 -0
  141. package/src/modules/manufacturing/docs/commands/DeactivateRouting.md +45 -0
  142. package/src/modules/manufacturing/docs/commands/DeactivateWorkCenter.md +45 -0
  143. package/src/modules/manufacturing/docs/commands/PauseWorkOrder.md +44 -0
  144. package/src/modules/manufacturing/docs/commands/RecordInventoryIssueOutcome.md +59 -0
  145. package/src/modules/manufacturing/docs/commands/RecordManufacturingCostSettlementAcknowledgment.md +49 -0
  146. package/src/modules/manufacturing/docs/commands/ReleaseProductionOrder.md +57 -0
  147. package/src/modules/manufacturing/docs/commands/ReopenProductionOrder.md +54 -0
  148. package/src/modules/manufacturing/docs/commands/ReportWorkOrderProgress.md +53 -0
  149. package/src/modules/manufacturing/docs/commands/RescheduleProductionOrder.md +45 -0
  150. package/src/modules/manufacturing/docs/commands/ResumeWorkOrder.md +44 -0
  151. package/src/modules/manufacturing/docs/commands/ReviewManufacturingCostSummary.md +52 -0
  152. package/src/modules/manufacturing/docs/commands/StartWorkOrder.md +46 -0
  153. package/src/modules/manufacturing/docs/commands/TechnicallyCompleteProductionOrder.md +51 -0
  154. package/src/modules/manufacturing/docs/commands/UnreleaseProductionOrder.md +46 -0
  155. package/src/modules/manufacturing/docs/commands/UpdateBillOfMaterial.md +48 -0
  156. package/src/modules/manufacturing/docs/commands/UpdateProductionOrder.md +48 -0
  157. package/src/modules/manufacturing/docs/commands/UpdateRouting.md +52 -0
  158. package/src/modules/manufacturing/docs/commands/UpdateWorkCenter.md +48 -0
  159. package/src/modules/manufacturing/docs/features/bill-of-material-management.md +83 -0
  160. package/src/modules/manufacturing/docs/features/manufacturing-cost-and-variance.md +191 -0
  161. package/src/modules/manufacturing/docs/features/production-order-lifecycle.md +103 -0
  162. package/src/modules/manufacturing/docs/features/routing-and-work-center-definition.md +63 -0
  163. package/src/modules/manufacturing/docs/features/work-order-execution.md +115 -0
  164. package/src/modules/manufacturing/docs/models/BillOfMaterial.md +60 -0
  165. package/src/modules/manufacturing/docs/models/ManufacturingCostSummary.md +66 -0
  166. package/src/modules/manufacturing/docs/models/ProductionOrder.md +76 -0
  167. package/src/modules/manufacturing/docs/models/Routing.md +58 -0
  168. package/src/modules/manufacturing/docs/models/WorkCenter.md +56 -0
  169. package/src/modules/manufacturing/docs/models/WorkOrder.md +63 -0
  170. package/src/modules/manufacturing/docs/queries/DetectBillOfMaterialCircularReference.md +39 -0
  171. package/src/modules/manufacturing/docs/queries/ExplodeBillOfMaterial.md +56 -0
  172. package/src/modules/manufacturing/docs/queries/GetBillOfMaterial.md +37 -0
  173. package/src/modules/manufacturing/docs/queries/GetManufacturingCostSummary.md +39 -0
  174. package/src/modules/manufacturing/docs/queries/GetProductionOrder.md +37 -0
  175. package/src/modules/manufacturing/docs/queries/GetRouting.md +39 -0
  176. package/src/modules/manufacturing/docs/queries/GetWorkCenter.md +35 -0
  177. package/src/modules/manufacturing/docs/queries/GetWorkOrder.md +38 -0
  178. package/src/modules/manufacturing/docs/queries/ListBillOfMaterialsByItem.md +42 -0
  179. package/src/modules/manufacturing/docs/queries/ListManufacturingCostSummariesByStatus.md +41 -0
  180. package/src/modules/manufacturing/docs/queries/ListProductionOrdersByStatus.md +41 -0
  181. package/src/modules/manufacturing/docs/queries/ListRoutingsByItem.md +42 -0
  182. package/src/modules/manufacturing/docs/queries/ListWorkCentersBySite.md +38 -0
  183. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByProductionOrder.md +39 -0
  184. package/src/modules/manufacturing/docs/queries/ListWorkOrdersByWorkCenter.md +43 -0
  185. package/src/modules/manufacturing/executor/.gitkeep +0 -0
  186. package/src/modules/manufacturing/generated/enums.ts +113 -0
  187. package/src/modules/manufacturing/generated/kysely-tailordb.ts +247 -0
  188. package/src/modules/manufacturing/index.ts +2 -0
  189. package/src/modules/manufacturing/lib/_db_deps.ts +22 -0
  190. package/src/modules/manufacturing/lib/errors.generated.ts +592 -0
  191. package/src/modules/manufacturing/lib/permissions.generated.ts +35 -0
  192. package/src/modules/manufacturing/lib/types.ts +111 -0
  193. package/src/modules/manufacturing/module.ts +226 -0
  194. package/src/modules/manufacturing/permissions.ts +3 -0
  195. package/src/modules/manufacturing/query/.gitkeep +0 -0
  196. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.generated.ts +5 -0
  197. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.test.ts +115 -0
  198. package/src/modules/manufacturing/query/detectBillOfMaterialCircularReference.ts +79 -0
  199. package/src/modules/manufacturing/query/explodeBillOfMaterial.generated.ts +5 -0
  200. package/src/modules/manufacturing/query/explodeBillOfMaterial.test.ts +445 -0
  201. package/src/modules/manufacturing/query/explodeBillOfMaterial.ts +306 -0
  202. package/src/modules/manufacturing/query/getBillOfMaterial.generated.ts +5 -0
  203. package/src/modules/manufacturing/query/getBillOfMaterial.test.ts +64 -0
  204. package/src/modules/manufacturing/query/getBillOfMaterial.ts +27 -0
  205. package/src/modules/manufacturing/query/getManufacturingCostSummary.generated.ts +5 -0
  206. package/src/modules/manufacturing/query/getManufacturingCostSummary.test.ts +147 -0
  207. package/src/modules/manufacturing/query/getManufacturingCostSummary.ts +46 -0
  208. package/src/modules/manufacturing/query/getProductionOrder.generated.ts +5 -0
  209. package/src/modules/manufacturing/query/getProductionOrder.test.ts +139 -0
  210. package/src/modules/manufacturing/query/getProductionOrder.ts +84 -0
  211. package/src/modules/manufacturing/query/getRouting.generated.ts +5 -0
  212. package/src/modules/manufacturing/query/getRouting.test.ts +71 -0
  213. package/src/modules/manufacturing/query/getRouting.ts +34 -0
  214. package/src/modules/manufacturing/query/getWorkCenter.generated.ts +5 -0
  215. package/src/modules/manufacturing/query/getWorkCenter.test.ts +37 -0
  216. package/src/modules/manufacturing/query/getWorkCenter.ts +21 -0
  217. package/src/modules/manufacturing/query/getWorkOrder.generated.ts +5 -0
  218. package/src/modules/manufacturing/query/getWorkOrder.test.ts +73 -0
  219. package/src/modules/manufacturing/query/getWorkOrder.ts +28 -0
  220. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.generated.ts +5 -0
  221. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.test.ts +107 -0
  222. package/src/modules/manufacturing/query/listBillOfMaterialsByItem.ts +58 -0
  223. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.generated.ts +5 -0
  224. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.test.ts +96 -0
  225. package/src/modules/manufacturing/query/listManufacturingCostSummariesByStatus.ts +77 -0
  226. package/src/modules/manufacturing/query/listProductionOrdersByStatus.generated.ts +5 -0
  227. package/src/modules/manufacturing/query/listProductionOrdersByStatus.test.ts +121 -0
  228. package/src/modules/manufacturing/query/listProductionOrdersByStatus.ts +83 -0
  229. package/src/modules/manufacturing/query/listRoutingsByItem.generated.ts +5 -0
  230. package/src/modules/manufacturing/query/listRoutingsByItem.test.ts +110 -0
  231. package/src/modules/manufacturing/query/listRoutingsByItem.ts +54 -0
  232. package/src/modules/manufacturing/query/listWorkCentersBySite.generated.ts +5 -0
  233. package/src/modules/manufacturing/query/listWorkCentersBySite.test.ts +81 -0
  234. package/src/modules/manufacturing/query/listWorkCentersBySite.ts +70 -0
  235. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.generated.ts +5 -0
  236. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.test.ts +102 -0
  237. package/src/modules/manufacturing/query/listWorkOrdersByProductionOrder.ts +53 -0
  238. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.generated.ts +5 -0
  239. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.test.ts +143 -0
  240. package/src/modules/manufacturing/query/listWorkOrdersByWorkCenter.ts +56 -0
  241. package/src/modules/manufacturing/seed/index.ts +19 -0
  242. package/src/modules/manufacturing/tailor.config.ts +13 -0
  243. package/src/modules/manufacturing/tailor.d.ts +13 -0
  244. package/src/modules/manufacturing/testing/commandTestUtils.ts +29 -0
  245. package/src/modules/manufacturing/testing/fixtures.ts +402 -0
  246. package/templates/scaffold/app/backend/package.json +9 -2
  247. package/templates/scaffold/app/backend/src/tests/utils/graphql-client.ts +66 -0
  248. package/templates/scaffold/app/backend/src/tests/utils/setup.ts +21 -0
  249. package/templates/scaffold/app/backend/tsconfig.json +9 -2
  250. package/templates/scaffold/app/backend/vitest.config.ts +35 -0
  251. package/templates/scaffold/app/frontend/package.json +2 -2
@@ -0,0 +1,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
+ });
@@ -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);