@tailor-platform/erp-kit 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (639) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +193 -69
  3. package/dist/cli.mjs +1038 -398
  4. package/package.json +7 -5
  5. package/skills/erp-kit-app-1-requirements/SKILL.md +27 -17
  6. package/skills/erp-kit-app-2-requirements-review/SKILL.md +5 -4
  7. package/skills/erp-kit-app-2-requirements-review/references/best-practices-check.md +10 -1
  8. package/skills/erp-kit-app-2-requirements-review/references/boundary-consistency-check.md +10 -1
  9. package/skills/erp-kit-app-3-plan/SKILL.md +31 -34
  10. package/skills/erp-kit-app-3-plan/references/resolver-extraction.md +22 -36
  11. package/skills/erp-kit-app-3-plan/references/screen-extraction.md +15 -1
  12. package/skills/erp-kit-app-3-plan/references/story-extraction.md +8 -2
  13. package/skills/erp-kit-app-4-plan-review/SKILL.md +1 -10
  14. package/skills/erp-kit-app-5-impl-backend/SKILL.md +10 -19
  15. package/skills/erp-kit-app-5-impl-backend/references/app-config.md +1 -22
  16. package/skills/erp-kit-app-5-impl-backend/references/module-wiring.md +0 -1
  17. package/skills/erp-kit-app-5-impl-backend/references/resolver-patterns.md +13 -4
  18. package/skills/erp-kit-app-6-impl-frontend/SKILL.md +5 -0
  19. package/skills/erp-kit-app-6-impl-frontend/references/pages.md +16 -46
  20. package/skills/erp-kit-app-7-impl-review/SKILL.md +13 -11
  21. package/skills/erp-kit-app-7-impl-review/references/resolver-doc-code-parity.md +16 -17
  22. package/skills/erp-kit-app-shared/SKILL.md +15 -0
  23. package/skills/erp-kit-app-shared/references/link-format-reference.md +13 -0
  24. package/skills/erp-kit-app-shared/references/naming-conventions.md +21 -0
  25. package/skills/erp-kit-app-shared/references/resolver-classification.md +23 -0
  26. package/skills/erp-kit-app-shared/references/schema-constraints.md +25 -0
  27. package/skills/erp-kit-module-1-requirements/SKILL.md +7 -13
  28. package/skills/erp-kit-module-1-requirements/references/feature-doc.md +1 -1
  29. package/skills/erp-kit-module-2-requirements-review/SKILL.md +21 -5
  30. package/skills/erp-kit-module-2-requirements-review/references/requirements-report-format.md +19 -0
  31. package/skills/erp-kit-module-3-plan/SKILL.md +6 -8
  32. package/skills/erp-kit-module-3-plan/references/naming.md +15 -1
  33. package/skills/erp-kit-module-4-plan-review/SKILL.md +21 -5
  34. package/skills/erp-kit-module-4-plan-review/references/parity-report-format.md +15 -0
  35. package/skills/erp-kit-module-5-impl/SKILL.md +12 -10
  36. package/skills/erp-kit-module-5-impl/references/generated-code.md +2 -2
  37. package/skills/erp-kit-module-6-impl-review/SKILL.md +21 -7
  38. package/skills/erp-kit-module-6-impl-review/references/error-implementation-parity.md +1 -1
  39. package/skills/erp-kit-module-6-impl-review/references/errors.md +1 -1
  40. package/skills/erp-kit-module-6-impl-review/references/impl-parity-report-format.md +15 -0
  41. package/skills/erp-kit-module-shared/SKILL.md +4 -0
  42. package/skills/erp-kit-module-shared/references/errors.md +1 -1
  43. package/skills/erp-kit-module-shared/references/queries.md +1 -1
  44. package/skills/erp-kit-module-shared/references/structure.md +1 -1
  45. package/skills/erp-kit-update/SKILL.md +2 -2
  46. package/src/commands/app/index.ts +75 -31
  47. package/src/commands/check.test.ts +1 -1
  48. package/src/commands/check.ts +2 -35
  49. package/src/commands/doc/index.ts +83 -0
  50. package/src/commands/doc/module.test.ts +119 -0
  51. package/src/commands/doc/module.ts +114 -0
  52. package/src/commands/doc/modules.test.ts +103 -0
  53. package/src/commands/doc/modules.ts +98 -0
  54. package/src/commands/doc/search.test.ts +94 -0
  55. package/src/commands/doc/search.ts +111 -0
  56. package/src/commands/generate-doc.test.ts +63 -0
  57. package/src/commands/generate-doc.ts +105 -0
  58. package/src/commands/index.ts +20 -8
  59. package/src/commands/init-module.test.ts +43 -0
  60. package/src/commands/init-module.ts +74 -0
  61. package/src/commands/lib/command-result.ts +30 -0
  62. package/src/commands/lib/discovery.test.ts +74 -0
  63. package/src/commands/lib/discovery.ts +106 -0
  64. package/src/commands/lib/paths.ts +22 -0
  65. package/src/commands/lib/sync-check-source.test.ts +197 -0
  66. package/src/commands/lib/sync-check-source.ts +100 -0
  67. package/src/commands/lib/sync-check-tests.test.ts +178 -0
  68. package/src/commands/lib/sync-check-tests.ts +69 -0
  69. package/src/commands/mock/index.ts +11 -6
  70. package/src/commands/module/generate.ts +39 -14
  71. package/src/commands/module/index.ts +31 -45
  72. package/src/commands/parse-doc-test-cases.ts +13 -2
  73. package/src/commands/sync-check.test.ts +6 -364
  74. package/src/commands/sync-check.ts +7 -251
  75. package/src/generator/generate-app-code.test.ts +121 -0
  76. package/src/generator/generate-app-code.ts +51 -0
  77. package/src/{commands/scaffold.test.ts → generator/generate-code-boilerplate.test.ts} +19 -89
  78. package/src/generator/generate-code.test.ts +57 -6
  79. package/src/generator/generate-code.ts +40 -157
  80. package/src/generator/generate-errors.ts +34 -0
  81. package/src/generator/generate-permissions.ts +12 -0
  82. package/src/generator/generate-shells.ts +28 -0
  83. package/src/generator/generate-stubs.ts +31 -0
  84. package/src/generator/parse-resolver-doc.test.ts +89 -0
  85. package/src/generator/parse-resolver-doc.ts +125 -0
  86. package/src/generator/scaffold.ts +57 -0
  87. package/src/generator/stub-templates.test.ts +55 -0
  88. package/src/generator/stub-templates.ts +145 -0
  89. package/src/integration.test.ts +2 -2
  90. package/src/modules/audit/README.md +46 -0
  91. package/src/modules/audit/command/activateAuditPolicy.generated.ts +6 -0
  92. package/src/modules/audit/command/activateAuditPolicy.test.ts +186 -0
  93. package/src/modules/audit/command/activateAuditPolicy.ts +97 -0
  94. package/src/modules/audit/command/createAuditPolicy.generated.ts +6 -0
  95. package/src/modules/audit/command/createAuditPolicy.test.ts +395 -0
  96. package/src/modules/audit/command/createAuditPolicy.ts +131 -0
  97. package/src/modules/audit/command/deactivateAuditPolicy.generated.ts +6 -0
  98. package/src/modules/audit/command/deactivateAuditPolicy.test.ts +138 -0
  99. package/src/modules/audit/command/deactivateAuditPolicy.ts +58 -0
  100. package/src/modules/audit/command/deleteAuditPolicy.generated.ts +6 -0
  101. package/src/modules/audit/command/deleteAuditPolicy.test.ts +121 -0
  102. package/src/modules/audit/command/deleteAuditPolicy.ts +52 -0
  103. package/src/modules/audit/command/logAuditEvent.generated.ts +6 -0
  104. package/src/modules/audit/command/logAuditEvent.test.ts +991 -0
  105. package/src/modules/audit/command/logAuditEvent.ts +357 -0
  106. package/src/modules/audit/command/reactivateAuditPolicy.generated.ts +6 -0
  107. package/src/modules/audit/command/reactivateAuditPolicy.test.ts +143 -0
  108. package/src/modules/audit/command/reactivateAuditPolicy.ts +79 -0
  109. package/src/modules/audit/command/registerAuditableEntity.generated.ts +6 -0
  110. package/src/modules/audit/command/registerAuditableEntity.test.ts +268 -0
  111. package/src/modules/audit/command/registerAuditableEntity.ts +94 -0
  112. package/src/modules/audit/command/replaceAuditPolicy.generated.ts +6 -0
  113. package/src/modules/audit/command/replaceAuditPolicy.test.ts +242 -0
  114. package/src/modules/audit/command/replaceAuditPolicy.ts +91 -0
  115. package/src/modules/audit/command/updateAuditPolicy.generated.ts +6 -0
  116. package/src/modules/audit/command/updateAuditPolicy.test.ts +284 -0
  117. package/src/modules/audit/command/updateAuditPolicy.ts +151 -0
  118. package/src/modules/audit/db/auditEntry.ts +47 -0
  119. package/src/modules/audit/db/auditPolicy.ts +33 -0
  120. package/src/modules/audit/db/auditableEntity.ts +22 -0
  121. package/src/modules/audit/db/changeDetail.ts +28 -0
  122. package/src/modules/audit/db/policyFieldRule.ts +23 -0
  123. package/src/modules/audit/docs/commands/ActivateAuditPolicy.md +69 -0
  124. package/src/modules/audit/docs/commands/CreateAuditPolicy.md +79 -0
  125. package/src/modules/audit/docs/commands/DeactivateAuditPolicy.md +55 -0
  126. package/src/modules/audit/docs/commands/DeleteAuditPolicy.md +55 -0
  127. package/src/modules/audit/docs/commands/LogAuditEvent.md +137 -0
  128. package/src/modules/audit/docs/commands/ReactivateAuditPolicy.md +58 -0
  129. package/src/modules/audit/docs/commands/RegisterAuditableEntity.md +62 -0
  130. package/src/modules/audit/docs/commands/ReplaceAuditPolicy.md +72 -0
  131. package/src/modules/audit/docs/commands/UpdateAuditPolicy.md +77 -0
  132. package/src/modules/audit/docs/features/audit-event-logging.md +126 -0
  133. package/src/modules/audit/docs/features/audit-policy-configuration.md +135 -0
  134. package/src/modules/audit/docs/features/field-level-change-tracking.md +95 -0
  135. package/src/modules/audit/docs/models/AuditEntry.md +55 -0
  136. package/src/modules/audit/docs/models/AuditPolicy.md +79 -0
  137. package/src/modules/audit/docs/models/AuditableEntity.md +38 -0
  138. package/src/modules/audit/docs/models/ChangeDetail.md +55 -0
  139. package/src/modules/audit/docs/models/PolicyFieldRule.md +45 -0
  140. package/src/modules/audit/docs/queries/GetAuditEntry.md +49 -0
  141. package/src/modules/audit/docs/queries/GetAuditPolicy.md +54 -0
  142. package/src/modules/audit/docs/queries/GetAuditSummary.md +84 -0
  143. package/src/modules/audit/docs/queries/GetChangeDetails.md +56 -0
  144. package/src/modules/audit/docs/queries/ListAuditPolicies.md +58 -0
  145. package/src/modules/audit/docs/queries/SearchAuditEntries.md +91 -0
  146. package/src/modules/audit/generated/kysely-tailordb.ts +92 -0
  147. package/src/modules/audit/index.ts +2 -0
  148. package/src/modules/audit/lib/_db_deps.ts +13 -0
  149. package/src/modules/audit/lib/errors.generated.ts +120 -0
  150. package/src/modules/audit/lib/permissions.generated.ts +14 -0
  151. package/src/modules/audit/lib/types.ts +28 -0
  152. package/src/modules/audit/module.ts +57 -0
  153. package/src/modules/audit/permissions.ts +39 -0
  154. package/src/modules/audit/query/getAuditEntry.generated.ts +5 -0
  155. package/src/modules/audit/query/getAuditEntry.test.ts +123 -0
  156. package/src/modules/audit/query/getAuditEntry.ts +36 -0
  157. package/src/modules/audit/query/getAuditPolicy.generated.ts +5 -0
  158. package/src/modules/audit/query/getAuditPolicy.test.ts +169 -0
  159. package/src/modules/audit/query/getAuditPolicy.ts +42 -0
  160. package/src/modules/audit/query/getAuditSummary.generated.ts +5 -0
  161. package/src/modules/audit/query/getAuditSummary.test.ts +632 -0
  162. package/src/modules/audit/query/getAuditSummary.ts +164 -0
  163. package/src/modules/audit/query/getChangeDetails.generated.ts +5 -0
  164. package/src/modules/audit/query/getChangeDetails.test.ts +195 -0
  165. package/src/modules/audit/query/getChangeDetails.ts +42 -0
  166. package/src/modules/audit/query/listAuditPolicies.generated.ts +5 -0
  167. package/src/modules/audit/query/listAuditPolicies.test.ts +239 -0
  168. package/src/modules/audit/query/listAuditPolicies.ts +100 -0
  169. package/src/modules/audit/query/searchAuditEntries.generated.ts +5 -0
  170. package/src/modules/audit/query/searchAuditEntries.test.ts +424 -0
  171. package/src/modules/audit/query/searchAuditEntries.ts +121 -0
  172. package/src/modules/audit/tailor.config.ts +13 -0
  173. package/src/modules/audit/tailor.d.ts +13 -0
  174. package/src/modules/audit/testing/fixtures.ts +215 -0
  175. package/src/modules/business-partner/README.md +60 -0
  176. package/src/modules/business-partner/command/.gitkeep +0 -0
  177. package/src/modules/business-partner/command/activatePartner.generated.ts +6 -0
  178. package/src/modules/business-partner/command/activatePartner.test.ts +59 -0
  179. package/src/modules/business-partner/command/activatePartner.ts +45 -0
  180. package/src/modules/business-partner/command/assignRoleToPartner.generated.ts +6 -0
  181. package/src/modules/business-partner/command/assignRoleToPartner.test.ts +113 -0
  182. package/src/modules/business-partner/command/assignRoleToPartner.ts +72 -0
  183. package/src/modules/business-partner/command/createContactPerson.generated.ts +6 -0
  184. package/src/modules/business-partner/command/createContactPerson.test.ts +193 -0
  185. package/src/modules/business-partner/command/createContactPerson.ts +98 -0
  186. package/src/modules/business-partner/command/createPartner.generated.ts +6 -0
  187. package/src/modules/business-partner/command/createPartner.test.ts +179 -0
  188. package/src/modules/business-partner/command/createPartner.ts +83 -0
  189. package/src/modules/business-partner/command/createPartnerAddress.generated.ts +6 -0
  190. package/src/modules/business-partner/command/createPartnerAddress.test.ts +195 -0
  191. package/src/modules/business-partner/command/createPartnerAddress.ts +119 -0
  192. package/src/modules/business-partner/command/createPartnerBankAccount.generated.ts +6 -0
  193. package/src/modules/business-partner/command/createPartnerBankAccount.test.ts +297 -0
  194. package/src/modules/business-partner/command/createPartnerBankAccount.ts +114 -0
  195. package/src/modules/business-partner/command/createPartnerIdentification.generated.ts +6 -0
  196. package/src/modules/business-partner/command/createPartnerIdentification.test.ts +255 -0
  197. package/src/modules/business-partner/command/createPartnerIdentification.ts +97 -0
  198. package/src/modules/business-partner/command/deactivateContactPerson.generated.ts +6 -0
  199. package/src/modules/business-partner/command/deactivateContactPerson.test.ts +70 -0
  200. package/src/modules/business-partner/command/deactivateContactPerson.ts +54 -0
  201. package/src/modules/business-partner/command/deactivatePartner.generated.ts +6 -0
  202. package/src/modules/business-partner/command/deactivatePartner.test.ts +59 -0
  203. package/src/modules/business-partner/command/deactivatePartner.ts +46 -0
  204. package/src/modules/business-partner/command/deleteContactPerson.generated.ts +6 -0
  205. package/src/modules/business-partner/command/deleteContactPerson.test.ts +61 -0
  206. package/src/modules/business-partner/command/deleteContactPerson.ts +48 -0
  207. package/src/modules/business-partner/command/deletePartner.generated.ts +6 -0
  208. package/src/modules/business-partner/command/deletePartner.test.ts +58 -0
  209. package/src/modules/business-partner/command/deletePartner.ts +46 -0
  210. package/src/modules/business-partner/command/deletePartnerAddress.generated.ts +6 -0
  211. package/src/modules/business-partner/command/deletePartnerAddress.test.ts +74 -0
  212. package/src/modules/business-partner/command/deletePartnerAddress.ts +52 -0
  213. package/src/modules/business-partner/command/deletePartnerBankAccount.generated.ts +6 -0
  214. package/src/modules/business-partner/command/deletePartnerBankAccount.test.ts +55 -0
  215. package/src/modules/business-partner/command/deletePartnerBankAccount.ts +36 -0
  216. package/src/modules/business-partner/command/deletePartnerIdentification.generated.ts +6 -0
  217. package/src/modules/business-partner/command/deletePartnerIdentification.test.ts +47 -0
  218. package/src/modules/business-partner/command/deletePartnerIdentification.ts +37 -0
  219. package/src/modules/business-partner/command/reactivateContactPerson.generated.ts +6 -0
  220. package/src/modules/business-partner/command/reactivateContactPerson.test.ts +48 -0
  221. package/src/modules/business-partner/command/reactivateContactPerson.ts +48 -0
  222. package/src/modules/business-partner/command/reactivatePartner.generated.ts +6 -0
  223. package/src/modules/business-partner/command/reactivatePartner.test.ts +59 -0
  224. package/src/modules/business-partner/command/reactivatePartner.ts +46 -0
  225. package/src/modules/business-partner/command/removeRoleFromPartner.generated.ts +6 -0
  226. package/src/modules/business-partner/command/removeRoleFromPartner.test.ts +82 -0
  227. package/src/modules/business-partner/command/removeRoleFromPartner.ts +73 -0
  228. package/src/modules/business-partner/command/setDefaultPartnerAddress.generated.ts +6 -0
  229. package/src/modules/business-partner/command/setDefaultPartnerAddress.test.ts +60 -0
  230. package/src/modules/business-partner/command/setDefaultPartnerAddress.ts +48 -0
  231. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.generated.ts +6 -0
  232. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.test.ts +56 -0
  233. package/src/modules/business-partner/command/setDefaultPartnerBankAccount.ts +51 -0
  234. package/src/modules/business-partner/command/setPrimaryContactPerson.generated.ts +6 -0
  235. package/src/modules/business-partner/command/setPrimaryContactPerson.test.ts +63 -0
  236. package/src/modules/business-partner/command/setPrimaryContactPerson.ts +55 -0
  237. package/src/modules/business-partner/command/updateContactPerson.generated.ts +6 -0
  238. package/src/modules/business-partner/command/updateContactPerson.test.ts +193 -0
  239. package/src/modules/business-partner/command/updateContactPerson.ts +92 -0
  240. package/src/modules/business-partner/command/updatePartner.generated.ts +6 -0
  241. package/src/modules/business-partner/command/updatePartner.test.ts +101 -0
  242. package/src/modules/business-partner/command/updatePartner.ts +76 -0
  243. package/src/modules/business-partner/command/updatePartnerAddress.generated.ts +6 -0
  244. package/src/modules/business-partner/command/updatePartnerAddress.test.ts +148 -0
  245. package/src/modules/business-partner/command/updatePartnerAddress.ts +64 -0
  246. package/src/modules/business-partner/command/updatePartnerBankAccount.generated.ts +6 -0
  247. package/src/modules/business-partner/command/updatePartnerBankAccount.test.ts +249 -0
  248. package/src/modules/business-partner/command/updatePartnerBankAccount.ts +109 -0
  249. package/src/modules/business-partner/command/updatePartnerIdentification.generated.ts +6 -0
  250. package/src/modules/business-partner/command/updatePartnerIdentification.test.ts +162 -0
  251. package/src/modules/business-partner/command/updatePartnerIdentification.ts +105 -0
  252. package/src/modules/business-partner/db/.gitkeep +0 -0
  253. package/src/modules/business-partner/db/businessPartner.ts +59 -0
  254. package/src/modules/business-partner/db/contactPerson.ts +49 -0
  255. package/src/modules/business-partner/db/partnerAddress.ts +45 -0
  256. package/src/modules/business-partner/db/partnerBankAccount.ts +53 -0
  257. package/src/modules/business-partner/db/partnerIdentification.ts +53 -0
  258. package/src/modules/business-partner/db/partnerRole.ts +43 -0
  259. package/src/modules/business-partner/docs/commands/ActivatePartner.md +39 -0
  260. package/src/modules/business-partner/docs/commands/AssignRoleToPartner.md +49 -0
  261. package/src/modules/business-partner/docs/commands/CreateContactPerson.md +59 -0
  262. package/src/modules/business-partner/docs/commands/CreatePartner.md +54 -0
  263. package/src/modules/business-partner/docs/commands/CreatePartnerAddress.md +60 -0
  264. package/src/modules/business-partner/docs/commands/CreatePartnerBankAccount.md +68 -0
  265. package/src/modules/business-partner/docs/commands/CreatePartnerIdentification.md +59 -0
  266. package/src/modules/business-partner/docs/commands/DeactivateContactPerson.md +42 -0
  267. package/src/modules/business-partner/docs/commands/DeactivatePartner.md +39 -0
  268. package/src/modules/business-partner/docs/commands/DeleteContactPerson.md +43 -0
  269. package/src/modules/business-partner/docs/commands/DeletePartner.md +40 -0
  270. package/src/modules/business-partner/docs/commands/DeletePartnerAddress.md +40 -0
  271. package/src/modules/business-partner/docs/commands/DeletePartnerBankAccount.md +35 -0
  272. package/src/modules/business-partner/docs/commands/DeletePartnerIdentification.md +33 -0
  273. package/src/modules/business-partner/docs/commands/ReactivateContactPerson.md +38 -0
  274. package/src/modules/business-partner/docs/commands/ReactivatePartner.md +39 -0
  275. package/src/modules/business-partner/docs/commands/RemoveRoleFromPartner.md +46 -0
  276. package/src/modules/business-partner/docs/commands/SetDefaultPartnerAddress.md +38 -0
  277. package/src/modules/business-partner/docs/commands/SetDefaultPartnerBankAccount.md +38 -0
  278. package/src/modules/business-partner/docs/commands/SetPrimaryContactPerson.md +43 -0
  279. package/src/modules/business-partner/docs/commands/UpdateContactPerson.md +66 -0
  280. package/src/modules/business-partner/docs/commands/UpdatePartner.md +48 -0
  281. package/src/modules/business-partner/docs/commands/UpdatePartnerAddress.md +46 -0
  282. package/src/modules/business-partner/docs/commands/UpdatePartnerBankAccount.md +64 -0
  283. package/src/modules/business-partner/docs/commands/UpdatePartnerIdentification.md +52 -0
  284. package/src/modules/business-partner/docs/features/contact-person-management.md +70 -0
  285. package/src/modules/business-partner/docs/features/partner-address-management.md +96 -0
  286. package/src/modules/business-partner/docs/features/partner-bank-account.md +70 -0
  287. package/src/modules/business-partner/docs/features/partner-identification.md +76 -0
  288. package/src/modules/business-partner/docs/features/partner-lifecycle.md +59 -0
  289. package/src/modules/business-partner/docs/features/partner-role-classification.md +73 -0
  290. package/src/modules/business-partner/docs/models/BusinessPartner.md +64 -0
  291. package/src/modules/business-partner/docs/models/ContactPerson.md +62 -0
  292. package/src/modules/business-partner/docs/models/PartnerAddress.md +52 -0
  293. package/src/modules/business-partner/docs/models/PartnerBankAccount.md +50 -0
  294. package/src/modules/business-partner/docs/models/PartnerIdentification.md +46 -0
  295. package/src/modules/business-partner/docs/models/PartnerRole.md +42 -0
  296. package/src/modules/business-partner/docs/queries/GetContactPerson.md +34 -0
  297. package/src/modules/business-partner/docs/queries/GetDefaultPartnerAddress.md +40 -0
  298. package/src/modules/business-partner/docs/queries/GetDefaultPartnerBankAccount.md +36 -0
  299. package/src/modules/business-partner/docs/queries/GetPartner.md +35 -0
  300. package/src/modules/business-partner/docs/queries/GetPartnerAddress.md +34 -0
  301. package/src/modules/business-partner/docs/queries/GetPartnerBankAccount.md +34 -0
  302. package/src/modules/business-partner/docs/queries/GetPartnerIdentification.md +34 -0
  303. package/src/modules/business-partner/docs/queries/GetPartnerRole.md +34 -0
  304. package/src/modules/business-partner/docs/queries/GetPrimaryContactPerson.md +36 -0
  305. package/src/modules/business-partner/docs/queries/ListContactPersonsByPartner.md +39 -0
  306. package/src/modules/business-partner/docs/queries/ListPartnerAddressesByPartner.md +41 -0
  307. package/src/modules/business-partner/docs/queries/ListPartnerBankAccountsByPartner.md +39 -0
  308. package/src/modules/business-partner/docs/queries/ListPartnerIdentificationsByPartner.md +41 -0
  309. package/src/modules/business-partner/docs/queries/ListPartnersByRole.md +47 -0
  310. package/src/modules/business-partner/executor/.gitkeep +0 -0
  311. package/src/modules/business-partner/generated/.gitkeep +0 -0
  312. package/src/modules/business-partner/generated/enums.ts +60 -0
  313. package/src/modules/business-partner/generated/kysely-tailordb.ts +114 -0
  314. package/src/modules/business-partner/index.ts +2 -0
  315. package/src/modules/business-partner/lib/_db_deps.ts +17 -0
  316. package/src/modules/business-partner/lib/errors.generated.ts +172 -0
  317. package/src/modules/business-partner/lib/errors.ts +2 -0
  318. package/src/modules/business-partner/lib/permissions.generated.ts +30 -0
  319. package/src/modules/business-partner/lib/types.ts +53 -0
  320. package/src/modules/business-partner/module.ts +181 -0
  321. package/src/modules/business-partner/permissions.ts +3 -0
  322. package/src/modules/business-partner/query/.gitkeep +0 -0
  323. package/src/modules/business-partner/query/getContactPerson.generated.ts +5 -0
  324. package/src/modules/business-partner/query/getContactPerson.test.ts +31 -0
  325. package/src/modules/business-partner/query/getContactPerson.ts +16 -0
  326. package/src/modules/business-partner/query/getDefaultPartnerAddress.generated.ts +5 -0
  327. package/src/modules/business-partner/query/getDefaultPartnerAddress.test.ts +45 -0
  328. package/src/modules/business-partner/query/getDefaultPartnerAddress.ts +30 -0
  329. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.generated.ts +5 -0
  330. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.test.ts +43 -0
  331. package/src/modules/business-partner/query/getDefaultPartnerBankAccount.ts +17 -0
  332. package/src/modules/business-partner/query/getPartner.generated.ts +5 -0
  333. package/src/modules/business-partner/query/getPartner.test.ts +31 -0
  334. package/src/modules/business-partner/query/getPartner.ts +16 -0
  335. package/src/modules/business-partner/query/getPartnerAddress.generated.ts +5 -0
  336. package/src/modules/business-partner/query/getPartnerAddress.test.ts +31 -0
  337. package/src/modules/business-partner/query/getPartnerAddress.ts +16 -0
  338. package/src/modules/business-partner/query/getPartnerBankAccount.generated.ts +5 -0
  339. package/src/modules/business-partner/query/getPartnerBankAccount.test.ts +31 -0
  340. package/src/modules/business-partner/query/getPartnerBankAccount.ts +16 -0
  341. package/src/modules/business-partner/query/getPartnerIdentification.generated.ts +5 -0
  342. package/src/modules/business-partner/query/getPartnerIdentification.test.ts +31 -0
  343. package/src/modules/business-partner/query/getPartnerIdentification.ts +16 -0
  344. package/src/modules/business-partner/query/getPartnerRole.generated.ts +5 -0
  345. package/src/modules/business-partner/query/getPartnerRole.test.ts +31 -0
  346. package/src/modules/business-partner/query/getPartnerRole.ts +19 -0
  347. package/src/modules/business-partner/query/getPrimaryContactPerson.generated.ts +5 -0
  348. package/src/modules/business-partner/query/getPrimaryContactPerson.test.ts +43 -0
  349. package/src/modules/business-partner/query/getPrimaryContactPerson.ts +17 -0
  350. package/src/modules/business-partner/query/listContactPersonsByPartner.generated.ts +5 -0
  351. package/src/modules/business-partner/query/listContactPersonsByPartner.test.ts +77 -0
  352. package/src/modules/business-partner/query/listContactPersonsByPartner.ts +32 -0
  353. package/src/modules/business-partner/query/listPartnerAddressesByPartner.generated.ts +5 -0
  354. package/src/modules/business-partner/query/listPartnerAddressesByPartner.test.ts +71 -0
  355. package/src/modules/business-partner/query/listPartnerAddressesByPartner.ts +37 -0
  356. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.generated.ts +5 -0
  357. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.test.ts +59 -0
  358. package/src/modules/business-partner/query/listPartnerBankAccountsByPartner.ts +32 -0
  359. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.generated.ts +5 -0
  360. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.test.ts +72 -0
  361. package/src/modules/business-partner/query/listPartnerIdentificationsByPartner.ts +40 -0
  362. package/src/modules/business-partner/query/listPartnersByRole.generated.ts +5 -0
  363. package/src/modules/business-partner/query/listPartnersByRole.test.ts +103 -0
  364. package/src/modules/business-partner/query/listPartnersByRole.ts +47 -0
  365. package/src/modules/business-partner/tailor.config.ts +13 -0
  366. package/src/modules/business-partner/tailor.d.ts +13 -0
  367. package/src/modules/business-partner/testing/fixtures.ts +204 -0
  368. package/src/modules/coa-management/README.md +61 -0
  369. package/src/modules/coa-management/command/.gitkeep +0 -0
  370. package/src/modules/coa-management/command/activateAccount.generated.ts +6 -0
  371. package/src/modules/coa-management/command/activateAccount.test.ts +125 -0
  372. package/src/modules/coa-management/command/activateAccount.ts +105 -0
  373. package/src/modules/coa-management/command/activateChartOfAccounts.generated.ts +6 -0
  374. package/src/modules/coa-management/command/activateChartOfAccounts.test.ts +113 -0
  375. package/src/modules/coa-management/command/activateChartOfAccounts.ts +104 -0
  376. package/src/modules/coa-management/command/createAccount.generated.ts +6 -0
  377. package/src/modules/coa-management/command/createAccount.test.ts +767 -0
  378. package/src/modules/coa-management/command/createAccount.ts +247 -0
  379. package/src/modules/coa-management/command/createAccountGroup.generated.ts +6 -0
  380. package/src/modules/coa-management/command/createAccountGroup.test.ts +494 -0
  381. package/src/modules/coa-management/command/createAccountGroup.ts +207 -0
  382. package/src/modules/coa-management/command/createChartOfAccounts.generated.ts +6 -0
  383. package/src/modules/coa-management/command/createChartOfAccounts.test.ts +502 -0
  384. package/src/modules/coa-management/command/createChartOfAccounts.ts +267 -0
  385. package/src/modules/coa-management/command/deactivateAccount.generated.ts +6 -0
  386. package/src/modules/coa-management/command/deactivateAccount.test.ts +199 -0
  387. package/src/modules/coa-management/command/deactivateAccount.ts +142 -0
  388. package/src/modules/coa-management/command/deactivateChartOfAccounts.generated.ts +6 -0
  389. package/src/modules/coa-management/command/deactivateChartOfAccounts.test.ts +91 -0
  390. package/src/modules/coa-management/command/deactivateChartOfAccounts.ts +88 -0
  391. package/src/modules/coa-management/command/deleteAccount.generated.ts +6 -0
  392. package/src/modules/coa-management/command/deleteAccount.test.ts +122 -0
  393. package/src/modules/coa-management/command/deleteAccount.ts +103 -0
  394. package/src/modules/coa-management/command/deleteAccountGroup.generated.ts +6 -0
  395. package/src/modules/coa-management/command/deleteAccountGroup.test.ts +120 -0
  396. package/src/modules/coa-management/command/deleteAccountGroup.ts +113 -0
  397. package/src/modules/coa-management/command/deleteChartOfAccounts.generated.ts +6 -0
  398. package/src/modules/coa-management/command/deleteChartOfAccounts.test.ts +154 -0
  399. package/src/modules/coa-management/command/deleteChartOfAccounts.ts +133 -0
  400. package/src/modules/coa-management/command/moveAccountGroup.generated.ts +6 -0
  401. package/src/modules/coa-management/command/moveAccountGroup.test.ts +199 -0
  402. package/src/modules/coa-management/command/moveAccountGroup.ts +145 -0
  403. package/src/modules/coa-management/command/reactivateAccount.generated.ts +6 -0
  404. package/src/modules/coa-management/command/reactivateAccount.test.ts +126 -0
  405. package/src/modules/coa-management/command/reactivateAccount.ts +123 -0
  406. package/src/modules/coa-management/command/updateAccount.generated.ts +6 -0
  407. package/src/modules/coa-management/command/updateAccount.test.ts +669 -0
  408. package/src/modules/coa-management/command/updateAccount.ts +370 -0
  409. package/src/modules/coa-management/command/updateAccountGroup.generated.ts +6 -0
  410. package/src/modules/coa-management/command/updateAccountGroup.test.ts +253 -0
  411. package/src/modules/coa-management/command/updateAccountGroup.ts +191 -0
  412. package/src/modules/coa-management/command/updateChartOfAccounts.generated.ts +6 -0
  413. package/src/modules/coa-management/command/updateChartOfAccounts.test.ts +153 -0
  414. package/src/modules/coa-management/command/updateChartOfAccounts.ts +133 -0
  415. package/src/modules/coa-management/db/.gitkeep +0 -0
  416. package/src/modules/coa-management/db/account.ts +119 -0
  417. package/src/modules/coa-management/db/accountGroup.ts +57 -0
  418. package/src/modules/coa-management/db/chartOfAccounts.ts +55 -0
  419. package/src/modules/coa-management/docs/commands/ActivateAccount.md +49 -0
  420. package/src/modules/coa-management/docs/commands/ActivateChartOfAccounts.md +47 -0
  421. package/src/modules/coa-management/docs/commands/CreateAccount.md +94 -0
  422. package/src/modules/coa-management/docs/commands/CreateAccountGroup.md +70 -0
  423. package/src/modules/coa-management/docs/commands/CreateChartOfAccounts.md +72 -0
  424. package/src/modules/coa-management/docs/commands/DeactivateAccount.md +65 -0
  425. package/src/modules/coa-management/docs/commands/DeactivateChartOfAccounts.md +44 -0
  426. package/src/modules/coa-management/docs/commands/DeleteAccount.md +52 -0
  427. package/src/modules/coa-management/docs/commands/DeleteAccountGroup.md +50 -0
  428. package/src/modules/coa-management/docs/commands/DeleteChartOfAccounts.md +48 -0
  429. package/src/modules/coa-management/docs/commands/MoveAccountGroup.md +57 -0
  430. package/src/modules/coa-management/docs/commands/ReactivateAccount.md +50 -0
  431. package/src/modules/coa-management/docs/commands/UpdateAccount.md +102 -0
  432. package/src/modules/coa-management/docs/commands/UpdateAccountGroup.md +62 -0
  433. package/src/modules/coa-management/docs/commands/UpdateChartOfAccounts.md +49 -0
  434. package/src/modules/coa-management/docs/features/account-group-hierarchy.md +81 -0
  435. package/src/modules/coa-management/docs/features/account-lifecycle.md +80 -0
  436. package/src/modules/coa-management/docs/features/account-management.md +114 -0
  437. package/src/modules/coa-management/docs/features/chart-of-accounts-setup.md +86 -0
  438. package/src/modules/coa-management/docs/models/Account.md +84 -0
  439. package/src/modules/coa-management/docs/models/AccountGroup.md +55 -0
  440. package/src/modules/coa-management/docs/models/ChartOfAccounts.md +65 -0
  441. package/src/modules/coa-management/docs/queries/DetectCircularReference.md +52 -0
  442. package/src/modules/coa-management/docs/queries/GetAccount.md +42 -0
  443. package/src/modules/coa-management/docs/queries/GetAccountGroup.md +42 -0
  444. package/src/modules/coa-management/docs/queries/GetChartOfAccounts.md +48 -0
  445. package/src/modules/coa-management/docs/queries/ListAccountGroups.md +42 -0
  446. package/src/modules/coa-management/docs/queries/ListAccounts.md +54 -0
  447. package/src/modules/coa-management/docs/queries/ListUnassignedAccounts.md +40 -0
  448. package/src/modules/coa-management/executor/.gitkeep +0 -0
  449. package/src/modules/coa-management/generated/.gitkeep +0 -0
  450. package/src/modules/coa-management/generated/enums.ts +45 -0
  451. package/src/modules/coa-management/generated/kysely-tailordb.ts +81 -0
  452. package/src/modules/coa-management/index.ts +2 -0
  453. package/src/modules/coa-management/lib/_db_deps.ts +17 -0
  454. package/src/modules/coa-management/lib/errors.generated.ts +162 -0
  455. package/src/modules/coa-management/lib/errors.ts +0 -0
  456. package/src/modules/coa-management/lib/permissions.generated.ts +20 -0
  457. package/src/modules/coa-management/lib/types.ts +22 -0
  458. package/src/modules/coa-management/module.ts +136 -0
  459. package/src/modules/coa-management/permissions.ts +3 -0
  460. package/src/modules/coa-management/query/.gitkeep +0 -0
  461. package/src/modules/coa-management/query/detectCircularReference.generated.ts +5 -0
  462. package/src/modules/coa-management/query/detectCircularReference.test.ts +88 -0
  463. package/src/modules/coa-management/query/detectCircularReference.ts +46 -0
  464. package/src/modules/coa-management/query/getAccount.generated.ts +5 -0
  465. package/src/modules/coa-management/query/getAccount.test.ts +55 -0
  466. package/src/modules/coa-management/query/getAccount.ts +25 -0
  467. package/src/modules/coa-management/query/getAccountGroup.generated.ts +5 -0
  468. package/src/modules/coa-management/query/getAccountGroup.test.ts +55 -0
  469. package/src/modules/coa-management/query/getAccountGroup.ts +25 -0
  470. package/src/modules/coa-management/query/getChartOfAccounts.generated.ts +5 -0
  471. package/src/modules/coa-management/query/getChartOfAccounts.test.ts +79 -0
  472. package/src/modules/coa-management/query/getChartOfAccounts.ts +28 -0
  473. package/src/modules/coa-management/query/listAccountGroups.generated.ts +5 -0
  474. package/src/modules/coa-management/query/listAccountGroups.test.ts +72 -0
  475. package/src/modules/coa-management/query/listAccountGroups.ts +49 -0
  476. package/src/modules/coa-management/query/listAccounts.generated.ts +5 -0
  477. package/src/modules/coa-management/query/listAccounts.test.ts +136 -0
  478. package/src/modules/coa-management/query/listAccounts.ts +82 -0
  479. package/src/modules/coa-management/query/listUnassignedAccounts.generated.ts +5 -0
  480. package/src/modules/coa-management/query/listUnassignedAccounts.test.ts +96 -0
  481. package/src/modules/coa-management/query/listUnassignedAccounts.ts +39 -0
  482. package/src/modules/coa-management/tailor.config.ts +13 -0
  483. package/src/modules/coa-management/tailor.d.ts +13 -0
  484. package/src/modules/coa-management/testing/fixtures.ts +201 -0
  485. package/src/modules/item-management/README.md +1 -1
  486. package/src/modules/organization/README.md +57 -0
  487. package/src/modules/organization/command/.gitkeep +0 -0
  488. package/src/modules/organization/command/activateCompany.generated.ts +6 -0
  489. package/src/modules/organization/command/activateCompany.test.ts +184 -0
  490. package/src/modules/organization/command/activateCompany.ts +92 -0
  491. package/src/modules/organization/command/createCompany.generated.ts +6 -0
  492. package/src/modules/organization/command/createCompany.test.ts +156 -0
  493. package/src/modules/organization/command/createCompany.ts +80 -0
  494. package/src/modules/organization/command/createDepartment.generated.ts +6 -0
  495. package/src/modules/organization/command/createDepartment.test.ts +239 -0
  496. package/src/modules/organization/command/createDepartment.ts +98 -0
  497. package/src/modules/organization/command/createSite.generated.ts +6 -0
  498. package/src/modules/organization/command/createSite.test.ts +262 -0
  499. package/src/modules/organization/command/createSite.ts +155 -0
  500. package/src/modules/organization/command/deactivateCompany.generated.ts +6 -0
  501. package/src/modules/organization/command/deactivateCompany.test.ts +58 -0
  502. package/src/modules/organization/command/deactivateCompany.ts +47 -0
  503. package/src/modules/organization/command/deactivateDepartment.generated.ts +6 -0
  504. package/src/modules/organization/command/deactivateDepartment.test.ts +115 -0
  505. package/src/modules/organization/command/deactivateDepartment.ts +63 -0
  506. package/src/modules/organization/command/deactivateSite.generated.ts +6 -0
  507. package/src/modules/organization/command/deactivateSite.test.ts +53 -0
  508. package/src/modules/organization/command/deactivateSite.ts +47 -0
  509. package/src/modules/organization/command/deleteCompany.generated.ts +6 -0
  510. package/src/modules/organization/command/deleteCompany.test.ts +99 -0
  511. package/src/modules/organization/command/deleteCompany.ts +66 -0
  512. package/src/modules/organization/command/reactivateCompany.generated.ts +6 -0
  513. package/src/modules/organization/command/reactivateCompany.test.ts +58 -0
  514. package/src/modules/organization/command/reactivateCompany.ts +47 -0
  515. package/src/modules/organization/command/reactivateDepartment.generated.ts +6 -0
  516. package/src/modules/organization/command/reactivateDepartment.test.ts +59 -0
  517. package/src/modules/organization/command/reactivateDepartment.ts +47 -0
  518. package/src/modules/organization/command/reactivateSite.generated.ts +6 -0
  519. package/src/modules/organization/command/reactivateSite.test.ts +53 -0
  520. package/src/modules/organization/command/reactivateSite.ts +47 -0
  521. package/src/modules/organization/command/updateCompany.generated.ts +6 -0
  522. package/src/modules/organization/command/updateCompany.test.ts +239 -0
  523. package/src/modules/organization/command/updateCompany.ts +127 -0
  524. package/src/modules/organization/command/updateDepartment.generated.ts +6 -0
  525. package/src/modules/organization/command/updateDepartment.test.ts +232 -0
  526. package/src/modules/organization/command/updateDepartment.ts +120 -0
  527. package/src/modules/organization/command/updateSite.generated.ts +6 -0
  528. package/src/modules/organization/command/updateSite.test.ts +274 -0
  529. package/src/modules/organization/command/updateSite.ts +176 -0
  530. package/src/modules/organization/db/.gitkeep +0 -0
  531. package/src/modules/organization/db/company.ts +44 -0
  532. package/src/modules/organization/db/department.ts +46 -0
  533. package/src/modules/organization/db/site.ts +44 -0
  534. package/src/modules/organization/docs/commands/ActivateCompany.md +62 -0
  535. package/src/modules/organization/docs/commands/CreateCompany.md +49 -0
  536. package/src/modules/organization/docs/commands/CreateDepartment.md +62 -0
  537. package/src/modules/organization/docs/commands/CreateSite.md +74 -0
  538. package/src/modules/organization/docs/commands/DeactivateCompany.md +40 -0
  539. package/src/modules/organization/docs/commands/DeactivateDepartment.md +44 -0
  540. package/src/modules/organization/docs/commands/DeactivateSite.md +38 -0
  541. package/src/modules/organization/docs/commands/DeleteCompany.md +50 -0
  542. package/src/modules/organization/docs/commands/ReactivateCompany.md +39 -0
  543. package/src/modules/organization/docs/commands/ReactivateDepartment.md +37 -0
  544. package/src/modules/organization/docs/commands/ReactivateSite.md +37 -0
  545. package/src/modules/organization/docs/commands/UpdateCompany.md +58 -0
  546. package/src/modules/organization/docs/commands/UpdateDepartment.md +64 -0
  547. package/src/modules/organization/docs/commands/UpdateSite.md +80 -0
  548. package/src/modules/organization/docs/features/company-lifecycle.md +76 -0
  549. package/src/modules/organization/docs/features/department-management.md +66 -0
  550. package/src/modules/organization/docs/features/site-management.md +86 -0
  551. package/src/modules/organization/docs/models/Company.md +60 -0
  552. package/src/modules/organization/docs/models/Department.md +57 -0
  553. package/src/modules/organization/docs/models/Site.md +57 -0
  554. package/src/modules/organization/docs/queries/DetectDepartmentCircularReference.md +50 -0
  555. package/src/modules/organization/docs/queries/GetCompany.md +40 -0
  556. package/src/modules/organization/docs/queries/GetDepartment.md +44 -0
  557. package/src/modules/organization/docs/queries/GetDepartmentChildren.md +40 -0
  558. package/src/modules/organization/docs/queries/GetSite.md +37 -0
  559. package/src/modules/organization/docs/queries/ListDepartmentsByCompany.md +54 -0
  560. package/src/modules/organization/docs/queries/ListSitesByCompany.md +54 -0
  561. package/src/modules/organization/executor/.gitkeep +0 -0
  562. package/src/modules/organization/generated/.gitkeep +0 -0
  563. package/src/modules/organization/generated/kysely-tailordb.ts +77 -0
  564. package/src/modules/organization/index.ts +2 -0
  565. package/src/modules/organization/lib/_db_deps.ts +10 -0
  566. package/src/modules/organization/lib/errors.generated.ts +117 -0
  567. package/src/modules/organization/lib/errors.ts +1 -0
  568. package/src/modules/organization/lib/permissions.generated.ts +19 -0
  569. package/src/modules/organization/lib/types.ts +16 -0
  570. package/src/modules/organization/module.ts +89 -0
  571. package/src/modules/organization/permissions.ts +3 -0
  572. package/src/modules/organization/query/.gitkeep +0 -0
  573. package/src/modules/organization/query/detectDepartmentCircularReference.generated.ts +5 -0
  574. package/src/modules/organization/query/detectDepartmentCircularReference.test.ts +102 -0
  575. package/src/modules/organization/query/detectDepartmentCircularReference.ts +27 -0
  576. package/src/modules/organization/query/getCompany.generated.ts +5 -0
  577. package/src/modules/organization/query/getCompany.test.ts +70 -0
  578. package/src/modules/organization/query/getCompany.ts +16 -0
  579. package/src/modules/organization/query/getDepartment.generated.ts +5 -0
  580. package/src/modules/organization/query/getDepartment.test.ts +85 -0
  581. package/src/modules/organization/query/getDepartment.ts +17 -0
  582. package/src/modules/organization/query/getDepartmentChildren.generated.ts +5 -0
  583. package/src/modules/organization/query/getDepartmentChildren.test.ts +75 -0
  584. package/src/modules/organization/query/getDepartmentChildren.ts +21 -0
  585. package/src/modules/organization/query/getSite.generated.ts +5 -0
  586. package/src/modules/organization/query/getSite.test.ts +55 -0
  587. package/src/modules/organization/query/getSite.ts +16 -0
  588. package/src/modules/organization/query/listDepartmentsByCompany.generated.ts +5 -0
  589. package/src/modules/organization/query/listDepartmentsByCompany.test.ts +124 -0
  590. package/src/modules/organization/query/listDepartmentsByCompany.ts +43 -0
  591. package/src/modules/organization/query/listSitesByCompany.generated.ts +5 -0
  592. package/src/modules/organization/query/listSitesByCompany.test.ts +126 -0
  593. package/src/modules/organization/query/listSitesByCompany.ts +41 -0
  594. package/src/modules/organization/tailor.config.ts +13 -0
  595. package/src/modules/organization/tailor.d.ts +13 -0
  596. package/src/modules/organization/testing/fixtures.ts +155 -0
  597. package/src/modules/primitives/README.md +1 -1
  598. package/src/modules/primitives/command/setBaseCurrency.test.ts +8 -64
  599. package/src/modules/primitives/command/setBaseCurrency.ts +6 -64
  600. package/src/modules/primitives/docs/commands/ActivateCategory.md +1 -1
  601. package/src/modules/primitives/docs/commands/ActivateCurrency.md +1 -1
  602. package/src/modules/primitives/docs/commands/ActivateUnit.md +1 -1
  603. package/src/modules/primitives/docs/commands/CreateExchangeRate.md +2 -2
  604. package/src/modules/primitives/docs/commands/CreateUnit.md +1 -1
  605. package/src/modules/primitives/docs/commands/DeactivateCategory.md +1 -1
  606. package/src/modules/primitives/docs/commands/DeactivateCurrency.md +1 -1
  607. package/src/modules/primitives/docs/commands/DeactivateUnit.md +1 -1
  608. package/src/modules/primitives/docs/commands/SetBaseCurrency.md +13 -23
  609. package/src/modules/primitives/docs/commands/SetReferenceUnit.md +1 -1
  610. package/src/modules/primitives/docs/features/currency-definitions.md +13 -14
  611. package/src/modules/primitives/docs/models/Currency.md +3 -4
  612. package/src/modules/primitives/docs/queries/ConvertAmount.md +2 -2
  613. package/src/modules/primitives/docs/queries/ConvertQuantity.md +2 -2
  614. package/src/modules/primitives/lib/errors.generated.ts +5 -0
  615. package/src/modules/product-management/README.md +1 -1
  616. package/src/modules/user-management/docs/commands/CreatePermission.md +3 -3
  617. package/src/modules/user-management/docs/commands/CreateRole.md +1 -1
  618. package/src/modules/user-management/docs/queries/ListRolePermissionsByRole.md +39 -0
  619. package/src/modules/user-management/docs/queries/ListUserRolesByUser.md +39 -0
  620. package/src/modules/user-management/generated/enums.ts +0 -15
  621. package/src/modules/user-management/generated/kysely-tailordb.ts +0 -11
  622. package/src/shared/createContext.ts +2 -1
  623. package/src/shared/defineQuery.ts +36 -1
  624. package/src/shared/requirePermission.ts +3 -3
  625. package/src/shared/types.ts +3 -0
  626. package/templates/scaffold/app/backend/package.json +8 -7
  627. package/templates/scaffold/app/frontend/eslint.config.js +12 -0
  628. package/templates/scaffold/app/frontend/package.json +19 -16
  629. package/templates/scaffold/app/frontend/src/hooks/use-toast.ts +30 -0
  630. package/templates/scaffold/app/frontend/src/pages/user-management/user/create/components/create-user-form.tsx +3 -2
  631. package/templates/scaffold/app/frontend/vite.config.ts +5 -5
  632. package/templates/workflows/erp-kit-check.yml +2 -2
  633. package/src/commands/module/list.test.ts +0 -57
  634. package/src/commands/module/list.ts +0 -64
  635. package/src/commands/scaffold.ts +0 -176
  636. /package/src/modules/{accounting → audit/db}/.gitkeep +0 -0
  637. /package/src/modules/audit/{.gitkeep → executor/.gitkeep} +0 -0
  638. /package/src/modules/{coa-management/.gitkeep → audit/lib/errors.ts} +0 -0
  639. /package/src/modules/{supplier-management → business-partner}/.gitkeep +0 -0
