@rytass/bpm-core-nestjs-module 0.1.1 → 0.1.3

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 (610) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/eslint.config.mjs +20 -0
  3. package/jest.config.cts +15 -0
  4. package/package.json +3 -3
  5. package/project.json +58 -0
  6. package/src/lib/attachment/attachment-options.spec.ts +45 -0
  7. package/src/lib/attachment/attachment-options.ts +130 -0
  8. package/src/lib/attachment/attachment-storage.provider.spec.ts +79 -0
  9. package/src/lib/attachment/attachment-storage.provider.ts +60 -0
  10. package/src/lib/attachment/{attachment-storage.token.d.ts → attachment-storage.token.ts} +3 -1
  11. package/src/lib/attachment/attachment.controller.ts +39 -0
  12. package/src/lib/attachment/attachment.entity.ts +59 -0
  13. package/src/lib/attachment/attachment.module.ts +149 -0
  14. package/src/lib/attachment/attachment.mutations.ts +22 -0
  15. package/src/lib/attachment/attachment.queries.ts +65 -0
  16. package/src/lib/attachment/attachment.service.spec.ts +319 -0
  17. package/src/lib/attachment/attachment.service.ts +477 -0
  18. package/src/lib/attachment/dto/upload-attachment.input.ts +60 -0
  19. package/src/lib/bpm/bpm-root.module.spec.ts +138 -0
  20. package/src/lib/bpm/bpm-root.module.ts +320 -0
  21. package/src/lib/bpm-auth/bpm-auth-context.extractor.ts +106 -0
  22. package/src/lib/bpm-auth/bpm-auth-context.ts +40 -0
  23. package/src/lib/bpm-auth/bpm-auth.authorization.ts +80 -0
  24. package/src/lib/bpm-auth/bpm-auth.decorators.ts +19 -0
  25. package/src/lib/bpm-auth/bpm-auth.guard.ts +31 -0
  26. package/src/lib/bpm-auth/bpm-auth.module.spec.ts +106 -0
  27. package/src/lib/bpm-auth/bpm-auth.module.ts +69 -0
  28. package/src/lib/bpm-auth/bpm-auth.options.ts +21 -0
  29. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.ts +55 -0
  30. package/src/lib/common/filters/all-exceptions.filter.ts +138 -0
  31. package/src/lib/common/iso-duration.ts +31 -0
  32. package/src/lib/condition/condition.module.ts +8 -0
  33. package/src/lib/condition/condition.service.spec.ts +50 -0
  34. package/src/lib/condition/condition.service.ts +141 -0
  35. package/src/lib/database/data-source.ts +7 -0
  36. package/src/lib/database/migration-runner.ts +24 -0
  37. package/src/lib/database/typeorm.config.ts +217 -0
  38. package/src/lib/delegation/delegation-rule.entity.ts +84 -0
  39. package/src/lib/delegation/delegation.enums.ts +21 -0
  40. package/src/lib/delegation/delegation.module.ts +14 -0
  41. package/src/lib/delegation/delegation.mutations.ts +92 -0
  42. package/src/lib/delegation/delegation.queries.ts +111 -0
  43. package/src/lib/delegation/delegation.service.spec.ts +295 -0
  44. package/src/lib/delegation/delegation.service.ts +482 -0
  45. package/src/lib/delegation/dto/delegation-rule.input.ts +125 -0
  46. package/src/lib/form/dto/form-definition.input.ts +73 -0
  47. package/src/lib/form/form-definition-version.entity.ts +66 -0
  48. package/src/lib/form/form-definition.entity.ts +54 -0
  49. package/src/lib/form/form-schema-lint.object.ts +10 -0
  50. package/src/lib/form/form-schema.validator.spec.ts +180 -0
  51. package/src/lib/form/form-schema.validator.ts +538 -0
  52. package/src/lib/form/form.enums.ts +20 -0
  53. package/src/lib/form/form.module.ts +19 -0
  54. package/src/lib/form/form.mutations.ts +68 -0
  55. package/src/lib/form/form.queries.ts +72 -0
  56. package/src/lib/form/form.service.spec.ts +144 -0
  57. package/src/lib/form/form.service.ts +490 -0
  58. package/src/lib/identity/identity-options.ts +41 -0
  59. package/src/lib/identity/identity.module.ts +96 -0
  60. package/src/lib/identity/identity.queries.ts +61 -0
  61. package/src/lib/identity/identity.service.spec.ts +105 -0
  62. package/src/lib/identity/identity.service.ts +200 -0
  63. package/src/lib/identity/member-base.adapter.spec.ts +77 -0
  64. package/src/lib/identity/member-base.adapter.ts +244 -0
  65. package/src/lib/identity/member-metadata-cache.entity.ts +27 -0
  66. package/src/lib/identity/member-profile.object.ts +28 -0
  67. package/src/lib/identity/{member-resolver.interface.d.ts → member-resolver.interface.ts} +11 -5
  68. package/src/lib/migrations/0000000000001-enable-postgres-extensions.ts +14 -0
  69. package/src/lib/migrations/{0000000001000-identity-organization-foundation.js → 0000000001000-identity-organization-foundation.ts} +38 -29
  70. package/src/lib/migrations/{0000000002000-form-builder-foundation.js → 0000000002000-form-builder-foundation.ts} +22 -21
  71. package/src/lib/migrations/{0000000003000-approval-template-foundation.js → 0000000003000-approval-template-foundation.ts} +24 -21
  72. package/src/lib/migrations/{0000000004000-workflow-engine-foundation.js → 0000000004000-workflow-engine-foundation.ts} +46 -34
  73. package/src/lib/migrations/{0000000005000-delegation-rules.js → 0000000005000-delegation-rules.ts} +20 -19
  74. package/src/lib/migrations/{0000000006000-notifications-sla.js → 0000000006000-notifications-sla.ts} +26 -23
  75. package/src/lib/migrations/{0000000007000-signatures-attachments.js → 0000000007000-signatures-attachments.ts} +28 -27
  76. package/src/lib/migrations/{0000000008000-approval-template-categories.js → 0000000008000-approval-template-categories.ts} +28 -25
  77. package/src/lib/migrations/{0000000009000-task-candidates.js → 0000000009000-task-candidates.ts} +26 -23
  78. package/src/lib/migrations/{0000000010000-notification-delivery-state.js → 0000000010000-notification-delivery-state.ts} +16 -17
  79. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.ts +19 -0
  80. package/src/lib/migrations/0000000012000-notification-sla-idempotency.ts +22 -0
  81. package/src/lib/migrations/0000000013000-workflow-query-indexes.ts +113 -0
  82. package/src/lib/migrations/index.ts +47 -0
  83. package/src/lib/notification/dto/notification-preference.input.ts +46 -0
  84. package/src/lib/notification/notification-delivery-scheduler.service.ts +69 -0
  85. package/src/lib/notification/notification-delivery.service.spec.ts +315 -0
  86. package/src/lib/notification/notification-delivery.service.ts +339 -0
  87. package/src/lib/notification/notification-dispatcher.token.ts +13 -0
  88. package/src/lib/notification/notification-options.module.ts +67 -0
  89. package/src/lib/notification/notification-options.spec.ts +93 -0
  90. package/src/lib/notification/notification-options.ts +452 -0
  91. package/src/lib/notification/notification-preference.entity.ts +35 -0
  92. package/src/lib/notification/notification-sla-scheduler.service.ts +66 -0
  93. package/src/lib/notification/notification-template.spec.ts +33 -0
  94. package/src/lib/notification/notification-template.ts +97 -0
  95. package/src/lib/notification/notification.entity.ts +102 -0
  96. package/src/lib/notification/notification.enums.ts +45 -0
  97. package/src/lib/notification/notification.module.ts +37 -0
  98. package/src/lib/notification/notification.mutations.ts +47 -0
  99. package/src/lib/notification/notification.queries.ts +64 -0
  100. package/src/lib/notification/notification.service.spec.ts +412 -0
  101. package/src/lib/notification/notification.service.ts +974 -0
  102. package/src/lib/organization/dto/manager-resolution.input.ts +91 -0
  103. package/src/lib/organization/dto/membership.input.ts +77 -0
  104. package/src/lib/organization/dto/org-unit.input.ts +96 -0
  105. package/src/lib/organization/dto/position.input.ts +50 -0
  106. package/src/lib/organization/json-metadata.ts +15 -0
  107. package/src/lib/organization/manager-resolution.entity.ts +44 -0
  108. package/src/lib/organization/membership.entity.ts +50 -0
  109. package/src/lib/organization/org-unit-tree-commit-result.object.ts +8 -0
  110. package/src/lib/organization/org-unit.entity.ts +53 -0
  111. package/src/lib/organization/organization-summary.object.ts +16 -0
  112. package/src/lib/organization/organization.enums.ts +19 -0
  113. package/src/lib/organization/organization.module.ts +27 -0
  114. package/src/lib/organization/organization.mutations.ts +115 -0
  115. package/src/lib/organization/organization.queries.ts +188 -0
  116. package/src/lib/organization/organization.service.spec.ts +760 -0
  117. package/src/lib/organization/organization.service.ts +1216 -0
  118. package/src/lib/organization/position.entity.ts +39 -0
  119. package/src/lib/organization/resolved-manager.object.ts +10 -0
  120. package/src/lib/signature/signature-options.spec.ts +60 -0
  121. package/src/lib/signature/signature-options.ts +120 -0
  122. package/src/lib/signature/signature-verification.object.ts +16 -0
  123. package/src/lib/signature/signature.entity.ts +62 -0
  124. package/src/lib/signature/signature.module.ts +80 -0
  125. package/src/lib/signature/signature.queries.ts +37 -0
  126. package/src/lib/signature/signature.service.spec.ts +134 -0
  127. package/src/lib/signature/signature.service.ts +205 -0
  128. package/src/lib/template/approval-template-category.entity.ts +40 -0
  129. package/src/lib/template/approval-template-version.entity.ts +82 -0
  130. package/src/lib/template/approval-template.entity.ts +64 -0
  131. package/src/lib/template/dto/approval-template.input.ts +147 -0
  132. package/src/lib/template/template.enums.ts +30 -0
  133. package/src/lib/template/template.module.ts +25 -0
  134. package/src/lib/template/template.mutations.ts +106 -0
  135. package/src/lib/template/template.queries.ts +119 -0
  136. package/src/lib/template/template.service.spec.ts +476 -0
  137. package/src/lib/template/template.service.ts +820 -0
  138. package/src/lib/template/workflow-definition.validator.spec.ts +595 -0
  139. package/src/lib/template/workflow-definition.validator.ts +539 -0
  140. package/src/lib/testing/cel-js.jest.ts +46 -0
  141. package/src/lib/workflow-engine/activity-log.entity.ts +48 -0
  142. package/src/lib/workflow-engine/approval-instance-page-info.object.ts +22 -0
  143. package/src/lib/workflow-engine/approval-instance.entity.ts +86 -0
  144. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.ts +18 -0
  145. package/src/lib/workflow-engine/dto/decide-task.input.ts +33 -0
  146. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.ts +22 -0
  147. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.ts +22 -0
  148. package/src/lib/workflow-engine/dto/submit-approval-instance.input.ts +27 -0
  149. package/src/lib/workflow-engine/task-candidate.entity.ts +56 -0
  150. package/src/lib/workflow-engine/task-decision.entity.ts +43 -0
  151. package/src/lib/workflow-engine/task.entity.ts +85 -0
  152. package/src/lib/workflow-engine/workflow-condition-evaluator.ts +168 -0
  153. package/src/lib/workflow-engine/workflow-dashboard-summary.object.ts +22 -0
  154. package/src/lib/workflow-engine/workflow-dry-run.object.ts +58 -0
  155. package/src/lib/workflow-engine/workflow-engine.enums.ts +99 -0
  156. package/src/lib/workflow-engine/workflow-engine.module.ts +75 -0
  157. package/src/lib/workflow-engine/workflow-engine.mutations.ts +85 -0
  158. package/src/lib/workflow-engine/workflow-engine.queries.ts +229 -0
  159. package/src/lib/workflow-engine/workflow-engine.service.spec.ts +2689 -0
  160. package/src/lib/workflow-engine/workflow-engine.service.ts +4707 -0
  161. package/src/lib/workflow-engine/{workflow-engine.tokens.d.ts → workflow-engine.tokens.ts} +9 -3
  162. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.ts +69 -0
  163. package/src/lib/workflow-engine/workflow-token.entity.ts +40 -0
  164. package/tsconfig.json +24 -0
  165. package/tsconfig.lib.json +16 -0
  166. package/tsconfig.spec.json +16 -0
  167. package/LICENSE +0 -21
  168. package/src/index.js +0 -18
  169. package/src/index.js.map +0 -1
  170. package/src/lib/attachment/attachment-options.d.ts +0 -44
  171. package/src/lib/attachment/attachment-options.js +0 -62
  172. package/src/lib/attachment/attachment-options.js.map +0 -1
  173. package/src/lib/attachment/attachment-storage.provider.d.ts +0 -4
  174. package/src/lib/attachment/attachment-storage.provider.js +0 -35
  175. package/src/lib/attachment/attachment-storage.provider.js.map +0 -1
  176. package/src/lib/attachment/attachment-storage.token.js +0 -5
  177. package/src/lib/attachment/attachment-storage.token.js.map +0 -1
  178. package/src/lib/attachment/attachment.controller.d.ts +0 -8
  179. package/src/lib/attachment/attachment.controller.js +0 -38
  180. package/src/lib/attachment/attachment.controller.js.map +0 -1
  181. package/src/lib/attachment/attachment.entity.d.ts +0 -14
  182. package/src/lib/attachment/attachment.entity.js +0 -74
  183. package/src/lib/attachment/attachment.entity.js.map +0 -1
  184. package/src/lib/attachment/attachment.module.d.ts +0 -27
  185. package/src/lib/attachment/attachment.module.js +0 -103
  186. package/src/lib/attachment/attachment.module.js.map +0 -1
  187. package/src/lib/attachment/attachment.mutations.d.ts +0 -8
  188. package/src/lib/attachment/attachment.mutations.js +0 -35
  189. package/src/lib/attachment/attachment.mutations.js.map +0 -1
  190. package/src/lib/attachment/attachment.queries.d.ts +0 -9
  191. package/src/lib/attachment/attachment.queries.js +0 -78
  192. package/src/lib/attachment/attachment.queries.js.map +0 -1
  193. package/src/lib/attachment/attachment.service.d.ts +0 -49
  194. package/src/lib/attachment/attachment.service.js +0 -276
  195. package/src/lib/attachment/attachment.service.js.map +0 -1
  196. package/src/lib/attachment/dto/upload-attachment.input.d.ts +0 -11
  197. package/src/lib/attachment/dto/upload-attachment.input.js +0 -68
  198. package/src/lib/attachment/dto/upload-attachment.input.js.map +0 -1
  199. package/src/lib/attachment/index.js +0 -9
  200. package/src/lib/attachment/index.js.map +0 -1
  201. package/src/lib/bpm/bpm-root.module.d.ts +0 -155
  202. package/src/lib/bpm/bpm-root.module.js +0 -170
  203. package/src/lib/bpm/bpm-root.module.js.map +0 -1
  204. package/src/lib/bpm/index.js +0 -5
  205. package/src/lib/bpm/index.js.map +0 -1
  206. package/src/lib/bpm-auth/bpm-auth-context.d.ts +0 -24
  207. package/src/lib/bpm-auth/bpm-auth-context.extractor.d.ts +0 -4
  208. package/src/lib/bpm-auth/bpm-auth-context.extractor.js +0 -69
  209. package/src/lib/bpm-auth/bpm-auth-context.extractor.js.map +0 -1
  210. package/src/lib/bpm-auth/bpm-auth-context.js +0 -15
  211. package/src/lib/bpm-auth/bpm-auth-context.js.map +0 -1
  212. package/src/lib/bpm-auth/bpm-auth.authorization.d.ts +0 -12
  213. package/src/lib/bpm-auth/bpm-auth.authorization.js +0 -67
  214. package/src/lib/bpm-auth/bpm-auth.authorization.js.map +0 -1
  215. package/src/lib/bpm-auth/bpm-auth.decorators.d.ts +0 -3
  216. package/src/lib/bpm-auth/bpm-auth.decorators.js +0 -11
  217. package/src/lib/bpm-auth/bpm-auth.decorators.js.map +0 -1
  218. package/src/lib/bpm-auth/bpm-auth.guard.d.ts +0 -7
  219. package/src/lib/bpm-auth/bpm-auth.guard.js +0 -24
  220. package/src/lib/bpm-auth/bpm-auth.guard.js.map +0 -1
  221. package/src/lib/bpm-auth/bpm-auth.module.d.ts +0 -6
  222. package/src/lib/bpm-auth/bpm-auth.module.js +0 -69
  223. package/src/lib/bpm-auth/bpm-auth.module.js.map +0 -1
  224. package/src/lib/bpm-auth/bpm-auth.options.d.ts +0 -11
  225. package/src/lib/bpm-auth/bpm-auth.options.js +0 -3
  226. package/src/lib/bpm-auth/bpm-auth.options.js.map +0 -1
  227. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.d.ts +0 -10
  228. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js +0 -40
  229. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js.map +0 -1
  230. package/src/lib/bpm-auth/index.js +0 -10
  231. package/src/lib/bpm-auth/index.js.map +0 -1
  232. package/src/lib/common/filters/all-exceptions.filter.d.ts +0 -25
  233. package/src/lib/common/filters/all-exceptions.filter.js +0 -93
  234. package/src/lib/common/filters/all-exceptions.filter.js.map +0 -1
  235. package/src/lib/common/index.js +0 -5
  236. package/src/lib/common/index.js.map +0 -1
  237. package/src/lib/common/iso-duration.d.ts +0 -1
  238. package/src/lib/common/iso-duration.js +0 -27
  239. package/src/lib/common/iso-duration.js.map +0 -1
  240. package/src/lib/condition/condition.module.d.ts +0 -2
  241. package/src/lib/condition/condition.module.js +0 -16
  242. package/src/lib/condition/condition.module.js.map +0 -1
  243. package/src/lib/condition/condition.service.d.ts +0 -16
  244. package/src/lib/condition/condition.service.js +0 -82
  245. package/src/lib/condition/condition.service.js.map +0 -1
  246. package/src/lib/condition/index.js +0 -6
  247. package/src/lib/condition/index.js.map +0 -1
  248. package/src/lib/database/data-source.d.ts +0 -4
  249. package/src/lib/database/data-source.js +0 -7
  250. package/src/lib/database/data-source.js.map +0 -1
  251. package/src/lib/database/index.js +0 -6
  252. package/src/lib/database/index.js.map +0 -1
  253. package/src/lib/database/migration-runner.d.ts +0 -1
  254. package/src/lib/database/migration-runner.js +0 -23
  255. package/src/lib/database/migration-runner.js.map +0 -1
  256. package/src/lib/database/typeorm.config.d.ts +0 -14
  257. package/src/lib/database/typeorm.config.js +0 -135
  258. package/src/lib/database/typeorm.config.js.map +0 -1
  259. package/src/lib/delegation/delegation-rule.entity.d.ts +0 -19
  260. package/src/lib/delegation/delegation-rule.entity.js +0 -99
  261. package/src/lib/delegation/delegation-rule.entity.js.map +0 -1
  262. package/src/lib/delegation/delegation.enums.d.ts +0 -10
  263. package/src/lib/delegation/delegation.enums.js +0 -23
  264. package/src/lib/delegation/delegation.enums.js.map +0 -1
  265. package/src/lib/delegation/delegation.module.d.ts +0 -2
  266. package/src/lib/delegation/delegation.module.js +0 -22
  267. package/src/lib/delegation/delegation.module.js.map +0 -1
  268. package/src/lib/delegation/delegation.mutations.d.ts +0 -11
  269. package/src/lib/delegation/delegation.mutations.js +0 -87
  270. package/src/lib/delegation/delegation.mutations.js.map +0 -1
  271. package/src/lib/delegation/delegation.queries.d.ts +0 -10
  272. package/src/lib/delegation/delegation.queries.js +0 -91
  273. package/src/lib/delegation/delegation.queries.js.map +0 -1
  274. package/src/lib/delegation/delegation.service.d.ts +0 -56
  275. package/src/lib/delegation/delegation.service.js +0 -268
  276. package/src/lib/delegation/delegation.service.js.map +0 -1
  277. package/src/lib/delegation/dto/delegation-rule.input.d.ts +0 -24
  278. package/src/lib/delegation/dto/delegation-rule.input.js +0 -143
  279. package/src/lib/delegation/dto/delegation-rule.input.js.map +0 -1
  280. package/src/lib/delegation/index.js +0 -9
  281. package/src/lib/delegation/index.js.map +0 -1
  282. package/src/lib/form/dto/form-definition.input.d.ts +0 -21
  283. package/src/lib/form/dto/form-definition.input.js +0 -103
  284. package/src/lib/form/dto/form-definition.input.js.map +0 -1
  285. package/src/lib/form/form-definition-version.entity.d.ts +0 -17
  286. package/src/lib/form/form-definition-version.entity.js +0 -84
  287. package/src/lib/form/form-definition-version.entity.js.map +0 -1
  288. package/src/lib/form/form-definition.entity.d.ts +0 -13
  289. package/src/lib/form/form-definition.entity.js +0 -66
  290. package/src/lib/form/form-definition.entity.js.map +0 -1
  291. package/src/lib/form/form-schema-lint.object.d.ts +0 -4
  292. package/src/lib/form/form-schema-lint.object.js +0 -20
  293. package/src/lib/form/form-schema-lint.object.js.map +0 -1
  294. package/src/lib/form/form-schema.validator.d.ts +0 -13
  295. package/src/lib/form/form-schema.validator.js +0 -354
  296. package/src/lib/form/form-schema.validator.js.map +0 -1
  297. package/src/lib/form/form.enums.d.ts +0 -9
  298. package/src/lib/form/form.enums.js +0 -22
  299. package/src/lib/form/form.enums.js.map +0 -1
  300. package/src/lib/form/form.module.d.ts +0 -2
  301. package/src/lib/form/form.module.js +0 -27
  302. package/src/lib/form/form.module.js.map +0 -1
  303. package/src/lib/form/form.mutations.d.ts +0 -14
  304. package/src/lib/form/form.mutations.js +0 -88
  305. package/src/lib/form/form.mutations.js.map +0 -1
  306. package/src/lib/form/form.queries.d.ts +0 -16
  307. package/src/lib/form/form.queries.js +0 -96
  308. package/src/lib/form/form.queries.js.map +0 -1
  309. package/src/lib/form/form.service.d.ts +0 -35
  310. package/src/lib/form/form.service.js +0 -297
  311. package/src/lib/form/form.service.js.map +0 -1
  312. package/src/lib/form/index.js +0 -12
  313. package/src/lib/form/index.js.map +0 -1
  314. package/src/lib/identity/identity-options.d.ts +0 -13
  315. package/src/lib/identity/identity-options.js +0 -20
  316. package/src/lib/identity/identity-options.js.map +0 -1
  317. package/src/lib/identity/identity.module.d.ts +0 -17
  318. package/src/lib/identity/identity.module.js +0 -65
  319. package/src/lib/identity/identity.module.js.map +0 -1
  320. package/src/lib/identity/identity.queries.d.ts +0 -12
  321. package/src/lib/identity/identity.queries.js +0 -79
  322. package/src/lib/identity/identity.queries.js.map +0 -1
  323. package/src/lib/identity/identity.service.d.ts +0 -22
  324. package/src/lib/identity/identity.service.js +0 -141
  325. package/src/lib/identity/identity.service.js.map +0 -1
  326. package/src/lib/identity/index.js +0 -7
  327. package/src/lib/identity/index.js.map +0 -1
  328. package/src/lib/identity/member-base.adapter.d.ts +0 -95
  329. package/src/lib/identity/member-base.adapter.js +0 -137
  330. package/src/lib/identity/member-base.adapter.js.map +0 -1
  331. package/src/lib/identity/member-metadata-cache.entity.d.ts +0 -8
  332. package/src/lib/identity/member-metadata-cache.entity.js +0 -39
  333. package/src/lib/identity/member-metadata-cache.entity.js.map +0 -1
  334. package/src/lib/identity/member-profile.object.d.ts +0 -8
  335. package/src/lib/identity/member-profile.object.js +0 -37
  336. package/src/lib/identity/member-profile.object.js.map +0 -1
  337. package/src/lib/identity/member-resolver.interface.js +0 -19
  338. package/src/lib/identity/member-resolver.interface.js.map +0 -1
  339. package/src/lib/migrations/0000000000000-enable-postgres-extensions.d.ts +0 -6
  340. package/src/lib/migrations/0000000000000-enable-postgres-extensions.js +0 -17
  341. package/src/lib/migrations/0000000000000-enable-postgres-extensions.js.map +0 -1
  342. package/src/lib/migrations/0000000001000-identity-organization-foundation.d.ts +0 -6
  343. package/src/lib/migrations/0000000001000-identity-organization-foundation.js.map +0 -1
  344. package/src/lib/migrations/0000000002000-form-builder-foundation.d.ts +0 -6
  345. package/src/lib/migrations/0000000002000-form-builder-foundation.js.map +0 -1
  346. package/src/lib/migrations/0000000003000-approval-template-foundation.d.ts +0 -6
  347. package/src/lib/migrations/0000000003000-approval-template-foundation.js.map +0 -1
  348. package/src/lib/migrations/0000000004000-workflow-engine-foundation.d.ts +0 -6
  349. package/src/lib/migrations/0000000004000-workflow-engine-foundation.js.map +0 -1
  350. package/src/lib/migrations/0000000005000-delegation-rules.d.ts +0 -6
  351. package/src/lib/migrations/0000000005000-delegation-rules.js.map +0 -1
  352. package/src/lib/migrations/0000000006000-notifications-sla.d.ts +0 -6
  353. package/src/lib/migrations/0000000006000-notifications-sla.js.map +0 -1
  354. package/src/lib/migrations/0000000007000-signatures-attachments.d.ts +0 -6
  355. package/src/lib/migrations/0000000007000-signatures-attachments.js.map +0 -1
  356. package/src/lib/migrations/0000000008000-approval-template-categories.d.ts +0 -6
  357. package/src/lib/migrations/0000000008000-approval-template-categories.js.map +0 -1
  358. package/src/lib/migrations/0000000009000-task-candidates.d.ts +0 -6
  359. package/src/lib/migrations/0000000009000-task-candidates.js.map +0 -1
  360. package/src/lib/migrations/0000000010000-notification-delivery-state.d.ts +0 -6
  361. package/src/lib/migrations/0000000010000-notification-delivery-state.js.map +0 -1
  362. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.d.ts +0 -6
  363. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js +0 -22
  364. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js.map +0 -1
  365. package/src/lib/migrations/0000000012000-notification-sla-idempotency.d.ts +0 -6
  366. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js +0 -21
  367. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js.map +0 -1
  368. package/src/lib/migrations/0000000013000-workflow-query-indexes.d.ts +0 -6
  369. package/src/lib/migrations/0000000013000-workflow-query-indexes.js +0 -80
  370. package/src/lib/migrations/0000000013000-workflow-query-indexes.js.map +0 -1
  371. package/src/lib/migrations/index.d.ts +0 -16
  372. package/src/lib/migrations/index.js +0 -49
  373. package/src/lib/migrations/index.js.map +0 -1
  374. package/src/lib/notification/dto/notification-preference.input.d.ts +0 -9
  375. package/src/lib/notification/dto/notification-preference.input.js +0 -53
  376. package/src/lib/notification/dto/notification-preference.input.js.map +0 -1
  377. package/src/lib/notification/index.js +0 -11
  378. package/src/lib/notification/index.js.map +0 -1
  379. package/src/lib/notification/notification-delivery-scheduler.service.d.ts +0 -13
  380. package/src/lib/notification/notification-delivery-scheduler.service.js +0 -53
  381. package/src/lib/notification/notification-delivery-scheduler.service.js.map +0 -1
  382. package/src/lib/notification/notification-delivery.service.d.ts +0 -23
  383. package/src/lib/notification/notification-delivery.service.js +0 -245
  384. package/src/lib/notification/notification-delivery.service.js.map +0 -1
  385. package/src/lib/notification/notification-dispatcher.token.d.ts +0 -7
  386. package/src/lib/notification/notification-dispatcher.token.js +0 -5
  387. package/src/lib/notification/notification-dispatcher.token.js.map +0 -1
  388. package/src/lib/notification/notification-options.d.ts +0 -215
  389. package/src/lib/notification/notification-options.js +0 -128
  390. package/src/lib/notification/notification-options.js.map +0 -1
  391. package/src/lib/notification/notification-options.module.d.ts +0 -23
  392. package/src/lib/notification/notification-options.module.js +0 -41
  393. package/src/lib/notification/notification-options.module.js.map +0 -1
  394. package/src/lib/notification/notification-preference.entity.d.ts +0 -10
  395. package/src/lib/notification/notification-preference.entity.js +0 -50
  396. package/src/lib/notification/notification-preference.entity.js.map +0 -1
  397. package/src/lib/notification/notification-sla-scheduler.service.d.ts +0 -13
  398. package/src/lib/notification/notification-sla-scheduler.service.js +0 -51
  399. package/src/lib/notification/notification-sla-scheduler.service.js.map +0 -1
  400. package/src/lib/notification/notification-template.d.ts +0 -13
  401. package/src/lib/notification/notification-template.js +0 -61
  402. package/src/lib/notification/notification-template.js.map +0 -1
  403. package/src/lib/notification/notification.entity.d.ts +0 -23
  404. package/src/lib/notification/notification.entity.js +0 -122
  405. package/src/lib/notification/notification.entity.js.map +0 -1
  406. package/src/lib/notification/notification.enums.d.ts +0 -24
  407. package/src/lib/notification/notification.enums.js +0 -45
  408. package/src/lib/notification/notification.enums.js.map +0 -1
  409. package/src/lib/notification/notification.module.d.ts +0 -2
  410. package/src/lib/notification/notification.module.js +0 -45
  411. package/src/lib/notification/notification.module.js.map +0 -1
  412. package/src/lib/notification/notification.mutations.d.ts +0 -11
  413. package/src/lib/notification/notification.mutations.js +0 -64
  414. package/src/lib/notification/notification.mutations.js.map +0 -1
  415. package/src/lib/notification/notification.queries.d.ts +0 -11
  416. package/src/lib/notification/notification.queries.js +0 -77
  417. package/src/lib/notification/notification.queries.js.map +0 -1
  418. package/src/lib/notification/notification.service.d.ts +0 -81
  419. package/src/lib/notification/notification.service.js +0 -621
  420. package/src/lib/notification/notification.service.js.map +0 -1
  421. package/src/lib/organization/dto/manager-resolution.input.d.ts +0 -18
  422. package/src/lib/organization/dto/manager-resolution.input.js +0 -107
  423. package/src/lib/organization/dto/manager-resolution.input.js.map +0 -1
  424. package/src/lib/organization/dto/membership.input.d.ts +0 -16
  425. package/src/lib/organization/dto/membership.input.js +0 -99
  426. package/src/lib/organization/dto/membership.input.js.map +0 -1
  427. package/src/lib/organization/dto/org-unit.input.d.ts +0 -24
  428. package/src/lib/organization/dto/org-unit.input.js +0 -119
  429. package/src/lib/organization/dto/org-unit.input.js.map +0 -1
  430. package/src/lib/organization/dto/position.input.d.ts +0 -13
  431. package/src/lib/organization/dto/position.input.js +0 -70
  432. package/src/lib/organization/dto/position.input.js.map +0 -1
  433. package/src/lib/organization/index.js +0 -16
  434. package/src/lib/organization/index.js.map +0 -1
  435. package/src/lib/organization/json-metadata.d.ts +0 -1
  436. package/src/lib/organization/json-metadata.js +0 -14
  437. package/src/lib/organization/json-metadata.js.map +0 -1
  438. package/src/lib/organization/manager-resolution.entity.d.ts +0 -11
  439. package/src/lib/organization/manager-resolution.entity.js +0 -55
  440. package/src/lib/organization/manager-resolution.entity.js.map +0 -1
  441. package/src/lib/organization/membership.entity.d.ts +0 -11
  442. package/src/lib/organization/membership.entity.js +0 -60
  443. package/src/lib/organization/membership.entity.js.map +0 -1
  444. package/src/lib/organization/org-unit-tree-commit-result.object.d.ts +0 -4
  445. package/src/lib/organization/org-unit-tree-commit-result.object.js +0 -17
  446. package/src/lib/organization/org-unit-tree-commit-result.object.js.map +0 -1
  447. package/src/lib/organization/org-unit.entity.d.ts +0 -13
  448. package/src/lib/organization/org-unit.entity.js +0 -64
  449. package/src/lib/organization/org-unit.entity.js.map +0 -1
  450. package/src/lib/organization/organization-summary.object.d.ts +0 -6
  451. package/src/lib/organization/organization-summary.object.js +0 -28
  452. package/src/lib/organization/organization-summary.object.js.map +0 -1
  453. package/src/lib/organization/organization.enums.d.ts +0 -11
  454. package/src/lib/organization/organization.enums.js +0 -22
  455. package/src/lib/organization/organization.enums.js.map +0 -1
  456. package/src/lib/organization/organization.module.d.ts +0 -2
  457. package/src/lib/organization/organization.module.js +0 -35
  458. package/src/lib/organization/organization.module.js.map +0 -1
  459. package/src/lib/organization/organization.mutations.d.ts +0 -26
  460. package/src/lib/organization/organization.mutations.js +0 -148
  461. package/src/lib/organization/organization.mutations.js.map +0 -1
  462. package/src/lib/organization/organization.queries.d.ts +0 -23
  463. package/src/lib/organization/organization.queries.js +0 -202
  464. package/src/lib/organization/organization.queries.js.map +0 -1
  465. package/src/lib/organization/organization.service.d.ts +0 -102
  466. package/src/lib/organization/organization.service.js +0 -751
  467. package/src/lib/organization/organization.service.js.map +0 -1
  468. package/src/lib/organization/position.entity.d.ts +0 -9
  469. package/src/lib/organization/position.entity.js +0 -48
  470. package/src/lib/organization/position.entity.js.map +0 -1
  471. package/src/lib/organization/resolved-manager.object.d.ts +0 -4
  472. package/src/lib/organization/resolved-manager.object.js +0 -20
  473. package/src/lib/organization/resolved-manager.object.js.map +0 -1
  474. package/src/lib/signature/index.js +0 -8
  475. package/src/lib/signature/index.js.map +0 -1
  476. package/src/lib/signature/signature-options.d.ts +0 -33
  477. package/src/lib/signature/signature-options.js +0 -51
  478. package/src/lib/signature/signature-options.js.map +0 -1
  479. package/src/lib/signature/signature-verification.object.d.ts +0 -6
  480. package/src/lib/signature/signature-verification.object.js +0 -28
  481. package/src/lib/signature/signature-verification.object.js.map +0 -1
  482. package/src/lib/signature/signature.entity.d.ts +0 -16
  483. package/src/lib/signature/signature.entity.js +0 -88
  484. package/src/lib/signature/signature.entity.js.map +0 -1
  485. package/src/lib/signature/signature.module.d.ts +0 -11
  486. package/src/lib/signature/signature.module.js +0 -60
  487. package/src/lib/signature/signature.module.js.map +0 -1
  488. package/src/lib/signature/signature.queries.d.ts +0 -11
  489. package/src/lib/signature/signature.queries.js +0 -46
  490. package/src/lib/signature/signature.queries.js.map +0 -1
  491. package/src/lib/signature/signature.service.d.ts +0 -25
  492. package/src/lib/signature/signature.service.js +0 -124
  493. package/src/lib/signature/signature.service.js.map +0 -1
  494. package/src/lib/template/approval-template-category.entity.d.ts +0 -9
  495. package/src/lib/template/approval-template-category.entity.js +0 -49
  496. package/src/lib/template/approval-template-category.entity.js.map +0 -1
  497. package/src/lib/template/approval-template-version.entity.d.ts +0 -21
  498. package/src/lib/template/approval-template-version.entity.js +0 -106
  499. package/src/lib/template/approval-template-version.entity.js.map +0 -1
  500. package/src/lib/template/approval-template.entity.d.ts +0 -14
  501. package/src/lib/template/approval-template.entity.js +0 -74
  502. package/src/lib/template/approval-template.entity.js.map +0 -1
  503. package/src/lib/template/dto/approval-template.input.d.ts +0 -36
  504. package/src/lib/template/dto/approval-template.input.js +0 -187
  505. package/src/lib/template/dto/approval-template.input.js.map +0 -1
  506. package/src/lib/template/index.js +0 -12
  507. package/src/lib/template/index.js.map +0 -1
  508. package/src/lib/template/template.enums.d.ts +0 -14
  509. package/src/lib/template/template.enums.js +0 -31
  510. package/src/lib/template/template.enums.js.map +0 -1
  511. package/src/lib/template/template.module.d.ts +0 -2
  512. package/src/lib/template/template.module.js +0 -33
  513. package/src/lib/template/template.module.js.map +0 -1
  514. package/src/lib/template/template.mutations.d.ts +0 -20
  515. package/src/lib/template/template.mutations.js +0 -139
  516. package/src/lib/template/template.mutations.js.map +0 -1
  517. package/src/lib/template/template.queries.d.ts +0 -16
  518. package/src/lib/template/template.queries.js +0 -133
  519. package/src/lib/template/template.queries.js.map +0 -1
  520. package/src/lib/template/template.service.d.ts +0 -64
  521. package/src/lib/template/template.service.js +0 -521
  522. package/src/lib/template/template.service.js.map +0 -1
  523. package/src/lib/template/workflow-definition.validator.d.ts +0 -9
  524. package/src/lib/template/workflow-definition.validator.js +0 -362
  525. package/src/lib/template/workflow-definition.validator.js.map +0 -1
  526. package/src/lib/testing/cel-js.jest.d.ts +0 -15
  527. package/src/lib/testing/cel-js.jest.js +0 -27
  528. package/src/lib/testing/cel-js.jest.js.map +0 -1
  529. package/src/lib/workflow-engine/activity-log.entity.d.ts +0 -12
  530. package/src/lib/workflow-engine/activity-log.entity.js +0 -62
  531. package/src/lib/workflow-engine/activity-log.entity.js.map +0 -1
  532. package/src/lib/workflow-engine/approval-instance-page-info.object.d.ts +0 -8
  533. package/src/lib/workflow-engine/approval-instance-page-info.object.js +0 -36
  534. package/src/lib/workflow-engine/approval-instance-page-info.object.js.map +0 -1
  535. package/src/lib/workflow-engine/approval-instance.entity.d.ts +0 -22
  536. package/src/lib/workflow-engine/approval-instance.entity.js +0 -113
  537. package/src/lib/workflow-engine/approval-instance.entity.js.map +0 -1
  538. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.d.ts +0 -5
  539. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js +0 -29
  540. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js.map +0 -1
  541. package/src/lib/workflow-engine/dto/decide-task.input.d.ts +0 -9
  542. package/src/lib/workflow-engine/dto/decide-task.input.js +0 -47
  543. package/src/lib/workflow-engine/dto/decide-task.input.js.map +0 -1
  544. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.d.ts +0 -6
  545. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js +0 -34
  546. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js.map +0 -1
  547. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.d.ts +0 -6
  548. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js +0 -34
  549. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js.map +0 -1
  550. package/src/lib/workflow-engine/dto/submit-approval-instance.input.d.ts +0 -7
  551. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js +0 -40
  552. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js.map +0 -1
  553. package/src/lib/workflow-engine/index.js +0 -24
  554. package/src/lib/workflow-engine/index.js.map +0 -1
  555. package/src/lib/workflow-engine/task-candidate.entity.d.ts +0 -14
  556. package/src/lib/workflow-engine/task-candidate.entity.js +0 -72
  557. package/src/lib/workflow-engine/task-candidate.entity.js.map +0 -1
  558. package/src/lib/workflow-engine/task-decision.entity.d.ts +0 -12
  559. package/src/lib/workflow-engine/task-decision.entity.js +0 -60
  560. package/src/lib/workflow-engine/task-decision.entity.js.map +0 -1
  561. package/src/lib/workflow-engine/task.entity.d.ts +0 -20
  562. package/src/lib/workflow-engine/task.entity.js +0 -109
  563. package/src/lib/workflow-engine/task.entity.js.map +0 -1
  564. package/src/lib/workflow-engine/workflow-condition-evaluator.d.ts +0 -5
  565. package/src/lib/workflow-engine/workflow-condition-evaluator.js +0 -107
  566. package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +0 -1
  567. package/src/lib/workflow-engine/workflow-dashboard-summary.object.d.ts +0 -8
  568. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js +0 -36
  569. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js.map +0 -1
  570. package/src/lib/workflow-engine/workflow-dry-run.object.d.ts +0 -21
  571. package/src/lib/workflow-engine/workflow-dry-run.object.js +0 -86
  572. package/src/lib/workflow-engine/workflow-dry-run.object.js.map +0 -1
  573. package/src/lib/workflow-engine/workflow-engine.enums.d.ts +0 -58
  574. package/src/lib/workflow-engine/workflow-engine.enums.js +0 -95
  575. package/src/lib/workflow-engine/workflow-engine.enums.js.map +0 -1
  576. package/src/lib/workflow-engine/workflow-engine.module.d.ts +0 -9
  577. package/src/lib/workflow-engine/workflow-engine.module.js +0 -76
  578. package/src/lib/workflow-engine/workflow-engine.module.js.map +0 -1
  579. package/src/lib/workflow-engine/workflow-engine.mutations.d.ts +0 -19
  580. package/src/lib/workflow-engine/workflow-engine.mutations.js +0 -106
  581. package/src/lib/workflow-engine/workflow-engine.mutations.js.map +0 -1
  582. package/src/lib/workflow-engine/workflow-engine.queries.d.ts +0 -29
  583. package/src/lib/workflow-engine/workflow-engine.queries.js +0 -225
  584. package/src/lib/workflow-engine/workflow-engine.queries.js.map +0 -1
  585. package/src/lib/workflow-engine/workflow-engine.service.d.ts +0 -145
  586. package/src/lib/workflow-engine/workflow-engine.service.js +0 -2785
  587. package/src/lib/workflow-engine/workflow-engine.service.js.map +0 -1
  588. package/src/lib/workflow-engine/workflow-engine.tokens.js +0 -5
  589. package/src/lib/workflow-engine/workflow-engine.tokens.js.map +0 -1
  590. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.d.ts +0 -18
  591. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js +0 -39
  592. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js.map +0 -1
  593. package/src/lib/workflow-engine/workflow-token.entity.d.ts +0 -10
  594. package/src/lib/workflow-engine/workflow-token.entity.js +0 -50
  595. package/src/lib/workflow-engine/workflow-token.entity.js.map +0 -1
  596. /package/src/{index.d.ts → index.ts} +0 -0
  597. /package/src/lib/attachment/{index.d.ts → index.ts} +0 -0
  598. /package/src/lib/bpm/{index.d.ts → index.ts} +0 -0
  599. /package/src/lib/bpm-auth/{index.d.ts → index.ts} +0 -0
  600. /package/src/lib/common/{index.d.ts → index.ts} +0 -0
  601. /package/src/lib/condition/{index.d.ts → index.ts} +0 -0
  602. /package/src/lib/database/{index.d.ts → index.ts} +0 -0
  603. /package/src/lib/delegation/{index.d.ts → index.ts} +0 -0
  604. /package/src/lib/form/{index.d.ts → index.ts} +0 -0
  605. /package/src/lib/identity/{index.d.ts → index.ts} +0 -0
  606. /package/src/lib/notification/{index.d.ts → index.ts} +0 -0
  607. /package/src/lib/organization/{index.d.ts → index.ts} +0 -0
  608. /package/src/lib/signature/{index.d.ts → index.ts} +0 -0
  609. /package/src/lib/template/{index.d.ts → index.ts} +0 -0
  610. /package/src/lib/workflow-engine/{index.d.ts → index.ts} +0 -0
