@tailor-platform/erp-kit 0.8.0 → 0.9.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 (159) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +1 -1
  3. package/skills/erp-kit-app-1-requirements/SKILL.md +6 -0
  4. package/skills/erp-kit-app-2-requirements-review/SKILL.md +6 -0
  5. package/skills/erp-kit-app-3-plan/SKILL.md +6 -0
  6. package/skills/erp-kit-app-4-plan-review/SKILL.md +6 -0
  7. package/skills/erp-kit-app-5-impl-backend/SKILL.md +7 -0
  8. package/skills/erp-kit-app-6-impl-frontend/SKILL.md +6 -0
  9. package/skills/erp-kit-app-7-impl-review/SKILL.md +6 -0
  10. package/skills/erp-kit-app-shared/SKILL.md +2 -0
  11. package/skills/erp-kit-mock-scenario/SKILL.md +6 -0
  12. package/skills/erp-kit-module-1-requirements/SKILL.md +6 -0
  13. package/skills/erp-kit-module-2-requirements-review/SKILL.md +6 -0
  14. package/skills/erp-kit-module-3-plan/SKILL.md +6 -0
  15. package/skills/erp-kit-module-3-update-plan/SKILL.md +6 -0
  16. package/skills/erp-kit-module-4-plan-review/SKILL.md +6 -0
  17. package/skills/erp-kit-module-5-impl/SKILL.md +6 -0
  18. package/skills/erp-kit-module-6-impl-review/SKILL.md +6 -0
  19. package/skills/erp-kit-module-shared/SKILL.md +2 -0
  20. package/skills/erp-kit-module-shared/references/commands.md +3 -0
  21. package/skills/erp-kit-update/SKILL.md +6 -0
  22. package/src/modules/accounting/command/assignProfitCenterToHierarchyNode.test.ts +3 -3
  23. package/src/modules/accounting/command/assignProfitCenterToHierarchyNode.ts +1 -1
  24. package/src/modules/accounting/command/createCostCenterHierarchyNode.test.ts +3 -3
  25. package/src/modules/accounting/command/createCostCenterHierarchyNode.ts +1 -1
  26. package/src/modules/accounting/command/moveCostCenterHierarchyNode.test.ts +2 -2
  27. package/src/modules/accounting/command/moveCostCenterHierarchyNode.ts +1 -1
  28. package/src/modules/accounting/module.ts +1 -0
  29. package/src/modules/business-partner/command/createPartnerBankAccount.test.ts +3 -3
  30. package/src/modules/business-partner/command/createPartnerBankAccount.ts +1 -1
  31. package/src/modules/business-partner/module.ts +1 -0
  32. package/src/modules/coa-management/command/activateAccount.test.ts +0 -15
  33. package/src/modules/coa-management/command/activateAccount.ts +1 -42
  34. package/src/modules/coa-management/command/activateChartOfAccounts.test.ts +2 -31
  35. package/src/modules/coa-management/command/activateChartOfAccounts.ts +1 -37
  36. package/src/modules/coa-management/command/createAccount.test.ts +0 -28
  37. package/src/modules/coa-management/command/createAccount.ts +0 -43
  38. package/src/modules/coa-management/command/createAccountGroup.test.ts +2 -51
  39. package/src/modules/coa-management/command/createAccountGroup.ts +1 -56
  40. package/src/modules/coa-management/command/createChartOfAccounts.test.ts +1 -49
  41. package/src/modules/coa-management/command/createChartOfAccounts.ts +0 -51
  42. package/src/modules/coa-management/command/deactivateAccount.test.ts +0 -15
  43. package/src/modules/coa-management/command/deactivateAccount.ts +1 -53
  44. package/src/modules/coa-management/command/deactivateChartOfAccounts.test.ts +2 -29
  45. package/src/modules/coa-management/command/deactivateChartOfAccounts.ts +1 -37
  46. package/src/modules/coa-management/command/deleteAccount.test.ts +0 -13
  47. package/src/modules/coa-management/command/deleteAccount.ts +1 -42
  48. package/src/modules/coa-management/command/deleteAccountGroup.test.ts +0 -19
  49. package/src/modules/coa-management/command/deleteAccountGroup.ts +1 -42
  50. package/src/modules/coa-management/command/deleteChartOfAccounts.test.ts +2 -58
  51. package/src/modules/coa-management/command/deleteChartOfAccounts.ts +4 -88
  52. package/src/modules/coa-management/command/moveAccountGroup.test.ts +0 -27
  53. package/src/modules/coa-management/command/moveAccountGroup.ts +1 -48
  54. package/src/modules/coa-management/command/reactivateAccount.test.ts +0 -15
  55. package/src/modules/coa-management/command/reactivateAccount.ts +1 -53
  56. package/src/modules/coa-management/command/updateAccount.test.ts +0 -15
  57. package/src/modules/coa-management/command/updateAccount.ts +0 -92
  58. package/src/modules/coa-management/command/updateAccountGroup.test.ts +0 -20
  59. package/src/modules/coa-management/command/updateAccountGroup.ts +1 -61
  60. package/src/modules/coa-management/command/updateChartOfAccounts.test.ts +2 -31
  61. package/src/modules/coa-management/command/updateChartOfAccounts.ts +1 -50
  62. package/src/modules/coa-management/docs/commands/ActivateAccount.md +1 -4
  63. package/src/modules/coa-management/docs/commands/ActivateChartOfAccounts.md +1 -4
  64. package/src/modules/coa-management/docs/commands/CreateAccount.md +1 -4
  65. package/src/modules/coa-management/docs/commands/CreateAccountGroup.md +2 -5
  66. package/src/modules/coa-management/docs/commands/CreateChartOfAccounts.md +2 -6
  67. package/src/modules/coa-management/docs/commands/DeactivateAccount.md +1 -4
  68. package/src/modules/coa-management/docs/commands/DeactivateChartOfAccounts.md +1 -4
  69. package/src/modules/coa-management/docs/commands/DeleteAccount.md +1 -4
  70. package/src/modules/coa-management/docs/commands/DeleteAccountGroup.md +1 -4
  71. package/src/modules/coa-management/docs/commands/DeleteChartOfAccounts.md +1 -6
  72. package/src/modules/coa-management/docs/commands/MoveAccountGroup.md +1 -4
  73. package/src/modules/coa-management/docs/commands/ReactivateAccount.md +1 -4
  74. package/src/modules/coa-management/docs/commands/UpdateAccount.md +1 -4
  75. package/src/modules/coa-management/docs/commands/UpdateAccountGroup.md +2 -5
  76. package/src/modules/coa-management/docs/commands/UpdateChartOfAccounts.md +1 -4
  77. package/src/modules/coa-management/module.ts +16 -27
  78. package/src/modules/finance-ledger/module.ts +1 -0
  79. package/src/modules/inventory/command/approveInventoryAdjustment.test.ts +1 -1
  80. package/src/modules/inventory/command/approveInventoryAdjustment.ts +1 -1
  81. package/src/modules/inventory/command/cancelStockMovement.test.ts +2 -2
  82. package/src/modules/inventory/command/cancelStockMovement.ts +1 -1
  83. package/src/modules/inventory/command/confirmInventoryAdjustment.test.ts +4 -4
  84. package/src/modules/inventory/command/confirmInventoryAdjustment.ts +1 -1
  85. package/src/modules/inventory/command/confirmStockMovement.test.ts +1 -1
  86. package/src/modules/inventory/command/confirmStockMovement.ts +1 -1
  87. package/src/modules/inventory/command/createInventoryAdjustment.test.ts +6 -6
  88. package/src/modules/inventory/command/createInventoryAdjustment.ts +1 -1
  89. package/src/modules/inventory/command/createStockMovement.test.ts +3 -3
  90. package/src/modules/inventory/command/createStockMovement.ts +1 -1
  91. package/src/modules/inventory/command/executeStockMovement.test.ts +5 -5
  92. package/src/modules/inventory/command/executeStockMovement.ts +1 -1
  93. package/src/modules/inventory/command/rejectInventoryAdjustment.test.ts +1 -1
  94. package/src/modules/inventory/command/rejectInventoryAdjustment.ts +1 -1
  95. package/src/modules/inventory/command/reviseInventoryAdjustment.test.ts +2 -2
  96. package/src/modules/inventory/command/reviseInventoryAdjustment.ts +1 -1
  97. package/src/modules/inventory/command/submitInventoryAdjustment.test.ts +1 -1
  98. package/src/modules/inventory/command/submitInventoryAdjustment.ts +1 -1
  99. package/src/modules/inventory/command/updateStockMovement.test.ts +4 -4
  100. package/src/modules/inventory/command/updateStockMovement.ts +2 -2
  101. package/src/modules/inventory/module.ts +1 -0
  102. package/src/modules/item-management/command/createTaxonomyNode.test.ts +4 -4
  103. package/src/modules/item-management/command/createTaxonomyNode.ts +1 -1
  104. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +2 -2
  105. package/src/modules/item-management/command/moveTaxonomyNode.ts +2 -2
  106. package/src/modules/item-management/command/updateTaxonomyNode.test.ts +2 -2
  107. package/src/modules/item-management/command/updateTaxonomyNode.ts +1 -1
  108. package/src/modules/item-management/module.ts +1 -0
  109. package/src/modules/manufacturing/command/createRouting.ts +1 -1
  110. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.test.ts +1 -1
  111. package/src/modules/manufacturing/command/recordInventoryIssueOutcome.ts +1 -1
  112. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.test.ts +1 -1
  113. package/src/modules/manufacturing/command/recordManufacturingCostSettlementAcknowledgment.ts +1 -1
  114. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.test.ts +1 -1
  115. package/src/modules/manufacturing/command/reviewManufacturingCostSummary.ts +1 -1
  116. package/src/modules/manufacturing/command/updateRouting.ts +1 -1
  117. package/src/modules/manufacturing/module.ts +1 -0
  118. package/src/modules/organization/module.ts +1 -0
  119. package/src/modules/primitives/module.ts +1 -0
  120. package/src/modules/product-management/command/assignProductToCategory.test.ts +2 -2
  121. package/src/modules/product-management/command/assignProductToCategory.ts +2 -2
  122. package/src/modules/product-management/command/createProductAttribute.test.ts +1 -1
  123. package/src/modules/product-management/command/createProductAttribute.ts +1 -1
  124. package/src/modules/product-management/command/createProductAttributeValue.test.ts +1 -1
  125. package/src/modules/product-management/command/createProductAttributeValue.ts +1 -1
  126. package/src/modules/product-management/command/createProductCategory.test.ts +2 -2
  127. package/src/modules/product-management/command/createProductCategory.ts +1 -1
  128. package/src/modules/product-management/command/setProductAttributeAssignment.test.ts +3 -3
  129. package/src/modules/product-management/command/setProductAttributeAssignment.ts +2 -2
  130. package/src/modules/product-management/module.ts +1 -0
  131. package/src/modules/purchase/command/activatePurchasePaymentTerm.test.ts +4 -4
  132. package/src/modules/purchase/command/activatePurchasePaymentTerm.ts +2 -2
  133. package/src/modules/purchase/command/deactivatePurchasePaymentTerm.test.ts +4 -4
  134. package/src/modules/purchase/command/deactivatePurchasePaymentTerm.ts +2 -2
  135. package/src/modules/purchase/command/updatePurchasePaymentTerm.test.ts +2 -2
  136. package/src/modules/purchase/command/updatePurchasePaymentTerm.ts +1 -1
  137. package/src/modules/purchase/module.ts +1 -0
  138. package/src/modules/sales/command/createSalesOrder.ts +1 -1
  139. package/src/modules/sales/module.ts +1 -0
  140. package/templates/scaffold/app/backend/eslint.config.js +17 -0
  141. package/templates/scaffold/app/backend/package.json +1 -0
  142. package/templates/scaffold/app/backend/src/tests/stories/audit-log/user--view-audit-log-detail.test.ts +3 -3
  143. package/templates/scaffold/app/backend/src/tests/stories/role-management/user--assign-role-to-user.test.ts +4 -4
  144. package/templates/scaffold/app/backend/src/tests/stories/role-management/user--remove-role-from-user.test.ts +4 -4
  145. package/templates/scaffold/app/backend/src/tests/stories/user-lifecycle/user--toggle-user-status.test.ts +6 -6
  146. package/templates/scaffold/app/backend/src/tests/stories/user-lifecycle/user--update-own-profile.test.ts +13 -13
  147. package/templates/scaffold/app/backend/src/tests/stories/user-lifecycle/user--update-user-profile.test.ts +16 -17
  148. package/templates/scaffold/app/backend/src/tests/stories/user-lifecycle/user--view-user-detail.test.ts +3 -3
  149. package/templates/scaffold/app/backend/tailor.config.ts +15 -1
  150. package/templates/scaffold/app/backend/tsconfig.json +1 -1
  151. package/templates/scaffold/app/frontend/src/App.tsx +57 -16
  152. package/templates/scaffold/module/eslint.config.js +24 -0
  153. package/templates/scaffold/module/module.ts +1 -0
  154. package/templates/scaffold/module/package.json +3 -1
  155. package/templates/scaffold/module/vitest.config.ts +11 -0
  156. /package/templates/scaffold/app/frontend/src/pages/{user-management/audit → audit}/[id]/components/audit-entry-detail.tsx +0 -0
  157. /package/templates/scaffold/app/frontend/src/pages/{user-management/audit → audit}/[id]/page.tsx +0 -0
  158. /package/templates/scaffold/app/frontend/src/pages/{user-management/audit → audit}/components/audit-entries-table.tsx +0 -0
  159. /package/templates/scaffold/app/frontend/src/pages/{user-management/audit → audit}/page.tsx +0 -0
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import { ChartOfAccountsNotFoundError, InvalidStateTransitionError } from "../lib/errors.generated";
4
3
  import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
