@tailor-platform/erp-kit 0.3.0 → 0.4.1

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 (817) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +35 -7
  3. package/dist/cli.mjs +783 -272
  4. package/package.json +5 -4
  5. package/skills/erp-kit-app-1-requirements/SKILL.md +39 -12
  6. package/skills/{erp-kit-app-3-plan → erp-kit-app-1-requirements}/references/story-extraction.md +1 -1
  7. package/skills/erp-kit-app-2-requirements-review/SKILL.md +29 -5
  8. package/skills/erp-kit-app-2-requirements-review/references/best-practices-check.md +4 -0
  9. package/skills/erp-kit-app-2-requirements-review/references/boundary-consistency-check.md +4 -0
  10. package/skills/erp-kit-app-2-requirements-review/references/story-quality-check.md +67 -0
  11. package/skills/erp-kit-app-3-plan/SKILL.md +51 -50
  12. package/skills/erp-kit-app-3-plan/references/resolver-extraction.md +24 -18
  13. package/skills/erp-kit-app-3-plan/references/screen-extraction.md +18 -2
  14. package/skills/erp-kit-app-4-plan-review/SKILL.md +16 -13
  15. package/skills/erp-kit-app-5-impl-backend/SKILL.md +19 -11
  16. package/skills/erp-kit-app-5-impl-backend/references/app-config.md +1 -22
  17. package/skills/erp-kit-app-5-impl-backend/references/module-wiring.md +0 -1
  18. package/skills/erp-kit-app-5-impl-backend/references/resolver-patterns.md +13 -4
  19. package/skills/erp-kit-app-6-impl-frontend/SKILL.md +16 -0
  20. package/skills/erp-kit-app-6-impl-frontend/references/pages.md +16 -46
  21. package/skills/erp-kit-app-7-impl-review/SKILL.md +29 -23
  22. package/skills/erp-kit-app-7-impl-review/references/resolver-doc-code-parity.md +16 -17
  23. package/skills/erp-kit-module-1-requirements/SKILL.md +6 -12
  24. package/skills/erp-kit-module-2-requirements-review/SKILL.md +29 -5
  25. package/skills/erp-kit-module-2-requirements-review/references/requirements-report-format.md +19 -0
  26. package/skills/erp-kit-module-3-plan/SKILL.md +2 -4
  27. package/skills/erp-kit-module-4-plan-review/SKILL.md +30 -7
  28. package/skills/erp-kit-module-4-plan-review/references/parity-report-format.md +15 -0
  29. package/skills/erp-kit-module-6-impl-review/SKILL.md +21 -7
  30. package/skills/erp-kit-module-6-impl-review/references/impl-parity-report-format.md +15 -0
  31. package/skills/erp-kit-module-shared/SKILL.md +4 -0
  32. package/skills/erp-kit-module-shared/references/commands.md +1 -1
  33. package/skills/erp-kit-module-shared/references/queries.md +5 -6
  34. package/src/commands/app/index.ts +28 -17
  35. package/src/commands/check.test.ts +1 -1
  36. package/src/commands/check.ts +2 -35
  37. package/src/commands/doc/index.ts +83 -0
  38. package/src/commands/doc/module.test.ts +119 -0
  39. package/src/commands/doc/module.ts +114 -0
  40. package/src/commands/doc/modules.test.ts +103 -0
  41. package/src/commands/doc/modules.ts +98 -0
  42. package/src/commands/doc/search.test.ts +94 -0
  43. package/src/commands/doc/search.ts +111 -0
  44. package/src/commands/generate-doc.ts +17 -10
  45. package/src/commands/index.ts +20 -8
  46. package/src/commands/lib/command-result.ts +30 -0
  47. package/src/commands/lib/discovery.test.ts +74 -0
  48. package/src/commands/lib/discovery.ts +106 -0
  49. package/src/commands/lib/paths.ts +22 -0
  50. package/src/commands/lib/sync-check-source.test.ts +197 -0
  51. package/src/commands/lib/sync-check-source.ts +100 -0
  52. package/src/commands/lib/sync-check-tests.test.ts +178 -0
  53. package/src/commands/lib/sync-check-tests.ts +69 -0
  54. package/src/commands/mock/index.ts +11 -6
  55. package/src/commands/module/generate.ts +13 -8
  56. package/src/commands/module/index.ts +17 -21
  57. package/src/commands/parse-doc-test-cases.ts +13 -2
  58. package/src/commands/sync-check.test.ts +6 -364
  59. package/src/commands/sync-check.ts +7 -251
  60. package/src/generator/generate-app-code.test.ts +121 -0
  61. package/src/generator/generate-app-code.ts +51 -0
  62. package/src/generator/generate-code-boilerplate.test.ts +1 -2
  63. package/src/generator/generate-code.test.ts +43 -12
  64. package/src/generator/generate-code.ts +12 -226
  65. package/src/generator/generate-errors.ts +34 -0
  66. package/src/generator/generate-permissions.ts +12 -0
  67. package/src/generator/generate-shells.ts +28 -0
  68. package/src/generator/generate-stubs.ts +31 -0
  69. package/src/generator/parse-resolver-doc.test.ts +89 -0
  70. package/src/generator/parse-resolver-doc.ts +125 -0
  71. package/src/generator/scaffold.ts +57 -0
  72. package/src/generator/stub-templates.test.ts +55 -0
  73. package/src/generator/stub-templates.ts +145 -0
  74. package/src/module.ts +3 -0
  75. package/src/modules/audit/README.md +46 -0
  76. package/src/modules/audit/command/activateAuditPolicy.generated.ts +6 -0
  77. package/src/modules/audit/command/activateAuditPolicy.test.ts +186 -0
  78. package/src/modules/audit/command/activateAuditPolicy.ts +97 -0
  79. package/src/modules/audit/command/createAuditPolicy.generated.ts +6 -0
  80. package/src/modules/audit/command/createAuditPolicy.test.ts +395 -0
  81. package/src/modules/audit/command/createAuditPolicy.ts +131 -0
  82. package/src/modules/audit/command/deactivateAuditPolicy.generated.ts +6 -0
  83. package/src/modules/audit/command/deactivateAuditPolicy.test.ts +138 -0
  84. package/src/modules/audit/command/deactivateAuditPolicy.ts +58 -0
  85. package/src/modules/audit/command/deleteAuditPolicy.generated.ts +6 -0
  86. package/src/modules/audit/command/deleteAuditPolicy.test.ts +121 -0
  87. package/src/modules/audit/command/deleteAuditPolicy.ts +52 -0
  88. package/src/modules/audit/command/logAuditEvent.generated.ts +6 -0
  89. package/src/modules/audit/command/logAuditEvent.test.ts +991 -0
  90. package/src/modules/audit/command/logAuditEvent.ts +357 -0
  91. package/src/modules/audit/command/reactivateAuditPolicy.generated.ts +6 -0
  92. package/src/modules/audit/command/reactivateAuditPolicy.test.ts +143 -0
  93. package/src/modules/audit/command/reactivateAuditPolicy.ts +79 -0
  94. package/src/modules/audit/command/registerAuditableEntity.generated.ts +6 -0
  95. package/src/modules/audit/command/registerAuditableEntity.test.ts +268 -0
  96. package/src/modules/audit/command/registerAuditableEntity.ts +94 -0
  97. package/src/modules/audit/command/replaceAuditPolicy.generated.ts +6 -0
  98. package/src/modules/audit/command/replaceAuditPolicy.test.ts +242 -0
  99. package/src/modules/audit/command/replaceAuditPolicy.ts +91 -0
  100. package/src/modules/audit/command/updateAuditPolicy.generated.ts +6 -0
  101. package/src/modules/audit/command/updateAuditPolicy.test.ts +284 -0
  102. package/src/modules/audit/command/updateAuditPolicy.ts +151 -0
  103. package/src/modules/audit/db/auditEntry.ts +47 -0
  104. package/src/modules/audit/db/auditPolicy.ts +33 -0
  105. package/src/modules/audit/db/auditableEntity.ts +22 -0
  106. package/src/modules/audit/db/changeDetail.ts +28 -0
  107. package/src/modules/audit/db/policyFieldRule.ts +23 -0
  108. package/src/modules/audit/docs/commands/ActivateAuditPolicy.md +69 -0
  109. package/src/modules/audit/docs/commands/CreateAuditPolicy.md +79 -0
  110. package/src/modules/audit/docs/commands/DeactivateAuditPolicy.md +55 -0
  111. package/src/modules/audit/docs/commands/DeleteAuditPolicy.md +55 -0
  112. package/src/modules/audit/docs/commands/LogAuditEvent.md +137 -0
  113. package/src/modules/audit/docs/commands/ReactivateAuditPolicy.md +58 -0
  114. package/src/modules/audit/docs/commands/RegisterAuditableEntity.md +62 -0
  115. package/src/modules/audit/docs/commands/ReplaceAuditPolicy.md +72 -0
  116. package/src/modules/audit/docs/commands/UpdateAuditPolicy.md +77 -0
  117. package/src/modules/audit/docs/features/audit-event-logging.md +126 -0
  118. package/src/modules/audit/docs/features/audit-policy-configuration.md +135 -0
  119. package/src/modules/audit/docs/features/field-level-change-tracking.md +95 -0
  120. package/src/modules/audit/docs/models/AuditEntry.md +55 -0
  121. package/src/modules/audit/docs/models/AuditPolicy.md +79 -0
  122. package/src/modules/audit/docs/models/AuditableEntity.md +38 -0
  123. package/src/modules/audit/docs/models/ChangeDetail.md +55 -0
  124. package/src/modules/audit/docs/models/PolicyFieldRule.md +45 -0
  125. package/src/modules/audit/docs/queries/GetAuditEntry.md +49 -0
  126. package/src/modules/audit/docs/queries/GetAuditPolicy.md +54 -0
  127. package/src/modules/audit/docs/queries/GetAuditSummary.md +84 -0
  128. package/src/modules/audit/docs/queries/GetChangeDetails.md +56 -0
  129. package/src/modules/audit/docs/queries/ListAuditPolicies.md +58 -0
  130. package/src/modules/audit/docs/queries/SearchAuditEntries.md +91 -0
  131. package/src/modules/audit/generated/kysely-tailordb.ts +92 -0
  132. package/src/modules/audit/index.ts +2 -0
  133. package/src/modules/audit/lib/_db_deps.ts +13 -0
  134. package/src/modules/audit/lib/errors.generated.ts +120 -0
  135. package/src/modules/audit/lib/permissions.generated.ts +14 -0
  136. package/src/modules/audit/lib/types.ts +33 -0
  137. package/src/modules/audit/module.ts +57 -0
  138. package/src/modules/audit/permissions.ts +39 -0
  139. package/src/modules/audit/query/getAuditEntry.generated.ts +5 -0
  140. package/src/modules/audit/query/getAuditEntry.test.ts +123 -0
  141. package/src/modules/audit/query/getAuditEntry.ts +42 -0
  142. package/src/modules/audit/query/getAuditPolicy.generated.ts +5 -0
  143. package/src/modules/audit/query/getAuditPolicy.test.ts +169 -0
  144. package/src/modules/audit/query/getAuditPolicy.ts +48 -0
  145. package/src/modules/audit/query/getAuditSummary.generated.ts +5 -0
  146. package/src/modules/audit/query/getAuditSummary.test.ts +632 -0
  147. package/src/modules/audit/query/getAuditSummary.ts +170 -0
  148. package/src/modules/audit/query/getChangeDetails.generated.ts +5 -0
  149. package/src/modules/audit/query/getChangeDetails.test.ts +195 -0
  150. package/src/modules/audit/query/getChangeDetails.ts +48 -0
  151. package/src/modules/audit/query/listAuditPolicies.generated.ts +5 -0
  152. package/src/modules/audit/query/listAuditPolicies.test.ts +239 -0
  153. package/src/modules/audit/query/listAuditPolicies.ts +100 -0
  154. package/src/modules/audit/query/searchAuditEntries.generated.ts +5 -0
  155. package/src/modules/audit/query/searchAuditEntries.test.ts +424 -0
  156. package/src/modules/audit/query/searchAuditEntries.ts +121 -0
  157. package/src/modules/audit/tailor.config.ts +13 -0
  158. package/src/modules/audit/tailor.d.ts +13 -0
  159. package/src/modules/audit/testing/fixtures.ts +215 -0
  160. package/src/modules/business-partner/README.md +60 -0
  161. package/src/modules/business-partner/command/activatePartner.generated.ts +6 -0
  162. package/src/modules/business-partner/command/activatePartner.test.ts +59 -0
  163. package/src/modules/business-partner/command/activatePartner.ts +45 -0
  164. package/src/modules/business-partner/command/assignRoleToPartner.generated.ts +6 -0
  165. package/src/modules/business-partner/command/assignRoleToPartner.test.ts +113 -0
  166. package/src/modules/business-partner/command/assignRoleToPartner.ts +72 -0
  167. package/src/modules/business-partner/command/createContactPerson.generated.ts +6 -0
  168. package/src/modules/business-partner/command/createContactPerson.test.ts +193 -0
  169. package/src/modules/business-partner/command/createContactPerson.ts +98 -0
  170. package/src/modules/business-partner/command/createPartner.generated.ts +6 -0
  171. package/src/modules/business-partner/command/createPartner.test.ts +179 -0
  172. package/src/modules/business-partner/command/createPartner.ts +82 -0
  173. package/src/modules/business-partner/command/createPartnerAddress.generated.ts +6 -0
  174. package/src/modules/business-partner/command/createPartnerAddress.test.ts +195 -0
  175. package/src/modules/business-partner/command/createPartnerAddress.ts +119 -0
  176. package/src/modules/business-partner/command/createPartnerBankAccount.generated.ts +6 -0
  177. package/src/modules/business-partner/command/createPartnerBankAccount.test.ts +297 -0
  178. package/src/modules/business-partner/command/createPartnerBankAccount.ts +114 -0
  179. package/src/modules/business-partner/command/createPartnerIdentification.generated.ts +6 -0
  180. package/src/modules/business-partner/command/createPartnerIdentification.test.ts +255 -0
  181. package/src/modules/business-partner/command/createPartnerIdentification.ts +97 -0
  182. package/src/modules/business-partner/command/deactivateContactPerson.generated.ts +6 -0
  183. package/src/modules/business-partner/command/deactivateContactPerson.test.ts +70 -0
  184. package/src/modules/business-partner/command/deactivateContactPerson.ts +54 -0
  185. package/src/modules/business-partner/command/deactivatePartner.generated.ts +6 -0
  186. package/src/modules/business-partner/command/deactivatePartner.test.ts +59 -0
  187. package/src/modules/business-partner/command/deactivatePartner.ts +46 -0
  188. package/src/modules/business-partner/command/deleteContactPerson.generated.ts +6 -0
  189. package/src/modules/business-partner/command/deleteContactPerson.test.ts +61 -0
  190. package/src/modules/business-partner/command/deleteContactPerson.ts +48 -0
  191. package/src/modules/business-partner/command/deletePartner.generated.ts +6 -0
  192. package/src/modules/business-partner/command/deletePartner.test.ts +58 -0
  193. package/src/modules/business-partner/command/deletePartner.ts +46 -0
  194. package/src/modules/business-partner/command/deletePartnerAddress.generated.ts +6 -0
  195. package/src/modules/business-partner/command/deletePartnerAddress.test.ts +74 -0
  196. package/src/modules/business-partner/command/deletePartnerAddress.ts +52 -0
  197. package/src/modules/business-partner/command/deletePartnerBankAccount.generated.ts +6 -0
  198. package/src/modules/business-partner/command/deletePartnerBankAccount.test.ts +55 -0
  199. package/src/modules/business-partner/command/deletePartnerBankAccount.ts +36 -0
  200. package/src/modules/business-partner/command/deletePartnerIdentification.generated.ts +6 -0
  201. package/src/modules/business-partner/command/deletePartnerIdentification.test.ts +47 -0
  202. package/src/modules/business-partner/command/deletePartnerIdentification.ts +37 -0
  203. package/src/modules/business-partner/command/reactivateContactPerson.generated.ts +6 -0
  204. package/src/modules/business-partner/command/reactivateContactPerson.test.ts +48 -0
  205. package/src/modules/business-partner/command/reactivateContactPerson.ts +48 -0
  206. package/src/modules/business-partner/command/reactivatePartner.generated.ts +6 -0
  207. package/src/modules/business-partner/command/reactivatePartner.test.ts +59 -0
  208. package/src/modules/business-partner/command/reactivatePartner.ts +46 -0
  209. package/src/modules/business-partner/command/removeRoleFromPartner.generated.ts +6 -0
  210. package/src/modules/business-partner/command/removeRoleFromPartner.test.ts +82 -0
  211. package/src/modules/business-partner/command/removeRoleFromPartner.ts +73 -0
  212. package/src/modules/business-partner/command/setDefaultPartnerAddress.generated.ts +6 -0
  213. package/src/modules/business-partner/command/setDefaultPartnerAddress.test.ts +60 -0
  214. package/src/modules/business-partner/command/setDefaultPartnerAddress.ts +48 -0
  215. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.generated.ts +6 -0
  216. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.test.ts +56 -0
  217. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.ts +51 -0
  218. package/src/modules/business-partner/command/setPrimaryContactPerson.generated.ts +6 -0
  219. package/src/modules/business-partner/command/setPrimaryContactPerson.test.ts +63 -0
  220. package/src/modules/business-partner/command/setPrimaryContactPerson.ts +55 -0
  221. package/src/modules/business-partner/command/updateContactPerson.generated.ts +6 -0
  222. package/src/modules/business-partner/command/updateContactPerson.test.ts +193 -0
  223. package/src/modules/business-partner/command/updateContactPerson.ts +92 -0
  224. package/src/modules/business-partner/command/updatePartner.generated.ts +6 -0
  225. package/src/modules/business-partner/command/updatePartner.test.ts +101 -0
  226. package/src/modules/business-partner/command/updatePartner.ts +76 -0
  227. package/src/modules/business-partner/command/updatePartnerAddress.generated.ts +6 -0
  228. package/src/modules/business-partner/command/updatePartnerAddress.test.ts +148 -0
  229. package/src/modules/business-partner/command/updatePartnerAddress.ts +64 -0
  230. package/src/modules/business-partner/command/updatePartnerBankAccount.generated.ts +6 -0
  231. package/src/modules/business-partner/command/updatePartnerBankAccount.test.ts +249 -0
  232. package/src/modules/business-partner/command/updatePartnerBankAccount.ts +109 -0
  233. package/src/modules/business-partner/command/updatePartnerIdentification.generated.ts +6 -0
  234. package/src/modules/business-partner/command/updatePartnerIdentification.test.ts +162 -0
  235. package/src/modules/business-partner/command/updatePartnerIdentification.ts +105 -0
  236. package/src/modules/business-partner/db/.gitkeep +0 -0
  237. package/src/modules/business-partner/db/businessPartner.ts +59 -0
  238. package/src/modules/business-partner/db/contactPerson.ts +49 -0
  239. package/src/modules/business-partner/db/partnerAddress.ts +45 -0
  240. package/src/modules/business-partner/db/partnerBankAccount.ts +53 -0
  241. package/src/modules/business-partner/db/partnerIdentification.ts +53 -0
  242. package/src/modules/business-partner/db/partnerRole.ts +43 -0
  243. package/src/modules/business-partner/docs/commands/ActivatePartner.md +39 -0
  244. package/src/modules/business-partner/docs/commands/AssignRoleToPartner.md +49 -0
  245. package/src/modules/business-partner/docs/commands/CreateContactPerson.md +59 -0
  246. package/src/modules/business-partner/docs/commands/CreatePartner.md +54 -0
  247. package/src/modules/business-partner/docs/commands/CreatePartnerAddress.md +60 -0
  248. package/src/modules/business-partner/docs/commands/CreatePartnerBankAccount.md +68 -0
  249. package/src/modules/business-partner/docs/commands/CreatePartnerIdentification.md +59 -0
  250. package/src/modules/business-partner/docs/commands/DeactivateContactPerson.md +42 -0
  251. package/src/modules/business-partner/docs/commands/DeactivatePartner.md +39 -0
  252. package/src/modules/business-partner/docs/commands/DeleteContactPerson.md +43 -0
  253. package/src/modules/business-partner/docs/commands/DeletePartner.md +40 -0
  254. package/src/modules/business-partner/docs/commands/DeletePartnerAddress.md +40 -0
  255. package/src/modules/business-partner/docs/commands/DeletePartnerBankAccount.md +35 -0
  256. package/src/modules/business-partner/docs/commands/DeletePartnerIdentification.md +33 -0
  257. package/src/modules/business-partner/docs/commands/ReactivateContactPerson.md +38 -0
  258. package/src/modules/business-partner/docs/commands/ReactivatePartner.md +39 -0
  259. package/src/modules/business-partner/docs/commands/RemoveRoleFromPartner.md +46 -0
  260. package/src/modules/business-partner/docs/commands/SetDefaultPartnerAddress.md +38 -0
  261. package/src/modules/business-partner/docs/commands/SetDefaultPartnerBankAccount.md +38 -0
  262. package/src/modules/business-partner/docs/commands/SetPrimaryContactPerson.md +43 -0
  263. package/src/modules/business-partner/docs/commands/UpdateContactPerson.md +66 -0
  264. package/src/modules/business-partner/docs/commands/UpdatePartner.md +48 -0
  265. package/src/modules/business-partner/docs/commands/UpdatePartnerAddress.md +46 -0
  266. package/src/modules/business-partner/docs/commands/UpdatePartnerBankAccount.md +64 -0
  267. package/src/modules/business-partner/docs/commands/UpdatePartnerIdentification.md +52 -0
  268. package/src/modules/business-partner/docs/features/contact-person-management.md +70 -0
  269. package/src/modules/business-partner/docs/features/partner-address-management.md +96 -0
  270. package/src/modules/business-partner/docs/features/partner-bank-account.md +70 -0
  271. package/src/modules/business-partner/docs/features/partner-identification.md +76 -0
  272. package/src/modules/business-partner/docs/features/partner-lifecycle.md +59 -0
  273. package/src/modules/business-partner/docs/features/partner-role-classification.md +73 -0
  274. package/src/modules/business-partner/docs/models/BusinessPartner.md +64 -0
  275. package/src/modules/business-partner/docs/models/ContactPerson.md +62 -0
  276. package/src/modules/business-partner/docs/models/PartnerAddress.md +52 -0
  277. package/src/modules/business-partner/docs/models/PartnerBankAccount.md +50 -0
  278. package/src/modules/business-partner/docs/models/PartnerIdentification.md +46 -0
  279. package/src/modules/business-partner/docs/models/PartnerRole.md +42 -0
  280. package/src/modules/business-partner/docs/queries/GetContactPerson.md +34 -0
  281. package/src/modules/business-partner/docs/queries/GetDefaultPartnerAddress.md +40 -0
  282. package/src/modules/business-partner/docs/queries/GetDefaultPartnerBankAccount.md +36 -0
  283. package/src/modules/business-partner/docs/queries/GetPartner.md +35 -0
  284. package/src/modules/business-partner/docs/queries/GetPartnerAddress.md +34 -0
  285. package/src/modules/business-partner/docs/queries/GetPartnerBankAccount.md +34 -0
  286. package/src/modules/business-partner/docs/queries/GetPartnerIdentification.md +34 -0
  287. package/src/modules/business-partner/docs/queries/GetPartnerRole.md +34 -0
  288. package/src/modules/business-partner/docs/queries/GetPrimaryContactPerson.md +36 -0
  289. package/src/modules/business-partner/docs/queries/ListContactPersonsByPartner.md +39 -0
  290. package/src/modules/business-partner/docs/queries/ListPartnerAddressesByPartner.md +41 -0
  291. package/src/modules/business-partner/docs/queries/ListPartnerBankAccountsByPartner.md +39 -0
  292. package/src/modules/business-partner/docs/queries/ListPartnerIdentificationsByPartner.md +41 -0
  293. package/src/modules/business-partner/docs/queries/ListPartnersByRole.md +47 -0
  294. package/src/modules/business-partner/executor/.gitkeep +0 -0
  295. package/src/modules/business-partner/generated/.gitkeep +0 -0
  296. package/src/modules/business-partner/generated/enums.ts +60 -0
  297. package/src/modules/business-partner/generated/kysely-tailordb.ts +114 -0
  298. package/src/modules/business-partner/index.ts +2 -0
  299. package/src/modules/business-partner/lib/_db_deps.ts +17 -0
  300. package/src/modules/business-partner/lib/errors.generated.ts +172 -0
  301. package/src/modules/business-partner/lib/permissions.generated.ts +30 -0
  302. package/src/modules/business-partner/lib/types.ts +58 -0
  303. package/src/modules/business-partner/module.ts +181 -0
  304. package/src/modules/business-partner/permissions.ts +3 -0
  305. package/src/modules/business-partner/query/.gitkeep +0 -0
  306. package/src/modules/business-partner/query/getContactPerson.generated.ts +5 -0
  307. package/src/modules/business-partner/query/getContactPerson.test.ts +31 -0
  308. package/src/modules/business-partner/query/getContactPerson.ts +16 -0
  309. package/src/modules/business-partner/query/getDefaultPartnerAddress.generated.ts +5 -0
  310. package/src/modules/business-partner/query/getDefaultPartnerAddress.test.ts +45 -0
  311. package/src/modules/business-partner/query/getDefaultPartnerAddress.ts +30 -0
  312. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.generated.ts +5 -0
  313. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.test.ts +43 -0
  314. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.ts +17 -0
  315. package/src/modules/business-partner/query/getPartner.generated.ts +5 -0
  316. package/src/modules/business-partner/query/getPartner.test.ts +31 -0
  317. package/src/modules/business-partner/query/getPartner.ts +16 -0
  318. package/src/modules/business-partner/query/getPartnerAddress.generated.ts +5 -0
  319. package/src/modules/business-partner/query/getPartnerAddress.test.ts +31 -0
  320. package/src/modules/business-partner/query/getPartnerAddress.ts +16 -0
  321. package/src/modules/business-partner/query/getPartnerBankAccount.generated.ts +5 -0
  322. package/src/modules/business-partner/query/getPartnerBankAccount.test.ts +31 -0
  323. package/src/modules/business-partner/query/getPartnerBankAccount.ts +16 -0
  324. package/src/modules/business-partner/query/getPartnerIdentification.generated.ts +5 -0
  325. package/src/modules/business-partner/query/getPartnerIdentification.test.ts +31 -0
  326. package/src/modules/business-partner/query/getPartnerIdentification.ts +16 -0
  327. package/src/modules/business-partner/query/getPartnerRole.generated.ts +5 -0
  328. package/src/modules/business-partner/query/getPartnerRole.test.ts +31 -0
  329. package/src/modules/business-partner/query/getPartnerRole.ts +19 -0
  330. package/src/modules/business-partner/query/getPrimaryContactPerson.generated.ts +5 -0
  331. package/src/modules/business-partner/query/getPrimaryContactPerson.test.ts +43 -0
  332. package/src/modules/business-partner/query/getPrimaryContactPerson.ts +17 -0
  333. package/src/modules/business-partner/query/listContactPersonsByPartner.generated.ts +5 -0
  334. package/src/modules/business-partner/query/listContactPersonsByPartner.test.ts +77 -0
  335. package/src/modules/business-partner/query/listContactPersonsByPartner.ts +32 -0
  336. package/src/modules/business-partner/query/listPartnerAddressesByPartner.generated.ts +5 -0
  337. package/src/modules/business-partner/query/listPartnerAddressesByPartner.test.ts +71 -0
  338. package/src/modules/business-partner/query/listPartnerAddressesByPartner.ts +37 -0
  339. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.generated.ts +5 -0
  340. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.test.ts +59 -0
  341. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.ts +32 -0
  342. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.generated.ts +5 -0
  343. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.test.ts +72 -0
  344. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.ts +40 -0
  345. package/src/modules/business-partner/query/listPartnersByRole.generated.ts +5 -0
  346. package/src/modules/business-partner/query/listPartnersByRole.test.ts +103 -0
  347. package/src/modules/business-partner/query/listPartnersByRole.ts +47 -0
  348. package/src/modules/business-partner/tailor.config.ts +13 -0
  349. package/src/modules/business-partner/tailor.d.ts +13 -0
  350. package/src/modules/business-partner/testing/fixtures.ts +204 -0
  351. package/src/modules/coa-management/README.md +61 -0
  352. package/src/modules/coa-management/command/.gitkeep +0 -0
  353. package/src/modules/coa-management/command/activateAccount.generated.ts +6 -0
  354. package/src/modules/coa-management/command/activateAccount.test.ts +125 -0
  355. package/src/modules/coa-management/command/activateAccount.ts +105 -0
  356. package/src/modules/coa-management/command/activateChartOfAccounts.generated.ts +6 -0
  357. package/src/modules/coa-management/command/activateChartOfAccounts.test.ts +113 -0
  358. package/src/modules/coa-management/command/activateChartOfAccounts.ts +104 -0
  359. package/src/modules/coa-management/command/createAccount.generated.ts +6 -0
  360. package/src/modules/coa-management/command/createAccount.test.ts +767 -0
  361. package/src/modules/coa-management/command/createAccount.ts +247 -0
  362. package/src/modules/coa-management/command/createAccountGroup.generated.ts +6 -0
  363. package/src/modules/coa-management/command/createAccountGroup.test.ts +494 -0
  364. package/src/modules/coa-management/command/createAccountGroup.ts +207 -0
  365. package/src/modules/coa-management/command/createChartOfAccounts.generated.ts +6 -0
  366. package/src/modules/coa-management/command/createChartOfAccounts.test.ts +502 -0
  367. package/src/modules/coa-management/command/createChartOfAccounts.ts +267 -0
  368. package/src/modules/coa-management/command/deactivateAccount.generated.ts +6 -0
  369. package/src/modules/coa-management/command/deactivateAccount.test.ts +199 -0
  370. package/src/modules/coa-management/command/deactivateAccount.ts +142 -0
  371. package/src/modules/coa-management/command/deactivateChartOfAccounts.generated.ts +6 -0
  372. package/src/modules/coa-management/command/deactivateChartOfAccounts.test.ts +91 -0
  373. package/src/modules/coa-management/command/deactivateChartOfAccounts.ts +88 -0
  374. package/src/modules/coa-management/command/deleteAccount.generated.ts +6 -0
  375. package/src/modules/coa-management/command/deleteAccount.test.ts +122 -0
  376. package/src/modules/coa-management/command/deleteAccount.ts +103 -0
  377. package/src/modules/coa-management/command/deleteAccountGroup.generated.ts +6 -0
  378. package/src/modules/coa-management/command/deleteAccountGroup.test.ts +120 -0
  379. package/src/modules/coa-management/command/deleteAccountGroup.ts +113 -0
  380. package/src/modules/coa-management/command/deleteChartOfAccounts.generated.ts +6 -0
  381. package/src/modules/coa-management/command/deleteChartOfAccounts.test.ts +154 -0
  382. package/src/modules/coa-management/command/deleteChartOfAccounts.ts +133 -0
  383. package/src/modules/coa-management/command/moveAccountGroup.generated.ts +6 -0
  384. package/src/modules/coa-management/command/moveAccountGroup.test.ts +199 -0
  385. package/src/modules/coa-management/command/moveAccountGroup.ts +145 -0
  386. package/src/modules/coa-management/command/reactivateAccount.generated.ts +6 -0
  387. package/src/modules/coa-management/command/reactivateAccount.test.ts +126 -0
  388. package/src/modules/coa-management/command/reactivateAccount.ts +123 -0
  389. package/src/modules/coa-management/command/updateAccount.generated.ts +6 -0
  390. package/src/modules/coa-management/command/updateAccount.test.ts +669 -0
  391. package/src/modules/coa-management/command/updateAccount.ts +370 -0
  392. package/src/modules/coa-management/command/updateAccountGroup.generated.ts +6 -0
  393. package/src/modules/coa-management/command/updateAccountGroup.test.ts +253 -0
  394. package/src/modules/coa-management/command/updateAccountGroup.ts +191 -0
  395. package/src/modules/coa-management/command/updateChartOfAccounts.generated.ts +6 -0
  396. package/src/modules/coa-management/command/updateChartOfAccounts.test.ts +153 -0
  397. package/src/modules/coa-management/command/updateChartOfAccounts.ts +133 -0
  398. package/src/modules/coa-management/db/.gitkeep +0 -0
  399. package/src/modules/coa-management/db/account.ts +119 -0
  400. package/src/modules/coa-management/db/accountGroup.ts +57 -0
  401. package/src/modules/coa-management/db/chartOfAccounts.ts +55 -0
  402. package/src/modules/coa-management/docs/commands/ActivateAccount.md +49 -0
  403. package/src/modules/coa-management/docs/commands/ActivateChartOfAccounts.md +47 -0
  404. package/src/modules/coa-management/docs/commands/CreateAccount.md +94 -0
  405. package/src/modules/coa-management/docs/commands/CreateAccountGroup.md +70 -0
  406. package/src/modules/coa-management/docs/commands/CreateChartOfAccounts.md +72 -0
  407. package/src/modules/coa-management/docs/commands/DeactivateAccount.md +65 -0
  408. package/src/modules/coa-management/docs/commands/DeactivateChartOfAccounts.md +44 -0
  409. package/src/modules/coa-management/docs/commands/DeleteAccount.md +52 -0
  410. package/src/modules/coa-management/docs/commands/DeleteAccountGroup.md +50 -0
  411. package/src/modules/coa-management/docs/commands/DeleteChartOfAccounts.md +48 -0
  412. package/src/modules/coa-management/docs/commands/MoveAccountGroup.md +57 -0
  413. package/src/modules/coa-management/docs/commands/ReactivateAccount.md +50 -0
  414. package/src/modules/coa-management/docs/commands/UpdateAccount.md +102 -0
  415. package/src/modules/coa-management/docs/commands/UpdateAccountGroup.md +62 -0
  416. package/src/modules/coa-management/docs/commands/UpdateChartOfAccounts.md +49 -0
  417. package/src/modules/coa-management/docs/features/account-group-hierarchy.md +81 -0
  418. package/src/modules/coa-management/docs/features/account-lifecycle.md +80 -0
  419. package/src/modules/coa-management/docs/features/account-management.md +114 -0
  420. package/src/modules/coa-management/docs/features/chart-of-accounts-setup.md +86 -0
  421. package/src/modules/coa-management/docs/models/Account.md +84 -0
  422. package/src/modules/coa-management/docs/models/AccountGroup.md +55 -0
  423. package/src/modules/coa-management/docs/models/ChartOfAccounts.md +65 -0
  424. package/src/modules/coa-management/docs/queries/DetectCircularReference.md +52 -0
  425. package/src/modules/coa-management/docs/queries/GetAccount.md +42 -0
  426. package/src/modules/coa-management/docs/queries/GetAccountGroup.md +42 -0
  427. package/src/modules/coa-management/docs/queries/GetChartOfAccounts.md +48 -0
  428. package/src/modules/coa-management/docs/queries/ListAccountGroups.md +42 -0
  429. package/src/modules/coa-management/docs/queries/ListAccounts.md +54 -0
  430. package/src/modules/coa-management/docs/queries/ListUnassignedAccounts.md +40 -0
  431. package/src/modules/coa-management/executor/.gitkeep +0 -0
  432. package/src/modules/coa-management/generated/.gitkeep +0 -0
  433. package/src/modules/coa-management/generated/enums.ts +45 -0
  434. package/src/modules/coa-management/generated/kysely-tailordb.ts +81 -0
  435. package/src/modules/coa-management/index.ts +2 -0
  436. package/src/modules/coa-management/lib/_db_deps.ts +17 -0
  437. package/src/modules/coa-management/lib/errors.generated.ts +162 -0
  438. package/src/modules/coa-management/lib/permissions.generated.ts +20 -0
  439. package/src/modules/coa-management/lib/types.ts +27 -0
  440. package/src/modules/coa-management/module.ts +136 -0
  441. package/src/modules/coa-management/permissions.ts +3 -0
  442. package/src/modules/coa-management/query/.gitkeep +0 -0
  443. package/src/modules/coa-management/query/detectCircularReference.generated.ts +5 -0
  444. package/src/modules/coa-management/query/detectCircularReference.test.ts +88 -0
  445. package/src/modules/coa-management/query/detectCircularReference.ts +46 -0
  446. package/src/modules/coa-management/query/getAccount.generated.ts +5 -0
  447. package/src/modules/coa-management/query/getAccount.test.ts +55 -0
  448. package/src/modules/coa-management/query/getAccount.ts +25 -0
  449. package/src/modules/coa-management/query/getAccountGroup.generated.ts +5 -0
  450. package/src/modules/coa-management/query/getAccountGroup.test.ts +55 -0
  451. package/src/modules/coa-management/query/getAccountGroup.ts +25 -0
  452. package/src/modules/coa-management/query/getChartOfAccounts.generated.ts +5 -0
  453. package/src/modules/coa-management/query/getChartOfAccounts.test.ts +79 -0
  454. package/src/modules/coa-management/query/getChartOfAccounts.ts +28 -0
  455. package/src/modules/coa-management/query/listAccountGroups.generated.ts +5 -0
  456. package/src/modules/coa-management/query/listAccountGroups.test.ts +72 -0
  457. package/src/modules/coa-management/query/listAccountGroups.ts +49 -0
  458. package/src/modules/coa-management/query/listAccounts.generated.ts +5 -0
  459. package/src/modules/coa-management/query/listAccounts.test.ts +136 -0
  460. package/src/modules/coa-management/query/listAccounts.ts +82 -0
  461. package/src/modules/coa-management/query/listUnassignedAccounts.generated.ts +5 -0
  462. package/src/modules/coa-management/query/listUnassignedAccounts.test.ts +96 -0
  463. package/src/modules/coa-management/query/listUnassignedAccounts.ts +39 -0
  464. package/src/modules/coa-management/tailor.config.ts +13 -0
  465. package/src/modules/coa-management/tailor.d.ts +13 -0
  466. package/src/modules/coa-management/testing/fixtures.ts +201 -0
  467. package/src/modules/item-management/README.md +1 -1
  468. package/src/modules/item-management/command/activateItem.generated.ts +1 -1
  469. package/src/modules/item-management/command/activateItem.ts +1 -1
  470. package/src/modules/item-management/command/assignItemToTaxonomy.generated.ts +1 -1
  471. package/src/modules/item-management/command/assignItemToTaxonomy.ts +1 -1
  472. package/src/modules/item-management/command/createItem.generated.ts +1 -1
  473. package/src/modules/item-management/command/createItem.test.ts +1 -1
  474. package/src/modules/item-management/command/createItem.ts +1 -1
  475. package/src/modules/item-management/command/createTaxonomyNode.generated.ts +1 -1
  476. package/src/modules/item-management/command/createTaxonomyNode.test.ts +1 -1
  477. package/src/modules/item-management/command/createTaxonomyNode.ts +1 -1
  478. package/src/modules/item-management/command/deactivateItem.generated.ts +1 -1
  479. package/src/modules/item-management/command/deactivateItem.ts +1 -1
  480. package/src/modules/item-management/command/deleteItem.generated.ts +1 -1
  481. package/src/modules/item-management/command/deleteItem.ts +1 -1
  482. package/src/modules/item-management/command/deleteTaxonomyNode.generated.ts +1 -1
  483. package/src/modules/item-management/command/deleteTaxonomyNode.ts +1 -1
  484. package/src/modules/item-management/command/moveTaxonomyNode.generated.ts +1 -1
  485. package/src/modules/item-management/command/moveTaxonomyNode.test.ts +1 -1
  486. package/src/modules/item-management/command/moveTaxonomyNode.ts +1 -1
  487. package/src/modules/item-management/command/reactivateItem.generated.ts +1 -1
  488. package/src/modules/item-management/command/reactivateItem.ts +1 -1
  489. package/src/modules/item-management/command/removeItemFromTaxonomy.generated.ts +1 -1
  490. package/src/modules/item-management/command/removeItemFromTaxonomy.ts +1 -1
  491. package/src/modules/item-management/command/updateItem.generated.ts +1 -1
  492. package/src/modules/item-management/command/updateItem.test.ts +1 -1
  493. package/src/modules/item-management/command/updateItem.ts +1 -1
  494. package/src/modules/item-management/command/updateTaxonomyNode.generated.ts +1 -1
  495. package/src/modules/item-management/command/updateTaxonomyNode.ts +1 -1
  496. package/src/modules/item-management/lib/errors.generated.ts +1 -1
  497. package/src/modules/item-management/lib/permissions.generated.ts +1 -1
  498. package/src/modules/item-management/lib/types.ts +6 -1
  499. package/src/modules/item-management/module.ts +1 -1
  500. package/src/modules/item-management/query/calculateNodeDepth.generated.ts +1 -1
  501. package/src/modules/item-management/query/calculateNodeDepth.ts +1 -1
  502. package/src/modules/item-management/query/calculateSubtreeDepth.generated.ts +1 -1
  503. package/src/modules/item-management/query/calculateSubtreeDepth.ts +1 -1
  504. package/src/modules/item-management/query/detectCircularReference.generated.ts +1 -1
  505. package/src/modules/item-management/query/detectCircularReference.ts +1 -1
  506. package/src/modules/item-management/query/getItem.generated.ts +1 -1
  507. package/src/modules/item-management/query/getItem.ts +1 -1
  508. package/src/modules/item-management/query/getItemTaxonomyAssignment.generated.ts +1 -1
  509. package/src/modules/item-management/query/getItemTaxonomyAssignment.ts +1 -1
  510. package/src/modules/item-management/query/getTaxonomyNode.generated.ts +1 -1
  511. package/src/modules/item-management/query/getTaxonomyNode.ts +1 -1
  512. package/src/modules/item-management/query/getTaxonomyNodeAssignments.generated.ts +1 -1
  513. package/src/modules/item-management/query/getTaxonomyNodeAssignments.ts +1 -1
  514. package/src/modules/item-management/query/getTaxonomyNodeChildren.generated.ts +1 -1
  515. package/src/modules/item-management/query/getTaxonomyNodeChildren.ts +1 -1
  516. package/src/modules/organization/README.md +57 -0
  517. package/src/modules/organization/command/.gitkeep +0 -0
  518. package/src/modules/organization/command/activateCompany.generated.ts +6 -0
  519. package/src/modules/organization/command/activateCompany.test.ts +184 -0
  520. package/src/modules/organization/command/activateCompany.ts +92 -0
  521. package/src/modules/organization/command/createCompany.generated.ts +6 -0
  522. package/src/modules/organization/command/createCompany.test.ts +156 -0
  523. package/src/modules/organization/command/createCompany.ts +80 -0
  524. package/src/modules/organization/command/createDepartment.generated.ts +6 -0
  525. package/src/modules/organization/command/createDepartment.test.ts +239 -0
  526. package/src/modules/organization/command/createDepartment.ts +98 -0
  527. package/src/modules/organization/command/createSite.generated.ts +6 -0
  528. package/src/modules/organization/command/createSite.test.ts +262 -0
  529. package/src/modules/organization/command/createSite.ts +155 -0
  530. package/src/modules/organization/command/deactivateCompany.generated.ts +6 -0
  531. package/src/modules/organization/command/deactivateCompany.test.ts +58 -0
  532. package/src/modules/organization/command/deactivateCompany.ts +47 -0
  533. package/src/modules/organization/command/deactivateDepartment.generated.ts +6 -0
  534. package/src/modules/organization/command/deactivateDepartment.test.ts +115 -0
  535. package/src/modules/organization/command/deactivateDepartment.ts +63 -0
  536. package/src/modules/organization/command/deactivateSite.generated.ts +6 -0
  537. package/src/modules/organization/command/deactivateSite.test.ts +53 -0
  538. package/src/modules/organization/command/deactivateSite.ts +47 -0
  539. package/src/modules/organization/command/deleteCompany.generated.ts +6 -0
  540. package/src/modules/organization/command/deleteCompany.test.ts +99 -0
  541. package/src/modules/organization/command/deleteCompany.ts +66 -0
  542. package/src/modules/organization/command/reactivateCompany.generated.ts +6 -0
  543. package/src/modules/organization/command/reactivateCompany.test.ts +58 -0
  544. package/src/modules/organization/command/reactivateCompany.ts +47 -0
  545. package/src/modules/organization/command/reactivateDepartment.generated.ts +6 -0
  546. package/src/modules/organization/command/reactivateDepartment.test.ts +59 -0
  547. package/src/modules/organization/command/reactivateDepartment.ts +47 -0
  548. package/src/modules/organization/command/reactivateSite.generated.ts +6 -0
  549. package/src/modules/organization/command/reactivateSite.test.ts +53 -0
  550. package/src/modules/organization/command/reactivateSite.ts +47 -0
  551. package/src/modules/organization/command/updateCompany.generated.ts +6 -0
  552. package/src/modules/organization/command/updateCompany.test.ts +239 -0
  553. package/src/modules/organization/command/updateCompany.ts +127 -0
  554. package/src/modules/organization/command/updateDepartment.generated.ts +6 -0
  555. package/src/modules/organization/command/updateDepartment.test.ts +232 -0
  556. package/src/modules/organization/command/updateDepartment.ts +120 -0
  557. package/src/modules/organization/command/updateSite.generated.ts +6 -0
  558. package/src/modules/organization/command/updateSite.test.ts +274 -0
  559. package/src/modules/organization/command/updateSite.ts +176 -0
  560. package/src/modules/organization/db/.gitkeep +0 -0
  561. package/src/modules/organization/db/company.ts +44 -0
  562. package/src/modules/organization/db/department.ts +46 -0
  563. package/src/modules/organization/db/site.ts +44 -0
  564. package/src/modules/organization/docs/commands/ActivateCompany.md +62 -0
  565. package/src/modules/organization/docs/commands/CreateCompany.md +49 -0
  566. package/src/modules/organization/docs/commands/CreateDepartment.md +62 -0
  567. package/src/modules/organization/docs/commands/CreateSite.md +74 -0
  568. package/src/modules/organization/docs/commands/DeactivateCompany.md +40 -0
  569. package/src/modules/organization/docs/commands/DeactivateDepartment.md +44 -0
  570. package/src/modules/organization/docs/commands/DeactivateSite.md +38 -0
  571. package/src/modules/organization/docs/commands/DeleteCompany.md +50 -0
  572. package/src/modules/organization/docs/commands/ReactivateCompany.md +39 -0
  573. package/src/modules/organization/docs/commands/ReactivateDepartment.md +37 -0
  574. package/src/modules/organization/docs/commands/ReactivateSite.md +37 -0
  575. package/src/modules/organization/docs/commands/UpdateCompany.md +58 -0
  576. package/src/modules/organization/docs/commands/UpdateDepartment.md +64 -0
  577. package/src/modules/organization/docs/commands/UpdateSite.md +80 -0
  578. package/src/modules/organization/docs/features/company-lifecycle.md +76 -0
  579. package/src/modules/organization/docs/features/department-management.md +66 -0
  580. package/src/modules/organization/docs/features/site-management.md +86 -0
  581. package/src/modules/organization/docs/models/Company.md +60 -0
  582. package/src/modules/organization/docs/models/Department.md +57 -0
  583. package/src/modules/organization/docs/models/Site.md +57 -0
  584. package/src/modules/organization/docs/queries/DetectDepartmentCircularReference.md +50 -0
  585. package/src/modules/organization/docs/queries/GetCompany.md +40 -0
  586. package/src/modules/organization/docs/queries/GetDepartment.md +44 -0
  587. package/src/modules/organization/docs/queries/GetDepartmentChildren.md +40 -0
  588. package/src/modules/organization/docs/queries/GetSite.md +37 -0
  589. package/src/modules/organization/docs/queries/ListDepartmentsByCompany.md +54 -0
  590. package/src/modules/organization/docs/queries/ListSitesByCompany.md +54 -0
  591. package/src/modules/organization/executor/.gitkeep +0 -0
  592. package/src/modules/organization/generated/.gitkeep +0 -0
  593. package/src/modules/organization/generated/kysely-tailordb.ts +77 -0
  594. package/src/modules/organization/index.ts +2 -0
  595. package/src/modules/organization/lib/_db_deps.ts +10 -0
  596. package/src/modules/organization/lib/errors.generated.ts +117 -0
  597. package/src/modules/organization/lib/permissions.generated.ts +19 -0
  598. package/src/modules/organization/lib/types.ts +21 -0
  599. package/src/modules/organization/module.ts +89 -0
  600. package/src/modules/organization/permissions.ts +3 -0
  601. package/src/modules/organization/query/.gitkeep +0 -0
  602. package/src/modules/organization/query/detectDepartmentCircularReference.generated.ts +5 -0
  603. package/src/modules/organization/query/detectDepartmentCircularReference.test.ts +102 -0
  604. package/src/modules/organization/query/detectDepartmentCircularReference.ts +27 -0
  605. package/src/modules/organization/query/getCompany.generated.ts +5 -0
  606. package/src/modules/organization/query/getCompany.test.ts +70 -0
  607. package/src/modules/organization/query/getCompany.ts +16 -0
  608. package/src/modules/organization/query/getDepartment.generated.ts +5 -0
  609. package/src/modules/organization/query/getDepartment.test.ts +85 -0
  610. package/src/modules/organization/query/getDepartment.ts +17 -0
  611. package/src/modules/organization/query/getDepartmentChildren.generated.ts +5 -0
  612. package/src/modules/organization/query/getDepartmentChildren.test.ts +75 -0
  613. package/src/modules/organization/query/getDepartmentChildren.ts +21 -0
  614. package/src/modules/organization/query/getSite.generated.ts +5 -0
  615. package/src/modules/organization/query/getSite.test.ts +55 -0
  616. package/src/modules/organization/query/getSite.ts +16 -0
  617. package/src/modules/organization/query/listDepartmentsByCompany.generated.ts +5 -0
  618. package/src/modules/organization/query/listDepartmentsByCompany.test.ts +124 -0
  619. package/src/modules/organization/query/listDepartmentsByCompany.ts +43 -0
  620. package/src/modules/organization/query/listSitesByCompany.generated.ts +5 -0
  621. package/src/modules/organization/query/listSitesByCompany.test.ts +126 -0
  622. package/src/modules/organization/query/listSitesByCompany.ts +41 -0
  623. package/src/modules/organization/tailor.config.ts +13 -0
  624. package/src/modules/organization/tailor.d.ts +13 -0
  625. package/src/modules/organization/testing/fixtures.ts +155 -0
  626. package/src/modules/primitives/README.md +1 -1
  627. package/src/modules/primitives/command/activateCategory.generated.ts +1 -1
  628. package/src/modules/primitives/command/activateCategory.ts +1 -1
  629. package/src/modules/primitives/command/activateCurrency.generated.ts +1 -1
  630. package/src/modules/primitives/command/activateCurrency.ts +1 -1
  631. package/src/modules/primitives/command/activateUnit.generated.ts +1 -1
  632. package/src/modules/primitives/command/activateUnit.ts +1 -1
  633. package/src/modules/primitives/command/createCategory.generated.ts +1 -1
  634. package/src/modules/primitives/command/createCategory.ts +1 -1
  635. package/src/modules/primitives/command/createCurrency.generated.ts +1 -1
  636. package/src/modules/primitives/command/createCurrency.ts +1 -1
  637. package/src/modules/primitives/command/createExchangeRate.generated.ts +1 -1
  638. package/src/modules/primitives/command/createExchangeRate.ts +1 -1
  639. package/src/modules/primitives/command/createUnit.generated.ts +1 -1
  640. package/src/modules/primitives/command/createUnit.ts +1 -1
  641. package/src/modules/primitives/command/deactivateCategory.generated.ts +1 -1
  642. package/src/modules/primitives/command/deactivateCategory.ts +1 -1
  643. package/src/modules/primitives/command/deactivateCurrency.generated.ts +1 -1
  644. package/src/modules/primitives/command/deactivateCurrency.ts +1 -1
  645. package/src/modules/primitives/command/deactivateUnit.generated.ts +1 -1
  646. package/src/modules/primitives/command/deactivateUnit.ts +1 -1
  647. package/src/modules/primitives/command/setBaseCurrency.generated.ts +1 -1
  648. package/src/modules/primitives/command/setBaseCurrency.test.ts +8 -64
  649. package/src/modules/primitives/command/setBaseCurrency.ts +6 -64
  650. package/src/modules/primitives/command/setReferenceUnit.generated.ts +1 -1
  651. package/src/modules/primitives/command/setReferenceUnit.ts +1 -1
  652. package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -1
  653. package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -1
  654. package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -1
  655. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +2 -2
  656. package/src/modules/primitives/docs/commands/CreateUnit.md +1 -1
  657. package/src/modules/primitives/docs/commands/DeactivateCategory.md +1 -1
  658. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +1 -1
  659. package/src/modules/primitives/docs/commands/DeactivateUnit.md +1 -1
  660. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +13 -23
  661. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +1 -1
  662. package/src/modules/primitives/docs/features/currency-definitions.md +13 -14
  663. package/src/modules/primitives/docs/models/Currency.md +3 -4
  664. package/src/modules/primitives/docs/queries/ConvertAmount.md +2 -2
  665. package/src/modules/primitives/docs/queries/ConvertQuantity.md +2 -2
  666. package/src/modules/primitives/lib/errors.generated.ts +6 -1
  667. package/src/modules/primitives/lib/permissions.generated.ts +1 -1
  668. package/src/modules/primitives/lib/types.ts +6 -1
  669. package/src/modules/primitives/module.ts +1 -1
  670. package/src/modules/primitives/query/convertAmount.generated.ts +1 -1
  671. package/src/modules/primitives/query/convertAmount.test.ts +1 -1
  672. package/src/modules/primitives/query/convertAmount.ts +1 -1
  673. package/src/modules/primitives/query/convertQuantity.generated.ts +1 -1
  674. package/src/modules/primitives/query/convertQuantity.test.ts +1 -1
  675. package/src/modules/primitives/query/convertQuantity.ts +1 -1
  676. package/src/modules/primitives/query/getBaseCurrency.generated.ts +1 -1
  677. package/src/modules/primitives/query/getBaseCurrency.test.ts +1 -1
  678. package/src/modules/primitives/query/getBaseCurrency.ts +1 -1
  679. package/src/modules/primitives/query/getCurrency.generated.ts +1 -1
  680. package/src/modules/primitives/query/getCurrency.ts +1 -1
  681. package/src/modules/primitives/query/getUnit.generated.ts +1 -1
  682. package/src/modules/primitives/query/getUnit.ts +1 -1
  683. package/src/modules/primitives/query/getUoMCategory.generated.ts +1 -1
  684. package/src/modules/primitives/query/getUoMCategory.ts +1 -1
  685. package/src/modules/primitives/query/listUnitsByCategory.generated.ts +1 -1
  686. package/src/modules/primitives/query/listUnitsByCategory.ts +2 -2
  687. package/src/modules/product-management/README.md +1 -1
  688. package/src/modules/product-management/command/activateProduct.generated.ts +1 -1
  689. package/src/modules/product-management/command/activateProduct.ts +1 -1
  690. package/src/modules/product-management/command/assignProductToCategory.generated.ts +1 -1
  691. package/src/modules/product-management/command/assignProductToCategory.ts +1 -1
  692. package/src/modules/product-management/command/createProduct.generated.ts +1 -1
  693. package/src/modules/product-management/command/createProduct.test.ts +1 -1
  694. package/src/modules/product-management/command/createProduct.ts +1 -1
  695. package/src/modules/product-management/command/createProductAttribute.generated.ts +1 -1
  696. package/src/modules/product-management/command/createProductAttribute.ts +1 -1
  697. package/src/modules/product-management/command/createProductAttributeValue.generated.ts +1 -1
  698. package/src/modules/product-management/command/createProductAttributeValue.ts +1 -1
  699. package/src/modules/product-management/command/createProductCategory.generated.ts +1 -1
  700. package/src/modules/product-management/command/createProductCategory.test.ts +1 -1
  701. package/src/modules/product-management/command/createProductCategory.ts +1 -1
  702. package/src/modules/product-management/command/deactivateProduct.generated.ts +1 -1
  703. package/src/modules/product-management/command/deactivateProduct.ts +1 -1
  704. package/src/modules/product-management/command/deleteProduct.generated.ts +1 -1
  705. package/src/modules/product-management/command/deleteProduct.ts +1 -1
  706. package/src/modules/product-management/command/deleteProductAttribute.generated.ts +1 -1
  707. package/src/modules/product-management/command/deleteProductAttribute.ts +1 -1
  708. package/src/modules/product-management/command/deleteProductAttributeValue.generated.ts +1 -1
  709. package/src/modules/product-management/command/deleteProductAttributeValue.ts +1 -1
  710. package/src/modules/product-management/command/deleteProductCategory.generated.ts +1 -1
  711. package/src/modules/product-management/command/deleteProductCategory.ts +1 -1
  712. package/src/modules/product-management/command/generateVariants.generated.ts +1 -1
  713. package/src/modules/product-management/command/generateVariants.test.ts +1 -1
  714. package/src/modules/product-management/command/generateVariants.ts +1 -1
  715. package/src/modules/product-management/command/moveProductCategory.generated.ts +1 -1
  716. package/src/modules/product-management/command/moveProductCategory.test.ts +1 -1
  717. package/src/modules/product-management/command/moveProductCategory.ts +1 -1
  718. package/src/modules/product-management/command/reactivateProduct.generated.ts +1 -1
  719. package/src/modules/product-management/command/reactivateProduct.ts +1 -1
  720. package/src/modules/product-management/command/removeProductFromCategory.generated.ts +1 -1
  721. package/src/modules/product-management/command/removeProductFromCategory.ts +1 -1
  722. package/src/modules/product-management/command/setProductAttributeAssignment.generated.ts +1 -1
  723. package/src/modules/product-management/command/setProductAttributeAssignment.ts +1 -1
  724. package/src/modules/product-management/command/updateProduct.generated.ts +1 -1
  725. package/src/modules/product-management/command/updateProduct.test.ts +1 -1
  726. package/src/modules/product-management/command/updateProduct.ts +1 -1
  727. package/src/modules/product-management/command/updateProductAttribute.generated.ts +1 -1
  728. package/src/modules/product-management/command/updateProductAttribute.ts +1 -1
  729. package/src/modules/product-management/command/updateProductAttributeValue.generated.ts +1 -1
  730. package/src/modules/product-management/command/updateProductAttributeValue.ts +1 -1
  731. package/src/modules/product-management/command/updateProductCategory.generated.ts +1 -1
  732. package/src/modules/product-management/command/updateProductCategory.ts +1 -1
  733. package/src/modules/product-management/lib/errors.generated.ts +1 -1
  734. package/src/modules/product-management/lib/permissions.generated.ts +1 -1
  735. package/src/modules/product-management/lib/types.ts +6 -1
  736. package/src/modules/product-management/module.ts +1 -1
  737. package/src/modules/product-management/query/calculateCategoryDepth.generated.ts +1 -1
  738. package/src/modules/product-management/query/calculateCategoryDepth.ts +1 -1
  739. package/src/modules/product-management/query/detectCategoryCircularReference.generated.ts +1 -1
  740. package/src/modules/product-management/query/detectCategoryCircularReference.ts +1 -1
  741. package/src/modules/product-management/query/getProduct.generated.ts +1 -1
  742. package/src/modules/product-management/query/getProduct.ts +1 -1
  743. package/src/modules/product-management/query/getProductAttribute.generated.ts +1 -1
  744. package/src/modules/product-management/query/getProductAttribute.ts +1 -1
  745. package/src/modules/product-management/query/getProductAttributeAssignment.generated.ts +1 -1
  746. package/src/modules/product-management/query/getProductAttributeAssignment.ts +1 -1
  747. package/src/modules/product-management/query/getProductAttributeValue.generated.ts +1 -1
  748. package/src/modules/product-management/query/getProductAttributeValue.ts +1 -1
  749. package/src/modules/product-management/query/getProductCategory.generated.ts +1 -1
  750. package/src/modules/product-management/query/getProductCategory.ts +1 -1
  751. package/src/modules/product-management/query/getProductCategoryAssignment.generated.ts +1 -1
  752. package/src/modules/product-management/query/getProductCategoryAssignment.ts +1 -1
  753. package/src/modules/product-management/query/getProductVariant.generated.ts +1 -1
  754. package/src/modules/product-management/query/getProductVariant.ts +1 -1
  755. package/src/modules/product-management/query/listAttributeAssignmentsByAttribute.generated.ts +1 -1
  756. package/src/modules/product-management/query/listAttributeAssignmentsByAttribute.ts +1 -1
  757. package/src/modules/product-management/query/listCategoryAssignmentsByProduct.generated.ts +1 -1
  758. package/src/modules/product-management/query/listCategoryAssignmentsByProduct.ts +1 -1
  759. package/src/modules/product-management/query/listProductAttributeAssignments.generated.ts +1 -1
  760. package/src/modules/product-management/query/listProductAttributeAssignments.ts +1 -1
  761. package/src/modules/product-management/query/listProductAttributeValues.generated.ts +1 -1
  762. package/src/modules/product-management/query/listProductAttributeValues.ts +1 -1
  763. package/src/modules/product-management/query/listProductCategoryAssignments.generated.ts +1 -1
  764. package/src/modules/product-management/query/listProductCategoryAssignments.ts +1 -1
  765. package/src/modules/product-management/query/listProductCategoryChildren.generated.ts +1 -1
  766. package/src/modules/product-management/query/listProductCategoryChildren.ts +1 -1
  767. package/src/modules/product-management/query/listProductVariants.generated.ts +1 -1
  768. package/src/modules/product-management/query/listProductVariants.ts +1 -1
  769. package/src/modules/user-management/command/activateUser.generated.ts +1 -1
  770. package/src/modules/user-management/command/activateUser.ts +1 -1
  771. package/src/modules/user-management/command/assignPermissionToRole.generated.ts +1 -1
  772. package/src/modules/user-management/command/assignPermissionToRole.ts +1 -1
  773. package/src/modules/user-management/command/assignRoleToUser.generated.ts +1 -1
  774. package/src/modules/user-management/command/assignRoleToUser.ts +1 -1
  775. package/src/modules/user-management/command/createPermission.generated.ts +1 -1
  776. package/src/modules/user-management/command/createPermission.ts +1 -1
  777. package/src/modules/user-management/command/createRole.generated.ts +1 -1
  778. package/src/modules/user-management/command/createRole.ts +1 -1
  779. package/src/modules/user-management/command/createUser.generated.ts +1 -1
  780. package/src/modules/user-management/command/createUser.ts +1 -1
  781. package/src/modules/user-management/command/deactivateUser.generated.ts +1 -1
  782. package/src/modules/user-management/command/deactivateUser.ts +1 -1
  783. package/src/modules/user-management/command/reactivateUser.generated.ts +1 -1
  784. package/src/modules/user-management/command/reactivateUser.ts +1 -1
  785. package/src/modules/user-management/command/revokePermissionFromRole.generated.ts +1 -1
  786. package/src/modules/user-management/command/revokePermissionFromRole.ts +1 -1
  787. package/src/modules/user-management/command/revokeRoleFromUser.generated.ts +1 -1
  788. package/src/modules/user-management/command/revokeRoleFromUser.ts +1 -1
  789. package/src/modules/user-management/docs/commands/CreatePermission.md +3 -3
  790. package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
  791. package/src/modules/user-management/docs/queries/ListRolePermissionsByRole.md +39 -0
  792. package/src/modules/user-management/docs/queries/ListUserRolesByUser.md +39 -0
  793. package/src/modules/user-management/generated/enums.ts +0 -15
  794. package/src/modules/user-management/generated/kysely-tailordb.ts +0 -11
  795. package/src/modules/user-management/lib/errors.generated.ts +1 -1
  796. package/src/modules/user-management/lib/permissions.generated.ts +1 -1
  797. package/src/modules/user-management/lib/types.ts +6 -1
  798. package/src/modules/user-management/module.ts +1 -1
  799. package/src/shared/createContext.ts +2 -1
  800. package/src/shared/defineQuery.ts +36 -1
  801. package/src/shared/requirePermission.ts +3 -3
  802. package/src/shared/types.ts +3 -0
  803. package/templates/scaffold/app/backend/package.json +4 -3
  804. package/templates/scaffold/app/frontend/eslint.config.js +12 -0
  805. package/templates/scaffold/app/frontend/package.json +10 -7
  806. package/templates/scaffold/app/frontend/src/hooks/use-toast.ts +30 -0
  807. package/templates/scaffold/app/frontend/src/pages/user-management/user/create/components/create-user-form.tsx +3 -2
  808. package/templates/scaffold/app/frontend/vite.config.ts +5 -5
  809. package/templates/scaffold/module/lib/types.ts +6 -1
  810. package/templates/scaffold/module/permissions.ts +1 -1
  811. package/src/commands/module/list.test.ts +0 -57
  812. package/src/commands/module/list.ts +0 -64
  813. package/templates/scaffold/module/lib/errors.ts +0 -1
  814. /package/src/modules/{accounting → audit/db}/.gitkeep +0 -0
  815. /package/src/modules/audit/{.gitkeep → executor/.gitkeep} +0 -0
  816. /package/src/modules/{coa-management → business-partner}/.gitkeep +0 -0
  817. /package/src/modules/{supplier-management → business-partner/command}/.gitkeep +0 -0