@@ -0,0 +1,1216 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import {
3
+ BadRequestException,
4
+ ConflictException,
5
+ Injectable,
6
+ NotFoundException,
7
+ } from '@nestjs/common';
8
+ import { InjectRepository } from '@nestjs/typeorm';
9
+ import {
10
+ DataSource,
11
+ FindOptionsWhere,
12
+ ILike,
13
+ In,
14
+ IsNull,
15
+ LessThanOrEqual,
16
+ MoreThanOrEqual,
17
+ Not,
18
+ Repository,
19
+ } from 'typeorm';
20
+ import { ManagerResolutionEntity } from './manager-resolution.entity';
21
+ import { MembershipEntity } from './membership.entity';
22
+ import { OrgUnitEntity } from './org-unit.entity';
23
+ import {
24
+ ManagerResolutionScopeTypeEnum,
25
+ OrgUnitTypeEnum,
26
+ } from './organization.enums';
27
+ import { PositionEntity } from './position.entity';
28
+ import {
29
+ CreateManagerResolutionInput,
30
+ UpdateManagerResolutionInput,
31
+ } from './dto/manager-resolution.input';
32
+ import {
33
+ CreateMembershipInput,
34
+ UpdateMembershipInput,
35
+ } from './dto/membership.input';
36
+ import {
37
+ CommitOrgUnitTreeDraftInput,
38
+ CommitOrgUnitTreeDraftMoveInput,
39
+ CreateOrgUnitInput,
40
+ UpdateOrgUnitInput,
41
+ } from './dto/org-unit.input';
42
+ import { OrgUnitTreeCommitResultObject } from './org-unit-tree-commit-result.object';
43
+ import { CreatePositionInput, UpdatePositionInput } from './dto/position.input';
44
+ import { parseMetadataJson } from './json-metadata';
45
+ import { OrganizationSummaryObject } from './organization-summary.object';
46
+
47
+ const ROOT_PATH_PREFIX = 'org';
48
+ const BUSINESS_TIME_ZONE = 'Asia/Taipei';
49
+
50
+ @Injectable()
51
+ export class OrganizationService {
52
+ constructor(
53
+ private readonly dataSource: DataSource,
54
+ @InjectRepository(OrgUnitEntity)
55
+ private readonly orgUnitRepository: Repository<OrgUnitEntity>,
56
+ @InjectRepository(PositionEntity)
57
+ private readonly positionRepository: Repository<PositionEntity>,
58
+ @InjectRepository(MembershipEntity)
59
+ private readonly membershipRepository: Repository<MembershipEntity>,
60
+ @InjectRepository(ManagerResolutionEntity)
61
+ private readonly managerResolutionRepository: Repository<ManagerResolutionEntity>,
62
+ ) {}
63
+
64
+ async createOrgUnit(input: CreateOrgUnitInput): Promise<OrgUnitEntity> {
65
+ await this.assertOrgUnitCodeAvailable(input.code);
66
+ const id = randomUUID();
67
+ const parent = input.parentId
68
+ ? await this.getOrgUnitOrThrow(input.parentId)
69
+ : null;
70
+ const pathSegment = this.buildPathSegment(id);
71
+ const path = parent
72
+ ? `${parent.path}.${pathSegment}`
73
+ : `${ROOT_PATH_PREFIX}.${pathSegment}`;
74
+ const entity = this.orgUnitRepository.create({
75
+ code: input.code,
76
+ id,
77
+ metadata: parseMetadataJson(input.metadataJson),
78
+ name: input.name,
79
+ parentId: input.parentId,
80
+ path,
81
+ type: input.type,
82
+ });
83
+
84
+ return this.orgUnitRepository.save(entity);
85
+ }
86
+
87
+ async updateOrgUnit(input: UpdateOrgUnitInput): Promise<OrgUnitEntity> {
88
+ const existing = await this.getOrgUnitOrThrow(input.id);
89
+ const nextParentId =
90
+ input.parentId === undefined ? existing.parentId : input.parentId;
91
+ const parent = nextParentId
92
+ ? await this.getOrgUnitOrThrow(nextParentId)
93
+ : null;
94
+ const nextCode = input.code ?? existing.code;
95
+
96
+ if (nextParentId === existing.id) {
97
+ throw new BadRequestException('Org unit cannot be its own parent');
98
+ }
99
+
100
+ if (parent && parent.path.startsWith(`${existing.path}.`)) {
101
+ throw new BadRequestException(
102
+ 'Org unit cannot be moved under its descendant',
103
+ );
104
+ }
105
+
106
+ await this.assertOrgUnitCodeAvailable(nextCode, existing.id);
107
+ const pathSegment = this.buildPathSegment(existing.id);
108
+ const path = parent
109
+ ? `${parent.path}.${pathSegment}`
110
+ : `${ROOT_PATH_PREFIX}.${pathSegment}`;
111
+ const previousPath = existing.path;
112
+
113
+ const next = this.orgUnitRepository.merge(existing, {
114
+ code: nextCode,
115
+ metadata: input.metadataJson
116
+ ? parseMetadataJson(input.metadataJson)
117
+ : existing.metadata,
118
+ name: input.name ?? existing.name,
119
+ parentId: nextParentId,
120
+ path,
121
+ type: input.type ?? existing.type,
122
+ });
123
+
124
+ return this.dataSource.transaction(
125
+ async (manager): Promise<OrgUnitEntity> => {
126
+ const saved = await manager.getRepository(OrgUnitEntity).save(next);
127
+
128
+ if (saved.path !== previousPath) {
129
+ const descendants = await manager
130
+ .getRepository(OrgUnitEntity)
131
+ .createQueryBuilder('orgUnit')
132
+ .where('orgUnit.deleted_at IS NULL')
133
+ .andWhere('orgUnit.path <@ :previousPath', { previousPath })
134
+ .andWhere('orgUnit.id != :id', { id: saved.id })
135
+ .getMany();
136
+
137
+ await Promise.all(
138
+ descendants.map((descendant): Promise<OrgUnitEntity> => {
139
+ const updatedDescendant = manager
140
+ .getRepository(OrgUnitEntity)
141
+ .merge(descendant, {
142
+ path: descendant.path.replace(previousPath, saved.path),
143
+ });
144
+
145
+ return manager
146
+ .getRepository(OrgUnitEntity)
147
+ .save(updatedDescendant);
148
+ }),
149
+ );
150
+ }
151
+
152
+ return saved;
153
+ },
154
+ );
155
+ }
156
+
157
+ async commitOrgUnitTreeDraft(
158
+ input: CommitOrgUnitTreeDraftInput,
159
+ ): Promise<OrgUnitTreeCommitResultObject> {
160
+ if (!input.moves.length) {
161
+ return { orgUnits: [] };
162
+ }
163
+
164
+ this.assertUniqueOrgUnitTreeMoves(input.moves);
165
+
166
+ return this.dataSource.transaction(
167
+ async (manager): Promise<OrgUnitTreeCommitResultObject> => {
168
+ const repository = manager.getRepository(OrgUnitEntity);
169
+ const orgUnitIds = collectOrgUnitTreeDraftIds(input.moves);
170
+ const orgUnits = await repository.find({
171
+ where: { deletedAt: IsNull(), id: In(orgUnitIds) },
172
+ });
173
+ const orgUnitById = new Map(
174
+ orgUnits.map((orgUnit): readonly [string, OrgUnitEntity] => [
175
+ orgUnit.id,
176
+ orgUnit,
177
+ ]),
178
+ );
179
+
180
+ this.assertOrgUnitTreeDraftReferences(input.moves, orgUnitById);
181
+ this.assertOrgUnitTreeDraftVersions(input.moves, orgUnitById);
182
+ this.assertOrgUnitTreeDraftHierarchy(input.moves, orgUnitById);
183
+
184
+ const affectedOrgUnits = new Map<string, OrgUnitEntity>();
185
+ const sortedMoves = sortOrgUnitTreeDraftMoves(input.moves);
186
+
187
+ for (const move of sortedMoves) {
188
+ const existing = orgUnitById.get(move.id);
189
+
190
+ if (!existing) {
191
+ throw new NotFoundException(`Org unit ${move.id} was not found`);
192
+ }
193
+
194
+ const parent = move.parentId
195
+ ? orgUnitById.get(move.parentId) ?? null
196
+ : null;
197
+ const previousPath = existing.path;
198
+ const nextPath = parent
199
+ ? `${parent.path}.${this.buildPathSegment(existing.id)}`
200
+ : `${ROOT_PATH_PREFIX}.${this.buildPathSegment(existing.id)}`;
201
+
202
+ if (
203
+ existing.parentId === move.parentId &&
204
+ existing.path === nextPath
205
+ ) {
206
+ affectedOrgUnits.set(existing.id, existing);
207
+ continue;
208
+ }
209
+
210
+ const saved = await repository.save(
211
+ repository.merge(existing, {
212
+ parentId: move.parentId,
213
+ path: nextPath,
214
+ }),
215
+ );
216
+
217
+ orgUnitById.set(saved.id, saved);
218
+ affectedOrgUnits.set(saved.id, saved);
219
+
220
+ const descendants = await repository
221
+ .createQueryBuilder('orgUnit')
222
+ .where('orgUnit.deleted_at IS NULL')
223
+ .andWhere('orgUnit.path <@ :previousPath', { previousPath })
224
+ .andWhere('orgUnit.id != :id', { id: saved.id })
225
+ .getMany();
226
+
227
+ for (const descendant of descendants) {
228
+ const updatedDescendant = await repository.save(
229
+ repository.merge(descendant, {
230
+ path: descendant.path.replace(previousPath, saved.path),
231
+ }),
232
+ );
233
+
234
+ orgUnitById.set(updatedDescendant.id, updatedDescendant);
235
+ affectedOrgUnits.set(updatedDescendant.id, updatedDescendant);
236
+ }
237
+ }
238
+
239
+ return {
240
+ orgUnits: [...affectedOrgUnits.values()].sort(compareOrgUnitPath),
241
+ };
242
+ },
243
+ );
244
+ }
245
+
246
+ async deleteOrgUnit(id: string): Promise<boolean> {
247
+ await this.getOrgUnitOrThrow(id);
248
+ const childCount = await this.orgUnitRepository.count({
249
+ where: { deletedAt: IsNull(), parentId: id },
250
+ });
251
+
252
+ if (childCount > 0) {
253
+ throw new BadRequestException(
254
+ 'Org unit with child units cannot be deleted',
255
+ );
256
+ }
257
+
258
+ const membershipCount = await this.membershipRepository.count({
259
+ where: { orgUnitId: id },
260
+ });
261
+
262
+ if (membershipCount > 0) {
263
+ throw new BadRequestException(
264
+ 'Org unit with memberships cannot be deleted',
265
+ );
266
+ }
267
+
268
+ await this.orgUnitRepository.softDelete(id);
269
+
270
+ return true;
271
+ }
272
+
273
+ async getOrgUnit(id: string): Promise<OrgUnitEntity> {
274
+ return this.getOrgUnitOrThrow(id);
275
+ }
276
+
277
+ async listOrgUnits({
278
+ page,
279
+ pageSize,
280
+ parentId,
281
+ searchText,
282
+ type,
283
+ }: {
284
+ readonly page?: number | null;
285
+ readonly pageSize?: number | null;
286
+ readonly parentId?: string | null;
287
+ readonly searchText?: string | null;
288
+ readonly type?: OrgUnitTypeEnum | null;
289
+ } = {}): Promise<readonly OrgUnitEntity[]> {
290
+ return this.orgUnitRepository.find({
291
+ ...createPaginationFindOptions({ page, pageSize }),
292
+ order: { path: 'ASC' },
293
+ where: createOrgUnitWhere({ parentId, searchText, type }),
294
+ });
295
+ }
296
+
297
+ async countOrgUnits({
298
+ parentId,
299
+ searchText,
300
+ type,
301
+ }: {
302
+ readonly parentId?: string | null;
303
+ readonly searchText?: string | null;
304
+ readonly type?: OrgUnitTypeEnum | null;
305
+ } = {}): Promise<number> {
306
+ return this.orgUnitRepository.count({
307
+ where: createOrgUnitWhere({ parentId, searchText, type }),
308
+ });
309
+ }
310
+
311
+ async createPosition(input: CreatePositionInput): Promise<PositionEntity> {
312
+ await this.assertPositionCodeAvailable(input.code);
313
+
314
+ return this.positionRepository.save(
315
+ this.positionRepository.create({
316
+ code: input.code,
317
+ level: input.level,
318
+ metadata: parseMetadataJson(input.metadataJson),
319
+ name: input.name,
320
+ }),
321
+ );
322
+ }
323
+
324
+ async updatePosition(input: UpdatePositionInput): Promise<PositionEntity> {
325
+ const existing = await this.getPositionOrThrow(input.id);
326
+ const nextCode = input.code ?? existing.code;
327
+
328
+ await this.assertPositionCodeAvailable(nextCode, existing.id);
329
+
330
+ const next = this.positionRepository.merge(existing, {
331
+ code: nextCode,
332
+ level: input.level ?? existing.level,
333
+ metadata: input.metadataJson
334
+ ? parseMetadataJson(input.metadataJson)
335
+ : existing.metadata,
336
+ name: input.name ?? existing.name,
337
+ });
338
+
339
+ return this.positionRepository.save(next);
340
+ }
341
+
342
+ async listPositions({
343
+ page,
344
+ pageSize,
345
+ searchText,
346
+ }: {
347
+ readonly page?: number | null;
348
+ readonly pageSize?: number | null;
349
+ readonly searchText?: string | null;
350
+ } = {}): Promise<readonly PositionEntity[]> {
351
+ return this.positionRepository.find({
352
+ ...createPaginationFindOptions({ page, pageSize }),
353
+ order: { level: 'DESC', code: 'ASC' },
354
+ where: createPositionWhere({ searchText }),
355
+ });
356
+ }
357
+
358
+ async countPositions({
359
+ searchText,
360
+ }: {
361
+ readonly searchText?: string | null;
362
+ } = {}): Promise<number> {
363
+ return this.positionRepository.count({
364
+ where: createPositionWhere({ searchText }),
365
+ });
366
+ }
367
+
368
+ async createMembership(
369
+ input: CreateMembershipInput,
370
+ ): Promise<MembershipEntity> {
371
+ await this.assertMembershipReferences(input);
372
+ await this.assertMembershipDateRange(
373
+ input.effectiveFrom,
374
+ input.effectiveTo,
375
+ );
376
+
377
+ if (input.isPrimary) {
378
+ await this.clearPrimaryMemberships(input.memberId);
379
+ }
380
+
381
+ return this.membershipRepository.save(
382
+ this.membershipRepository.create(input),
383
+ );
384
+ }
385
+
386
+ async updateMembership(
387
+ input: UpdateMembershipInput,
388
+ ): Promise<MembershipEntity> {
389
+ const existing = await this.getMembershipOrThrow(input.id);
390
+ const nextMemberId = existing.memberId;
391
+ const nextOrgUnitId = input.orgUnitId ?? existing.orgUnitId;
392
+ const nextPositionId =
393
+ input.positionId === undefined ? existing.positionId : input.positionId;
394
+ const nextEffectiveFrom = input.effectiveFrom ?? existing.effectiveFrom;
395
+ const nextEffectiveTo =
396
+ input.effectiveTo === undefined
397
+ ? existing.effectiveTo
398
+ : input.effectiveTo;
399
+ const nextIsPrimary = input.isPrimary ?? existing.isPrimary;
400
+
401
+ await this.assertMembershipReferences({
402
+ effectiveFrom: nextEffectiveFrom,
403
+ effectiveTo: nextEffectiveTo,
404
+ isPrimary: nextIsPrimary,
405
+ memberId: nextMemberId,
406
+ orgUnitId: nextOrgUnitId,
407
+ positionId: nextPositionId,
408
+ });
409
+ await this.assertMembershipDateRange(nextEffectiveFrom, nextEffectiveTo);
410
+
411
+ if (nextIsPrimary) {
412
+ await this.clearPrimaryMemberships(nextMemberId, existing.id);
413
+ }
414
+
415
+ const next = this.membershipRepository.merge(existing, {
416
+ effectiveFrom: nextEffectiveFrom,
417
+ effectiveTo: nextEffectiveTo,
418
+ isPrimary: nextIsPrimary,
419
+ orgUnitId: nextOrgUnitId,
420
+ positionId: nextPositionId,
421
+ });
422
+
423
+ return this.membershipRepository.save(next);
424
+ }
425
+
426
+ async deleteMembership(id: string): Promise<boolean> {
427
+ await this.getMembershipOrThrow(id);
428
+ await this.membershipRepository.delete(id);
429
+
430
+ return true;
431
+ }
432
+
433
+ async listMemberships({
434
+ activeOnly = false,
435
+ memberId,
436
+ orgUnitId,
437
+ page,
438
+ pageSize,
439
+ positionId,
440
+ }: {
441
+ readonly activeOnly?: boolean;
442
+ readonly memberId?: string | null;
443
+ readonly orgUnitId?: string | null;
444
+ readonly page?: number | null;
445
+ readonly pageSize?: number | null;
446
+ readonly positionId?: string | null;
447
+ } = {}): Promise<readonly MembershipEntity[]> {
448
+ const date = this.toDateOnly(new Date());
449
+
450
+ return this.membershipRepository.find({
451
+ ...createPaginationFindOptions({ page, pageSize }),
452
+ order: { memberId: 'ASC', isPrimary: 'DESC', effectiveFrom: 'DESC' },
453
+ where: createMembershipWhere({
454
+ activeOnly,
455
+ date,
456
+ memberId,
457
+ orgUnitId,
458
+ positionId,
459
+ }),
460
+ });
461
+ }
462
+
463
+ async countMemberships({
464
+ activeOnly = false,
465
+ memberId,
466
+ orgUnitId,
467
+ positionId,
468
+ }: {
469
+ readonly activeOnly?: boolean;
470
+ readonly memberId?: string | null;
471
+ readonly orgUnitId?: string | null;
472
+ readonly positionId?: string | null;
473
+ } = {}): Promise<number> {
474
+ const date = this.toDateOnly(new Date());
475
+
476
+ return this.membershipRepository.count({
477
+ where: createMembershipWhere({
478
+ activeOnly,
479
+ date,
480
+ memberId,
481
+ orgUnitId,
482
+ positionId,
483
+ }),
484
+ });
485
+ }
486
+
487
+ async createManagerResolution(
488
+ input: CreateManagerResolutionInput,
489
+ ): Promise<ManagerResolutionEntity> {
490
+ await this.assertManagerResolutionReferences(input);
491
+ await this.assertMembershipDateRange(
492
+ input.effectiveFrom,
493
+ input.effectiveTo,
494
+ );
495
+
496
+ return this.managerResolutionRepository.save(
497
+ this.managerResolutionRepository.create(input),
498
+ );
499
+ }
500
+
501
+ async updateManagerResolution(
502
+ input: UpdateManagerResolutionInput,
503
+ ): Promise<ManagerResolutionEntity> {
504
+ const existing = await this.getManagerResolutionOrThrow(input.id);
505
+ const nextScopeType = input.scopeType ?? existing.scopeType;
506
+ const nextScopeId = input.scopeId ?? existing.scopeId;
507
+ const nextManagerMemberId =
508
+ input.managerMemberId ?? existing.managerMemberId;
509
+ const nextEffectiveFrom = input.effectiveFrom ?? existing.effectiveFrom;
510
+ const nextEffectiveTo =
511
+ input.effectiveTo === undefined
512
+ ? existing.effectiveTo
513
+ : input.effectiveTo;
514
+
515
+ await this.assertManagerResolutionReferences({
516
+ effectiveFrom: nextEffectiveFrom,
517
+ effectiveTo: nextEffectiveTo,
518
+ managerMemberId: nextManagerMemberId,
519
+ priority: input.priority ?? existing.priority,
520
+ scopeId: nextScopeId,
521
+ scopeType: nextScopeType,
522
+ });
523
+ await this.assertMembershipDateRange(nextEffectiveFrom, nextEffectiveTo);
524
+
525
+ const next = this.managerResolutionRepository.merge(existing, {
526
+ effectiveFrom: nextEffectiveFrom,
527
+ effectiveTo: nextEffectiveTo,
528
+ managerMemberId: nextManagerMemberId,
529
+ priority: input.priority ?? existing.priority,
530
+ scopeId: nextScopeId,
531
+ scopeType: nextScopeType,
532
+ });
533
+
534
+ return this.managerResolutionRepository.save(next);
535
+ }
536
+
537
+ async deleteManagerResolution(id: string): Promise<boolean> {
538
+ await this.getManagerResolutionOrThrow(id);
539
+ await this.managerResolutionRepository.delete(id);
540
+
541
+ return true;
542
+ }
543
+
544
+ async listManagerResolutions({
545
+ activeOnly = false,
546
+ page,
547
+ pageSize,
548
+ scopeId,
549
+ scopeType,
550
+ }: {
551
+ readonly activeOnly?: boolean;
552
+ readonly page?: number | null;
553
+ readonly pageSize?: number | null;
554
+ readonly scopeId?: string | null;
555
+ readonly scopeType?: ManagerResolutionScopeTypeEnum | null;
556
+ } = {}): Promise<readonly ManagerResolutionEntity[]> {
557
+ const date = this.toDateOnly(new Date());
558
+
559
+ return this.managerResolutionRepository.find({
560
+ ...createPaginationFindOptions({ page, pageSize }),
561
+ order: { priority: 'DESC', createdAt: 'DESC' },
562
+ where: createManagerResolutionWhere({
563
+ activeOnly,
564
+ date,
565
+ scopeId,
566
+ scopeType,
567
+ }),
568
+ });
569
+ }
570
+
571
+ async countManagerResolutions({
572
+ activeOnly = false,
573
+ scopeId,
574
+ scopeType,
575
+ }: {
576
+ readonly activeOnly?: boolean;
577
+ readonly scopeId?: string | null;
578
+ readonly scopeType?: ManagerResolutionScopeTypeEnum | null;
579
+ } = {}): Promise<number> {
580
+ const date = this.toDateOnly(new Date());
581
+
582
+ return this.managerResolutionRepository.count({
583
+ where: createManagerResolutionWhere({
584
+ activeOnly,
585
+ date,
586
+ scopeId,
587
+ scopeType,
588
+ }),
589
+ });
590
+ }
591
+
592
+ async readOrganizationSummary(): Promise<OrganizationSummaryObject> {
593
+ const [
594
+ orgUnitCount,
595
+ positionCount,
596
+ membershipCount,
597
+ managerResolutionCount,
598
+ ] = await Promise.all([
599
+ this.orgUnitRepository.count({ where: { deletedAt: IsNull() } }),
600
+ this.positionRepository.count(),
601
+ this.membershipRepository.count(),
602
+ this.managerResolutionRepository.count(),
603
+ ]);
604
+
605
+ return {
606
+ managerResolutionCount,
607
+ membershipCount,
608
+ orgUnitCount,
609
+ positionCount,
610
+ };
611
+ }
612
+
613
+ async resolveManagerMemberId(
614
+ memberId: string,
615
+ effectiveAt = new Date(),
616
+ ): Promise<string | null> {
617
+ const date = this.toDateOnly(effectiveAt);
618
+ const memberships = await this.findActiveMemberships(memberId, date);
619
+ const orgUnits = await this.findMembershipOrgUnits(memberships);
620
+ const positionIds = memberships
621
+ .map((membership) => membership.positionId)
622
+ .filter((positionId): positionId is string => Boolean(positionId));
623
+ const candidatePairs = [
624
+ { scopeId: memberId, scopeType: ManagerResolutionScopeTypeEnum.MEMBER },
625
+ ...orgUnits.map((orgUnit) => ({
626
+ scopeId: orgUnit.id,
627
+ scopeType: ManagerResolutionScopeTypeEnum.ORG_UNIT,
628
+ })),
629
+ ...positionIds.map((positionId) => ({
630
+ scopeId: positionId,
631
+ scopeType: ManagerResolutionScopeTypeEnum.POSITION,
632
+ })),
633
+ ];
634
+ const scopeIds = candidatePairs.map((pair) => pair.scopeId);
635
+ const resolutions = scopeIds.length
636
+ ? await this.managerResolutionRepository.find({
637
+ where: {
638
+ scopeId: In(scopeIds),
639
+ scopeType: In([
640
+ ManagerResolutionScopeTypeEnum.MEMBER,
641
+ ManagerResolutionScopeTypeEnum.ORG_UNIT,
642
+ ManagerResolutionScopeTypeEnum.POSITION,
643
+ ]),
644
+ },
645
+ })
646
+ : [];
647
+
648
+ const active = resolutions
649
+ .filter((resolution) =>
650
+ candidatePairs.some(
651
+ (pair) =>
652
+ pair.scopeId === resolution.scopeId &&
653
+ pair.scopeType === resolution.scopeType,
654
+ ),
655
+ )
656
+ .filter((resolution) => this.isDateActive(resolution, date))
657
+ .sort((left, right) => this.compareManagerResolution(left, right));
658
+
659
+ return active[0]?.managerMemberId ?? null;
660
+ }
661
+
662
+ private assertUniqueOrgUnitTreeMoves(
663
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
664
+ ): void {
665
+ const moveIds = moves.map((move) => move.id);
666
+ const uniqueMoveIds = new Set(moveIds);
667
+
668
+ if (uniqueMoveIds.size !== moveIds.length) {
669
+ throw new BadRequestException(
670
+ 'Org unit tree draft includes duplicate moves',
671
+ );
672
+ }
673
+ }
674
+
675
+ private assertOrgUnitTreeDraftReferences(
676
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
677
+ orgUnitById: ReadonlyMap<string, OrgUnitEntity>,
678
+ ): void {
679
+ const missingId = collectOrgUnitTreeDraftIds(moves).find(
680
+ (id) => !orgUnitById.has(id),
681
+ );
682
+
683
+ if (missingId) {
684
+ throw new NotFoundException(`Org unit ${missingId} was not found`);
685
+ }
686
+ }
687
+
688
+ private assertOrgUnitTreeDraftVersions(
689
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
690
+ orgUnitById: ReadonlyMap<string, OrgUnitEntity>,
691
+ ): void {
692
+ const staleMove = moves.find((move) => {
693
+ const orgUnit = orgUnitById.get(move.id);
694
+
695
+ return orgUnit
696
+ ? orgUnit.updatedAt.getTime() !== parseBaseUpdatedAt(move).getTime()
697
+ : false;
698
+ });
699
+
700
+ if (staleMove) {
701
+ throw new ConflictException(
702
+ `Org unit ${staleMove.id} has changed since this draft was based`,
703
+ );
704
+ }
705
+ }
706
+
707
+ private assertOrgUnitTreeDraftHierarchy(
708
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
709
+ orgUnitById: ReadonlyMap<string, OrgUnitEntity>,
710
+ ): void {
711
+ for (const move of moves) {
712
+ if (move.id === move.parentId) {
713
+ throw new BadRequestException('Org unit cannot be its own parent');
714
+ }
715
+
716
+ const existing = orgUnitById.get(move.id);
717
+ const parent = move.parentId ? orgUnitById.get(move.parentId) : null;
718
+
719
+ if (existing && parent?.path.startsWith(`${existing.path}.`)) {
720
+ throw new BadRequestException(
721
+ 'Org unit cannot be moved under its descendant',
722
+ );
723
+ }
724
+ }
725
+
726
+ assertOrgUnitTreeDraftHasNoCycles(moves);
727
+ }
728
+
729
+ private async getOrgUnitOrThrow(id: string): Promise<OrgUnitEntity> {
730
+ const entity = await this.orgUnitRepository.findOne({
731
+ where: { deletedAt: IsNull(), id },
732
+ });
733
+
734
+ if (!entity) {
735
+ throw new NotFoundException(`Org unit ${id} was not found`);
736
+ }
737
+
738
+ return entity;
739
+ }
740
+
741
+ private async getPositionOrThrow(id: string): Promise<PositionEntity> {
742
+ const entity = await this.positionRepository.findOne({ where: { id } });
743
+
744
+ if (!entity) {
745
+ throw new NotFoundException(`Position ${id} was not found`);
746
+ }
747
+
748
+ return entity;
749
+ }
750
+
751
+ private async assertOrgUnitCodeAvailable(
752
+ code: string,
753
+ exceptId?: string,
754
+ ): Promise<void> {
755
+ const existing = await this.orgUnitRepository.findOne({
756
+ where: {
757
+ code,
758
+ deletedAt: IsNull(),
759
+ ...(exceptId ? { id: Not(exceptId) } : {}),
760
+ },
761
+ });
762
+
763
+ if (existing) {
764
+ throw new BadRequestException(`Org unit code ${code} is already used`);
765
+ }
766
+ }
767
+
768
+ private async assertPositionCodeAvailable(
769
+ code: string,
770
+ exceptId?: string,
771
+ ): Promise<void> {
772
+ const existing = await this.positionRepository.findOne({
773
+ where: {
774
+ code,
775
+ ...(exceptId ? { id: Not(exceptId) } : {}),
776
+ },
777
+ });
778
+
779
+ if (existing) {
780
+ throw new BadRequestException(`Position code ${code} is already used`);
781
+ }
782
+ }
783
+
784
+ private async assertMembershipReferences(
785
+ input: CreateMembershipInput,
786
+ ): Promise<void> {
787
+ await this.getOrgUnitOrThrow(input.orgUnitId);
788
+
789
+ if (input.positionId) {
790
+ await this.getPositionOrThrow(input.positionId);
791
+ }
792
+ }
793
+
794
+ private async assertManagerResolutionReferences(
795
+ input: CreateManagerResolutionInput,
796
+ ): Promise<void> {
797
+ if (
798
+ input.scopeType === ManagerResolutionScopeTypeEnum.MEMBER &&
799
+ input.scopeId === input.managerMemberId
800
+ ) {
801
+ throw new BadRequestException('Manager cannot be the scoped member');
802
+ }
803
+
804
+ if (input.scopeType === ManagerResolutionScopeTypeEnum.ORG_UNIT) {
805
+ await this.getOrgUnitOrThrow(input.scopeId);
806
+ }
807
+
808
+ if (input.scopeType === ManagerResolutionScopeTypeEnum.POSITION) {
809
+ await this.getPositionOrThrow(input.scopeId);
810
+ }
811
+ }
812
+
813
+ private async assertMembershipDateRange(
814
+ effectiveFrom: string,
815
+ effectiveTo: string | null,
816
+ ): Promise<void> {
817
+ if (effectiveTo && effectiveFrom > effectiveTo) {
818
+ throw new BadRequestException(
819
+ 'Effective from cannot be after effective to',
820
+ );
821
+ }
822
+ }
823
+
824
+ private async clearPrimaryMemberships(
825
+ memberId: string,
826
+ exceptId?: string,
827
+ ): Promise<void> {
828
+ await this.membershipRepository.update(
829
+ {
830
+ isPrimary: true,
831
+ memberId,
832
+ ...(exceptId ? { id: Not(exceptId) } : {}),
833
+ },
834
+ { isPrimary: false },
835
+ );
836
+ }
837
+
838
+ private async getMembershipOrThrow(id: string): Promise<MembershipEntity> {
839
+ const entity = await this.membershipRepository.findOne({ where: { id } });
840
+
841
+ if (!entity) {
842
+ throw new NotFoundException(`Membership ${id} was not found`);
843
+ }
844
+
845
+ return entity;
846
+ }
847
+
848
+ private async getManagerResolutionOrThrow(
849
+ id: string,
850
+ ): Promise<ManagerResolutionEntity> {
851
+ const entity = await this.managerResolutionRepository.findOne({
852
+ where: { id },
853
+ });
854
+
855
+ if (!entity) {
856
+ throw new NotFoundException(`Manager resolution ${id} was not found`);
857
+ }
858
+
859
+ return entity;
860
+ }
861
+
862
+ private async findActiveMemberships(
863
+ memberId: string,
864
+ date: string,
865
+ ): Promise<readonly MembershipEntity[]> {
866
+ const memberships = await this.membershipRepository.find({
867
+ where: { memberId },
868
+ });
869
+
870
+ return memberships.filter((membership) =>
871
+ this.isDateActive(membership, date),
872
+ );
873
+ }
874
+
875
+ private async findMembershipOrgUnits(
876
+ memberships: readonly MembershipEntity[],
877
+ ): Promise<readonly OrgUnitEntity[]> {
878
+ const orgUnitIds = memberships.map((membership) => membership.orgUnitId);
879
+
880
+ if (!orgUnitIds.length) {
881
+ return [];
882
+ }
883
+
884
+ const directOrgUnits = await this.orgUnitRepository.find({
885
+ where: { deletedAt: IsNull(), id: In(orgUnitIds) },
886
+ });
887
+ const ancestorGroups = await Promise.all(
888
+ directOrgUnits.map((orgUnit) =>
889
+ this.orgUnitRepository
890
+ .createQueryBuilder('orgUnit')
891
+ .where('orgUnit.deleted_at IS NULL')
892
+ .andWhere('orgUnit.path @> :path', { path: orgUnit.path })
893
+ .getMany(),
894
+ ),
895
+ );
896
+ const flattened = ancestorGroups.flat();
897
+ const byId = new Map(
898
+ flattened.map((orgUnit): readonly [string, OrgUnitEntity] => [
899
+ orgUnit.id,
900
+ orgUnit,
901
+ ]),
902
+ );
903
+
904
+ return [...byId.values()];
905
+ }
906
+
907
+ private buildPathSegment(id: string): string {
908
+ return `n${id.replace(/-/g, '_')}`;
909
+ }
910
+
911
+ private toDateOnly(date: Date): string {
912
+ const parts = new Intl.DateTimeFormat('en-US', {
913
+ day: '2-digit',
914
+ month: '2-digit',
915
+ timeZone: BUSINESS_TIME_ZONE,
916
+ year: 'numeric',
917
+ }).formatToParts(date);
918
+ const byType = new Map(
919
+ parts.map((part): readonly [string, string] => [part.type, part.value]),
920
+ );
921
+
922
+ return `${byType.get('year') ?? '1970'}-${byType.get('month') ?? '01'}-${byType.get('day') ?? '01'}`;
923
+ }
924
+
925
+ private isDateActive(
926
+ value: Pick<
927
+ MembershipEntity | ManagerResolutionEntity,
928
+ 'effectiveFrom' | 'effectiveTo'
929
+ >,
930
+ date: string,
931
+ ): boolean {
932
+ return (
933
+ value.effectiveFrom <= date &&
934
+ (!value.effectiveTo || value.effectiveTo >= date)
935
+ );
936
+ }
937
+
938
+ private compareManagerResolution(
939
+ left: ManagerResolutionEntity,
940
+ right: ManagerResolutionEntity,
941
+ ): number {
942
+ const priorityDiff = right.priority - left.priority;
943
+
944
+ if (priorityDiff !== 0) {
945
+ return priorityDiff;
946
+ }
947
+
948
+ return (
949
+ this.scopeRank(right.scopeType) - this.scopeRank(left.scopeType) ||
950
+ right.effectiveFrom.localeCompare(left.effectiveFrom)
951
+ );
952
+ }
953
+
954
+ private scopeRank(scopeType: ManagerResolutionScopeTypeEnum): number {
955
+ const ranks: Readonly<Record<ManagerResolutionScopeTypeEnum, number>> = {
956
+ [ManagerResolutionScopeTypeEnum.MEMBER]: 3,
957
+ [ManagerResolutionScopeTypeEnum.ORG_UNIT]: 2,
958
+ [ManagerResolutionScopeTypeEnum.POSITION]: 1,
959
+ };
960
+
961
+ return ranks[scopeType];
962
+ }
963
+ }
964
+
965
+ function createOrgUnitWhere({
966
+ parentId,
967
+ searchText,
968
+ type,
969
+ }: {
970
+ readonly parentId?: string | null;
971
+ readonly searchText?: string | null;
972
+ readonly type?: OrgUnitTypeEnum | null;
973
+ }):
974
+ | FindOptionsWhere<OrgUnitEntity>
975
+ | FindOptionsWhere<OrgUnitEntity>[] {
976
+ const parentWhere: FindOptionsWhere<OrgUnitEntity> =
977
+ parentId === undefined
978
+ ? {}
979
+ : { parentId: parentId === null ? IsNull() : parentId };
980
+ const baseWhere: FindOptionsWhere<OrgUnitEntity> = {
981
+ deletedAt: IsNull(),
982
+ ...parentWhere,
983
+ ...(type ? { type } : {}),
984
+ };
985
+ const trimmedSearchText = searchText?.trim();
986
+
987
+ if (!trimmedSearchText) {
988
+ return baseWhere;
989
+ }
990
+
991
+ const searchPattern = `%${trimmedSearchText}%`;
992
+
993
+ return [
994
+ { ...baseWhere, code: ILike(searchPattern) },
995
+ { ...baseWhere, name: ILike(searchPattern) },
996
+ ];
997
+ }
998
+
999
+ function createPositionWhere({
1000
+ searchText,
1001
+ }: {
1002
+ readonly searchText?: string | null;
1003
+ }):
1004
+ | FindOptionsWhere<PositionEntity>
1005
+ | FindOptionsWhere<PositionEntity>[] {
1006
+ const trimmedSearchText = searchText?.trim();
1007
+
1008
+ if (!trimmedSearchText) {
1009
+ return {};
1010
+ }
1011
+
1012
+ const searchPattern = `%${trimmedSearchText}%`;
1013
+
1014
+ return [{ code: ILike(searchPattern) }, { name: ILike(searchPattern) }];
1015
+ }
1016
+
1017
+ function createMembershipWhere({
1018
+ activeOnly,
1019
+ date,
1020
+ memberId,
1021
+ orgUnitId,
1022
+ positionId,
1023
+ }: {
1024
+ readonly activeOnly: boolean;
1025
+ readonly date: string;
1026
+ readonly memberId?: string | null;
1027
+ readonly orgUnitId?: string | null;
1028
+ readonly positionId?: string | null;
1029
+ }):
1030
+ | FindOptionsWhere<MembershipEntity>
1031
+ | FindOptionsWhere<MembershipEntity>[] {
1032
+ const baseWhere: FindOptionsWhere<MembershipEntity> = {
1033
+ ...(memberId ? { memberId } : {}),
1034
+ ...(orgUnitId ? { orgUnitId } : {}),
1035
+ ...(positionId ? { positionId } : {}),
1036
+ };
1037
+
1038
+ if (!activeOnly) {
1039
+ return baseWhere;
1040
+ }
1041
+
1042
+ return [
1043
+ {
1044
+ ...baseWhere,
1045
+ effectiveFrom: LessThanOrEqual(date),
1046
+ effectiveTo: IsNull(),
1047
+ },
1048
+ {
1049
+ ...baseWhere,
1050
+ effectiveFrom: LessThanOrEqual(date),
1051
+ effectiveTo: MoreThanOrEqual(date),
1052
+ },
1053
+ ];
1054
+ }
1055
+
1056
+ function createManagerResolutionWhere({
1057
+ activeOnly,
1058
+ date,
1059
+ scopeId,
1060
+ scopeType,
1061
+ }: {
1062
+ readonly activeOnly: boolean;
1063
+ readonly date: string;
1064
+ readonly scopeId?: string | null;
1065
+ readonly scopeType?: ManagerResolutionScopeTypeEnum | null;
1066
+ }):
1067
+ | FindOptionsWhere<ManagerResolutionEntity>
1068
+ | FindOptionsWhere<ManagerResolutionEntity>[] {
1069
+ const baseWhere: FindOptionsWhere<ManagerResolutionEntity> = {
1070
+ ...(scopeId ? { scopeId } : {}),
1071
+ ...(scopeType ? { scopeType } : {}),
1072
+ };
1073
+
1074
+ if (!activeOnly) {
1075
+ return baseWhere;
1076
+ }
1077
+
1078
+ return [
1079
+ {
1080
+ ...baseWhere,
1081
+ effectiveFrom: LessThanOrEqual(date),
1082
+ effectiveTo: IsNull(),
1083
+ },
1084
+ {
1085
+ ...baseWhere,
1086
+ effectiveFrom: LessThanOrEqual(date),
1087
+ effectiveTo: MoreThanOrEqual(date),
1088
+ },
1089
+ ];
1090
+ }
1091
+
1092
+ function createPaginationFindOptions({
1093
+ page,
1094
+ pageSize,
1095
+ }: {
1096
+ readonly page?: number | null;
1097
+ readonly pageSize?: number | null;
1098
+ }): { readonly skip?: number; readonly take?: number } {
1099
+ const normalizedPageSize = normalizePageSize(pageSize);
1100
+
1101
+ if (!normalizedPageSize) {
1102
+ return {};
1103
+ }
1104
+
1105
+ return {
1106
+ skip: (normalizePage(page) - 1) * normalizedPageSize,
1107
+ take: normalizedPageSize,
1108
+ };
1109
+ }
1110
+
1111
+ function normalizePage(page?: number | null): number {
1112
+ if (typeof page !== 'number' || !Number.isFinite(page)) {
1113
+ return 1;
1114
+ }
1115
+
1116
+ return Math.max(1, Math.floor(page));
1117
+ }
1118
+
1119
+ function normalizePageSize(pageSize?: number | null): number | null {
1120
+ if (typeof pageSize !== 'number' || !Number.isFinite(pageSize)) {
1121
+ return null;
1122
+ }
1123
+
1124
+ return Math.min(100, Math.max(1, Math.floor(pageSize)));
1125
+ }
1126
+
1127
+ function collectOrgUnitTreeDraftIds(
1128
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
1129
+ ): readonly string[] {
1130
+ return [
1131
+ ...new Set(
1132
+ moves.flatMap((move) =>
1133
+ move.parentId ? [move.id, move.parentId] : [move.id],
1134
+ ),
1135
+ ),
1136
+ ];
1137
+ }
1138
+
1139
+ function parseBaseUpdatedAt(move: CommitOrgUnitTreeDraftMoveInput): Date {
1140
+ const baseUpdatedAt = new Date(move.baseUpdatedAt);
1141
+
1142
+ if (Number.isNaN(baseUpdatedAt.getTime())) {
1143
+ throw new BadRequestException(
1144
+ `Org unit ${move.id} baseUpdatedAt is invalid`,
1145
+ );
1146
+ }
1147
+
1148
+ return baseUpdatedAt;
1149
+ }
1150
+
1151
+ function assertOrgUnitTreeDraftHasNoCycles(
1152
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
1153
+ ): void {
1154
+ const parentById = new Map(
1155
+ moves.map((move): readonly [string, string | null] => [
1156
+ move.id,
1157
+ move.parentId,
1158
+ ]),
1159
+ );
1160
+
1161
+ for (const move of moves) {
1162
+ const visited = new Set<string>();
1163
+ let parentId = move.parentId;
1164
+
1165
+ while (parentId && parentById.has(parentId)) {
1166
+ if (parentId === move.id || visited.has(parentId)) {
1167
+ throw new BadRequestException(
1168
+ 'Org unit tree draft cannot create a cycle',
1169
+ );
1170
+ }
1171
+
1172
+ visited.add(parentId);
1173
+ parentId = parentById.get(parentId) ?? null;
1174
+ }
1175
+ }
1176
+ }
1177
+
1178
+ function sortOrgUnitTreeDraftMoves(
1179
+ moves: readonly CommitOrgUnitTreeDraftMoveInput[],
1180
+ ): readonly CommitOrgUnitTreeDraftMoveInput[] {
1181
+ const moveById = new Map(
1182
+ moves.map((move): readonly [string, CommitOrgUnitTreeDraftMoveInput] => [
1183
+ move.id,
1184
+ move,
1185
+ ]),
1186
+ );
1187
+
1188
+ return [...moves].sort((left, right) => {
1189
+ const leftDepth = calculateOrgUnitTreeDraftMoveDepth(left, moveById);
1190
+ const rightDepth = calculateOrgUnitTreeDraftMoveDepth(right, moveById);
1191
+
1192
+ return leftDepth - rightDepth || left.id.localeCompare(right.id);
1193
+ });
1194
+ }
1195
+
1196
+ function calculateOrgUnitTreeDraftMoveDepth(
1197
+ move: CommitOrgUnitTreeDraftMoveInput,
1198
+ moveById: ReadonlyMap<string, CommitOrgUnitTreeDraftMoveInput>,
1199
+ ): number {
1200
+ let depth = 0;
1201
+ let parentId = move.parentId;
1202
+
1203
+ while (parentId && moveById.has(parentId)) {
1204
+ depth += 1;
1205
+ parentId = moveById.get(parentId)?.parentId ?? null;
1206
+ }
1207
+
1208
+ return depth;
1209
+ }
1210
+
1211
+ function compareOrgUnitPath(
1212
+ left: OrgUnitEntity,
1213
+ right: OrgUnitEntity,
1214
+ ): number {
1215
+ return left.path.localeCompare(right.path);
1216
+ }