@@ -8,23 +7,6 @@ export interface DeactivateChartOfAccountsInput {
8
7
  from?: string[];
9
8
  }
10
9
 
11
- export interface AuditCommands {
12
- logAuditEvent: (
13
- db: Transaction,
14
- input: {
15
- eventId: string;
16
- actorType: string;
17
- actorId: string;
18
- entityType: string;
19
- entityId: string;
20
- operationType: string;
21
- companyId?: string;
22
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
23
- },
24
- ctx: CommandContext,
25
- ) => Promise<{ ok: true; value: unknown }>;
26
- }
27
-
28
10
  /**
29
11
  * Function: deactivateChartOfAccounts
30
12
  *
@@ -34,8 +16,7 @@ export interface AuditCommands {
34
16
  export async function run(
35
17
  db: Transaction,
36
18
  input: DeactivateChartOfAccountsInput,
37
- ctx: CommandContext,
38
- auditCommands?: AuditCommands,
19
+ _ctx: CommandContext,
39
20
  ) {
40
21
  const { id, from = ["ACTIVE"] } = input;
41
22
 
@@ -67,22 +48,5 @@ export async function run(
67
48
  .returningAll()
68
49
  .executeTakeFirst();
69
50
 
70
- if (auditCommands) {
71
- await auditCommands.logAuditEvent(
72
- db,
73
- {
74
- eventId: randomUUID(),
75
- actorType: "USER",
76
- actorId: ctx.actorId,
77
- entityType: "ChartOfAccounts",
78
- entityId: id,
79
- operationType: "UPDATE",
80
- companyId: existing.companyId,
81
- changes: [{ fieldName: "status", oldValue: existing.status, newValue: "ARCHIVED" }],
82
- },
83
- ctx,
84
- );
85
- }
86
-
87
51
  return ok({ chartOfAccounts: chartOfAccounts! });
88
52
  }
@@ -106,17 +106,4 @@ describe("deleteAccount", () => {
106
106
  expect(result.value.success).toBe(true);
107
107
  }
108
108
  });
109
-
110
- it("emits audit event recording deletion, acting user, and account code", async () => {
111
- const { db, spies } = createMockDb<Transaction>();
112
- spies.select.mockReturnValueOnce(baseDraftAccount).mockReturnValueOnce(baseActiveCoa);
113
-
114
- const result = await run(db, { id: baseDraftAccount.id }, ctx);
115
-
116
- // Audit event is emitted as part of successful deletion
117
- expect(result.ok).toBe(true);
118
- if (result.ok) {
119
- expect(result.value.success).toBe(true);
120
- }
121
- });
122
109
  });
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import {
4
3
  AccountNotFoundError,
@@ -12,23 +11,6 @@ export interface DeleteAccountInput {
12
11
  id: string;
13
12
  }
14
13
 
15
- export interface AuditCommands {
16
- logAuditEvent: (
17
- db: Transaction,
18
- input: {
19
- eventId: string;
20
- actorType: string;
21
- actorId: string;
22
- entityType: string;
23
- entityId: string;
24
- operationType: string;
25
- companyId?: string;
26
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
27
- },
28
- ctx: CommandContext,
29
- ) => Promise<{ ok: true; value: unknown }>;
30
- }
31
-
32
14
  /**
33
15
  * Function: deleteAccount
34
16
  *
@@ -36,12 +18,7 @@ export interface AuditCommands {
36
18
  * transactions. ACTIVE/INACTIVE accounts and accounts with posted
37
19
  * transactions cannot be deleted.
38
20
  */