@@ -0,0 +1,991 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createMockDb } from "../../../testing/index";
3
+ import type { Transaction } from "../generated/kysely-tailordb";
4
+ import {
5
+ EntityTypeNotRegisteredError,
6
+ InvalidActorError,
7
+ InvalidOperationTypeError,
8
+ ScopeMismatchError,
9
+ MissingEntityIdError,
10
+ MissingEventIdError,
11
+ ValidationErrorError,
12
+ } from "../lib/errors.generated";
13
+ import {
14
+ companyBoundEntity,
15
+ globalEntity,
16
+ activePolicy,
17
+ captureFieldRule,
18
+ maskFieldRule,
19
+ hashFieldRule,
20
+ excludeFieldRule,
21
+ baseAuditEntry,
22
+ } from "../testing/fixtures";
23
+ import { run, type LogAuditEventInput } from "./logAuditEvent";
24
+ import { logAuditEvent } from "./logAuditEvent.generated";
25
+ import { type CommandContext, InsufficientPermissionError } from "@tailor-platform/erp-kit/module";
26
+
27
+ const ctx: CommandContext = {
28
+ actorId: "test-actor",
29
+ permissions: ["audit:logAuditEvent"],
30
+ };
31
+
32
+ function baseInput(overrides?: Partial<LogAuditEventInput>): LogAuditEventInput {
33
+ return {
34
+ eventId: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11",
35
+ actorType: "USER",
36
+ actorId: "user-1",
37
+ entityType: "SalesOrder",
38
+ entityId: "so-1",
39
+ operationType: "UPDATE",
40
+ companyId: "company-1",
41
+ changes: [{ fieldName: "amount", oldValue: "100.00", newValue: "95.00" }],
42
+ ...overrides,
43
+ };
44
+ }
45
+
46
+ function setupHappyPath(spies: ReturnType<typeof createMockDb<Transaction>>["spies"]) {
47
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
48
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup
49
+ spies.select.mockReturnValueOnce(activePolicy); // active policy lookup
50
+ spies.select.mockReturnValueOnce([captureFieldRule]); // field rules (execute returns array)
51
+ spies.insert.mockReturnValue(baseAuditEntry); // audit entry insert
52
+ }
53
+
54
+ describe("logAuditEvent", () => {
55
+ it("creates AuditEntry with all required fields for a CREATE operation", async () => {
56
+ const { db, spies } = createMockDb<Transaction>();
57
+ const createPolicy = { ...activePolicy, operationType: "CREATE" };
58
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
59
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup
60
+ spies.select.mockReturnValueOnce(createPolicy); // policy
61
+ spies.select.mockReturnValueOnce([captureFieldRule]); // field rules
62
+ const createEntry = { ...baseAuditEntry, operationType: "CREATE" };
63
+ spies.insert.mockReturnValue(createEntry);
64
+
65
+ const result = await run(
66
+ db,
67
+ baseInput({
68
+ operationType: "CREATE",
69
+ changes: [{ fieldName: "amount", newValue: "100.00" }],
70
+ }),
71
+ ctx,
72
+ );
73
+
74
+ expect(result.ok).toBe(true);
75
+ if (result.ok) {
76
+ expect(result.value.created).toBe(true);
77
+ expect(result.value.auditEntry).toBeDefined();
78
+ }
79
+ });
80
+
81
+ it("creates AuditEntry with all required fields for an UPDATE operation", async () => {
82
+ const { db, spies } = createMockDb<Transaction>();
83
+ setupHappyPath(spies);
84
+
85
+ const result = await run(db, baseInput(), ctx);
86
+
87
+ expect(result.ok).toBe(true);
88
+ if (result.ok) {
89
+ expect(result.value.created).toBe(true);
90
+ expect(result.value.auditEntry).toBeDefined();
91
+ }
92
+ });
93
+
94
+ it("creates AuditEntry with all required fields for a DELETE operation", async () => {
95
+ const { db, spies } = createMockDb<Transaction>();
96
+ const deletePolicy = { ...activePolicy, operationType: "DELETE" };
97
+ spies.select.mockReturnValueOnce(undefined);
98
+ spies.select.mockReturnValueOnce(companyBoundEntity);
99
+ spies.select.mockReturnValueOnce(deletePolicy);
100
+ spies.select.mockReturnValueOnce([captureFieldRule]);
101
+ const deleteEntry = { ...baseAuditEntry, operationType: "DELETE" };
102
+ spies.insert.mockReturnValue(deleteEntry);
103
+
104
+ const result = await run(
105
+ db,
106
+ baseInput({
107
+ operationType: "DELETE",
108
+ changes: [{ fieldName: "amount", oldValue: "100.00" }],
109
+ }),
110
+ ctx,
111
+ );
112
+
113
+ expect(result.ok).toBe(true);
114
+ if (result.ok) {
115
+ expect(result.value.created).toBe(true);
116
+ }
117
+ });
118
+
119
+ it("silently skips AuditEntry creation for UPDATE with empty changes array (zero-delta no-op)", async () => {
120
+ const { db, spies } = createMockDb<Transaction>();
121
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
122
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup
123
+ spies.select.mockReturnValueOnce(activePolicy); // policy
124
+
125
+ const result = await run(db, baseInput({ changes: [] }), ctx);
126
+
127
+ expect(result.ok).toBe(true);
128
+ if (result.ok) {
129
+ expect(result.value.created).toBe(false);
130
+ }
131
+ expect(spies.insert).not.toHaveBeenCalled();
132
+ });
133
+
134
+ it("rejects CREATE with empty changes array", async () => {
135
+ const { db, spies } = createMockDb<Transaction>();
136
+ const createPolicy = { ...activePolicy, operationType: "CREATE" };
137
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
138
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup
139
+ spies.select.mockReturnValueOnce(createPolicy); // policy
140
+
141
+ const result = await run(db, baseInput({ operationType: "CREATE", changes: [] }), ctx);
142
+
143
+ expect(result.ok).toBe(false);
144
+ if (!result.ok) {
145
+ expect(result.error).toBeInstanceOf(ValidationErrorError);
146
+ }
147
+ expect(spies.insert).not.toHaveBeenCalled();
148
+ });
149
+
150
+ it("rejects DELETE with empty changes array", async () => {
151
+ const { db, spies } = createMockDb<Transaction>();
152
+ const deletePolicy = { ...activePolicy, operationType: "DELETE" };
153
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
154
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup
155
+ spies.select.mockReturnValueOnce(deletePolicy); // policy
156
+
157
+ const result = await run(db, baseInput({ operationType: "DELETE", changes: [] }), ctx);
158
+
159
+ expect(result.ok).toBe(false);
160
+ if (!result.ok) {
161
+ expect(result.error).toBeInstanceOf(ValidationErrorError);
162
+ }
163
+ expect(spies.insert).not.toHaveBeenCalled();
164
+ });
165
+
166
+ it("silently discards duplicate eventId without creating a new entry", async () => {
167
+ const { db, spies } = createMockDb<Transaction>();
168
+ spies.select.mockReturnValueOnce(baseAuditEntry); // duplicate found
169
+
170
+ const result = await run(db, baseInput(), ctx);
171
+
172
+ expect(result.ok).toBe(true);
173
+ if (result.ok) {
174
+ expect(result.value.created).toBe(false);
175
+ expect(result.value.auditEntry).toEqual(baseAuditEntry);
176
+ }
177
+ expect(spies.insert).not.toHaveBeenCalled();
178
+ });
179
+
180
+ it("rejects event with unregistered entityType", async () => {
181
+ const { db, spies } = createMockDb<Transaction>();
182
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
183
+ spies.select.mockReturnValueOnce(undefined); // entity not found
184
+
185
+ const result = await run(db, baseInput({ entityType: "UnknownEntity" }), ctx);
186
+
187
+ expect(result.ok).toBe(false);
188
+ if (!result.ok) {
189
+ expect(result.error).toBeInstanceOf(EntityTypeNotRegisteredError);
190
+ }
191
+ });
192
+
193
+ it("rejects event with missing actorType", async () => {
194
+ const { db, spies } = createMockDb<Transaction>();
195
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
196
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity found
197
+
198
+ const result = await run(db, baseInput({ actorType: "" }), ctx);
199
+
200
+ expect(result.ok).toBe(false);
201
+ if (!result.ok) {
202
+ expect(result.error).toBeInstanceOf(InvalidActorError);
203
+ }
204
+ });
205
+
206
+ it("rejects event with invalid actorType", async () => {
207
+ const { db, spies } = createMockDb<Transaction>();
208
+ spies.select.mockReturnValueOnce(undefined);
209
+ spies.select.mockReturnValueOnce(companyBoundEntity);
210
+
211
+ const result = await run(db, baseInput({ actorType: "ROBOT" }), ctx);
212
+
213
+ expect(result.ok).toBe(false);
214
+ if (!result.ok) {
215
+ expect(result.error).toBeInstanceOf(InvalidActorError);
216
+ }
217
+ });
218
+
219
+ it("rejects event with empty actorId", async () => {
220
+ const { db, spies } = createMockDb<Transaction>();
221
+ spies.select.mockReturnValueOnce(undefined);
222
+ spies.select.mockReturnValueOnce(companyBoundEntity);
223
+
224
+ const result = await run(db, baseInput({ actorId: "" }), ctx);
225
+
226
+ expect(result.ok).toBe(false);
227
+ if (!result.ok) {
228
+ expect(result.error).toBeInstanceOf(InvalidActorError);
229
+ }
230
+ });
231
+
232
+ it("rejects event with invalid operationType", async () => {
233
+ const { db, spies } = createMockDb<Transaction>();
234
+ spies.select.mockReturnValueOnce(undefined);
235
+ spies.select.mockReturnValueOnce(companyBoundEntity);
236
+
237
+ const result = await run(db, baseInput({ operationType: "ARCHIVE" }), ctx);
238
+
239
+ expect(result.ok).toBe(false);
240
+ if (!result.ok) {
241
+ expect(result.error).toBeInstanceOf(InvalidOperationTypeError);
242
+ }
243
+ });
244
+
245
+ it("rejects event with companyId on a global entity", async () => {
246
+ const { db, spies } = createMockDb<Transaction>();
247
+ spies.select.mockReturnValueOnce(undefined);
248
+ spies.select.mockReturnValueOnce(globalEntity);
249
+
250
+ const result = await run(db, baseInput({ entityType: "User", companyId: "company-1" }), ctx);
251
+
252
+ expect(result.ok).toBe(false);
253
+ if (!result.ok) {
254
+ expect(result.error).toBeInstanceOf(ScopeMismatchError);
255
+ }
256
+ });
257
+
258
+ it("rejects event with null companyId on a company-bound entity", async () => {
259
+ const { db, spies } = createMockDb<Transaction>();
260
+ spies.select.mockReturnValueOnce(undefined);
261
+ spies.select.mockReturnValueOnce(companyBoundEntity);
262
+
263
+ const result = await run(db, baseInput({ companyId: undefined }), ctx);
264
+
265
+ expect(result.ok).toBe(false);
266
+ if (!result.ok) {
267
+ expect(result.error).toBeInstanceOf(ScopeMismatchError);
268
+ }
269
+ });
270
+
271
+ it("silently discards event when no active policy matches entity + operation + scope", async () => {
272
+ const { db, spies } = createMockDb<Transaction>();
273
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
274
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity found
275
+ spies.select.mockReturnValueOnce(undefined); // no active policy
276
+
277
+ const result = await run(db, baseInput(), ctx);
278
+
279
+ expect(result.ok).toBe(true);
280
+ if (result.ok) {
281
+ expect(result.value.auditEntry).toBeNull();
282
+ expect(result.value.created).toBe(false);
283
+ }
284
+ expect(spies.insert).not.toHaveBeenCalled();
285
+ });
286
+
287
+ it("generates system timestamp and ignores caller-supplied timestamp", async () => {
288
+ const { db, spies } = createMockDb<Transaction>();
289
+ setupHappyPath(spies);
290
+
291
+ const result = await run(db, baseInput(), ctx);
292
+
293
+ expect(result.ok).toBe(true);
294
+ // The values spy captures what was passed to insert; the timestamp should be a Date
295
+ expect(spies.values).toHaveBeenCalled();
296
+ const insertedValues = spies.values.mock.calls[0][0] as Record<string, unknown>;
297
+ expect(insertedValues.timestamp).toBeInstanceOf(Date);
298
+ });
299
+
300
+ it("creates ChangeDetail records linked to the parent AuditEntry", async () => {
301
+ const { db, spies } = createMockDb<Transaction>();
302
+ setupHappyPath(spies);
303
+
304
+ const result = await run(db, baseInput(), ctx);
305
+
306
+ expect(result.ok).toBe(true);
307
+ // First insert call is AuditEntry, second is ChangeDetail
308
+ expect(spies.values).toHaveBeenCalledTimes(2);
309
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
310
+ expect(changeValues.auditEntryId).toBe(baseAuditEntry.id);
311
+ expect(changeValues.fieldName).toBe("amount");
312
+ });
313
+
314
+ it("applies MASK sensitivity mode to field values", async () => {
315
+ const { db, spies } = createMockDb<Transaction>();
316
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
317
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
318
+ spies.select.mockReturnValueOnce(activePolicy); // policy
319
+ spies.select.mockReturnValueOnce([maskFieldRule]); // mask rule for customerId
320
+ spies.insert.mockReturnValue(baseAuditEntry);
321
+
322
+ const result = await run(
323
+ db,
324
+ baseInput({
325
+ changes: [{ fieldName: "customerId", oldValue: "CUST-12345", newValue: "CUST-67890" }],
326
+ }),
327
+ ctx,
328
+ );
329
+
330
+ expect(result.ok).toBe(true);
331
+ // Verify masked values in change detail insert
332
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
333
+ expect(changeValues.oldValue).toBe("******2345");
334
+ expect(changeValues.newValue).toBe("******7890");
335
+ });
336
+
337
+ it("applies HASH sensitivity mode to field values", async () => {
338
+ const { db, spies } = createMockDb<Transaction>();
339
+ spies.select.mockReturnValueOnce(undefined);
340
+ spies.select.mockReturnValueOnce(companyBoundEntity);
341
+ spies.select.mockReturnValueOnce(activePolicy);
342
+ spies.select.mockReturnValueOnce([hashFieldRule]); // hash rule for status
343
+ spies.insert.mockReturnValue(baseAuditEntry);
344
+
345
+ const result = await run(
346
+ db,
347
+ baseInput({ changes: [{ fieldName: "status", oldValue: "OPEN", newValue: "CLOSED" }] }),
348
+ ctx,
349
+ );
350
+
351
+ expect(result.ok).toBe(true);
352
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
353
+ // Hash values should be SHA-256 hex strings (64 chars), not the original
354
+ expect(changeValues.oldValue).not.toBe("OPEN");
355
+ expect(changeValues.newValue).not.toBe("CLOSED");
356
+ expect(typeof changeValues.oldValue).toBe("string");
357
+ expect(typeof changeValues.newValue).toBe("string");
358
+ expect(changeValues.oldValue).toHaveLength(64);
359
+ expect(changeValues.newValue).toHaveLength(64);
360
+ expect(changeValues.oldValue).toMatch(/^[0-9a-f]{64}$/);
361
+ expect(changeValues.newValue).toMatch(/^[0-9a-f]{64}$/);
362
+ });
363
+
364
+ it("applies EXCLUDE sensitivity mode storing null for both oldValue and newValue", async () => {
365
+ const { db, spies } = createMockDb<Transaction>();
366
+ spies.select.mockReturnValueOnce(undefined);
367
+ spies.select.mockReturnValueOnce(companyBoundEntity);
368
+ spies.select.mockReturnValueOnce(activePolicy);
369
+ spies.select.mockReturnValueOnce([excludeFieldRule]); // exclude rule for amount
370
+ spies.insert.mockReturnValue(baseAuditEntry);
371
+
372
+ const result = await run(
373
+ db,
374
+ baseInput({ changes: [{ fieldName: "amount", oldValue: "100.00", newValue: "95.00" }] }),
375
+ ctx,
376
+ );
377
+
378
+ expect(result.ok).toBe(true);
379
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
380
+ expect(changeValues.oldValue).toBeNull();
381
+ expect(changeValues.newValue).toBeNull();
382
+ });
383
+
384
+ it("serializes scalar field values as strings", async () => {
385
+ const { db, spies } = createMockDb<Transaction>();
386
+ setupHappyPath(spies);
387
+
388
+ const result = await run(
389
+ db,
390
+ baseInput({ changes: [{ fieldName: "amount", oldValue: "100", newValue: "200" }] }),
391
+ ctx,
392
+ );
393
+
394
+ expect(result.ok).toBe(true);
395
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
396
+ expect(typeof changeValues.oldValue).toBe("string");
397
+ expect(typeof changeValues.newValue).toBe("string");
398
+ });
399
+
400
+ it("strips rich text / HTML values to plain text before storage", async () => {
401
+ const { db, spies } = createMockDb<Transaction>();
402
+ const richtextEntity = {
403
+ ...companyBoundEntity,
404
+ auditableFields: JSON.stringify([{ fieldName: "amount", fieldType: "richtext" }]),
405
+ };
406
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
407
+ spies.select.mockReturnValueOnce(richtextEntity); // entity lookup
408
+ spies.select.mockReturnValueOnce(activePolicy); // active policy lookup
409
+ spies.select.mockReturnValueOnce([captureFieldRule]); // field rules
410
+ spies.insert.mockReturnValue(baseAuditEntry);
411
+
412
+ const result = await run(
413
+ db,
414
+ baseInput({
415
+ changes: [
416
+ {
417
+ fieldName: "amount",
418
+ oldValue: "<b>100</b>",
419
+ newValue: "<p>200</p>",
420
+ fieldType: "richtext" as const,
421
+ },
422
+ ],
423
+ }),
424
+ ctx,
425
+ );
426
+
427
+ expect(result.ok).toBe(true);
428
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
429
+ expect(changeValues.oldValue).toBe("100");
430
+ expect(changeValues.newValue).toBe("200");
431
+ });
432
+
433
+ it("serializes collection / array field values as JSON strings", async () => {
434
+ const { db, spies } = createMockDb<Transaction>();
435
+ const arrayEntity = {
436
+ ...companyBoundEntity,
437
+ auditableFields: JSON.stringify([{ fieldName: "tags", fieldType: "array" }]),
438
+ };
439
+ const arrayFieldRule = { ...captureFieldRule, fieldName: "tags" };
440
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
441
+ spies.select.mockReturnValueOnce(arrayEntity); // entity lookup with array field type
442
+ spies.select.mockReturnValueOnce(activePolicy); // active policy lookup
443
+ spies.select.mockReturnValueOnce([arrayFieldRule]); // field rules
444
+ spies.insert.mockReturnValue(baseAuditEntry);
445
+
446
+ // Array values passed as actual arrays — normalizeFieldValue should JSON.stringify them
447
+ const result = await run(
448
+ db,
449
+ baseInput({
450
+ changes: [{ fieldName: "tags", oldValue: ["a", "b"], newValue: ["a", "b", "c"] }],
451
+ }),
452
+ ctx,
453
+ );
454
+
455
+ expect(result.ok).toBe(true);
456
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
457
+ expect(changeValues.oldValue).toBe(JSON.stringify(["a", "b"]));
458
+ expect(changeValues.newValue).toBe(JSON.stringify(["a", "b", "c"]));
459
+ });
460
+
461
+ it("captures relation field values as foreign key ID strings", async () => {
462
+ const { db, spies } = createMockDb<Transaction>();
463
+ // customerId is registered as "relation" in fixtures
464
+ const relationPolicy = { ...activePolicy };
465
+ const relationFieldRule = { ...captureFieldRule, fieldName: "customerId" };
466
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
467
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity lookup (customerId is relation)
468
+ spies.select.mockReturnValueOnce(relationPolicy); // active policy lookup
469
+ spies.select.mockReturnValueOnce([relationFieldRule]); // field rules
470
+ spies.insert.mockReturnValue(baseAuditEntry);
471
+
472
+ const result = await run(
473
+ db,
474
+ baseInput({
475
+ changes: [
476
+ {
477
+ fieldName: "customerId",
478
+ oldValue: { id: "fk-id-123", name: "Old Relation" },
479
+ newValue: { id: "fk-id-456", name: "New Relation" },
480
+ fieldType: "relation" as const,
481
+ },
482
+ ],
483
+ }),
484
+ ctx,
485
+ );
486
+
487
+ expect(result.ok).toBe(true);
488
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
489
+ expect(changeValues.oldValue).toBe("fk-id-123");
490
+ expect(changeValues.newValue).toBe("fk-id-456");
491
+ });
492
+
493
+ it("captures file attachment fields as reference metadata only (file ID, filename, size)", async () => {
494
+ const { db, spies } = createMockDb<Transaction>();
495
+ const fileEntity = {
496
+ ...companyBoundEntity,
497
+ auditableFields: JSON.stringify([{ fieldName: "attachment", fieldType: "file" }]),
498
+ };
499
+ const fileFieldRule = { ...captureFieldRule, fieldName: "attachment" };
500
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
501
+ spies.select.mockReturnValueOnce(fileEntity); // entity lookup with file field type
502
+ spies.select.mockReturnValueOnce(activePolicy); // active policy lookup
503
+ spies.select.mockReturnValueOnce([fileFieldRule]); // field rules
504
+ spies.insert.mockReturnValue(baseAuditEntry);
505
+
506
+ // Pass file metadata as an object — normalizeFieldValue should serialize it
507
+ const fileMeta = { fileId: "f-1", filename: "doc.pdf", size: 1024 };
508
+ const result = await run(
509
+ db,
510
+ baseInput({
511
+ changes: [{ fieldName: "attachment", oldValue: null, newValue: fileMeta }],
512
+ }),
513
+ ctx,
514
+ );
515
+
516
+ expect(result.ok).toBe(true);
517
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
518
+ expect(changeValues.newValue).toBe(
519
+ JSON.stringify({ fileId: "f-1", filename: "doc.pdf", size: 1024 }),
520
+ );
521
+ });
522
+
523
+ it("excludes binary / BLOB fields from audit capture", async () => {
524
+ const { db, spies } = createMockDb<Transaction>();
525
+ // Register entity with a binary field so the exclusion happens via effectiveFieldType filtering
526
+ const binaryEntity = {
527
+ ...companyBoundEntity,
528
+ auditableFields: JSON.stringify([
529
+ { fieldName: "amount", fieldType: "scalar" },
530
+ { fieldName: "binaryData", fieldType: "binary" },
531
+ ]),
532
+ };
533
+ const binaryFieldRules = [
534
+ captureFieldRule,
535
+ { ...captureFieldRule, id: "rule-bin", fieldName: "binaryData" },
536
+ ];
537
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
538
+ spies.select.mockReturnValueOnce(binaryEntity); // entity with binary field
539
+ spies.select.mockReturnValueOnce(activePolicy); // active policy
540
+ spies.select.mockReturnValueOnce(binaryFieldRules); // field rules include binary field
541
+ spies.insert.mockReturnValue(baseAuditEntry);
542
+
543
+ const result = await run(
544
+ db,
545
+ baseInput({
546
+ changes: [
547
+ { fieldName: "amount", oldValue: "100", newValue: "200" },
548
+ { fieldName: "binaryData", oldValue: "AQID", newValue: "BAUG" },
549
+ ],
550
+ }),
551
+ ctx,
552
+ );
553
+
554
+ expect(result.ok).toBe(true);
555
+ // Only 2 insert calls: AuditEntry + 1 ChangeDetail (binaryData dropped by fieldType filter)
556
+ expect(spies.values).toHaveBeenCalledTimes(2);
557
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
558
+ expect(changeValues.fieldName).toBe("amount");
559
+ });
560
+
561
+ it("excludes computed / derived fields from audit capture", async () => {
562
+ const { db, spies } = createMockDb<Transaction>();
563
+ // Register entity with a computed field so the exclusion happens via effectiveFieldType filtering
564
+ const computedEntity = {
565
+ ...companyBoundEntity,
566
+ auditableFields: JSON.stringify([
567
+ { fieldName: "amount", fieldType: "scalar" },
568
+ { fieldName: "computedTotal", fieldType: "computed" },
569
+ ]),
570
+ };
571
+ const computedFieldRules = [
572
+ captureFieldRule,
573
+ { ...captureFieldRule, id: "rule-comp", fieldName: "computedTotal" },
574
+ ];
575
+ spies.select.mockReturnValueOnce(undefined); // no duplicate eventId
576
+ spies.select.mockReturnValueOnce(computedEntity); // entity with computed field
577
+ spies.select.mockReturnValueOnce(activePolicy); // active policy
578
+ spies.select.mockReturnValueOnce(computedFieldRules); // field rules include computed field
579
+ spies.insert.mockReturnValue(baseAuditEntry);
580
+
581
+ const result = await run(
582
+ db,
583
+ baseInput({
584
+ changes: [
585
+ { fieldName: "amount", oldValue: "100", newValue: "200" },
586
+ { fieldName: "computedTotal", oldValue: "500", newValue: "600" },
587
+ ],
588
+ }),
589
+ ctx,
590
+ );
591
+
592
+ expect(result.ok).toBe(true);
593
+ // Only 2 insert calls: AuditEntry + 1 ChangeDetail (computedTotal dropped by fieldType filter)
594
+ expect(spies.values).toHaveBeenCalledTimes(2);
595
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
596
+ expect(changeValues.fieldName).toBe("amount");
597
+ });
598
+
599
+ it("silently drops fields from changes array that are not in entity's registered auditableFields", async () => {
600
+ const { db, spies } = createMockDb<Transaction>();
601
+ setupHappyPath(spies);
602
+
603
+ const result = await run(
604
+ db,
605
+ baseInput({
606
+ changes: [
607
+ { fieldName: "amount", oldValue: "100", newValue: "200" },
608
+ { fieldName: "unknownField", oldValue: "a", newValue: "b" },
609
+ ],
610
+ }),
611
+ ctx,
612
+ );
613
+
614
+ expect(result.ok).toBe(true);
615
+ // AuditEntry insert + 1 ChangeDetail (unknownField dropped)
616
+ expect(spies.values).toHaveBeenCalledTimes(2);
617
+ });
618
+
619
+ it("silently drops fields from changes array that are not in the active policy's field rules when policy has explicit rules", async () => {
620
+ const { db, spies } = createMockDb<Transaction>();
621
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
622
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
623
+ spies.select.mockReturnValueOnce(activePolicy); // policy
624
+ // Only captureFieldRule for "amount" — "status" has no rule
625
+ spies.select.mockReturnValueOnce([captureFieldRule]);
626
+ spies.insert.mockReturnValue(baseAuditEntry);
627
+
628
+ const result = await run(
629
+ db,
630
+ baseInput({
631
+ changes: [
632
+ { fieldName: "amount", oldValue: "100", newValue: "200" },
633
+ { fieldName: "status", oldValue: "OPEN", newValue: "CLOSED" },
634
+ ],
635
+ }),
636
+ ctx,
637
+ );
638
+
639
+ expect(result.ok).toBe(true);
640
+ // AuditEntry insert + 1 ChangeDetail (status dropped by policy rules)
641
+ expect(spies.values).toHaveBeenCalledTimes(2);
642
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
643
+ expect(changeValues.fieldName).toBe("amount");
644
+ });
645
+
646
+ it("truncates values exceeding 4,000 characters with [truncated] suffix", async () => {
647
+ const { db, spies } = createMockDb<Transaction>();
648
+ setupHappyPath(spies);
649
+
650
+ const longValue = "x".repeat(5000);
651
+ const result = await run(
652
+ db,
653
+ baseInput({ changes: [{ fieldName: "amount", oldValue: "100", newValue: longValue }] }),
654
+ ctx,
655
+ );
656
+
657
+ expect(result.ok).toBe(true);
658
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
659
+ expect(changeValues.newValue).toHaveLength(4000);
660
+ expect(changeValues.newValue).toMatch(/\[truncated\]$/);
661
+ });
662
+
663
+ it("stores correlationId when provided", async () => {
664
+ const { db, spies } = createMockDb<Transaction>();
665
+ setupHappyPath(spies);
666
+
667
+ const correlationId = "a1b2c3d4-e5f6-7890-abcd-ef1234567890";
668
+ const result = await run(db, baseInput({ correlationId }), ctx);
669
+
670
+ expect(result.ok).toBe(true);
671
+ const entryValues = spies.values.mock.calls[0][0] as Record<string, unknown>;
672
+ expect(entryValues.correlationId).toBe(correlationId);
673
+ });
674
+
675
+ it("stores actorMetadata (ipAddress, userAgent, sessionId, requestId) when provided", async () => {
676
+ const { db, spies } = createMockDb<Transaction>();
677
+ setupHappyPath(spies);
678
+
679
+ const result = await run(
680
+ db,
681
+ baseInput({
682
+ ipAddress: "192.168.1.1",
683
+ userAgent: "TestAgent/1.0",
684
+ sessionId: "sess-1",
685
+ requestId: "req-1",
686
+ }),
687
+ ctx,
688
+ );
689
+
690
+ expect(result.ok).toBe(true);
691
+ const entryValues = spies.values.mock.calls[0][0] as Record<string, unknown>;
692
+ expect(entryValues.ipAddress).toBe("192.168.1.1");
693
+ expect(entryValues.userAgent).toBe("TestAgent/1.0");
694
+ expect(entryValues.sessionId).toBe("sess-1");
695
+ expect(entryValues.requestId).toBe("req-1");
696
+ });
697
+
698
+ it("stores onBehalfOf when provided", async () => {
699
+ const { db, spies } = createMockDb<Transaction>();
700
+ setupHappyPath(spies);
701
+
702
+ const result = await run(db, baseInput({ onBehalfOf: "user-3" }), ctx);
703
+
704
+ expect(result.ok).toBe(true);
705
+ const entryValues = spies.values.mock.calls[0][0] as Record<string, unknown>;
706
+ expect(entryValues.onBehalfOf).toBe("user-3");
707
+ });
708
+
709
+ it("creates ChangeDetail with null oldValue for CREATE operations", async () => {
710
+ const { db, spies } = createMockDb<Transaction>();
711
+ const createPolicy = { ...activePolicy, operationType: "CREATE" };
712
+ spies.select.mockReturnValueOnce(undefined);
713
+ spies.select.mockReturnValueOnce(companyBoundEntity);
714
+ spies.select.mockReturnValueOnce(createPolicy);
715
+ spies.select.mockReturnValueOnce([captureFieldRule]);
716
+ spies.insert.mockReturnValue({ ...baseAuditEntry, operationType: "CREATE" });
717
+
718
+ const result = await run(
719
+ db,
720
+ baseInput({
721
+ operationType: "CREATE",
722
+ changes: [{ fieldName: "amount", oldValue: null, newValue: "100.00" }],
723
+ }),
724
+ ctx,
725
+ );
726
+
727
+ expect(result.ok).toBe(true);
728
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
729
+ expect(changeValues.oldValue).toBeNull();
730
+ expect(changeValues.newValue).toBe("100.00");
731
+ });
732
+
733
+ it("creates ChangeDetail with null newValue for DELETE operations", async () => {
734
+ const { db, spies } = createMockDb<Transaction>();
735
+ const deletePolicy = { ...activePolicy, operationType: "DELETE" };
736
+ spies.select.mockReturnValueOnce(undefined);
737
+ spies.select.mockReturnValueOnce(companyBoundEntity);
738
+ spies.select.mockReturnValueOnce(deletePolicy);
739
+ spies.select.mockReturnValueOnce([captureFieldRule]);
740
+ spies.insert.mockReturnValue({ ...baseAuditEntry, operationType: "DELETE" });
741
+
742
+ const result = await run(
743
+ db,
744
+ baseInput({
745
+ operationType: "DELETE",
746
+ changes: [{ fieldName: "amount", oldValue: "100.00", newValue: null }],
747
+ }),
748
+ ctx,
749
+ );
750
+
751
+ expect(result.ok).toBe(true);
752
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
753
+ expect(changeValues.oldValue).toBe("100.00");
754
+ expect(changeValues.newValue).toBeNull();
755
+ });
756
+
757
+ it("filters out unchanged fields for UPDATE operations", async () => {
758
+ const { db, spies } = createMockDb<Transaction>();
759
+ setupHappyPath(spies);
760
+
761
+ const result = await run(
762
+ db,
763
+ baseInput({
764
+ changes: [
765
+ { fieldName: "amount", oldValue: "100.00", newValue: "200.00" },
766
+ { fieldName: "status", oldValue: "OPEN", newValue: "OPEN" }, // unchanged
767
+ ],
768
+ }),
769
+ ctx,
770
+ );
771
+
772
+ expect(result.ok).toBe(true);
773
+ if (result.ok) {
774
+ expect(result.value.created).toBe(true);
775
+ }
776
+ // AuditEntry + 1 ChangeDetail (unchanged "status" dropped)
777
+ expect(spies.values).toHaveBeenCalledTimes(2);
778
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
779
+ expect(changeValues.fieldName).toBe("amount");
780
+ });
781
+
782
+ it("returns no-op when all UPDATE changes are unchanged after normalization", async () => {
783
+ const { db, spies } = createMockDb<Transaction>();
784
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
785
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
786
+ spies.select.mockReturnValueOnce(activePolicy); // policy
787
+ spies.select.mockReturnValueOnce([captureFieldRule]); // field rules
788
+ spies.insert.mockReturnValue(baseAuditEntry);
789
+
790
+ const result = await run(
791
+ db,
792
+ baseInput({
793
+ changes: [{ fieldName: "amount", oldValue: "100.00", newValue: "100.00" }],
794
+ }),
795
+ ctx,
796
+ );
797
+
798
+ expect(result.ok).toBe(true);
799
+ if (result.ok) {
800
+ expect(result.value.created).toBe(false);
801
+ expect(result.value.auditEntry).toBeNull();
802
+ }
803
+ expect(spies.insert).not.toHaveBeenCalled();
804
+ });
805
+
806
+ it("enforces oldValue=null for CREATE even if caller provides oldValue", async () => {
807
+ const { db, spies } = createMockDb<Transaction>();
808
+ const createPolicy = { ...activePolicy, operationType: "CREATE" };
809
+ spies.select.mockReturnValueOnce(undefined);
810
+ spies.select.mockReturnValueOnce(companyBoundEntity);
811
+ spies.select.mockReturnValueOnce(createPolicy);
812
+ spies.select.mockReturnValueOnce([captureFieldRule]);
813
+ spies.insert.mockReturnValue({ ...baseAuditEntry, operationType: "CREATE" });
814
+
815
+ const result = await run(
816
+ db,
817
+ baseInput({
818
+ operationType: "CREATE",
819
+ changes: [{ fieldName: "amount", oldValue: "should-be-null", newValue: "100.00" }],
820
+ }),
821
+ ctx,
822
+ );
823
+
824
+ expect(result.ok).toBe(true);
825
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
826
+ expect(changeValues.oldValue).toBeNull();
827
+ expect(changeValues.newValue).toBe("100.00");
828
+ });
829
+
830
+ it("enforces newValue=null for DELETE even if caller provides newValue", async () => {
831
+ const { db, spies } = createMockDb<Transaction>();
832
+ const deletePolicy = { ...activePolicy, operationType: "DELETE" };
833
+ spies.select.mockReturnValueOnce(undefined);
834
+ spies.select.mockReturnValueOnce(companyBoundEntity);
835
+ spies.select.mockReturnValueOnce(deletePolicy);
836
+ spies.select.mockReturnValueOnce([captureFieldRule]);
837
+ spies.insert.mockReturnValue({ ...baseAuditEntry, operationType: "DELETE" });
838
+
839
+ const result = await run(
840
+ db,
841
+ baseInput({
842
+ operationType: "DELETE",
843
+ changes: [{ fieldName: "amount", oldValue: "100.00", newValue: "should-be-null" }],
844
+ }),
845
+ ctx,
846
+ );
847
+
848
+ expect(result.ok).toBe(true);
849
+ const changeValues = spies.values.mock.calls[1][0] as Record<string, unknown>;
850
+ expect(changeValues.oldValue).toBe("100.00");
851
+ expect(changeValues.newValue).toBeNull();
852
+ });
853
+
854
+ it("rejects event with empty entityId", async () => {
855
+ const { db, spies } = createMockDb<Transaction>();
856
+ spies.select.mockReturnValueOnce(undefined);
857
+ spies.select.mockReturnValueOnce(companyBoundEntity);
858
+
859
+ const result = await run(db, baseInput({ entityId: "" }), ctx);
860
+
861
+ expect(result.ok).toBe(false);
862
+ if (!result.ok) {
863
+ expect(result.error).toBeInstanceOf(MissingEntityIdError);
864
+ }
865
+ });
866
+
867
+ it("rejects event with non-UUID correlationId", async () => {
868
+ const { db, spies } = createMockDb<Transaction>();
869
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
870
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity found
871
+
872
+ const result = await run(db, baseInput({ correlationId: "not-a-valid-uuid" }), ctx);
873
+
874
+ expect(result.ok).toBe(false);
875
+ if (!result.ok) {
876
+ expect(result.error).toBeInstanceOf(ValidationErrorError);
877
+ }
878
+ });
879
+
880
+ it("rejects event with empty eventId", async () => {
881
+ const { db } = createMockDb<Transaction>();
882
+
883
+ const result = await run(db, baseInput({ eventId: "" }), ctx);
884
+
885
+ expect(result.ok).toBe(false);
886
+ if (!result.ok) {
887
+ expect(result.error).toBeInstanceOf(MissingEventIdError);
888
+ }
889
+ });
890
+
891
+ it("rejects event with non-UUID eventId", async () => {
892
+ const { db } = createMockDb<Transaction>();
893
+
894
+ const result = await run(db, baseInput({ eventId: "not-a-uuid" }), ctx);
895
+
896
+ expect(result.ok).toBe(false);
897
+ if (!result.ok) {
898
+ expect(result.error).toBeInstanceOf(MissingEventIdError);
899
+ }
900
+ });
901
+
902
+ it("rejects CREATE when all changes are filtered out by policy rules", async () => {
903
+ const { db, spies } = createMockDb<Transaction>();
904
+ const createPolicy = { ...activePolicy, operationType: "CREATE" };
905
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
906
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
907
+ spies.select.mockReturnValueOnce(createPolicy); // policy
908
+ // Only captureFieldRule for "amount" — submitted field "status" has no rule
909
+ spies.select.mockReturnValueOnce([captureFieldRule]);
910
+
911
+ const result = await run(
912
+ db,
913
+ baseInput({
914
+ operationType: "CREATE",
915
+ changes: [{ fieldName: "status", newValue: "OPEN" }],
916
+ }),
917
+ ctx,
918
+ );
919
+
920
+ expect(result.ok).toBe(false);
921
+ if (!result.ok) {
922
+ expect(result.error).toBeInstanceOf(ValidationErrorError);
923
+ }
924
+ expect(spies.insert).not.toHaveBeenCalled();
925
+ });
926
+
927
+ it("rejects DELETE when all changes are filtered out by policy rules", async () => {
928
+ const { db, spies } = createMockDb<Transaction>();
929
+ const deletePolicy = { ...activePolicy, operationType: "DELETE" };
930
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
931
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
932
+ spies.select.mockReturnValueOnce(deletePolicy); // policy
933
+ // Only captureFieldRule for "amount" — submitted field "status" has no rule
934
+ spies.select.mockReturnValueOnce([captureFieldRule]);
935
+
936
+ const result = await run(
937
+ db,
938
+ baseInput({
939
+ operationType: "DELETE",
940
+ changes: [{ fieldName: "status", oldValue: "OPEN" }],
941
+ }),
942
+ ctx,
943
+ );
944
+
945
+ expect(result.ok).toBe(false);
946
+ if (!result.ok) {
947
+ expect(result.error).toBeInstanceOf(ValidationErrorError);
948
+ }
949
+ expect(spies.insert).not.toHaveBeenCalled();
950
+ });
951
+
952
+ it("returns no-op for UPDATE when all changes are filtered out by policy rules", async () => {
953
+ const { db, spies } = createMockDb<Transaction>();
954
+ spies.select.mockReturnValueOnce(undefined); // no duplicate
955
+ spies.select.mockReturnValueOnce(companyBoundEntity); // entity
956
+ spies.select.mockReturnValueOnce(activePolicy); // policy
957
+ // Only captureFieldRule for "amount" — submitted field "status" has no rule
958
+ spies.select.mockReturnValueOnce([captureFieldRule]);
959
+
960
+ const result = await run(
961
+ db,
962
+ baseInput({
963
+ changes: [{ fieldName: "status", oldValue: "OPEN", newValue: "CLOSED" }],
964
+ }),
965
+ ctx,
966
+ );
967
+
968
+ expect(result.ok).toBe(true);
969
+ if (result.ok) {
970
+ expect(result.value.created).toBe(false);
971
+ expect(result.value.auditEntry).toBeNull();
972
+ }
973
+ expect(spies.insert).not.toHaveBeenCalled();
974
+ });
975
+
976
+ it("rejects caller without logAuditEvent permission", async () => {
977
+ const { db } = createMockDb<Transaction>();
978
+ const command = logAuditEvent();
979
+ const noPermCtx: CommandContext = {
980
+ actorId: "test-actor",
981
+ permissions: [],
982
+ };
983
+
984
+ const result = await command(db, baseInput(), noPermCtx);
985
+
986
+ expect(result.ok).toBe(false);
987
+ if (!result.ok) {
988
+ expect(result.error).toBeInstanceOf(InsufficientPermissionError);
989
+ }
990
+ });
991
+ });