@@ -0,0 +1,126 @@
1
+ # Audit Event Logging
2
+
3
+ ## Overview
4
+
5
+ Audit Event Logging captures create, update, and delete operations performed across all ERP modules as immutable audit entries. Each entry records a unique `eventId` for deduplication and idempotency, an optional `correlationId` for grouping related events within a logical operation (e.g., bulk updates, cascading workflows, saga steps), the actor identity (actorType and actorId, with optional metadata such as IP address, user agent, sessionId, requestId, or onBehalfOf reference), the operation type (CREATE, UPDATE, DELETE), the affected entity reference (entityType and entityId), and a precise timestamp. Entries may optionally be scoped by companyId for multi-company data isolation; entries for global entities (e.g., user accounts, currencies) omit companyId and are queryable without company scope.
6
+
7
+ Audit entries are strictly append-only — once written, they cannot be modified or deleted. This immutability guarantee is fundamental to compliance, forensic investigation, and regulatory reporting. The audit log serves as a universal change history that is queryable by actor, entity type, specific record, operation type, and date range, enabling both real-time monitoring and historical analysis of system activity.
8
+
9
+ Other modules emit audit events by calling the audit module's ingestion interface. Every audit event submitted must include: `eventId` (UUID, unique, used for idempotency), `actorType` (USER/SYSTEM/SERVICE), `actorId`, `entityType` (must match a registered auditable entity), `entityId`, `operationType` (CREATE/UPDATE/DELETE), and `changes` (array of ChangeDetail). Optional fields include: `correlationId` (UUID, groups related events from a single logical operation), `onBehalfOf` (delegated userId), `actorMetadata` (ipAddress, userAgent, sessionId, requestId), and `companyId` (required for company-bound entities, must be null for global entities). The timestamp is always system-generated at ingestion time and cannot be supplied or overridden by the caller, ensuring a consistent and tamper-resistant time source.
10
+
11
+ The audit module enforces idempotency on `eventId` — submitting the same eventId more than once silently discards the duplicate (returns success, no new entry). This allows emitting modules to safely retry on transient failures. Ingestion is eventually consistent with the source operation: emitting modules complete their own database transaction first, then submit the audit event. The audit module does not participate in the emitting module's transaction, preventing audit failures from blocking business operations. If the audit write fails permanently after retries, the emitting module logs the failure for operational alerting. Duplicate eventIds are silently accepted; missing required fields, unknown entityTypes, and scope mismatches are rejected with validation errors; events for entity + operation combinations with no active policy are silently discarded.
12
+
13
+ Modules that want their entities to be auditable register them with the audit module at initialization time, declaring `entityName`, `entityScope` (COMPANY_BOUND or GLOBAL), and `auditableFields` (list of field names eligible for audit capture). The audit module validates that `entityType` in incoming events matches a registered entity.
14
+
15
+ ## Business Purpose
16
+
17
+ Audit Event Logging provides an append-only record of data mutations captured by the system. Because ingestion is eventually consistent and decoupled from source transactions, coverage is best-effort — not every mutation is guaranteed to be recorded (see ingestion contract above). However, once an entry is written it is immutable and cannot be altered or deleted. This enables:
18
+
19
+ - Regulatory compliance: immutable, append-only logs support SOX, GDPR, and industry-specific audit trail requirements (applications requiring guaranteed capture should layer an outbox or replay mechanism on top)
20
+ - Forensic investigation: security teams can trace exactly who or what changed data, when, and from where
21
+ - Operational accountability: managers can review change history for any business record to understand how it reached its current state
22
+ - Multi-company isolation: company-scoped audit entries ensure each legal entity's audit trail is independently queryable
23
+ - Global entity auditing: changes to foundational, non-company-scoped entities (users, currencies, UoM) are captured without requiring a company context
24
+ - Change attribution: every mutation is linked to a specific actor identity (human user, system process, or service account), eliminating ambiguity about responsibility
25
+
26
+ The append-only constraint on stored entries ensures:
27
+
28
+ - Historical integrity of recorded entries is preserved — no entry can be retroactively altered or removed
29
+ - Stored audit trails remain admissible for external auditors and regulators
30
+ - System administrators cannot suppress evidence of recorded operations
31
+
32
+ ## Process Flow
33
+
34
+ ```mermaid
35
+ flowchart TD
36
+ A[Actor Performs Operation] --> B[System Identifies Operation Type]
37
+ B --> C{Operation Type}
38
+ C -->|CREATE| D[Capture New Entity State]
39
+ C -->|UPDATE| E[Capture Changed Entity State]
40
+ C -->|DELETE| F[Capture Deleted Entity Reference]
41
+ D --> G[Resolve Actor Identity]
42
+ E --> G
43
+ F --> G
44
+ G --> H[Record actorType + actorId + Metadata]
45
+ H --> I[Determine Scope: company or global]
46
+ I --> J[Build AuditEntry]
47
+ J --> K[Write Immutable Entry]
48
+ K --> L[Entry Available for Query]
49
+ ```
50
+
51
+ Querying the audit log follows this pattern. All query paths enforce authorization — the caller must hold `viewAuditHistory` (for detail queries) or `viewAuditSummary` (for aggregate queries) at the appropriate scope:
52
+
53
+ ```mermaid
54
+ flowchart TD
55
+ A[Query Request] --> AA{Caller holds required permission?}
56
+ AA -->|No| AB[Reject: insufficient permissions]
57
+ AA -->|Yes| B{Filter Criteria}
58
+ B --> C[By Actor: actorType + actorId]
59
+ B --> D[By Entity Type + Entity ID]
60
+ B --> E[By Operation Type]
61
+ B --> F[By Date Range]
62
+ C --> G{Scope Filter}
63
+ D --> G
64
+ E --> G
65
+ F --> G
66
+ G -->|companyId provided| H[Return company-scoped entries\nif caller has company-scoped permission]
67
+ G -->|no companyId| I[Return global entries only\nif caller has global-scoped permission]
68
+ G -->|both requested| J[Return combined results\nfor scopes caller is authorized for]
69
+ ```
70
+
71
+ ## Scenario Patterns
72
+
73
+ - **Record Creation Tracking**: A user creates a new sales order. The system writes an AuditEntry with operation CREATE, actorType = USER, the actor's actorId (userId), the entityType (e.g., SalesOrder), the new record's entityId, companyId, and a timestamp — providing a permanent record of who created the order and when
74
+ - **Field Update History**: A user modifies the status of a purchase order. An AuditEntry with operation UPDATE is appended, capturing the actor identity and the affected entity reference, enabling reviewers to see the full change timeline for that record
75
+ - **Deletion Accountability**: An operator deletes a draft invoice. An AuditEntry with operation DELETE is written, ensuring the deletion is permanently recorded even though the original record no longer exists
76
+ - **System-Initiated Change**: A scheduled job recalculates exchange rates. The system writes an AuditEntry with actorType = SYSTEM, actorId referencing the job identifier, and no companyId (global scope), capturing automated changes with the same fidelity as human-initiated ones
77
+ - **Delegated Action**: An administrator performs an action on behalf of another user. The AuditEntry records actorType = USER, actorId = the administrator's userId, and onBehalfOf = the target user's userId, preserving the full delegation chain
78
+ - **Security Investigation**: A security team investigates suspicious activity by querying all audit entries for a specific actorId within a date range, across all operation types, to reconstruct the actor's actions
79
+ - **Compliance Audit**: An external auditor requests the full change history for a specific entity (e.g., all mutations to a particular general ledger account) by querying audit entries filtered by entityType and entityId within a fiscal period
80
+ - **Multi-Company Audit Isolation**: A parent organization runs an audit for one subsidiary. Queries are scoped by companyId, returning only audit entries belonging to that legal entity without leaking data from other companies
81
+ - **Global Entity Audit**: An auditor reviews all changes to user accounts (a non-company-scoped entity). Queries omit companyId to retrieve the full global change history for user-management entities
82
+
83
+ ## Test Cases
84
+
85
+ - Every accepted audit event for an entity and operation type that has an active policy produces exactly one AuditEntry — except UPDATE events with an empty changes array (zero-delta), which are silently accepted as a no-op without creating an AuditEntry
86
+ - AuditEntry contains required fields: eventId, actorType, actorId, entityType, entityId, operationType, and timestamp
87
+ - companyId is required for company-scoped entities and optional (null) for global entities
88
+ - actorType is required and must be one of USER, SYSTEM, or SERVICE
89
+ - actorId is required and must be non-empty regardless of actorType
90
+ - onBehalfOf is optional and stores the delegated user's identity when an action is performed on behalf of another actor
91
+ - AuditEntries cannot be modified after creation — update attempts are rejected
92
+ - AuditEntries cannot be deleted — delete attempts are rejected
93
+ - Actor metadata (IP address, user agent) is optional and stored when provided
94
+ - Querying by actorId returns only entries for that actor
95
+ - Querying by entityType and entityId returns the full change history for a specific record
96
+ - Querying by operationType filters entries to only the specified operation (CREATE, UPDATE, or DELETE)
97
+ - Querying by date range returns only entries whose timestamp falls within the range
98
+ - Querying with companyId returns only entries scoped to that company
99
+ - Querying without companyId returns only global (company-unscoped) entries
100
+ - An operation performed without a valid actor identity (actorType + actorId) is rejected
101
+ - Timestamp is system-generated and cannot be overridden by the caller
102
+ - Audit entries are returned in chronological order by default
103
+ - Large result sets support pagination for efficient retrieval
104
+ - Every audit event must include a unique eventId (UUID)
105
+ - Submitting an event with a duplicate eventId is silently accepted without creating a new entry
106
+ - correlationId is optional and links related events from a single logical operation
107
+ - Events referencing an unregistered entityType are rejected with a validation error
108
+ - Events with companyId on a global entity are rejected with a validation error
109
+ - Events with null companyId on a company-bound entity are rejected with a validation error
110
+ - Events for an entity + operation with no active policy are silently discarded
111
+ - Entity registration requires entityName, entityScope, and auditableFields
112
+ - actorMetadata (ipAddress, userAgent, sessionId, requestId) is optional and stored when provided
113
+ - Querying audit entries requires the `viewAuditHistory` permission at the appropriate scope
114
+ - A caller with company-scoped `viewAuditHistory` can only retrieve entries for their assigned companies
115
+ - A caller with global-scoped `viewAuditHistory` can retrieve global (company-unscoped) entries
116
+ - A caller without `viewAuditHistory` permission receives an authorization error when querying audit entries
117
+ - Querying audit summaries requires the `viewAuditSummary` permission at the appropriate scope
118
+
119
+ ## Reference Links
120
+
121
+ - [Odoo Mail Tracking (mail.thread)](https://www.odoo.com/documentation/19.0/developer/reference/backend/orm.html#odoo.models.Model)
122
+ - [Dynamics 365 Database Logging (sysdatabaselog)](https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/sysadmin/configure-manage-database-log)
123
+ - [SAP Change Document Header (CDHDR)](https://help.sap.com/docs/SAP_S4HANA_ON-PREMISE/f76052846e404ea8811eb06534b6e099/5e13e82b8ca111d1a6090000e8353423.html)
124
+ - [Oracle Financials Audit Trail](https://docs.oracle.com/en/cloud/saas/financials/24d/faigl/overview-of-financials.html)
125
+ - [AWS CloudTrail Documentation](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-user-guide.html)
126
+ - [AWS CloudTrail userIdentity](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference-user-identity.html)
@@ -0,0 +1,135 @@
1
+ # Audit Policy Configuration
2
+
3
+ ## Overview
4
+
5
+ Audit Policy Configuration allows administrators to define precisely which entities, fields, and operation types are tracked by the audit system. Rather than auditing everything indiscriminately — which carries significant performance costs — policies let organizations target the data changes that matter most for compliance, security, and operational oversight. Each policy specifies a target entity, a single operation type (CREATE, UPDATE, or DELETE) to record, the subset of fields to capture, and how sensitive field values should be handled during capture. To audit multiple operation types on the same entity, administrators create separate policies — one per operation type.
6
+
7
+ Policies follow a lifecycle state machine (DRAFT → ACTIVE ↔ INACTIVE) that enables safe rollout and rollback of audit configurations. A policy in DRAFT can be reviewed and refined before it takes effect; activating a policy begins audit capture; deactivating a policy stops capture without losing the policy definition or any previously recorded audit entries. Policies can be scoped globally or to a specific company, supporting multi-company deployments where different legal entities have different compliance requirements.
8
+
9
+ A uniqueness constraint ensures that at most one ACTIVE policy exists for a given combination of (entityName, companyId, operationType). This eliminates ambiguity about which field set applies and avoids unintended field-set merges. To change the audited fields for an existing active configuration, use the atomic replacement operation (`replaceAuditPolicy`), which deactivates the current ACTIVE policy and activates the replacement DRAFT policy in a single transaction — guaranteeing no gap in audit coverage. Alternatively, an administrator can manually deactivate the current policy and then activate the replacement, but this two-step approach creates a brief window where no policy is active and events for that entity + operation combination will be silently discarded.
10
+
11
+ Policy scope must match the target entity's scope to eliminate precedence ambiguity. Company-bound entities (entities that always carry a companyId) can only have company-scoped policies — global policies (companyId = null) are rejected at creation time for company-bound entities. Conversely, global entities (entities that never carry a companyId, e.g., user accounts, currencies) can only have global policies — company-scoped policies are rejected at creation time for global entities. This scope-matching rule ensures that for any given operation, exactly zero or one ACTIVE policy can match. Entity scope (company-bound vs global) is declared at entity registration time and is immutable. Because entityName and companyId are immutable after creation, the audit module validates this scope-matching constraint at policy creation time only.
12
+
13
+ ## Business Purpose
14
+
15
+ - Regulatory compliance demands that certain fields (e.g., financial amounts, approval statuses) are tracked, while auditing every field is unnecessary and expensive
16
+ - Security teams need visibility into sensitive data changes (e.g., user role assignments, payment details) without flooding the audit log with noise
17
+ - Sensitive data protection requires that field values containing personal data, credentials, or payment details are masked, hashed, or excluded from audit logs to prevent the audit trail from becoming a secondary store of sensitive information
18
+ - Performance optimization requires granular control — Dynamics 365, SAP, and other major ERPs explicitly warn that broad audit configurations degrade system throughput
19
+ - Multi-company organizations need different audit policies per legal entity to reflect varying local regulatory requirements
20
+ - Safe rollout of audit changes is critical — a misconfigured policy should be testable in DRAFT before it starts capturing production data
21
+ - Rollback capability allows administrators to deactivate a policy immediately if it causes performance issues or captures unintended data
22
+
23
+ ## Process Flow
24
+
25
+ ```mermaid
26
+ stateDiagram-v2
27
+ [*] --> Draft: createAuditPolicy
28
+ Draft --> Active: activateAuditPolicy
29
+ Draft --> Active: replaceAuditPolicy (atomic swap:\ndeactivates current ACTIVE,\nactivates this DRAFT)
30
+ Active --> Inactive: deactivateAuditPolicy
31
+ Inactive --> Active: reactivateAuditPolicy
32
+ Draft --> [*]: deleteAuditPolicy
33
+ ```
34
+
35
+ The policy creation and configuration flow follows this pattern:
36
+
37
+ ```mermaid
38
+ flowchart TD
39
+ A[Create Audit Policy] --> B[Specify Target Entity]
40
+ B --> C[Select Operation Type]
41
+ C --> D[Add Field-Level Rules]
42
+ D --> D2[Set Sensitivity Mode per Field]
43
+ D2 --> E{All fields configured?}
44
+ E -->|No| D
45
+ E -->|Yes| F{Ready to activate?}
46
+ F -->|No| G[Continue editing]
47
+ G --> B
48
+ F -->|Yes| H{Conflicting ACTIVE policy exists?}
49
+ H -->|Yes| H2{Use atomic replacement?}
50
+ H2 -->|Yes| H3[replaceAuditPolicy:\nDeactivate existing + Activate new\nin single transaction]
51
+ H2 -->|No| H4[Reject: deactivate existing first]
52
+ H3 --> J[Audit Capture Begins]
53
+ H -->|No| I[Activate Policy]
54
+ I --> J
55
+ ```
56
+
57
+ When a data operation occurs, the system evaluates active policies to determine whether to record an audit entry. Because policy scope must match entity scope, the lookup is unambiguous — there is at most one matching ACTIVE policy:
58
+
59
+ ```mermaid
60
+ flowchart TD
61
+ A[Data Operation Occurs] --> B{Entity is company-bound?}
62
+ B -->|Yes| C[Look up ACTIVE policy for entity + companyId + operation]
63
+ B -->|No| D[Look up ACTIVE global policy for entity + operation]
64
+ C --> E{Policy found?}
65
+ D --> E
66
+ E -->|No| F[No audit entry]
67
+ E -->|Yes| G{Field-level rules defined?}
68
+ G -->|No| H[Audit all eligible fields on entity]
69
+ G -->|Yes| I[Audit only specified fields]
70
+ H --> J[Apply sensitivity modes to values]
71
+ I --> J
72
+ J --> K[Record Audit Entry with processed values]
73
+ ```
74
+
75
+ ## Scenario Patterns
76
+
77
+ - **Compliance-Driven Field Tracking**: A financial services company creates two policies — one for UPDATE and one for DELETE — on the Journal Entry entity, each tracking only the amount, account, and approval status fields. This satisfies SOX requirements without capturing every minor metadata change
78
+ - **Security Monitoring**: An administrator creates three policies (one per operation type: CREATE, UPDATE, DELETE) on the User Role Assignment entity, each capturing every field. This provides a complete record of privilege changes for security review
79
+ - **Sensitive Data Protection**: A policy auditing the Employee entity marks the socialSecurityNumber field with sensitivity mode EXCLUDE (omitted from audit logs entirely) and the salary field with MASK (stored as a masked value like "***"). This ensures the audit trail records that these fields changed without exposing the raw values
80
+ - **Gradual Rollout**: A new audit policy for the Sales Order entity is created in DRAFT and reviewed by the compliance team. After validation, it is activated in a single company first, then additional company-scoped policies are created and activated for other subsidiaries
81
+ - **Performance Remediation**: An overly broad policy auditing all fields on a high-volume entity is deactivated immediately when monitoring reveals degraded throughput. A replacement policy targeting only critical fields is created and activated
82
+ - **Multi-Company Differentiation**: A global organization creates separate policies for its EU and US subsidiaries — the EU policy audits personal data fields to comply with GDPR (with appropriate masking), while the US policy focuses on financial fields for SOX compliance
83
+ - **Atomic Policy Replacement**: An administrator needs to add new fields to an active policy. They create a new DRAFT policy with the updated field set and use `replaceAuditPolicy` to atomically deactivate the existing ACTIVE policy and activate the replacement in a single transaction — ensuring no gap in audit coverage
84
+ - **Manual Policy Replacement (with gap)**: An administrator deactivates the existing ACTIVE policy and then activates a replacement DRAFT policy in two separate steps. During the window between deactivation and activation, events for that entity + operation are silently discarded. This approach is acceptable when a brief audit gap is tolerable
85
+ - **Policy Cleanup**: A DRAFT policy that was created for evaluation but never needed is deleted without affecting any audit data
86
+
87
+ ## Test Cases
88
+
89
+ - Audit policy lifecycle follows DRAFT → ACTIVE ↔ INACTIVE state machine
90
+ - Policies can only be created in DRAFT status
91
+ - Only DRAFT policies can be deleted; ACTIVE and INACTIVE policies cannot
92
+ - Target entity name is required and must be non-empty
93
+ - Exactly one operation type (CREATE, UPDATE, or DELETE) must be specified per policy
94
+ - Operation types are limited to the set CREATE, UPDATE, DELETE — invalid values are rejected
95
+ - A policy without field-level rules audits all eligible fields on the target entity (as defined by the entity's auditableFields registration)
96
+ - A policy with field-level rules audits only the specified fields (which must be a subset of the entity's auditableFields)
97
+ - Field names in field-level rules must be non-empty strings
98
+ - Duplicate field names within the same policy are rejected
99
+ - Activating a policy without a target entity is rejected
100
+ - A policy must have exactly one operation type — policies without an operation type cannot be activated
101
+ - Only ACTIVE policies trigger audit capture; DRAFT and INACTIVE policies do not
102
+ - Deactivating a policy stops new audit capture but does not delete previously recorded audit entries
103
+ - Reactivating an INACTIVE policy resumes audit capture
104
+ - Company-scoped policies apply only to operations within that company
105
+ - A global policy (no company scope) applies to operations on global entities
106
+ - At most one ACTIVE policy can exist for a given (entityName, companyId, operationType) combination — activating a conflicting policy is rejected
107
+ - Activating a policy that would conflict with an existing ACTIVE policy returns an error instructing the administrator to deactivate the existing policy first
108
+ - Creating a global policy (companyId = null) for a company-bound entity is rejected
109
+ - Creating a company-scoped policy for a global entity is rejected
110
+ - Entity scope (company-bound vs global) is validated at policy creation time
111
+ - For any given entity + operation, at most one ACTIVE policy can match because policy scope must align with entity scope
112
+ - Deleting an ACTIVE or INACTIVE policy returns an error
113
+ - Each field-level rule supports a sensitivity mode: CAPTURE (default, store raw value), MASK (store masked representation), HASH (store one-way hash), or EXCLUDE (omit value entirely)
114
+ - When sensitivity mode is MASK, the stored value replaces all but the last 4 characters with asterisks (or stores "***" if the value is 4 characters or fewer)
115
+ - When sensitivity mode is HASH, the stored value is a one-way hash of the original value
116
+ - When sensitivity mode is EXCLUDE, the ChangeDetail records that the field changed but stores null for both oldValue and newValue
117
+ - Fields without an explicit sensitivity mode default to CAPTURE (raw value stored)
118
+ - Sensitivity modes are enforced at write time — once an audit entry is persisted, the original raw value cannot be recovered from MASK, HASH, or EXCLUDE entries
119
+ - `replaceAuditPolicy` atomically deactivates the current ACTIVE policy and activates the replacement DRAFT policy in a single transaction
120
+ - `replaceAuditPolicy` requires the replacement policy to be in DRAFT status — non-DRAFT replacements are rejected
121
+ - `replaceAuditPolicy` requires an existing ACTIVE policy for the same (entityName, companyId, operationType) — if none exists, the operation is rejected (use `activateAuditPolicy` instead)
122
+ - After `replaceAuditPolicy`, the previously active policy is in INACTIVE status and the replacement is in ACTIVE status
123
+ - During manual (non-atomic) replacement, events arriving between deactivation and activation are silently discarded
124
+ - Creating, updating, activating, deactivating, reactivating, replacing, and deleting audit policies requires the `manageAuditPolicies` permission at the appropriate scope
125
+ - A caller with company-scoped `manageAuditPolicies` can only manage policies for their assigned companies
126
+ - A caller with global-scoped `manageAuditPolicies` can only manage global policies
127
+ - A caller without `manageAuditPolicies` permission receives an authorization error when attempting policy operations
128
+
129
+ ## Reference Links
130
+
131
+ - [Dynamics 365 Configure Entity and Attribute Auditing](https://learn.microsoft.com/en-us/power-platform/admin/manage-dataverse-auditing)
132
+ - [SAP Change Document Objects](https://help.sap.com/docs/ABAP_PLATFORM_NEW/b5670aaaa2364a29935f40b16499972d/4873464e4cf611d3a6510000e835363f.html)
133
+ - [Odoo Tracking Field Changes](https://www.odoo.com/documentation/19.0/developer/reference/backend/orm.html)
134
+ - [AWS CloudTrail Trail Configuration](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-concepts.html)
135
+ - [Microsoft Dataverse Auditing](https://learn.microsoft.com/en-us/power-platform/admin/manage-dataverse-auditing)
@@ -0,0 +1,95 @@
1
+ # Field-Level Change Tracking
2
+
3
+ ## Overview
4
+
5
+ Field-Level Change Tracking records the old and new values for each individual field that changes within a business operation, enabling precise before/after comparison at the most granular level. Each change detail is linked to a parent audit entry and captures the field name, previous value, and new value as serialized strings. This approach follows the common ERP pattern of a ChangeDetail entity associated with a parent AuditEntry by auditEntryId.
6
+
7
+ Not all field types are eligible for audit capture. Scalar fields (string, number, boolean, date, enum) and short text fields (≤ 4,000 characters) are serialized as strings and stored as-is. Long text fields (> 4,000 characters) are truncated to 4,000 characters with a `[truncated]` suffix. Rich text / HTML fields are stripped to plain text, then truncated to 4,000 characters if needed. Binary / BLOB fields are excluded from audit capture entirely and are not eligible for policy field rules. File attachment references capture only the reference metadata (file ID, filename, size), not the file content. Relation fields (foreign keys) capture the foreign key ID as a string. Collection / array fields are serialized as JSON strings and truncated to 4,000 characters if the serialized form exceeds the limit. Computed / derived fields are excluded from audit capture because they are not persisted state. Fields not listed in the entity's `auditableFields` registration are never captured regardless of type. The 4,000-character truncation limit applies after sensitivity mode processing (MASK/HASH/CAPTURE).
8
+
9
+ By storing values as serialized strings regardless of the original data type, the system maintains a uniform storage model while supporting any eligible field type. For CREATE operations the old value is null and the new value contains the initial value; for DELETE operations the old value contains the last known value and the new value is null; for UPDATE operations both values are populated but only for fields that actually changed. This design keeps the audit trail compact while providing full traceability.
10
+
11
+ Field values are subject to the sensitivity mode defined in the applicable audit policy. Fields configured as MASK store a masked representation, HASH fields store a one-way hash, and EXCLUDE fields record that a change occurred but store null for both old and new values. This ensures that change tracking does not become a secondary store of sensitive data.
12
+
13
+ ## Business Purpose
14
+
15
+ - **Regulatory compliance**: Auditors and regulators require evidence of exactly what changed, when, and by whom — field-level tracking satisfies SOX, GDPR, and similar mandates that demand detailed change history
16
+ - **Sensitive data protection**: Field values are processed through sensitivity modes (CAPTURE, MASK, HASH, EXCLUDE) defined in audit policies before storage, preventing the audit trail from containing raw personal data, credentials, or payment information
17
+ - **Dispute resolution**: When discrepancies arise (e.g., pricing, quantities, addresses), field-level history provides an authoritative record of the before and after state for each affected field
18
+ - **Operational debugging**: Support teams can quickly identify which field change caused a downstream issue without reviewing entire record snapshots
19
+ - **Rollback analysis**: Knowing the previous value of each changed field enables informed decisions about whether and how to revert a change
20
+ - **Change auditing across modules**: Every transactional module (accounting, sales, purchasing, inventory) and global modules (user-management, primitives) benefit from a single, consistent mechanism for capturing field-level mutations
21
+
22
+ ## Process Flow
23
+
24
+ ```mermaid
25
+ flowchart TD
26
+ A[Business Operation Occurs] --> B[Determine Operation Type]
27
+ B --> C{CREATE / UPDATE / DELETE?}
28
+ C -->|CREATE| D[Capture all initial field values]
29
+ C -->|UPDATE| E[Compare old and new record states]
30
+ C -->|DELETE| F[Capture all last-known field values]
31
+ D --> G[Generate ChangeDetail entries\noldValue = null, newValue = initial value]
32
+ E --> H[Generate ChangeDetail entries\nonly for fields that differ]
33
+ F --> I[Generate ChangeDetail entries\noldValue = last value, newValue = null]
34
+ G --> J[Apply sensitivity modes from policy]
35
+ H --> J
36
+ I --> J
37
+ J --> K[Link ChangeDetail records to parent AuditEntry]
38
+ K --> L[Persist AuditEntry + ChangeDetails]
39
+ ```
40
+
41
+ ```mermaid
42
+ flowchart TD
43
+ A[Query Audit History for Record] --> B[Retrieve AuditEntry list]
44
+ B --> C[Select AuditEntry of interest]
45
+ C --> D[Retrieve linked ChangeDetail records]
46
+ D --> E[Display field-by-field before/after comparison]
47
+ ```
48
+
49
+ ## Scenario Patterns
50
+
51
+ - **Price Change Audit**: A sales manager updates the unit price on a quotation line. The system records a ChangeDetail with fieldName = unitPrice, oldValue = "100.00", and newValue = "95.00", providing a clear trail for margin analysis and approval workflows
52
+ - **Address Correction**: A customer service representative corrects a shipping address. Each modified address field (street, city, postalCode) produces its own ChangeDetail entry, allowing reviewers to see exactly which parts of the address were changed
53
+ - **Sensitive Field Masking**: An HR administrator updates an employee's salary. The policy defines salary with sensitivity mode MASK, so the ChangeDetail stores oldValue = "***" and newValue = "***", recording that the field changed without exposing the actual values
54
+ - **Excluded Field**: A system updates a user's password hash. The policy defines passwordHash with sensitivity mode EXCLUDE, so the ChangeDetail stores fieldName = "passwordHash" with both oldValue = null and newValue = null, recording that the field changed without storing any value
55
+ - **Record Creation Baseline**: When a new purchase order is created, ChangeDetail entries are generated for every populated field with oldValue = null, establishing the initial baseline for all future change comparisons
56
+ - **Record Deletion Snapshot**: When a draft invoice is deleted, ChangeDetail entries capture the last known value of every field with newValue = null, preserving a complete snapshot of the record at the time of deletion
57
+ - **Bulk Field Update**: An administrator updates the cost center on 50 inventory items. Each item's audit entry contains a single ChangeDetail for the costCenter field, keeping the audit trail compact by recording only the field that changed rather than full record snapshots
58
+ - **No-Op Detection**: A user saves a record without modifying any fields. No ChangeDetail entries are created because no field values differ, avoiding unnecessary audit noise
59
+
60
+ ## Test Cases
61
+
62
+ - Each ChangeDetail record is linked to exactly one parent AuditEntry via auditEntryId
63
+ - ChangeDetail captures fieldName, oldValue, and newValue as serialized strings
64
+ - For a CREATE operation, oldValue is null and newValue contains the initial field value
65
+ - For a DELETE operation, oldValue contains the last known value and newValue is null
66
+ - For an UPDATE operation, both oldValue and newValue are populated
67
+ - UPDATE operations generate ChangeDetail entries only for fields whose values actually changed
68
+ - Fields that did not change during an UPDATE are not recorded as ChangeDetail entries
69
+ - A save operation with no field changes produces zero ChangeDetail entries
70
+ - Multiple field changes within a single operation each produce a separate ChangeDetail record under the same AuditEntry
71
+ - fieldName is required and must be non-empty on every ChangeDetail record
72
+ - Values are stored as serialized strings regardless of the original data type (integer, boolean, date, etc.)
73
+ - Querying ChangeDetails by auditEntryId returns all field-level changes for that audit entry
74
+ - ChangeDetail records are immutable once persisted — they cannot be updated or deleted
75
+ - A CREATE operation on a record with N populated fields produces N ChangeDetail entries
76
+ - When the policy sensitivity mode for a field is MASK, oldValue and newValue store masked representations
77
+ - When the policy sensitivity mode for a field is HASH, oldValue and newValue store one-way hashes
78
+ - When the policy sensitivity mode for a field is EXCLUDE, both oldValue and newValue are null, but the ChangeDetail record is still created to indicate the field changed
79
+ - Fields without an explicit sensitivity mode in the policy default to CAPTURE (raw values stored)
80
+ - Binary / BLOB fields are excluded from audit capture and cannot be added to policy field rules
81
+ - Computed / derived fields are excluded from audit capture
82
+ - Long text values exceeding 4,000 characters are truncated with a `[truncated]` suffix
83
+ - Rich text / HTML field values are stripped to plain text before storage
84
+ - Collection / array field values are serialized as JSON strings
85
+ - Serialized values exceeding 4,000 characters are truncated with a `[truncated]` suffix
86
+ - Relation field values capture the foreign key ID as a string
87
+ - File attachment fields capture the reference metadata (file ID, filename, size), not the file content
88
+ - Truncation is applied after sensitivity mode processing
89
+
90
+ ## Reference Links
91
+
92
+ - [SAP Change Documents (CDHDR/CDPOS)](https://help.sap.com/doc/saphelp_nw75/7.5.5/en-US/4d/b2ba6fbb571e57e10000000a42189b/frameset.htm)
93
+ - [Odoo Mail Tracking Values](https://www.odoo.com/documentation/19.0/developer/reference/backend/logging.html)
94
+ - [Dynamics 365 Auditing Overview](https://learn.microsoft.com/en-us/power-platform/admin/manage-dataverse-auditing)
95
+ - [AWS CloudTrail Log Event Reference](https://docs.aws.amazon.com/awscloudtrail/latest/userguide/cloudtrail-event-reference.html)
@@ -0,0 +1,55 @@
1
+ # AuditEntry
2
+
3
+ ## Description
4
+
5
+ AuditEntry represents a single immutable record of a data mutation captured by the audit system. Each entry records a unique `eventId` for deduplication and idempotency, the actor identity (actorType and actorId, with optional metadata such as IP address, user agent, sessionId, requestId, or onBehalfOf reference), the operation type (CREATE, UPDATE, DELETE), the affected entity reference (entityType and entityId), and a system-generated timestamp. Entries may be scoped by companyId for multi-company data isolation; entries for global entities omit companyId. An optional correlationId groups related events within a logical operation (e.g., bulk updates, cascading workflows).
6
+
7
+ Examples: "User jdoe updated SalesOrder SO-1234 at 2025-01-15T10:30:00Z", "System job recalculated exchange rates (global scope, correlationId groups all rate changes)".
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ AppendOnly
14
+
15
+ ### Command Definitions
16
+
17
+ - [logAuditEvent](../commands/LogAuditEvent.md) - Ingest an audit event, creating an AuditEntry with linked ChangeDetail records
18
+
19
+ ### Query Definitions
20
+
21
+ - [getAuditEntry](../queries/GetAuditEntry.md) - Retrieve a single audit entry by ID
22
+ - [searchAuditEntries](../queries/SearchAuditEntries.md) - Search and filter audit entries by actor, entity, operation type, date range, and company scope
23
+ - [getAuditSummary](../queries/GetAuditSummary.md) - Retrieve aggregated audit entry counts grouped by configurable dimensions
24
+
25
+ ### Models
26
+
27
+ - AuditEntry
28
+ - ChangeDetail
29
+
30
+ ### Invariants
31
+
32
+ - eventId is globally unique (UUID) and used for idempotency — duplicate eventId submissions are silently discarded (returns success, no new entry created)
33
+ - Once persisted, an AuditEntry cannot be modified or deleted (append-only, immutable)
34
+ - actorType is required and must be one of USER, SYSTEM, or SERVICE
35
+ - actorId is required and must be non-empty regardless of actorType
36
+ - An operation submitted without a valid actor identity (actorType + actorId) is rejected
37
+ - entityType must reference a registered auditable entity — events referencing an unregistered entityType are rejected with a validation error
38
+ - operationType must be one of CREATE, UPDATE, or DELETE
39
+ - companyId is required for company-bound entities and must be null for global entities — scope mismatches are rejected with a validation error
40
+ - timestamp is system-generated at ingestion time and cannot be supplied or overridden by the caller
41
+ - correlationId is optional (UUID) and groups related events from a single logical operation (e.g., bulk updates, cascading workflows, saga steps)
42
+ - onBehalfOf is optional and stores the delegated user's identity when an action is performed on behalf of another actor
43
+ - actorMetadata (ipAddress, userAgent, sessionId, requestId) is optional and stored when provided
44
+ - An event for an entity + operation combination with no active policy is silently discarded (no entry created)
45
+ - Audit entries are returned in chronological order by default
46
+ - Large result sets support pagination for efficient retrieval
47
+ - Querying audit entries requires the `viewAuditHistory` permission at the appropriate scope
48
+ - Querying audit summaries requires the `viewAuditSummary` permission at the appropriate scope
49
+
50
+ ### Relationships
51
+
52
+ - **Has Many ChangeDetail**: Each AuditEntry has zero or more ChangeDetail records linked by auditEntryId, capturing field-level changes
53
+ - **References AuditableEntity**: entityType must match a registered AuditableEntity's entityName
54
+ - **References user-management (cross-module)**: actorId references a userId when actorType is USER; onBehalfOf references a delegated userId
55
+ - **References organization (cross-module)**: companyId references a Company from the organization module for company-scoped entries
@@ -0,0 +1,79 @@
1
+ # AuditPolicy
2
+
3
+ ## Description
4
+
5
+ AuditPolicy defines which entities, operations, and fields are tracked by the audit system. Each policy targets a specific entity and operation type, with an optional company scope for multi-company deployments. Policies follow a lifecycle state machine (DRAFT → ACTIVE ↔ INACTIVE) that enables safe rollout and rollback. Only ACTIVE policies trigger audit capture. A uniqueness constraint ensures at most one ACTIVE policy exists for a given (entityName, companyId, operationType) combination. Policy scope must match the target entity's scope — company-bound entities require company-scoped policies; global entities require global policies.
6
+
7
+ Examples: "ACTIVE policy auditing UPDATE on SalesOrder for company-123", "DRAFT policy under review for DELETE on JournalEntry".
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ Stateful
14
+
15
+ #### State Transitions
16
+
17
+ ```mermaid
18
+ stateDiagram-v2
19
+ [*] --> Draft: createAuditPolicy
20
+ Draft --> Active: activateAuditPolicy
21
+ Draft --> Active: replaceAuditPolicy (atomic swap)
22
+ Active --> Inactive: deactivateAuditPolicy
23
+ Inactive --> Active: reactivateAuditPolicy
24
+ Draft --> [*]: deleteAuditPolicy
25
+ note right of Draft: Only DRAFT policies can be deleted
26
+ note right of Active: At most one ACTIVE per (entityName, companyId, operationType)
27
+ ```
28
+
29
+ ### Command Definitions
30
+
31
+ - [createAuditPolicy](../commands/CreateAuditPolicy.md) - Create a new audit policy in DRAFT status
32
+ - [updateAuditPolicy](../commands/UpdateAuditPolicy.md) - Modify a DRAFT audit policy's configuration
33
+ - [activateAuditPolicy](../commands/ActivateAuditPolicy.md) - Transition a DRAFT policy to ACTIVE
34
+ - [deactivateAuditPolicy](../commands/DeactivateAuditPolicy.md) - Transition an ACTIVE policy to INACTIVE
35
+ - [reactivateAuditPolicy](../commands/ReactivateAuditPolicy.md) - Transition an INACTIVE policy back to ACTIVE
36
+ - [replaceAuditPolicy](../commands/ReplaceAuditPolicy.md) - Atomically deactivate current ACTIVE and activate a DRAFT replacement
37
+ - [deleteAuditPolicy](../commands/DeleteAuditPolicy.md) - Permanently remove a DRAFT policy
38
+
39
+ ### Query Definitions
40
+
41
+ - [getAuditPolicy](../queries/GetAuditPolicy.md) - Retrieve a single audit policy by ID
42
+ - [listAuditPolicies](../queries/ListAuditPolicies.md) - List audit policies with optional filters
43
+
44
+ ### Models
45
+
46
+ - AuditPolicy
47
+ - PolicyFieldRule
48
+
49
+ ### Invariants
50
+
51
+ - Policies are always created in DRAFT status
52
+ - At most one ACTIVE policy can exist for a given (entityName, companyId, operationType) combination — activating a conflicting policy is rejected with an error instructing the administrator to deactivate the existing policy first
53
+ - Only DRAFT policies can be deleted; ACTIVE and INACTIVE policies cannot — deleting an ACTIVE or INACTIVE policy returns an error
54
+ - Target entity name is required and must be non-empty
55
+ - Target entity name must reference a registered AuditableEntity
56
+ - Exactly one operation type (CREATE, UPDATE, or DELETE) must be specified per policy — policies without an operation type cannot be activated
57
+ - Operation types are limited to the set CREATE, UPDATE, DELETE — invalid values are rejected
58
+ - Activating a policy without a target entity is rejected
59
+ - Policy scope must match the target entity's scope: company-bound entities require company-scoped policies (companyId set); global entities require global policies (companyId null) — mismatches are rejected at creation time
60
+ - entityName and companyId are immutable after creation — scope is validated only at creation time; updates cannot change the policy's scope
61
+ - Only ACTIVE policies trigger audit capture; DRAFT and INACTIVE policies do not
62
+ - Deactivating a policy stops new audit capture but does not delete previously recorded audit entries
63
+ - Reactivating an INACTIVE policy resumes audit capture
64
+ - A policy without field-level rules audits all eligible fields on the target entity (as defined by the entity's auditableFields registration)
65
+ - A policy with field-level rules audits only the specified fields (which must be a subset of the entity's auditableFields)
66
+ - `replaceAuditPolicy` atomically deactivates the current ACTIVE policy and activates the replacement DRAFT policy in a single transaction
67
+ - `replaceAuditPolicy` requires the replacement policy to be in DRAFT status — non-DRAFT replacements are rejected
68
+ - `replaceAuditPolicy` requires an existing ACTIVE policy for the same (entityName, companyId, operationType) — if none exists, the operation is rejected (use `activateAuditPolicy` instead)
69
+ - After `replaceAuditPolicy`, the previously active policy is in INACTIVE status and the replacement is in ACTIVE status
70
+ - During manual (non-atomic) replacement, events arriving between deactivation and activation are silently discarded
71
+ - Creating, updating, activating, deactivating, reactivating, replacing, and deleting audit policies requires the `manageAuditPolicies` permission at the appropriate scope
72
+ - A caller with company-scoped `manageAuditPolicies` can only manage policies for their assigned companies
73
+ - A caller with global-scoped `manageAuditPolicies` can only manage global policies
74
+
75
+ ### Relationships
76
+
77
+ - **Has Many PolicyFieldRule**: Each AuditPolicy has zero or more PolicyFieldRule records defining field-level audit configuration
78
+ - **References AuditableEntity**: entityName must match a registered AuditableEntity
79
+ - **References organization (cross-module)**: companyId references a Company from the organization module for company-scoped policies
@@ -0,0 +1,38 @@
1
+ # AuditableEntity
2
+
3
+ ## Description
4
+
5
+ AuditableEntity represents a registration record for an entity that is eligible for audit tracking. Other modules register their entities at initialization time by declaring the entityName, entityScope (COMPANY_BOUND or GLOBAL), and the list of auditableFields eligible for capture. The audit module validates incoming audit events against registered entities — events referencing an unregistered entityType are rejected. Entity scope is immutable once registered and determines whether policies and audit entries for this entity must be company-scoped or global.
6
+
7
+ Examples: "SalesOrder registered as COMPANY_BOUND with fields [{fieldName: 'amount', fieldType: 'scalar'}, {fieldName: 'status', fieldType: 'scalar'}, {fieldName: 'customerId', fieldType: 'relation'}]", "User registered as GLOBAL with fields [{fieldName: 'email', fieldType: 'scalar'}, {fieldName: 'role', fieldType: 'scalar'}, {fieldName: 'isActive', fieldType: 'scalar'}]".
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ Standard
14
+
15
+ ### Command Definitions
16
+
17
+ - [registerAuditableEntity](../commands/RegisterAuditableEntity.md) - Register a new entity for audit tracking
18
+
19
+ ### Query Definitions
20
+
21
+ None. AuditableEntity registrations are referenced internally by the audit module during policy creation and event ingestion for validation purposes.
22
+
23
+ ### Models
24
+
25
+ - AuditableEntity
26
+
27
+ ### Invariants
28
+
29
+ - entityName is required, must be non-empty, and must be unique across all registrations
30
+ - entityScope must be one of COMPANY_BOUND or GLOBAL and is immutable once registered
31
+ - auditableFields is a JSON-serialized array of field definitions (`{fieldName: string, fieldType?: string}`) eligible for audit capture; must contain at least one entry
32
+ - Only the following fieldType values are eligible for inclusion in auditableFields: `scalar`, `richtext`, `array`, `relation`, `file`. Any other fieldType (e.g. `binary`, `computed`) is rejected
33
+
34
+ ### Relationships
35
+
36
+ - **Referenced By AuditEntry**: AuditEntry.entityType must match a registered AuditableEntity.entityName
37
+ - **Referenced By AuditPolicy**: AuditPolicy.entityName must match a registered AuditableEntity.entityName
38
+ - **Referenced By emitting modules (reverse dependency)**: Other ERP modules register their auditable entities at initialization time
@@ -0,0 +1,55 @@
1
+ # ChangeDetail
2
+
3
+ ## Description
4
+
5
+ ChangeDetail records the old and new values for a single field that changed within a business operation. Each ChangeDetail is linked to a parent AuditEntry via auditEntryId and captures the fieldName, previous value (oldValue), and new value (newValue) as serialized strings. Values are subject to the sensitivity mode defined in the applicable audit policy: CAPTURE stores raw values, MASK stores masked representations, HASH stores one-way hashes, and EXCLUDE stores null for both values while still recording that the field changed. Values exceeding 4,000 characters are truncated with a `[truncated]` suffix after sensitivity mode processing.
6
+
7
+ Examples: "unitPrice changed from '100.00' to '95.00'", "salary changed (MASK mode: '***' to '***')".
8
+
9
+ ## Domain Model Definitions
10
+
11
+ ### Model type
12
+
13
+ AppendOnly
14
+
15
+ ### Command Definitions
16
+
17
+ - [logAuditEvent](../commands/LogAuditEvent.md) - ChangeDetail records are created as part of the logAuditEvent command, linked to the parent AuditEntry
18
+
19
+ ### Query Definitions
20
+
21
+ - [getChangeDetails](../queries/GetChangeDetails.md) - Retrieve all field-level change details for a given audit entry
22
+
23
+ ### Models
24
+
25
+ - ChangeDetail
26
+
27
+ ### Invariants
28
+
29
+ - Each ChangeDetail belongs to exactly one parent AuditEntry via auditEntryId
30
+ - fieldName is required and must be non-empty
31
+ - Values are stored as serialized strings regardless of original data type (integer, boolean, date, etc.)
32
+ - For CREATE operations, oldValue is null and newValue contains the initial value
33
+ - For DELETE operations, oldValue contains the last known value and newValue is null
34
+ - For UPDATE operations, both oldValue and newValue are populated, but only for fields that actually changed
35
+ - A save operation with no field changes produces zero ChangeDetail entries (no-op detection)
36
+ - A CREATE operation on a record with N populated fields produces N ChangeDetail entries
37
+ - Once persisted, ChangeDetail records cannot be modified or deleted (immutable)
38
+ - Binary / BLOB fields are excluded from capture entirely and are not eligible for policy field rules
39
+ - Computed / derived fields are excluded from capture
40
+ - Long text fields (> 4,000 characters) are truncated to 4,000 characters with a `[truncated]` suffix
41
+ - Rich text / HTML field values are stripped to plain text before storage, then truncated if needed
42
+ - Collection / array field values are serialized as JSON strings and truncated to 4,000 characters if the serialized form exceeds the limit
43
+ - Relation field values capture the foreign key ID as a string
44
+ - File attachment fields capture reference metadata (file ID, filename, size), not the file content
45
+ - Truncation is applied after sensitivity mode processing (MASK/HASH/CAPTURE)
46
+ - When sensitivity mode is CAPTURE (default), raw values are stored
47
+ - When sensitivity mode is MASK, oldValue and newValue store masked representations
48
+ - When sensitivity mode is HASH, oldValue and newValue store one-way hashes
49
+ - When sensitivity mode is EXCLUDE, both oldValue and newValue are null, but the ChangeDetail record is still created to indicate the field changed
50
+ - Fields without an explicit sensitivity mode in the policy default to CAPTURE
51
+
52
+ ### Relationships
53
+
54
+ - **Belongs To AuditEntry**: Each ChangeDetail is linked to exactly one AuditEntry via auditEntryId
55
+ - **Governed By PolicyFieldRule**: Sensitivity mode applied to stored values is determined by the PolicyFieldRule associated with the parent AuditEntry's applicable AuditPolicy