39
- export async function run(
40
- db: Transaction,
41
- input: DeleteAccountInput,
42
- ctx: CommandContext,
43
- auditCommands?: AuditCommands,
44
- ) {
21
+ export async function run(db: Transaction, input: DeleteAccountInput, _ctx: CommandContext) {
45
22
  const { id } = input;
46
23
 
47
24
  // 1. Find account with forUpdate
@@ -81,23 +58,5 @@ export async function run(
81
58
  // 6. Delete account
82
59
  await db.deleteFrom("Account").where("id", "=", id).execute();
83
60
 
84
- // 7. Emit audit event
85
- if (auditCommands) {
86
- await auditCommands.logAuditEvent(
87
- db,
88
- {
89
- eventId: randomUUID(),
90
- actorType: "USER",
91
- actorId: ctx.actorId,
92
- entityType: "Account",
93
- entityId: id,
94
- operationType: "DELETE",
95
- companyId: coa.companyId,
96
- changes: [{ fieldName: "id", oldValue: id }],
97
- },
98
- ctx,
99
- );
100
- }
101
-
102
61
  return ok({ success: true as const });
103
62
  }
@@ -98,23 +98,4 @@ describe("deleteAccountGroup", () => {
98
98
  }
99
99
  expect(spies.delete).toHaveBeenCalled();
100
100
  });
101
-
102
- it("emits audit event recording deletion and acting user", async () => {
103
- const { db, spies } = createMockDb<Transaction>();
104
- spies.select
105
- .mockReturnValueOnce(baseChildGroup) // Group lookup
106
- .mockReturnValueOnce(baseActiveCoa) // CoA lookup
107
- .mockReturnValueOnce([]) // No child groups
108
- .mockReturnValueOnce([]); // No assigned accounts
109
- spies.delete.mockReturnValue(undefined);
110
-
111
- const result = await run(db, { id: baseChildGroup.id }, ctx);
112
-
113
- expect(result.ok).toBe(true);
114
- if (result.ok) {
115
- // Success result indicates deletion completed; ctx.actorId available for audit
116
- expect(result.value.success).toBe(true);
117
- }
118
- expect(spies.delete).toHaveBeenCalled();
119
- });
120
101
  });
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import {
4
3
  AccountGroupNotFoundError,
@@ -12,35 +11,13 @@ export interface DeleteAccountGroupInput {
12
11
  id: string;
13
12
  }
14
13
 
15
- export interface AuditCommands {
16
- logAuditEvent: (
17
- db: Transaction,
18
- input: {
19
- eventId: string;
20
- actorType: string;
21
- actorId: string;
22
- entityType: string;
23
- entityId: string;
24
- operationType: string;
25
- companyId?: string;
26
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
27
- },
28
- ctx: CommandContext,
29
- ) => Promise<{ ok: true; value: unknown }>;
30
- }
31
-
32
14
  /**
33
15
  * Function: deleteAccountGroup
34
16
  *
35
17
  * Permanently removes an account group that has no child groups and no assigned
36
18
  * accounts.
37
19
  */
38
- export async function run(
39
- db: Transaction,
40
- input: DeleteAccountGroupInput,
41
- ctx: CommandContext,
42
- auditCommands?: AuditCommands,
43
- ) {
20
+ export async function run(db: Transaction, input: DeleteAccountGroupInput, _ctx: CommandContext) {
44
21
  const { id } = input;
45
22
 
46
23
  // 1. Find the group
@@ -91,23 +68,5 @@ export async function run(
91
68
  // 5. Delete the group
92
69
  await db.deleteFrom("AccountGroup").where("id", "=", id).execute();
93
70
 
94
- // 6. Emit audit event
95
- if (auditCommands) {
96
- await auditCommands.logAuditEvent(
97
- db,
98
- {
99
- eventId: randomUUID(),
100
- actorType: "USER",
101
- actorId: ctx.actorId,
102
- entityType: "AccountGroup",
103
- entityId: id,
104
- operationType: "DELETE",
105
- companyId: coa!.companyId,
106
- changes: [{ fieldName: "id", oldValue: id }],
107
- },
108
- ctx,
109
- );
110
- }
111
-
112
71
  return ok({ success: true as const });
113
72
  }
@@ -1,9 +1,9 @@
1
- import { describe, expect, it, vi } from "vitest";
1
+ import { describe, expect, it } from "vitest";
2
2
  import { createMockDb } from "../../../testing/index";
3
3
  import type { Transaction } from "../generated/kysely-tailordb";
4
4
  import { ChartOfAccountsNotFoundError, InvalidStateTransitionError } from "../lib/errors.generated";
5
5
  import { baseDraftCoa, baseActiveCoa, baseArchivedCoa } from "../testing/fixtures";
6
- import { run, type AuditCommands } from "./deleteChartOfAccounts";
6
+ import { run } from "./deleteChartOfAccounts";
7
7
  import type { CommandContext } from "@tailor-platform/erp-kit/module";
8
8
 
9
9
  const ctx: CommandContext = {
@@ -95,60 +95,4 @@ describe("deleteChartOfAccounts", () => {
95
95
  // once for Account, once for AccountGroup, once for ChartOfAccounts
96
96
  expect(spies.delete).toHaveBeenCalledTimes(3);
97
97
  });
98
-
99
- it("emits audit events for each cascade-deleted Account and AccountGroup", async () => {
100
- const { db, spies } = createMockDb<Transaction>();
101
- const accounts = [{ id: "account-1", code: "1100", name: "Cash" }];
102
- const groups = [{ id: "group-1", code: "1000", name: "Assets" }];
103
- spies.select
104
- .mockReturnValueOnce(baseDraftCoa) // CoA lookup
105
- .mockReturnValueOnce(accounts) // accounts query
106
- .mockReturnValueOnce(groups); // groups query
107
-
108
- const mockLogAuditEvent = vi.fn().mockResolvedValue({ ok: true, value: {} });
109
- const auditCommands: AuditCommands = { logAuditEvent: mockLogAuditEvent };
110
-
111
- const result = await run(db, { id: baseDraftCoa.id }, ctx, auditCommands);
112
-
113
- expect(result.ok).toBe(true);
114
- // Should emit: 1 for account + 1 for group + 1 for CoA = 3 audit events
115
- expect(mockLogAuditEvent).toHaveBeenCalledTimes(3);
116
- expect(mockLogAuditEvent).toHaveBeenCalledWith(
117
- db,
118
- expect.objectContaining({ entityType: "Account", operationType: "DELETE" }),
119
- ctx,
120
- );
121
- expect(mockLogAuditEvent).toHaveBeenCalledWith(
122
- db,
123
- expect.objectContaining({ entityType: "AccountGroup", operationType: "DELETE" }),
124
- ctx,
125
- );
126
- });
127
-
128
- it("emits audit event recording CoA deletion and acting user", async () => {
129
- const { db, spies } = createMockDb<Transaction>();
130
- spies.select
131
- .mockReturnValueOnce(baseDraftCoa) // CoA lookup
132
- .mockReturnValueOnce([]) // no accounts
133
- .mockReturnValueOnce([]); // no groups
134
-
135
- const mockLogAuditEvent = vi.fn().mockResolvedValue({ ok: true, value: {} });
136
- const auditCommands: AuditCommands = { logAuditEvent: mockLogAuditEvent };
137
-
138
- const result = await run(db, { id: baseDraftCoa.id }, ctx, auditCommands);
139
-
140
- expect(result.ok).toBe(true);
141
- expect(mockLogAuditEvent).toHaveBeenCalledWith(
142
- db,
143
- expect.objectContaining({
144
- entityType: "ChartOfAccounts",
145
- entityId: baseDraftCoa.id,
146
- operationType: "DELETE",
147
- companyId: baseDraftCoa.companyId,
148
- actorType: "USER",
149
- actorId: ctx.actorId,
150
- }),
151
- ctx,
152
- );
153
- });
154
98
  });
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import { ChartOfAccountsNotFoundError, InvalidStateTransitionError } from "../lib/errors.generated";
4
3
  import { ok, err, type CommandContext } from "@tailor-platform/erp-kit/module";
@@ -7,23 +6,6 @@ export interface DeleteChartOfAccountsInput {
7
6
  id: string;
8
7
  }
9
8
 
10
- export interface AuditCommands {
11
- logAuditEvent: (
12
- db: Transaction,
13
- input: {
14
- eventId: string;
15
- actorType: string;
16
- actorId: string;
17
- entityType: string;
18
- entityId: string;
19
- operationType: string;
20
- companyId?: string;
21
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
22
- },
23
- ctx: CommandContext,
24
- ) => Promise<{ ok: true; value: unknown }>;
25
- }
26
-
27
9
  /**
28
10
  * Function: deleteChartOfAccounts
29
11
  *
@@ -33,8 +15,7 @@ export interface AuditCommands {
33
15
  export async function run(
34
16
  db: Transaction,
35
17
  input: DeleteChartOfAccountsInput,
36
- ctx: CommandContext,
37
- auditCommands?: AuditCommands,
18
+ _ctx: CommandContext,
38
19
  ) {
39
20
  const { id } = input;
40
21
 
@@ -55,79 +36,14 @@ export async function run(
55
36
  return err(new InvalidStateTransitionError(id));
56
37
  }
57
38
 
58
- // 3. Query child records before deleting (for audit)
59
- const accounts = await db
60
- .selectFrom("Account")
61
- .selectAll()
62
- .where("chartOfAccountsId", "=", id)
63
- .execute();
64
-
65
- const accountGroups = await db
66
- .selectFrom("AccountGroup")
67
- .selectAll()
68
- .where("chartOfAccountsId", "=", id)
69
- .execute();
70
-
71
- // 4. Cascade-delete all child Accounts
39
+ // 3. Cascade-delete all child Accounts
72
40
  await db.deleteFrom("Account").where("chartOfAccountsId", "=", id).execute();
73
41
 
74
- // 5. Cascade-delete all child AccountGroups
42
+ // 4. Cascade-delete all child AccountGroups
75
43
  await db.deleteFrom("AccountGroup").where("chartOfAccountsId", "=", id).execute();
76
44
 
77
- // 6. Delete the CoA itself
45
+ // 5. Delete the CoA itself
78
46
  await db.deleteFrom("ChartOfAccounts").where("id", "=", id).execute();
79
47
 
80
- // 7. Emit audit events
81
- if (auditCommands) {
82
- for (const account of accounts) {
83
- await auditCommands.logAuditEvent(
84
- db,
85
- {
86
- eventId: randomUUID(),
87
- actorType: "USER",
88
- actorId: ctx.actorId,
89
- entityType: "Account",
90
- entityId: account.id,
91
- operationType: "DELETE",
92
- companyId: existing.companyId,
93
- changes: [{ fieldName: "id", oldValue: account.id }],
94
- },
95
- ctx,
96
- );
97
- }
98
-
99
- for (const group of accountGroups) {
100
- await auditCommands.logAuditEvent(
101
- db,
102
- {
103
- eventId: randomUUID(),
104
- actorType: "USER",
105
- actorId: ctx.actorId,
106
- entityType: "AccountGroup",
107
- entityId: group.id,
108
- operationType: "DELETE",
109
- companyId: existing.companyId,
110
- changes: [{ fieldName: "id", oldValue: group.id }],
111
- },
112
- ctx,
113
- );
114
- }
115
-
116
- await auditCommands.logAuditEvent(
117
- db,
118
- {
119
- eventId: randomUUID(),
120
- actorType: "USER",
121
- actorId: ctx.actorId,
122
- entityType: "ChartOfAccounts",
123
- entityId: id,
124
- operationType: "DELETE",
125
- companyId: existing.companyId,
126
- changes: [{ fieldName: "id", oldValue: id }],
127
- },
128
- ctx,
129
- );
130
- }
131
-
132
48
  return ok({ success: true as const });
133
49
  }
@@ -169,31 +169,4 @@ describe("moveAccountGroup", () => {
169
169
  expect(result.value.previousParentAccountGroupId).toBe(baseRootGroup.id);
170
170
  }
171
171
  });
172
-
173
- it("emits audit event recording previous and new parent references", async () => {
174
- const { db, spies } = createMockDb<Transaction>();
175
- const movedGroup = { ...baseChildGroup, parentAccountGroupId: baseGroupNoRange.id };
176
- spies.select
177
- .mockReturnValueOnce(baseChildGroup) // Group lookup
178
- .mockReturnValueOnce(baseActiveCoa) // CoA lookup
179
- .mockReturnValueOnce(baseGroupNoRange); // New parent lookup
180
- spies.update.mockReturnValue(movedGroup);
181
-
182
- const result = await run(
183
- db,
184
- { id: baseChildGroup.id, newParentAccountGroupId: baseGroupNoRange.id },
185
- ctx,
186
- );
187
-
188
- expect(result.ok).toBe(true);
189
- if (result.ok) {
190
- // Return value contains previous and new parent for audit logging
191
- expect(result.value.previousParentAccountGroupId).toBe(baseRootGroup.id);
192
- expect(result.value.accountGroup.parentAccountGroupId).toBe(baseGroupNoRange.id);
193
- }
194
- expect(spies.update).toHaveBeenCalled();
195
- expect(spies.set).toHaveBeenCalledWith(
196
- expect.objectContaining({ parentAccountGroupId: baseGroupNoRange.id }),
197
- );
198
- });
199
172
  });
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import {
4
3
  AccountGroupNotFoundError,
@@ -13,35 +12,13 @@ export interface MoveAccountGroupInput {
13
12
  newParentAccountGroupId: string | null;
14
13
  }
15
14
 
16
- export interface AuditCommands {
17
- logAuditEvent: (
18
- db: Transaction,
19
- input: {
20
- eventId: string;
21
- actorType: string;
22
- actorId: string;
23
- entityType: string;
24
- entityId: string;
25
- operationType: string;
26
- companyId?: string;
27
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
28
- },
29
- ctx: CommandContext,
30
- ) => Promise<{ ok: true; value: unknown }>;
31
- }
32
-
33
15
  /**
34
16
  * Function: moveAccountGroup
35
17
  *
36
18
  * Reparents an account group by changing its parent reference within the same
37
19
  * CoA. Moving to a null parent promotes the group to a root group.
38
20
  */
39
- export async function run(
40
- db: Transaction,
41
- input: MoveAccountGroupInput,
42
- ctx: CommandContext,
43
- auditCommands?: AuditCommands,
44
- ) {
21
+ export async function run(db: Transaction, input: MoveAccountGroupInput, _ctx: CommandContext) {
45
22
  const { id, newParentAccountGroupId } = input;
46
23
 
47
24
  // 1. Find the group
@@ -117,29 +94,5 @@ export async function run(
117
94
  .returningAll()
118
95
  .executeTakeFirst();
119
96
 
120
- // 6. Emit audit event
121
- if (auditCommands) {
122
- await auditCommands.logAuditEvent(
123
- db,
124
- {
125
- eventId: randomUUID(),
126
- actorType: "USER",
127
- actorId: ctx.actorId,
128
- entityType: "AccountGroup",
129
- entityId: id,
130
- operationType: "UPDATE",
131
- companyId: coa!.companyId,
132
- changes: [
133
- {
134
- fieldName: "parentAccountGroupId",
135
- oldValue: existing.parentAccountGroupId,
136
- newValue: newParentAccountGroupId,
137
- },
138
- ],
139
- },
140
- ctx,
141
- );
142
- }
143
-
144
97
  return ok({ accountGroup: accountGroup!, previousParentAccountGroupId: previousParentId });
145
98
  }
@@ -108,19 +108,4 @@ describe("reactivateAccount", () => {
108
108
  expect(result.value.account.successorAccountId).toBeNull();
109
109
  }
110
110
  });
111
-
112
- it("emits audit event recording status transition from INACTIVE to ACTIVE", async () => {
113
- const { db, spies } = createMockDb<Transaction>();
114
- const reactivated = { ...baseInactiveAccount, status: "ACTIVE", successorAccountId: null };
115
- spies.select.mockReturnValueOnce(baseInactiveAccount).mockReturnValueOnce(baseActiveCoa);
116
- spies.update.mockReturnValue(reactivated);
117
-
118
- const result = await run(db, { id: baseInactiveAccount.id }, ctx);
119
-
120
- // Audit event is emitted as part of successful reactivation
121
- expect(result.ok).toBe(true);
122
- if (result.ok) {
123
- expect(result.value.account.status).toBe("ACTIVE");
124
- }
125
- });
126
111
  });
@@ -1,4 +1,3 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import type { Transaction } from "../generated/kysely-tailordb";
3
2
  import {
4
3
  AccountNotFoundError,
@@ -13,23 +12,6 @@ export interface ReactivateAccountInput {
13
12
  from?: string[];
14
13
  }
15
14
 
16
- export interface AuditCommands {
17
- logAuditEvent: (
18
- db: Transaction,
19
- input: {
20
- eventId: string;
21
- actorType: string;
22
- actorId: string;
23
- entityType: string;
24
- entityId: string;
25
- operationType: string;
26
- companyId?: string;
27
- changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[];
28
- },
29
- ctx: CommandContext,
30
- ) => Promise<{ ok: true; value: unknown }>;
31
- }
32
-
33
15
  /**
34
16
  * Function: reactivateAccount
35
17
  *
@@ -37,12 +19,7 @@ export interface AuditCommands {
37
19
  * eligible to receive journal postings again. Clears any previously mapped
38
20
  * successor account.
39
21
  */
40
- export async function run(
41
- db: Transaction,
42
- input: ReactivateAccountInput,
43
- ctx: CommandContext,
44
- auditCommands?: AuditCommands,
45
- ) {
22
+ export async function run(db: Transaction, input: ReactivateAccountInput, _ctx: CommandContext) {
46
23
  const { id, from = ["INACTIVE"] } = input;
47
24
 
48
25
  // 1. Find account with forUpdate
@@ -90,34 +67,5 @@ export async function run(
90
67
  .returningAll()
91
68
  .executeTakeFirst();
92
69
 
93
- // 6. Emit audit event
94
- if (auditCommands) {
95
- const changes: { fieldName: string; oldValue?: unknown; newValue?: unknown }[] = [
96
- { fieldName: "status", oldValue: account.status, newValue: "ACTIVE" },
97
- ];
98
- if (account.successorAccountId) {
99
- changes.push({
100
- fieldName: "successorAccountId",
101
- oldValue: account.successorAccountId,
102
- newValue: null,
103
- });
104
- }
105
-
106
- await auditCommands.logAuditEvent(
107
- db,
108
- {
109
- eventId: randomUUID(),
110
- actorType: "USER",
111
- actorId: ctx.actorId,
112
- entityType: "Account",
113
- entityId: id,
114
- operationType: "UPDATE",
115
- companyId: coa.companyId,
116
- changes,
117
- },
118
- ctx,
119
- );
120
- }
121
-
122
70
  return ok({ account: updated! });
123
71
  }
@@ -631,21 +631,6 @@ describe("updateAccount", () => {
631
631
  }
632
632
  });
633
633
 
634
- it("emits audit event recording previous and new field values for all changed attributes", async () => {
635
- const { db, spies } = createMockDb<Transaction>();
636
- const updated = { ...baseDraftAccount, name: "Audited Update" };
637
- spies.select.mockReturnValueOnce(baseDraftAccount).mockReturnValueOnce(baseActiveCoa);
638
- spies.update.mockReturnValue(updated);
639
-
640
- const result = await run(db, { id: baseDraftAccount.id, name: "Audited Update" }, ctx);
641
-
642
- // Audit event is emitted as part of successful update
643
- expect(result.ok).toBe(true);
644
- if (result.ok) {
645
- expect(result.value.account.name).toBe("Audited Update");
646
- }
647
- });
648
-
649
634
  it("passes custom fields through to update", async () => {
650
635
  const { db, spies } = createMockDb<Transaction>();
651
636
  const updated = { ...baseDraftAccount, customField: "custom-value" };