@rytass/bpm-core-nestjs-module 0.4.0 → 0.5.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 (658) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/eslint.config.mjs +20 -0
  3. package/jest.config.cts +15 -0
  4. package/package.json +2 -2
  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 +26 -0
  37. package/src/lib/database/reconcile-legacy-migrations.ts +87 -0
  38. package/src/lib/database/typeorm.config.ts +217 -0
  39. package/src/lib/delegation/delegation-rule.entity.ts +84 -0
  40. package/src/lib/delegation/delegation.enums.ts +21 -0
  41. package/src/lib/delegation/delegation.module.ts +14 -0
  42. package/src/lib/delegation/delegation.mutations.ts +92 -0
  43. package/src/lib/delegation/delegation.queries.ts +111 -0
  44. package/src/lib/delegation/delegation.service.spec.ts +295 -0
  45. package/src/lib/delegation/delegation.service.ts +482 -0
  46. package/src/lib/delegation/dto/delegation-rule.input.ts +125 -0
  47. package/src/lib/form/dto/form-definition.input.ts +90 -0
  48. package/src/lib/form/form-definition-version.entity.ts +66 -0
  49. package/src/lib/form/form-definition.entity.ts +54 -0
  50. package/src/lib/form/form-schema-lint.object.ts +10 -0
  51. package/src/lib/form/form-schema.validator.spec.ts +180 -0
  52. package/src/lib/form/form-schema.validator.ts +538 -0
  53. package/src/lib/form/form.enums.ts +20 -0
  54. package/src/lib/form/form.module.ts +19 -0
  55. package/src/lib/form/form.mutations.ts +73 -0
  56. package/src/lib/form/form.queries.ts +72 -0
  57. package/src/lib/form/form.service.spec.ts +371 -0
  58. package/src/lib/form/form.service.ts +607 -0
  59. package/src/lib/identity/identity-options.ts +41 -0
  60. package/src/lib/identity/identity.module.ts +96 -0
  61. package/src/lib/identity/identity.queries.ts +61 -0
  62. package/src/lib/identity/identity.service.spec.ts +328 -0
  63. package/src/lib/identity/identity.service.ts +277 -0
  64. package/src/lib/identity/member-base.adapter.spec.ts +122 -0
  65. package/src/lib/identity/member-base.adapter.ts +302 -0
  66. package/src/lib/identity/member-metadata-cache.entity.ts +27 -0
  67. package/src/lib/identity/{member-not-found.exception.d.ts → member-not-found.exception.ts} +8 -3
  68. package/src/lib/identity/member-profile.object.ts +28 -0
  69. package/src/lib/identity/member-resolver.interface.ts +140 -0
  70. package/src/lib/migrations/0000000000001-enable-postgres-extensions.ts +14 -0
  71. package/src/lib/migrations/{0000000001000-identity-organization-foundation.js → 0000000001000-identity-organization-foundation.ts} +38 -29
  72. package/src/lib/migrations/{0000000002000-form-builder-foundation.js → 0000000002000-form-builder-foundation.ts} +22 -21
  73. package/src/lib/migrations/{0000000003000-approval-template-foundation.js → 0000000003000-approval-template-foundation.ts} +24 -21
  74. package/src/lib/migrations/{0000000004000-workflow-engine-foundation.js → 0000000004000-workflow-engine-foundation.ts} +46 -34
  75. package/src/lib/migrations/{0000000005000-delegation-rules.js → 0000000005000-delegation-rules.ts} +20 -19
  76. package/src/lib/migrations/{0000000006000-notifications-sla.js → 0000000006000-notifications-sla.ts} +26 -23
  77. package/src/lib/migrations/{0000000007000-signatures-attachments.js → 0000000007000-signatures-attachments.ts} +28 -27
  78. package/src/lib/migrations/{0000000008000-approval-template-categories.js → 0000000008000-approval-template-categories.ts} +28 -25
  79. package/src/lib/migrations/{0000000009000-task-candidates.js → 0000000009000-task-candidates.ts} +26 -23
  80. package/src/lib/migrations/{0000000010000-notification-delivery-state.js → 0000000010000-notification-delivery-state.ts} +16 -17
  81. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.ts +19 -0
  82. package/src/lib/migrations/0000000012000-notification-sla-idempotency.ts +22 -0
  83. package/src/lib/migrations/0000000013000-workflow-query-indexes.ts +113 -0
  84. package/src/lib/migrations/0000000014000-notification-resolution.ts +29 -0
  85. package/src/lib/migrations/{0000000015000-backfill-stale-notification-resolution.js → 0000000015000-backfill-stale-notification-resolution.ts} +15 -16
  86. package/src/lib/migrations/{0000000016000-archive-parallel-form-drafts.js → 0000000016000-archive-parallel-form-drafts.ts} +15 -16
  87. package/src/lib/migrations/{0000000017000-adhoc-directives.js → 0000000017000-adhoc-directives.ts} +18 -19
  88. package/src/lib/migrations/index.ts +59 -0
  89. package/src/lib/notification/dto/notification-preference.input.ts +46 -0
  90. package/src/lib/notification/notification-delivery-scheduler.service.ts +69 -0
  91. package/src/lib/notification/notification-delivery.service.spec.ts +315 -0
  92. package/src/lib/notification/notification-delivery.service.ts +339 -0
  93. package/src/lib/notification/notification-dispatcher.token.ts +13 -0
  94. package/src/lib/notification/notification-options.module.ts +67 -0
  95. package/src/lib/notification/notification-options.spec.ts +93 -0
  96. package/src/lib/notification/notification-options.ts +452 -0
  97. package/src/lib/notification/notification-preference.entity.ts +35 -0
  98. package/src/lib/notification/notification-sla-scheduler.service.ts +66 -0
  99. package/src/lib/notification/notification-template.spec.ts +33 -0
  100. package/src/lib/notification/notification-template.ts +97 -0
  101. package/src/lib/notification/notification.entity.ts +129 -0
  102. package/src/lib/notification/notification.enums.ts +64 -0
  103. package/src/lib/notification/notification.module.ts +37 -0
  104. package/src/lib/notification/notification.mutations.ts +47 -0
  105. package/src/lib/notification/notification.queries.ts +64 -0
  106. package/src/lib/notification/notification.service.spec.ts +412 -0
  107. package/src/lib/notification/notification.service.ts +1129 -0
  108. package/src/lib/organization/dto/manager-resolution.input.ts +91 -0
  109. package/src/lib/organization/dto/membership.input.ts +77 -0
  110. package/src/lib/organization/dto/org-unit.input.ts +96 -0
  111. package/src/lib/organization/dto/position.input.ts +50 -0
  112. package/src/lib/organization/json-metadata.ts +15 -0
  113. package/src/lib/organization/manager-resolution.entity.ts +44 -0
  114. package/src/lib/organization/membership.entity.ts +50 -0
  115. package/src/lib/organization/org-unit-tree-commit-result.object.ts +8 -0
  116. package/src/lib/organization/org-unit.entity.ts +53 -0
  117. package/src/lib/organization/organization-summary.object.ts +16 -0
  118. package/src/lib/organization/organization.enums.ts +19 -0
  119. package/src/lib/organization/organization.module.ts +27 -0
  120. package/src/lib/organization/organization.mutations.ts +115 -0
  121. package/src/lib/organization/organization.queries.ts +191 -0
  122. package/src/lib/organization/organization.service.spec.ts +835 -0
  123. package/src/lib/organization/organization.service.ts +1245 -0
  124. package/src/lib/organization/position.entity.ts +39 -0
  125. package/src/lib/organization/resolved-manager.object.ts +10 -0
  126. package/src/lib/signature/signature-options.spec.ts +60 -0
  127. package/src/lib/signature/signature-options.ts +120 -0
  128. package/src/lib/signature/signature-verification.object.ts +16 -0
  129. package/src/lib/signature/signature.entity.ts +62 -0
  130. package/src/lib/signature/signature.module.ts +80 -0
  131. package/src/lib/signature/signature.queries.ts +37 -0
  132. package/src/lib/signature/signature.service.spec.ts +134 -0
  133. package/src/lib/signature/signature.service.ts +205 -0
  134. package/src/lib/template/approval-template-category.entity.ts +40 -0
  135. package/src/lib/template/approval-template-version.entity.ts +82 -0
  136. package/src/lib/template/approval-template.entity.ts +64 -0
  137. package/src/lib/template/compose-approval-template.object.ts +23 -0
  138. package/src/lib/template/compose-template.mutations.ts +22 -0
  139. package/src/lib/template/dto/approval-template.input.ts +147 -0
  140. package/src/lib/template/dto/compose-approval-template.input.ts +80 -0
  141. package/src/lib/template/template.enums.ts +30 -0
  142. package/src/lib/template/template.module.ts +33 -0
  143. package/src/lib/template/template.mutations.ts +106 -0
  144. package/src/lib/template/template.queries.ts +119 -0
  145. package/src/lib/template/template.service.spec.ts +822 -0
  146. package/src/lib/template/template.service.ts +1082 -0
  147. package/src/lib/template/workflow-definition.validator.spec.ts +595 -0
  148. package/src/lib/template/workflow-definition.validator.ts +539 -0
  149. package/src/lib/testing/cel-js.jest.ts +46 -0
  150. package/src/lib/workflow-engine/activity-log.entity.ts +48 -0
  151. package/src/lib/workflow-engine/adhoc-directive.entity.ts +90 -0
  152. package/src/lib/workflow-engine/adhoc.enums.ts +42 -0
  153. package/src/lib/workflow-engine/approval-instance-page-info.object.ts +22 -0
  154. package/src/lib/workflow-engine/approval-instance.entity.ts +86 -0
  155. package/src/lib/workflow-engine/dto/adhoc-notification.input.ts +18 -0
  156. package/src/lib/workflow-engine/dto/adhoc-target.input.ts +52 -0
  157. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.ts +18 -0
  158. package/src/lib/workflow-engine/dto/decide-task.input.ts +33 -0
  159. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.ts +22 -0
  160. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.ts +22 -0
  161. package/src/lib/workflow-engine/dto/submit-approval-instance.input.ts +27 -0
  162. package/src/lib/workflow-engine/task-candidate.entity.ts +56 -0
  163. package/src/lib/workflow-engine/task-decision.entity.ts +43 -0
  164. package/src/lib/workflow-engine/task.entity.ts +102 -0
  165. package/src/lib/workflow-engine/workflow-condition-evaluator.ts +168 -0
  166. package/src/lib/workflow-engine/workflow-dashboard-summary.object.ts +22 -0
  167. package/src/lib/workflow-engine/workflow-dry-run.object.ts +58 -0
  168. package/src/lib/workflow-engine/workflow-engine.enums.ts +102 -0
  169. package/src/lib/workflow-engine/workflow-engine.module.ts +77 -0
  170. package/src/lib/workflow-engine/workflow-engine.mutations.ts +193 -0
  171. package/src/lib/workflow-engine/workflow-engine.queries.ts +241 -0
  172. package/src/lib/workflow-engine/workflow-engine.service.spec.ts +3345 -0
  173. package/src/lib/workflow-engine/workflow-engine.service.ts +6022 -0
  174. package/src/lib/workflow-engine/{workflow-engine.tokens.d.ts → workflow-engine.tokens.ts} +9 -3
  175. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.ts +69 -0
  176. package/src/lib/workflow-engine/workflow-token.entity.ts +40 -0
  177. package/tsconfig.json +24 -0
  178. package/tsconfig.lib.json +16 -0
  179. package/tsconfig.spec.json +16 -0
  180. package/src/index.js +0 -18
  181. package/src/index.js.map +0 -1
  182. package/src/lib/attachment/attachment-options.d.ts +0 -44
  183. package/src/lib/attachment/attachment-options.js +0 -62
  184. package/src/lib/attachment/attachment-options.js.map +0 -1
  185. package/src/lib/attachment/attachment-storage.provider.d.ts +0 -4
  186. package/src/lib/attachment/attachment-storage.provider.js +0 -35
  187. package/src/lib/attachment/attachment-storage.provider.js.map +0 -1
  188. package/src/lib/attachment/attachment-storage.token.js +0 -5
  189. package/src/lib/attachment/attachment-storage.token.js.map +0 -1
  190. package/src/lib/attachment/attachment.controller.d.ts +0 -8
  191. package/src/lib/attachment/attachment.controller.js +0 -38
  192. package/src/lib/attachment/attachment.controller.js.map +0 -1
  193. package/src/lib/attachment/attachment.entity.d.ts +0 -14
  194. package/src/lib/attachment/attachment.entity.js +0 -74
  195. package/src/lib/attachment/attachment.entity.js.map +0 -1
  196. package/src/lib/attachment/attachment.module.d.ts +0 -27
  197. package/src/lib/attachment/attachment.module.js +0 -103
  198. package/src/lib/attachment/attachment.module.js.map +0 -1
  199. package/src/lib/attachment/attachment.mutations.d.ts +0 -8
  200. package/src/lib/attachment/attachment.mutations.js +0 -35
  201. package/src/lib/attachment/attachment.mutations.js.map +0 -1
  202. package/src/lib/attachment/attachment.queries.d.ts +0 -9
  203. package/src/lib/attachment/attachment.queries.js +0 -78
  204. package/src/lib/attachment/attachment.queries.js.map +0 -1
  205. package/src/lib/attachment/attachment.service.d.ts +0 -49
  206. package/src/lib/attachment/attachment.service.js +0 -276
  207. package/src/lib/attachment/attachment.service.js.map +0 -1
  208. package/src/lib/attachment/dto/upload-attachment.input.d.ts +0 -11
  209. package/src/lib/attachment/dto/upload-attachment.input.js +0 -68
  210. package/src/lib/attachment/dto/upload-attachment.input.js.map +0 -1
  211. package/src/lib/attachment/index.js +0 -9
  212. package/src/lib/attachment/index.js.map +0 -1
  213. package/src/lib/bpm/bpm-root.module.d.ts +0 -155
  214. package/src/lib/bpm/bpm-root.module.js +0 -170
  215. package/src/lib/bpm/bpm-root.module.js.map +0 -1
  216. package/src/lib/bpm/index.js +0 -5
  217. package/src/lib/bpm/index.js.map +0 -1
  218. package/src/lib/bpm-auth/bpm-auth-context.d.ts +0 -24
  219. package/src/lib/bpm-auth/bpm-auth-context.extractor.d.ts +0 -4
  220. package/src/lib/bpm-auth/bpm-auth-context.extractor.js +0 -69
  221. package/src/lib/bpm-auth/bpm-auth-context.extractor.js.map +0 -1
  222. package/src/lib/bpm-auth/bpm-auth-context.js +0 -15
  223. package/src/lib/bpm-auth/bpm-auth-context.js.map +0 -1
  224. package/src/lib/bpm-auth/bpm-auth.authorization.d.ts +0 -12
  225. package/src/lib/bpm-auth/bpm-auth.authorization.js +0 -67
  226. package/src/lib/bpm-auth/bpm-auth.authorization.js.map +0 -1
  227. package/src/lib/bpm-auth/bpm-auth.decorators.d.ts +0 -3
  228. package/src/lib/bpm-auth/bpm-auth.decorators.js +0 -11
  229. package/src/lib/bpm-auth/bpm-auth.decorators.js.map +0 -1
  230. package/src/lib/bpm-auth/bpm-auth.guard.d.ts +0 -7
  231. package/src/lib/bpm-auth/bpm-auth.guard.js +0 -24
  232. package/src/lib/bpm-auth/bpm-auth.guard.js.map +0 -1
  233. package/src/lib/bpm-auth/bpm-auth.module.d.ts +0 -6
  234. package/src/lib/bpm-auth/bpm-auth.module.js +0 -69
  235. package/src/lib/bpm-auth/bpm-auth.module.js.map +0 -1
  236. package/src/lib/bpm-auth/bpm-auth.options.d.ts +0 -11
  237. package/src/lib/bpm-auth/bpm-auth.options.js +0 -3
  238. package/src/lib/bpm-auth/bpm-auth.options.js.map +0 -1
  239. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.d.ts +0 -10
  240. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js +0 -40
  241. package/src/lib/bpm-auth/configurable-bpm-auth-context.accessor.js.map +0 -1
  242. package/src/lib/bpm-auth/index.js +0 -10
  243. package/src/lib/bpm-auth/index.js.map +0 -1
  244. package/src/lib/common/filters/all-exceptions.filter.d.ts +0 -25
  245. package/src/lib/common/filters/all-exceptions.filter.js +0 -93
  246. package/src/lib/common/filters/all-exceptions.filter.js.map +0 -1
  247. package/src/lib/common/index.js +0 -5
  248. package/src/lib/common/index.js.map +0 -1
  249. package/src/lib/common/iso-duration.d.ts +0 -1
  250. package/src/lib/common/iso-duration.js +0 -27
  251. package/src/lib/common/iso-duration.js.map +0 -1
  252. package/src/lib/condition/condition.module.d.ts +0 -2
  253. package/src/lib/condition/condition.module.js +0 -16
  254. package/src/lib/condition/condition.module.js.map +0 -1
  255. package/src/lib/condition/condition.service.d.ts +0 -16
  256. package/src/lib/condition/condition.service.js +0 -82
  257. package/src/lib/condition/condition.service.js.map +0 -1
  258. package/src/lib/condition/index.js +0 -6
  259. package/src/lib/condition/index.js.map +0 -1
  260. package/src/lib/database/data-source.d.ts +0 -4
  261. package/src/lib/database/data-source.js +0 -7
  262. package/src/lib/database/data-source.js.map +0 -1
  263. package/src/lib/database/index.js +0 -6
  264. package/src/lib/database/index.js.map +0 -1
  265. package/src/lib/database/migration-runner.d.ts +0 -1
  266. package/src/lib/database/migration-runner.js +0 -25
  267. package/src/lib/database/migration-runner.js.map +0 -1
  268. package/src/lib/database/reconcile-legacy-migrations.d.ts +0 -13
  269. package/src/lib/database/reconcile-legacy-migrations.js +0 -70
  270. package/src/lib/database/reconcile-legacy-migrations.js.map +0 -1
  271. package/src/lib/database/typeorm.config.d.ts +0 -14
  272. package/src/lib/database/typeorm.config.js +0 -135
  273. package/src/lib/database/typeorm.config.js.map +0 -1
  274. package/src/lib/delegation/delegation-rule.entity.d.ts +0 -19
  275. package/src/lib/delegation/delegation-rule.entity.js +0 -99
  276. package/src/lib/delegation/delegation-rule.entity.js.map +0 -1
  277. package/src/lib/delegation/delegation.enums.d.ts +0 -10
  278. package/src/lib/delegation/delegation.enums.js +0 -23
  279. package/src/lib/delegation/delegation.enums.js.map +0 -1
  280. package/src/lib/delegation/delegation.module.d.ts +0 -2
  281. package/src/lib/delegation/delegation.module.js +0 -22
  282. package/src/lib/delegation/delegation.module.js.map +0 -1
  283. package/src/lib/delegation/delegation.mutations.d.ts +0 -11
  284. package/src/lib/delegation/delegation.mutations.js +0 -87
  285. package/src/lib/delegation/delegation.mutations.js.map +0 -1
  286. package/src/lib/delegation/delegation.queries.d.ts +0 -10
  287. package/src/lib/delegation/delegation.queries.js +0 -91
  288. package/src/lib/delegation/delegation.queries.js.map +0 -1
  289. package/src/lib/delegation/delegation.service.d.ts +0 -56
  290. package/src/lib/delegation/delegation.service.js +0 -268
  291. package/src/lib/delegation/delegation.service.js.map +0 -1
  292. package/src/lib/delegation/dto/delegation-rule.input.d.ts +0 -24
  293. package/src/lib/delegation/dto/delegation-rule.input.js +0 -143
  294. package/src/lib/delegation/dto/delegation-rule.input.js.map +0 -1
  295. package/src/lib/delegation/index.js +0 -9
  296. package/src/lib/delegation/index.js.map +0 -1
  297. package/src/lib/form/dto/form-definition.input.d.ts +0 -26
  298. package/src/lib/form/dto/form-definition.input.js +0 -126
  299. package/src/lib/form/dto/form-definition.input.js.map +0 -1
  300. package/src/lib/form/form-definition-version.entity.d.ts +0 -17
  301. package/src/lib/form/form-definition-version.entity.js +0 -84
  302. package/src/lib/form/form-definition-version.entity.js.map +0 -1
  303. package/src/lib/form/form-definition.entity.d.ts +0 -13
  304. package/src/lib/form/form-definition.entity.js +0 -66
  305. package/src/lib/form/form-definition.entity.js.map +0 -1
  306. package/src/lib/form/form-schema-lint.object.d.ts +0 -4
  307. package/src/lib/form/form-schema-lint.object.js +0 -20
  308. package/src/lib/form/form-schema-lint.object.js.map +0 -1
  309. package/src/lib/form/form-schema.validator.d.ts +0 -13
  310. package/src/lib/form/form-schema.validator.js +0 -354
  311. package/src/lib/form/form-schema.validator.js.map +0 -1
  312. package/src/lib/form/form.enums.d.ts +0 -9
  313. package/src/lib/form/form.enums.js +0 -22
  314. package/src/lib/form/form.enums.js.map +0 -1
  315. package/src/lib/form/form.module.d.ts +0 -2
  316. package/src/lib/form/form.module.js +0 -27
  317. package/src/lib/form/form.module.js.map +0 -1
  318. package/src/lib/form/form.mutations.d.ts +0 -14
  319. package/src/lib/form/form.mutations.js +0 -89
  320. package/src/lib/form/form.mutations.js.map +0 -1
  321. package/src/lib/form/form.queries.d.ts +0 -16
  322. package/src/lib/form/form.queries.js +0 -96
  323. package/src/lib/form/form.queries.js.map +0 -1
  324. package/src/lib/form/form.service.d.ts +0 -49
  325. package/src/lib/form/form.service.js +0 -346
  326. package/src/lib/form/form.service.js.map +0 -1
  327. package/src/lib/form/index.js +0 -12
  328. package/src/lib/form/index.js.map +0 -1
  329. package/src/lib/identity/identity-options.d.ts +0 -13
  330. package/src/lib/identity/identity-options.js +0 -20
  331. package/src/lib/identity/identity-options.js.map +0 -1
  332. package/src/lib/identity/identity.module.d.ts +0 -17
  333. package/src/lib/identity/identity.module.js +0 -65
  334. package/src/lib/identity/identity.module.js.map +0 -1
  335. package/src/lib/identity/identity.queries.d.ts +0 -12
  336. package/src/lib/identity/identity.queries.js +0 -79
  337. package/src/lib/identity/identity.queries.js.map +0 -1
  338. package/src/lib/identity/identity.service.d.ts +0 -22
  339. package/src/lib/identity/identity.service.js +0 -141
  340. package/src/lib/identity/identity.service.js.map +0 -1
  341. package/src/lib/identity/index.js +0 -8
  342. package/src/lib/identity/index.js.map +0 -1
  343. package/src/lib/identity/member-base.adapter.d.ts +0 -95
  344. package/src/lib/identity/member-base.adapter.js +0 -137
  345. package/src/lib/identity/member-base.adapter.js.map +0 -1
  346. package/src/lib/identity/member-metadata-cache.entity.d.ts +0 -8
  347. package/src/lib/identity/member-metadata-cache.entity.js +0 -39
  348. package/src/lib/identity/member-metadata-cache.entity.js.map +0 -1
  349. package/src/lib/identity/member-not-found.exception.js +0 -26
  350. package/src/lib/identity/member-not-found.exception.js.map +0 -1
  351. package/src/lib/identity/member-profile.object.d.ts +0 -8
  352. package/src/lib/identity/member-profile.object.js +0 -37
  353. package/src/lib/identity/member-profile.object.js.map +0 -1
  354. package/src/lib/identity/member-resolver.interface.d.ts +0 -79
  355. package/src/lib/identity/member-resolver.interface.js +0 -19
  356. package/src/lib/identity/member-resolver.interface.js.map +0 -1
  357. package/src/lib/migrations/0000000000001-enable-postgres-extensions.d.ts +0 -6
  358. package/src/lib/migrations/0000000000001-enable-postgres-extensions.js +0 -17
  359. package/src/lib/migrations/0000000000001-enable-postgres-extensions.js.map +0 -1
  360. package/src/lib/migrations/0000000001000-identity-organization-foundation.d.ts +0 -6
  361. package/src/lib/migrations/0000000001000-identity-organization-foundation.js.map +0 -1
  362. package/src/lib/migrations/0000000002000-form-builder-foundation.d.ts +0 -6
  363. package/src/lib/migrations/0000000002000-form-builder-foundation.js.map +0 -1
  364. package/src/lib/migrations/0000000003000-approval-template-foundation.d.ts +0 -6
  365. package/src/lib/migrations/0000000003000-approval-template-foundation.js.map +0 -1
  366. package/src/lib/migrations/0000000004000-workflow-engine-foundation.d.ts +0 -6
  367. package/src/lib/migrations/0000000004000-workflow-engine-foundation.js.map +0 -1
  368. package/src/lib/migrations/0000000005000-delegation-rules.d.ts +0 -6
  369. package/src/lib/migrations/0000000005000-delegation-rules.js.map +0 -1
  370. package/src/lib/migrations/0000000006000-notifications-sla.d.ts +0 -6
  371. package/src/lib/migrations/0000000006000-notifications-sla.js.map +0 -1
  372. package/src/lib/migrations/0000000007000-signatures-attachments.d.ts +0 -6
  373. package/src/lib/migrations/0000000007000-signatures-attachments.js.map +0 -1
  374. package/src/lib/migrations/0000000008000-approval-template-categories.d.ts +0 -6
  375. package/src/lib/migrations/0000000008000-approval-template-categories.js.map +0 -1
  376. package/src/lib/migrations/0000000009000-task-candidates.d.ts +0 -6
  377. package/src/lib/migrations/0000000009000-task-candidates.js.map +0 -1
  378. package/src/lib/migrations/0000000010000-notification-delivery-state.d.ts +0 -6
  379. package/src/lib/migrations/0000000010000-notification-delivery-state.js.map +0 -1
  380. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.d.ts +0 -6
  381. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js +0 -22
  382. package/src/lib/migrations/0000000011000-remove-attachment-encryption-key.js.map +0 -1
  383. package/src/lib/migrations/0000000012000-notification-sla-idempotency.d.ts +0 -6
  384. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js +0 -21
  385. package/src/lib/migrations/0000000012000-notification-sla-idempotency.js.map +0 -1
  386. package/src/lib/migrations/0000000013000-workflow-query-indexes.d.ts +0 -6
  387. package/src/lib/migrations/0000000013000-workflow-query-indexes.js +0 -80
  388. package/src/lib/migrations/0000000013000-workflow-query-indexes.js.map +0 -1
  389. package/src/lib/migrations/0000000014000-notification-resolution.d.ts +0 -6
  390. package/src/lib/migrations/0000000014000-notification-resolution.js +0 -30
  391. package/src/lib/migrations/0000000014000-notification-resolution.js.map +0 -1
  392. package/src/lib/migrations/0000000015000-backfill-stale-notification-resolution.d.ts +0 -15
  393. package/src/lib/migrations/0000000015000-backfill-stale-notification-resolution.js.map +0 -1
  394. package/src/lib/migrations/0000000016000-archive-parallel-form-drafts.d.ts +0 -15
  395. package/src/lib/migrations/0000000016000-archive-parallel-form-drafts.js.map +0 -1
  396. package/src/lib/migrations/0000000017000-adhoc-directives.d.ts +0 -6
  397. package/src/lib/migrations/0000000017000-adhoc-directives.js.map +0 -1
  398. package/src/lib/migrations/index.d.ts +0 -20
  399. package/src/lib/migrations/index.js +0 -61
  400. package/src/lib/migrations/index.js.map +0 -1
  401. package/src/lib/notification/dto/notification-preference.input.d.ts +0 -9
  402. package/src/lib/notification/dto/notification-preference.input.js +0 -53
  403. package/src/lib/notification/dto/notification-preference.input.js.map +0 -1
  404. package/src/lib/notification/index.js +0 -11
  405. package/src/lib/notification/index.js.map +0 -1
  406. package/src/lib/notification/notification-delivery-scheduler.service.d.ts +0 -13
  407. package/src/lib/notification/notification-delivery-scheduler.service.js +0 -53
  408. package/src/lib/notification/notification-delivery-scheduler.service.js.map +0 -1
  409. package/src/lib/notification/notification-delivery.service.d.ts +0 -23
  410. package/src/lib/notification/notification-delivery.service.js +0 -245
  411. package/src/lib/notification/notification-delivery.service.js.map +0 -1
  412. package/src/lib/notification/notification-dispatcher.token.d.ts +0 -7
  413. package/src/lib/notification/notification-dispatcher.token.js +0 -5
  414. package/src/lib/notification/notification-dispatcher.token.js.map +0 -1
  415. package/src/lib/notification/notification-options.d.ts +0 -215
  416. package/src/lib/notification/notification-options.js +0 -128
  417. package/src/lib/notification/notification-options.js.map +0 -1
  418. package/src/lib/notification/notification-options.module.d.ts +0 -23
  419. package/src/lib/notification/notification-options.module.js +0 -41
  420. package/src/lib/notification/notification-options.module.js.map +0 -1
  421. package/src/lib/notification/notification-preference.entity.d.ts +0 -10
  422. package/src/lib/notification/notification-preference.entity.js +0 -50
  423. package/src/lib/notification/notification-preference.entity.js.map +0 -1
  424. package/src/lib/notification/notification-sla-scheduler.service.d.ts +0 -13
  425. package/src/lib/notification/notification-sla-scheduler.service.js +0 -51
  426. package/src/lib/notification/notification-sla-scheduler.service.js.map +0 -1
  427. package/src/lib/notification/notification-template.d.ts +0 -13
  428. package/src/lib/notification/notification-template.js +0 -61
  429. package/src/lib/notification/notification-template.js.map +0 -1
  430. package/src/lib/notification/notification.entity.d.ts +0 -31
  431. package/src/lib/notification/notification.entity.js +0 -149
  432. package/src/lib/notification/notification.entity.js.map +0 -1
  433. package/src/lib/notification/notification.enums.d.ts +0 -38
  434. package/src/lib/notification/notification.enums.js +0 -63
  435. package/src/lib/notification/notification.enums.js.map +0 -1
  436. package/src/lib/notification/notification.module.d.ts +0 -2
  437. package/src/lib/notification/notification.module.js +0 -45
  438. package/src/lib/notification/notification.module.js.map +0 -1
  439. package/src/lib/notification/notification.mutations.d.ts +0 -11
  440. package/src/lib/notification/notification.mutations.js +0 -64
  441. package/src/lib/notification/notification.mutations.js.map +0 -1
  442. package/src/lib/notification/notification.queries.d.ts +0 -11
  443. package/src/lib/notification/notification.queries.js +0 -77
  444. package/src/lib/notification/notification.queries.js.map +0 -1
  445. package/src/lib/notification/notification.service.d.ts +0 -121
  446. package/src/lib/notification/notification.service.js +0 -713
  447. package/src/lib/notification/notification.service.js.map +0 -1
  448. package/src/lib/organization/dto/manager-resolution.input.d.ts +0 -18
  449. package/src/lib/organization/dto/manager-resolution.input.js +0 -107
  450. package/src/lib/organization/dto/manager-resolution.input.js.map +0 -1
  451. package/src/lib/organization/dto/membership.input.d.ts +0 -16
  452. package/src/lib/organization/dto/membership.input.js +0 -99
  453. package/src/lib/organization/dto/membership.input.js.map +0 -1
  454. package/src/lib/organization/dto/org-unit.input.d.ts +0 -24
  455. package/src/lib/organization/dto/org-unit.input.js +0 -119
  456. package/src/lib/organization/dto/org-unit.input.js.map +0 -1
  457. package/src/lib/organization/dto/position.input.d.ts +0 -13
  458. package/src/lib/organization/dto/position.input.js +0 -70
  459. package/src/lib/organization/dto/position.input.js.map +0 -1
  460. package/src/lib/organization/index.js +0 -16
  461. package/src/lib/organization/index.js.map +0 -1
  462. package/src/lib/organization/json-metadata.d.ts +0 -1
  463. package/src/lib/organization/json-metadata.js +0 -14
  464. package/src/lib/organization/json-metadata.js.map +0 -1
  465. package/src/lib/organization/manager-resolution.entity.d.ts +0 -11
  466. package/src/lib/organization/manager-resolution.entity.js +0 -55
  467. package/src/lib/organization/manager-resolution.entity.js.map +0 -1
  468. package/src/lib/organization/membership.entity.d.ts +0 -11
  469. package/src/lib/organization/membership.entity.js +0 -60
  470. package/src/lib/organization/membership.entity.js.map +0 -1
  471. package/src/lib/organization/org-unit-tree-commit-result.object.d.ts +0 -4
  472. package/src/lib/organization/org-unit-tree-commit-result.object.js +0 -17
  473. package/src/lib/organization/org-unit-tree-commit-result.object.js.map +0 -1
  474. package/src/lib/organization/org-unit.entity.d.ts +0 -13
  475. package/src/lib/organization/org-unit.entity.js +0 -64
  476. package/src/lib/organization/org-unit.entity.js.map +0 -1
  477. package/src/lib/organization/organization-summary.object.d.ts +0 -6
  478. package/src/lib/organization/organization-summary.object.js +0 -28
  479. package/src/lib/organization/organization-summary.object.js.map +0 -1
  480. package/src/lib/organization/organization.enums.d.ts +0 -11
  481. package/src/lib/organization/organization.enums.js +0 -22
  482. package/src/lib/organization/organization.enums.js.map +0 -1
  483. package/src/lib/organization/organization.module.d.ts +0 -2
  484. package/src/lib/organization/organization.module.js +0 -35
  485. package/src/lib/organization/organization.module.js.map +0 -1
  486. package/src/lib/organization/organization.mutations.d.ts +0 -26
  487. package/src/lib/organization/organization.mutations.js +0 -148
  488. package/src/lib/organization/organization.mutations.js.map +0 -1
  489. package/src/lib/organization/organization.queries.d.ts +0 -23
  490. package/src/lib/organization/organization.queries.js +0 -204
  491. package/src/lib/organization/organization.queries.js.map +0 -1
  492. package/src/lib/organization/organization.service.d.ts +0 -103
  493. package/src/lib/organization/organization.service.js +0 -768
  494. package/src/lib/organization/organization.service.js.map +0 -1
  495. package/src/lib/organization/position.entity.d.ts +0 -9
  496. package/src/lib/organization/position.entity.js +0 -48
  497. package/src/lib/organization/position.entity.js.map +0 -1
  498. package/src/lib/organization/resolved-manager.object.d.ts +0 -4
  499. package/src/lib/organization/resolved-manager.object.js +0 -20
  500. package/src/lib/organization/resolved-manager.object.js.map +0 -1
  501. package/src/lib/signature/index.js +0 -8
  502. package/src/lib/signature/index.js.map +0 -1
  503. package/src/lib/signature/signature-options.d.ts +0 -33
  504. package/src/lib/signature/signature-options.js +0 -51
  505. package/src/lib/signature/signature-options.js.map +0 -1
  506. package/src/lib/signature/signature-verification.object.d.ts +0 -6
  507. package/src/lib/signature/signature-verification.object.js +0 -28
  508. package/src/lib/signature/signature-verification.object.js.map +0 -1
  509. package/src/lib/signature/signature.entity.d.ts +0 -16
  510. package/src/lib/signature/signature.entity.js +0 -88
  511. package/src/lib/signature/signature.entity.js.map +0 -1
  512. package/src/lib/signature/signature.module.d.ts +0 -11
  513. package/src/lib/signature/signature.module.js +0 -60
  514. package/src/lib/signature/signature.module.js.map +0 -1
  515. package/src/lib/signature/signature.queries.d.ts +0 -11
  516. package/src/lib/signature/signature.queries.js +0 -46
  517. package/src/lib/signature/signature.queries.js.map +0 -1
  518. package/src/lib/signature/signature.service.d.ts +0 -25
  519. package/src/lib/signature/signature.service.js +0 -124
  520. package/src/lib/signature/signature.service.js.map +0 -1
  521. package/src/lib/template/approval-template-category.entity.d.ts +0 -9
  522. package/src/lib/template/approval-template-category.entity.js +0 -49
  523. package/src/lib/template/approval-template-category.entity.js.map +0 -1
  524. package/src/lib/template/approval-template-version.entity.d.ts +0 -21
  525. package/src/lib/template/approval-template-version.entity.js +0 -106
  526. package/src/lib/template/approval-template-version.entity.js.map +0 -1
  527. package/src/lib/template/approval-template.entity.d.ts +0 -14
  528. package/src/lib/template/approval-template.entity.js +0 -74
  529. package/src/lib/template/approval-template.entity.js.map +0 -1
  530. package/src/lib/template/compose-approval-template.object.d.ts +0 -11
  531. package/src/lib/template/compose-approval-template.object.js +0 -36
  532. package/src/lib/template/compose-approval-template.object.js.map +0 -1
  533. package/src/lib/template/compose-template.mutations.d.ts +0 -8
  534. package/src/lib/template/compose-template.mutations.js +0 -32
  535. package/src/lib/template/compose-template.mutations.js.map +0 -1
  536. package/src/lib/template/dto/approval-template.input.d.ts +0 -36
  537. package/src/lib/template/dto/approval-template.input.js +0 -187
  538. package/src/lib/template/dto/approval-template.input.js.map +0 -1
  539. package/src/lib/template/dto/compose-approval-template.input.d.ts +0 -17
  540. package/src/lib/template/dto/compose-approval-template.input.js +0 -99
  541. package/src/lib/template/dto/compose-approval-template.input.js.map +0 -1
  542. package/src/lib/template/index.js +0 -14
  543. package/src/lib/template/index.js.map +0 -1
  544. package/src/lib/template/template.enums.d.ts +0 -14
  545. package/src/lib/template/template.enums.js +0 -31
  546. package/src/lib/template/template.enums.js.map +0 -1
  547. package/src/lib/template/template.module.d.ts +0 -2
  548. package/src/lib/template/template.module.js +0 -41
  549. package/src/lib/template/template.module.js.map +0 -1
  550. package/src/lib/template/template.mutations.d.ts +0 -20
  551. package/src/lib/template/template.mutations.js +0 -139
  552. package/src/lib/template/template.mutations.js.map +0 -1
  553. package/src/lib/template/template.queries.d.ts +0 -16
  554. package/src/lib/template/template.queries.js +0 -133
  555. package/src/lib/template/template.queries.js.map +0 -1
  556. package/src/lib/template/template.service.d.ts +0 -83
  557. package/src/lib/template/template.service.js +0 -654
  558. package/src/lib/template/template.service.js.map +0 -1
  559. package/src/lib/template/workflow-definition.validator.d.ts +0 -9
  560. package/src/lib/template/workflow-definition.validator.js +0 -362
  561. package/src/lib/template/workflow-definition.validator.js.map +0 -1
  562. package/src/lib/testing/cel-js.jest.d.ts +0 -15
  563. package/src/lib/testing/cel-js.jest.js +0 -27
  564. package/src/lib/testing/cel-js.jest.js.map +0 -1
  565. package/src/lib/workflow-engine/activity-log.entity.d.ts +0 -12
  566. package/src/lib/workflow-engine/activity-log.entity.js +0 -62
  567. package/src/lib/workflow-engine/activity-log.entity.js.map +0 -1
  568. package/src/lib/workflow-engine/adhoc-directive.entity.d.ts +0 -31
  569. package/src/lib/workflow-engine/adhoc-directive.entity.js +0 -105
  570. package/src/lib/workflow-engine/adhoc-directive.entity.js.map +0 -1
  571. package/src/lib/workflow-engine/adhoc.enums.d.ts +0 -21
  572. package/src/lib/workflow-engine/adhoc.enums.js +0 -42
  573. package/src/lib/workflow-engine/adhoc.enums.js.map +0 -1
  574. package/src/lib/workflow-engine/approval-instance-page-info.object.d.ts +0 -8
  575. package/src/lib/workflow-engine/approval-instance-page-info.object.js +0 -36
  576. package/src/lib/workflow-engine/approval-instance-page-info.object.js.map +0 -1
  577. package/src/lib/workflow-engine/approval-instance.entity.d.ts +0 -22
  578. package/src/lib/workflow-engine/approval-instance.entity.js +0 -113
  579. package/src/lib/workflow-engine/approval-instance.entity.js.map +0 -1
  580. package/src/lib/workflow-engine/dto/adhoc-notification.input.d.ts +0 -6
  581. package/src/lib/workflow-engine/dto/adhoc-notification.input.js +0 -28
  582. package/src/lib/workflow-engine/dto/adhoc-notification.input.js.map +0 -1
  583. package/src/lib/workflow-engine/dto/adhoc-target.input.d.ts +0 -16
  584. package/src/lib/workflow-engine/dto/adhoc-target.input.js +0 -61
  585. package/src/lib/workflow-engine/dto/adhoc-target.input.js.map +0 -1
  586. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.d.ts +0 -5
  587. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js +0 -29
  588. package/src/lib/workflow-engine/dto/cancel-approval-instance.input.js.map +0 -1
  589. package/src/lib/workflow-engine/dto/decide-task.input.d.ts +0 -9
  590. package/src/lib/workflow-engine/dto/decide-task.input.js +0 -47
  591. package/src/lib/workflow-engine/dto/decide-task.input.js.map +0 -1
  592. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.d.ts +0 -6
  593. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js +0 -34
  594. package/src/lib/workflow-engine/dto/dry-run-approval-workflow.input.js.map +0 -1
  595. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.d.ts +0 -6
  596. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js +0 -34
  597. package/src/lib/workflow-engine/dto/resubmit-approval-instance.input.js.map +0 -1
  598. package/src/lib/workflow-engine/dto/submit-approval-instance.input.d.ts +0 -7
  599. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js +0 -40
  600. package/src/lib/workflow-engine/dto/submit-approval-instance.input.js.map +0 -1
  601. package/src/lib/workflow-engine/index.js +0 -28
  602. package/src/lib/workflow-engine/index.js.map +0 -1
  603. package/src/lib/workflow-engine/task-candidate.entity.d.ts +0 -14
  604. package/src/lib/workflow-engine/task-candidate.entity.js +0 -72
  605. package/src/lib/workflow-engine/task-candidate.entity.js.map +0 -1
  606. package/src/lib/workflow-engine/task-decision.entity.d.ts +0 -12
  607. package/src/lib/workflow-engine/task-decision.entity.js +0 -60
  608. package/src/lib/workflow-engine/task-decision.entity.js.map +0 -1
  609. package/src/lib/workflow-engine/task.entity.d.ts +0 -25
  610. package/src/lib/workflow-engine/task.entity.js +0 -130
  611. package/src/lib/workflow-engine/task.entity.js.map +0 -1
  612. package/src/lib/workflow-engine/workflow-condition-evaluator.d.ts +0 -5
  613. package/src/lib/workflow-engine/workflow-condition-evaluator.js +0 -107
  614. package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +0 -1
  615. package/src/lib/workflow-engine/workflow-dashboard-summary.object.d.ts +0 -8
  616. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js +0 -36
  617. package/src/lib/workflow-engine/workflow-dashboard-summary.object.js.map +0 -1
  618. package/src/lib/workflow-engine/workflow-dry-run.object.d.ts +0 -21
  619. package/src/lib/workflow-engine/workflow-dry-run.object.js +0 -86
  620. package/src/lib/workflow-engine/workflow-dry-run.object.js.map +0 -1
  621. package/src/lib/workflow-engine/workflow-engine.enums.d.ts +0 -61
  622. package/src/lib/workflow-engine/workflow-engine.enums.js +0 -98
  623. package/src/lib/workflow-engine/workflow-engine.enums.js.map +0 -1
  624. package/src/lib/workflow-engine/workflow-engine.module.d.ts +0 -9
  625. package/src/lib/workflow-engine/workflow-engine.module.js +0 -78
  626. package/src/lib/workflow-engine/workflow-engine.module.js.map +0 -1
  627. package/src/lib/workflow-engine/workflow-engine.mutations.d.ts +0 -29
  628. package/src/lib/workflow-engine/workflow-engine.mutations.js +0 -218
  629. package/src/lib/workflow-engine/workflow-engine.mutations.js.map +0 -1
  630. package/src/lib/workflow-engine/workflow-engine.queries.d.ts +0 -31
  631. package/src/lib/workflow-engine/workflow-engine.queries.js +0 -237
  632. package/src/lib/workflow-engine/workflow-engine.queries.js.map +0 -1
  633. package/src/lib/workflow-engine/workflow-engine.service.d.ts +0 -188
  634. package/src/lib/workflow-engine/workflow-engine.service.js +0 -3561
  635. package/src/lib/workflow-engine/workflow-engine.service.js.map +0 -1
  636. package/src/lib/workflow-engine/workflow-engine.tokens.js +0 -5
  637. package/src/lib/workflow-engine/workflow-engine.tokens.js.map +0 -1
  638. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.d.ts +0 -18
  639. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js +0 -39
  640. package/src/lib/workflow-engine/workflow-service-task-dispatcher.token.js.map +0 -1
  641. package/src/lib/workflow-engine/workflow-token.entity.d.ts +0 -10
  642. package/src/lib/workflow-engine/workflow-token.entity.js +0 -50
  643. package/src/lib/workflow-engine/workflow-token.entity.js.map +0 -1
  644. /package/src/{index.d.ts → index.ts} +0 -0
  645. /package/src/lib/attachment/{index.d.ts → index.ts} +0 -0
  646. /package/src/lib/bpm/{index.d.ts → index.ts} +0 -0
  647. /package/src/lib/bpm-auth/{index.d.ts → index.ts} +0 -0
  648. /package/src/lib/common/{index.d.ts → index.ts} +0 -0
  649. /package/src/lib/condition/{index.d.ts → index.ts} +0 -0
  650. /package/src/lib/database/{index.d.ts → index.ts} +0 -0
  651. /package/src/lib/delegation/{index.d.ts → index.ts} +0 -0
  652. /package/src/lib/form/{index.d.ts → index.ts} +0 -0
  653. /package/src/lib/identity/{index.d.ts → index.ts} +0 -0
  654. /package/src/lib/notification/{index.d.ts → index.ts} +0 -0
  655. /package/src/lib/organization/{index.d.ts → index.ts} +0 -0
  656. /package/src/lib/signature/{index.d.ts → index.ts} +0 -0
  657. /package/src/lib/template/{index.d.ts → index.ts} +0 -0
  658. /package/src/lib/workflow-engine/{index.d.ts → index.ts} +0 -0
@@ -1,3561 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WorkflowEngineService = void 0;
4
- const tslib_1 = require("tslib");
5
- const common_1 = require("@nestjs/common");
6
- const typeorm_1 = require("@nestjs/typeorm");
7
- const typeorm_2 = require("typeorm");
8
- const attachment_service_1 = require("../attachment/attachment.service");
9
- const delegation_service_1 = require("../delegation/delegation.service");
10
- const form_definition_version_entity_1 = require("../form/form-definition-version.entity");
11
- const form_enums_1 = require("../form/form.enums");
12
- const notification_service_1 = require("../notification/notification.service");
13
- const notification_entity_1 = require("../notification/notification.entity");
14
- const notification_enums_1 = require("../notification/notification.enums");
15
- const signature_service_1 = require("../signature/signature.service");
16
- const condition_service_1 = require("../condition/condition.service");
17
- const manager_resolution_entity_1 = require("../organization/manager-resolution.entity");
18
- const membership_entity_1 = require("../organization/membership.entity");
19
- const org_unit_entity_1 = require("../organization/org-unit.entity");
20
- const organization_enums_1 = require("../organization/organization.enums");
21
- const approval_template_version_entity_1 = require("../template/approval-template-version.entity");
22
- const approval_template_entity_1 = require("../template/approval-template.entity");
23
- const template_enums_1 = require("../template/template.enums");
24
- const activity_log_entity_1 = require("./activity-log.entity");
25
- const adhoc_directive_entity_1 = require("./adhoc-directive.entity");
26
- const adhoc_enums_1 = require("./adhoc.enums");
27
- const approval_instance_entity_1 = require("./approval-instance.entity");
28
- const task_decision_entity_1 = require("./task-decision.entity");
29
- const task_candidate_entity_1 = require("./task-candidate.entity");
30
- const task_entity_1 = require("./task.entity");
31
- const workflow_engine_enums_1 = require("./workflow-engine.enums");
32
- const workflow_condition_evaluator_1 = require("./workflow-condition-evaluator");
33
- const workflow_dry_run_object_1 = require("./workflow-dry-run.object");
34
- const workflow_service_task_dispatcher_token_1 = require("./workflow-service-task-dispatcher.token");
35
- const workflow_token_entity_1 = require("./workflow-token.entity");
36
- const MAX_PROCESSING_STEPS = 500;
37
- const DEFAULT_APPROVAL_HISTORY_TASK_LIMIT = 50;
38
- const SYSTEM_DRY_RUN_INSTANCE_ID = 'dry-run-instance';
39
- const WORKFLOW_READ_ALL_PERMISSIONS = new Set([
40
- 'bpm:*',
41
- 'bpm.workflow.read_all',
42
- 'bpm:workflow:read_all',
43
- 'workflow.read_all',
44
- ]);
45
- let WorkflowEngineService = class WorkflowEngineService {
46
- constructor(approvalInstanceRepository, workflowTokenRepository, taskRepository, taskCandidateRepository, taskDecisionRepository, notificationRepository, activityLogRepository, adhocDirectiveRepository, approvalTemplateRepository, approvalTemplateVersionRepository, formDefinitionVersionRepository, attachmentService, conditionService, delegationService, notificationService, signatureService, serviceTaskDispatcher = new workflow_service_task_dispatcher_token_1.DefaultWorkflowServiceTaskDispatcher()) {
47
- this.approvalInstanceRepository = approvalInstanceRepository;
48
- this.workflowTokenRepository = workflowTokenRepository;
49
- this.taskRepository = taskRepository;
50
- this.taskCandidateRepository = taskCandidateRepository;
51
- this.taskDecisionRepository = taskDecisionRepository;
52
- this.notificationRepository = notificationRepository;
53
- this.activityLogRepository = activityLogRepository;
54
- this.adhocDirectiveRepository = adhocDirectiveRepository;
55
- this.approvalTemplateRepository = approvalTemplateRepository;
56
- this.approvalTemplateVersionRepository = approvalTemplateVersionRepository;
57
- this.formDefinitionVersionRepository = formDefinitionVersionRepository;
58
- this.attachmentService = attachmentService;
59
- this.conditionService = conditionService;
60
- this.delegationService = delegationService;
61
- this.notificationService = notificationService;
62
- this.signatureService = signatureService;
63
- this.serviceTaskDispatcher = serviceTaskDispatcher;
64
- }
65
- async submitApprovalInstance(input) {
66
- const formData = parseJsonObject(input.formDataJson, 'formDataJson');
67
- const template = await this.getTemplateOrThrow(input.templateId);
68
- if (!template.currentVersionId) {
69
- throw new common_1.ConflictException('Approval template does not have a published version');
70
- }
71
- const templateVersion = await this.getPublishedTemplateVersionOrThrow(template.currentVersionId);
72
- const formDefinitionVersion = await this.getPublishedFormVersionOrThrow(templateVersion.formDefinitionVersionId);
73
- validateSubmittedFormData(formDefinitionVersion.schema, formData);
74
- const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
75
- ? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
76
- : await this.readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
77
- if (!this.conditionService.evaluateBoolean(templateVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel')) {
78
- throw new common_1.ConflictException('Current member is not allowed to start this approval template');
79
- }
80
- const startedAt = new Date();
81
- const startNode = templateVersion.workflowDefinition.nodes.find((node) => node.type === 'startEvent');
82
- if (!startNode) {
83
- throw new common_1.BadRequestException('Published workflow does not include a startEvent');
84
- }
85
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
86
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
87
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
88
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
89
- const instance = await instanceRepository.save(instanceRepository.create({
90
- completedAt: null,
91
- formData,
92
- formDefinitionSnapshot: {
93
- formDefinitionVersionId: formDefinitionVersion.id,
94
- schema: formDefinitionVersion.schema,
95
- uiSchema: formDefinitionVersion.uiSchema,
96
- version: formDefinitionVersion.version,
97
- },
98
- initiatorMemberId: input.initiatorMemberId,
99
- initiatorMetadataSnapshot,
100
- startedAt,
101
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
102
- templateId: template.id,
103
- templateVersionId: templateVersion.id,
104
- title: input.title?.trim() || template.name,
105
- workflowSnapshot: templateVersion.workflowDefinition,
106
- }));
107
- await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
108
- formData,
109
- instanceId: instance.id,
110
- });
111
- const token = await tokenRepository.save(tokenRepository.create({
112
- consumedAt: null,
113
- currentNodeId: startNode.id,
114
- instanceId: instance.id,
115
- parentTokenId: null,
116
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
117
- }));
118
- await activityRepository.save([
119
- activityRepository.create({
120
- actorMemberId: input.initiatorMemberId,
121
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_STARTED,
122
- instanceId: instance.id,
123
- nodeId: startNode.id,
124
- payload: {
125
- templateId: template.id,
126
- templateVersionId: templateVersion.id,
127
- },
128
- taskId: null,
129
- }),
130
- activityRepository.create({
131
- actorMemberId: null,
132
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
133
- instanceId: instance.id,
134
- nodeId: startNode.id,
135
- payload: {
136
- tokenId: token.id,
137
- },
138
- taskId: null,
139
- }),
140
- ]);
141
- await this.processRunningInstance(manager, instance);
142
- return instance;
143
- });
144
- }
145
- async processInstance(instanceId) {
146
- await this.approvalInstanceRepository.manager.transaction(async (manager) => {
147
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
148
- instanceId,
149
- ]);
150
- const instance = await manager
151
- .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
152
- .findOne({ where: { id: instanceId } });
153
- if (!instance) {
154
- throw new common_1.NotFoundException(`Approval instance ${instanceId} was not found`);
155
- }
156
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
157
- actorMemberId: null,
158
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.ENGINE_PROCESS_REQUESTED,
159
- instanceId,
160
- nodeId: null,
161
- payload: {
162
- state: instance.state,
163
- },
164
- taskId: null,
165
- }));
166
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
167
- return;
168
- }
169
- await this.processRunningInstance(manager, instance);
170
- });
171
- }
172
- async decideTask(input) {
173
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
174
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
175
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
176
- const taskDecisionRepository = manager.getRepository(task_decision_entity_1.TaskDecisionEntity);
177
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
178
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
179
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
180
- const task = await taskRepository.findOne({
181
- where: { id: input.taskId },
182
- });
183
- if (!task) {
184
- throw new common_1.NotFoundException(`Task ${input.taskId} was not found`);
185
- }
186
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
187
- task.instanceId,
188
- ]);
189
- if (task.status !== workflow_engine_enums_1.TaskStatusEnum.PENDING &&
190
- task.status !== workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS) {
191
- throw new common_1.ConflictException(`Task ${task.id} is not pending`);
192
- }
193
- const { actorCandidate, taskCandidates } = await this.readTaskActorContext(manager, task, input.decidedByMemberId);
194
- if (input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED &&
195
- input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.REJECTED &&
196
- input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED &&
197
- input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED) {
198
- throw new common_1.ConflictException(`Task decision action ${input.action} is not supported yet`);
199
- }
200
- const decisionComment = input.comment?.trim() || null;
201
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.REJECTED &&
202
- !decisionComment) {
203
- throw new common_1.BadRequestException('Reject decision comment is required');
204
- }
205
- const transferToMemberId = input.transferToMemberId?.trim() || null;
206
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED &&
207
- !transferToMemberId) {
208
- throw new common_1.BadRequestException('Transfer target member is required');
209
- }
210
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED &&
211
- transferToMemberId === input.decidedByMemberId) {
212
- throw new common_1.ConflictException('Transfer target must be a different member');
213
- }
214
- const instance = await instanceRepository.findOne({
215
- where: { id: task.instanceId },
216
- });
217
- if (!instance) {
218
- throw new common_1.NotFoundException(`Approval instance ${task.instanceId} was not found`);
219
- }
220
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
221
- throw new common_1.ConflictException(`Approval instance ${instance.id} is not running`);
222
- }
223
- const returnToNodeId = input.action === workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED
224
- ? readReturnTargetNodeId(instance.workflowSnapshot, task.nodeId, input.returnToNodeId)
225
- : null;
226
- const decidedAt = new Date();
227
- const claimedTask = await taskRepository.save({
228
- ...task,
229
- assigneeMemberId: task.assigneeMemberId ?? input.decidedByMemberId,
230
- openedAt: task.openedAt ?? decidedAt,
231
- originalAssigneeMemberId: task.originalAssigneeMemberId ??
232
- actorCandidate?.originalMemberId ??
233
- input.decidedByMemberId,
234
- status: workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS,
235
- });
236
- const claimedCandidate = actorCandidate
237
- ? await taskCandidateRepository.save({
238
- ...actorCandidate,
239
- claimedAt: actorCandidate.claimedAt ?? decidedAt,
240
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED,
241
- })
242
- : null;
243
- const signature = await this.signatureService.signTaskDecision(manager, {
244
- action: input.action,
245
- comment: decisionComment,
246
- decidedAt,
247
- instance,
248
- returnToNodeId,
249
- signerMemberId: input.decidedByMemberId,
250
- task: claimedTask,
251
- transferToMemberId,
252
- });
253
- const decision = await taskDecisionRepository.save(taskDecisionRepository.create({
254
- action: input.action,
255
- comment: decisionComment,
256
- decidedAt,
257
- decidedByMemberId: input.decidedByMemberId,
258
- returnToNodeId,
259
- signatureId: signature.id,
260
- taskId: task.id,
261
- transferToMemberId,
262
- }));
263
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED) {
264
- await this.transferTask(manager, instance, claimedTask, decision, signature, transferToMemberId, decisionComment, decidedAt, claimedCandidate);
265
- await this.notificationService.resolveTaskNotifications({
266
- actingMemberId: input.decidedByMemberId,
267
- manager,
268
- resolution: notification_enums_1.NotificationResolutionEnum.TRANSFERRED,
269
- supersedeOthers: true,
270
- taskId: claimedTask.id,
271
- });
272
- return decision;
273
- }
274
- const nextCandidateRows = await this.markTaskCandidateDecision({
275
- action: input.action,
276
- candidate: claimedCandidate,
277
- candidates: taskCandidates,
278
- decidedAt,
279
- manager,
280
- task: claimedTask,
281
- });
282
- const shouldCompleteTask = shouldCompleteTaskAfterDecision({
283
- action: input.action,
284
- candidates: nextCandidateRows,
285
- decisionPolicy: readDecisionPolicySnapshot(claimedTask),
286
- });
287
- const completedTask = shouldCompleteTask
288
- ? await taskRepository.save({
289
- ...claimedTask,
290
- completedAt: decidedAt,
291
- status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED,
292
- })
293
- : await taskRepository.save({
294
- ...claimedTask,
295
- status: workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS,
296
- });
297
- await activityRepository.save(activityRepository.create({
298
- actorMemberId: input.decidedByMemberId,
299
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_DECIDED,
300
- instanceId: instance.id,
301
- nodeId: claimedTask.nodeId,
302
- payload: {
303
- action: input.action,
304
- comment: decisionComment,
305
- decisionId: decision.id,
306
- signatureId: signature.id,
307
- signedPayloadHash: signature.signedPayloadHash,
308
- },
309
- taskId: completedTask.id,
310
- }));
311
- // Resolve the recipient's "待簽" notification so its inline 同意/拒絕
312
- // actions disappear once the task is decided. When the task has ended
313
- // (rejected / returned, or an approval that completed it), every other
314
- // candidate's notification is superseded too; a non-completing approval
315
- // on a multi-approver task leaves the remaining approvers' open.
316
- const taskEnded = input.action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED || shouldCompleteTask;
317
- await this.notificationService.resolveTaskNotifications({
318
- actingMemberId: input.decidedByMemberId,
319
- manager,
320
- resolution: mapDecisionToResolution(input.action),
321
- supersedeOthers: taskEnded,
322
- taskId: completedTask.id,
323
- });
324
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED &&
325
- shouldCompleteTask) {
326
- // Ad-hoc gate: the token may only advance once every task bound to
327
- // this token+node (the original task plus any ad-hoc countersign /
328
- // pre-approval tasks) has reached a terminal state. Nodes without
329
- // ad-hoc tasks keep the original single-task behaviour.
330
- if (await this.hasOpenTasksForTokenNode(manager, claimedTask.tokenId, claimedTask.nodeId)) {
331
- return decision;
332
- }
333
- const token = await tokenRepository.findOne({
334
- where: { id: claimedTask.tokenId },
335
- });
336
- if (!token) {
337
- throw new common_1.NotFoundException(`Workflow token ${claimedTask.tokenId} was not found`);
338
- }
339
- if (token.status !== workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) {
340
- throw new common_1.ConflictException(`Workflow token ${token.id} is not waiting for a task decision`);
341
- }
342
- const activeToken = await tokenRepository.save({
343
- ...token,
344
- consumedAt: null,
345
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
346
- });
347
- await this.dispatchAdhocStageNotifications(manager, instance, claimedTask.nodeId, 'APPROVED');
348
- await this.advanceTokenToOutgoingNodes(manager, instance, activeToken, readWorkflowNodeOrThrow(instance.workflowSnapshot, claimedTask.nodeId));
349
- await this.processRunningInstance(manager, instance);
350
- return decision;
351
- }
352
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED) {
353
- return decision;
354
- }
355
- if (input.action === workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED) {
356
- await this.returnInstanceToNode(manager, instance, completedTask, returnToNodeId, decidedAt);
357
- return decision;
358
- }
359
- if (await this.handleAdhocPreApprovalRejection(manager, instance, completedTask, decidedAt, decisionComment)) {
360
- return decision;
361
- }
362
- await this.rejectInstance(manager, instance, completedTask, decidedAt);
363
- return decision;
364
- });
365
- }
366
- async cancelApprovalInstance(input) {
367
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
368
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
369
- input.instanceId,
370
- ]);
371
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
372
- const instance = await instanceRepository.findOne({
373
- where: { id: input.instanceId },
374
- });
375
- if (!instance) {
376
- throw new common_1.NotFoundException(`Approval instance ${input.instanceId} was not found`);
377
- }
378
- if (instance.initiatorMemberId !== input.cancelledByMemberId) {
379
- throw new common_1.ConflictException(`Approval instance ${instance.id} can only be cancelled by its initiator`);
380
- }
381
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING &&
382
- instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED) {
383
- throw new common_1.ConflictException(`Approval instance ${instance.id} is not cancellable`);
384
- }
385
- const cancelledAt = new Date();
386
- await this.consumeOpenRuntimeState(manager, instance, cancelledAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
387
- await this.notificationService.supersedeInstanceTaskNotifications({
388
- instanceId: instance.id,
389
- manager,
390
- });
391
- const cancelledInstance = await instanceRepository.save({
392
- ...instance,
393
- completedAt: cancelledAt,
394
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.CANCELLED,
395
- });
396
- await this.dispatchAdhocCompletionNotifications(manager, cancelledInstance, workflow_engine_enums_1.ApprovalInstanceStateEnum.CANCELLED);
397
- await this.cancelPendingAdhocDirectives(manager, instance.id, null);
398
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
399
- actorMemberId: input.cancelledByMemberId,
400
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_CANCELLED,
401
- instanceId: instance.id,
402
- nodeId: null,
403
- payload: {
404
- comment: input.comment?.trim() || null,
405
- },
406
- taskId: null,
407
- }));
408
- return cancelledInstance;
409
- });
410
- }
411
- async resubmitApprovalInstance(input) {
412
- const formData = parseJsonObject(input.formDataJson, 'formDataJson');
413
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
414
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
415
- input.instanceId,
416
- ]);
417
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
418
- const instance = await instanceRepository.findOne({
419
- where: { id: input.instanceId },
420
- });
421
- if (!instance) {
422
- throw new common_1.NotFoundException(`Approval instance ${input.instanceId} was not found`);
423
- }
424
- if (instance.initiatorMemberId !== input.initiatorMemberId) {
425
- throw new common_1.ConflictException(`Approval instance ${instance.id} can only be resubmitted by its initiator`);
426
- }
427
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED) {
428
- throw new common_1.ConflictException(`Approval instance ${instance.id} is not returned`);
429
- }
430
- validateSubmittedFormData(readFormDefinitionSnapshotSchema(instance.formDefinitionSnapshot), formData);
431
- const resubmittedInstance = await instanceRepository.save({
432
- ...instance,
433
- completedAt: null,
434
- formData,
435
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
436
- title: input.title?.trim() || instance.title,
437
- });
438
- await this.attachmentService.bindFormDataAttachmentsToInstance(manager, {
439
- formData,
440
- instanceId: instance.id,
441
- });
442
- const returnedContext = await this.readLatestReturnedInstanceContext(manager, instance.id);
443
- const runtimeInstance = returnedContext?.resubmitStrategy === 'FROM_RETURN_POINT'
444
- ? await this.resumeReturnedInstanceFromReturnPoint(manager, resubmittedInstance, returnedContext, new Date())
445
- : resubmittedInstance;
446
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
447
- actorMemberId: input.initiatorMemberId,
448
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RESUBMITTED,
449
- instanceId: instance.id,
450
- nodeId: null,
451
- payload: {
452
- resubmitStrategy: returnedContext?.resubmitStrategy ?? 'RESTART',
453
- },
454
- taskId: null,
455
- }));
456
- await this.processRunningInstance(manager, runtimeInstance);
457
- return runtimeInstance;
458
- });
459
- }
460
- async requestAdhocCountersign({ comment, requestedByMemberId, target, taskId, }) {
461
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
462
- const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: true });
463
- const targetValue = buildAdhocTargetValue(target);
464
- // Rejects WEBHOOK targets — countersigners must be members.
465
- buildAdhocApproverResolver(targetValue);
466
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
467
- const directive = await directiveRepository.save(directiveRepository.create({
468
- channels: null,
469
- comment: comment?.trim() || null,
470
- consumedAt: null,
471
- createdByMemberId: requestedByMemberId,
472
- instanceId: instance.id,
473
- onReject: null,
474
- originNodeId: task.nodeId,
475
- originTaskId: task.id,
476
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
477
- targetKind: targetValue.kind,
478
- targetValue: { ...targetValue },
479
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.COUNTERSIGN,
480
- }));
481
- await this.recordAdhocDirectiveActivity(manager, directive, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CREATED, requestedByMemberId, { nodeLabel: node.data.label });
482
- return directive;
483
- });
484
- }
485
- async requestAdhocPreApproval({ comment, onReject, requestedByMemberId, target, taskId, }) {
486
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
487
- const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: true });
488
- const targetValue = buildAdhocTargetValue(target);
489
- // Rejects WEBHOOK targets — pre-approvers must be members.
490
- buildAdhocApproverResolver(targetValue);
491
- const now = new Date();
492
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
493
- const directive = await directiveRepository.save(directiveRepository.create({
494
- channels: null,
495
- comment: comment?.trim() || null,
496
- consumedAt: now,
497
- createdByMemberId: requestedByMemberId,
498
- instanceId: instance.id,
499
- onReject,
500
- originNodeId: task.nodeId,
501
- originTaskId: task.id,
502
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CONSUMED,
503
- targetKind: targetValue.kind,
504
- targetValue: { ...targetValue },
505
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.PRE_APPROVAL,
506
- }));
507
- const adhocTask = await this.createAdhocTaskForDirective(manager, instance, task.tokenId, node, directive, now);
508
- await this.recordAdhocDirectiveActivity(manager, directive, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CREATED, requestedByMemberId, { adhocTaskId: adhocTask.id, nodeLabel: node.data.label, onReject });
509
- return adhocTask;
510
- });
511
- }
512
- async configureAdhocNotification({ channels, requestedByMemberId, target, taskId, type, }) {
513
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
514
- const { instance, node, task } = await this.loadAdhocOperationContext(manager, taskId, requestedByMemberId, { requireAllowAddSigner: false });
515
- const targetValue = buildAdhocTargetValue(target);
516
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
517
- const directive = await directiveRepository.save(directiveRepository.create({
518
- channels: channels?.length ? [...channels] : null,
519
- comment: null,
520
- consumedAt: null,
521
- createdByMemberId: requestedByMemberId,
522
- instanceId: instance.id,
523
- onReject: null,
524
- originNodeId: task.nodeId,
525
- originTaskId: task.id,
526
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
527
- targetKind: targetValue.kind,
528
- targetValue: { ...targetValue },
529
- type,
530
- }));
531
- await this.recordAdhocDirectiveActivity(manager, directive, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CREATED, requestedByMemberId, { nodeLabel: node.data.label });
532
- return directive;
533
- });
534
- }
535
- async cancelAdhocDirective({ cancelledByMemberId, directiveId, }) {
536
- return this.approvalInstanceRepository.manager.transaction(async (manager) => {
537
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
538
- const directive = await directiveRepository.findOne({
539
- where: { id: directiveId },
540
- });
541
- if (!directive) {
542
- throw new common_1.NotFoundException(`Ad-hoc directive ${directiveId} was not found`);
543
- }
544
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
545
- directive.instanceId,
546
- ]);
547
- if (directive.createdByMemberId !== cancelledByMemberId) {
548
- throw new common_1.ConflictException(`Ad-hoc directive ${directive.id} can only be cancelled by its creator`);
549
- }
550
- if (directive.status !== adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING) {
551
- throw new common_1.ConflictException(`Ad-hoc directive ${directive.id} is not pending`);
552
- }
553
- const cancelledDirective = await directiveRepository.save({
554
- ...directive,
555
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CANCELLED,
556
- });
557
- await this.recordAdhocDirectiveActivity(manager, cancelledDirective, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CANCELLED, cancelledByMemberId);
558
- return cancelledDirective;
559
- });
560
- }
561
- async listAdhocDirectives(instanceId, scope) {
562
- await this.getApprovalInstance(instanceId, scope);
563
- return this.adhocDirectiveRepository.find({
564
- order: { createdAt: 'ASC' },
565
- where: { instanceId },
566
- });
567
- }
568
- async readTaskActorContext(manager, task, actingMemberId) {
569
- const taskCandidates = await manager
570
- .getRepository(task_candidate_entity_1.TaskCandidateEntity)
571
- .find({ where: { taskId: task.id } });
572
- const actorCandidate = taskCandidates.find((candidate) => candidate.memberId === actingMemberId) ?? null;
573
- const isDirectAssignee = task.assigneeMemberId === actingMemberId;
574
- if (!isDirectAssignee && !actorCandidate) {
575
- throw new common_1.ConflictException(`Task ${task.id} is assigned to another member`);
576
- }
577
- if (actorCandidate &&
578
- actorCandidate.status !== workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING &&
579
- actorCandidate.status !== workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED) {
580
- throw new common_1.ConflictException(`Task ${task.id} was already decided by this member`);
581
- }
582
- return { actorCandidate, taskCandidates };
583
- }
584
- async loadAdhocOperationContext(manager, taskId, actingMemberId, options) {
585
- const task = await manager
586
- .getRepository(task_entity_1.TaskEntity)
587
- .findOne({ where: { id: taskId } });
588
- if (!task) {
589
- throw new common_1.NotFoundException(`Task ${taskId} was not found`);
590
- }
591
- await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
592
- task.instanceId,
593
- ]);
594
- if (task.status !== workflow_engine_enums_1.TaskStatusEnum.PENDING &&
595
- task.status !== workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS) {
596
- throw new common_1.ConflictException(`Task ${task.id} is not pending`);
597
- }
598
- await this.readTaskActorContext(manager, task, actingMemberId);
599
- const instance = await manager
600
- .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
601
- .findOne({ where: { id: task.instanceId } });
602
- if (!instance) {
603
- throw new common_1.NotFoundException(`Approval instance ${task.instanceId} was not found`);
604
- }
605
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
606
- throw new common_1.ConflictException(`Approval instance ${instance.id} is not running`);
607
- }
608
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
609
- if (node.type !== 'userTask') {
610
- throw new common_1.ConflictException(`Task ${task.id} is not bound to a user task node`);
611
- }
612
- if (options.requireAllowAddSigner && !node.data.allowAddSigner) {
613
- throw new common_1.ForbiddenException(`簽核節點「${node.data.label}」does not allow ad-hoc signers`);
614
- }
615
- return { instance, node, task };
616
- }
617
- async recordAdhocDirectiveActivity(manager, directive, eventType, actorMemberId, extraPayload = {}) {
618
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
619
- await activityRepository.save(activityRepository.create({
620
- actorMemberId,
621
- eventType,
622
- instanceId: directive.instanceId,
623
- nodeId: directive.originNodeId,
624
- payload: {
625
- ...extraPayload,
626
- directiveId: directive.id,
627
- directiveStatus: directive.status,
628
- directiveType: directive.type,
629
- targetKind: directive.targetKind,
630
- },
631
- taskId: directive.originTaskId,
632
- }));
633
- }
634
- async createAdhocTaskForDirective(manager, instance, tokenId, node, directive, now) {
635
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
636
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
637
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
638
- const label = directive.type === adhoc_enums_1.AdhocDirectiveTypeEnum.COUNTERSIGN
639
- ? '臨時會簽'
640
- : '臨時加簽';
641
- const resolvedCandidates = await this.resolveApproverResolver(manager, instance, buildAdhocApproverResolver(readAdhocTargetValue(directive)), `${label}「${node.data.label}」`);
642
- const candidates = await this.applyDelegationToResolvedCandidates(instance, node.id, resolvedCandidates);
643
- if (candidates.length === 0) {
644
- throw new common_1.ConflictException(`${label}「${node.data.label}」 did not resolve to any member id`);
645
- }
646
- const primaryCandidate = candidates[0];
647
- const task = await taskRepository.save(taskRepository.create({
648
- adhocDirectiveId: directive.id,
649
- adhocOriginTaskId: directive.originTaskId,
650
- adhocType: directive.type,
651
- assigneeMemberId: candidates.length === 1 ? primaryCandidate.memberId : null,
652
- assignmentType: candidates.length === 1
653
- ? workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER
654
- : workflow_engine_enums_1.TaskAssignmentTypeEnum.CANDIDATE_GROUP,
655
- completedAt: null,
656
- createdAt: now,
657
- decisionPolicySnapshot: { type: 'SINGLE' },
658
- delegationChain: candidates.length === 1 ? primaryCandidate.delegationChain : [],
659
- instanceId: instance.id,
660
- isAdhoc: true,
661
- nodeId: node.id,
662
- openedAt: null,
663
- originalAssigneeMemberId: candidates.length === 1 ? primaryCandidate.originalMemberId : null,
664
- slaDueAt: null,
665
- status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
666
- tokenId,
667
- }));
668
- const savedCandidates = await taskCandidateRepository.save(candidates.map((candidate) => taskCandidateRepository.create({
669
- claimedAt: null,
670
- createdAt: now,
671
- decidedAt: null,
672
- delegationChain: candidate.delegationChain,
673
- memberId: candidate.memberId,
674
- originalMemberId: candidate.originalMemberId,
675
- sourceType: candidate.sourceType,
676
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
677
- taskId: task.id,
678
- })));
679
- task.candidateMemberIds = savedCandidates.map((candidate) => candidate.memberId);
680
- await activityRepository.save(activityRepository.create({
681
- actorMemberId: directive.createdByMemberId,
682
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
683
- instanceId: instance.id,
684
- nodeId: node.id,
685
- payload: {
686
- adhocDirectiveId: directive.id,
687
- adhocType: directive.type,
688
- assigneeMemberId: task.assigneeMemberId,
689
- assignmentType: task.assignmentType,
690
- candidateMemberIds: savedCandidates.map((candidate) => candidate.memberId),
691
- originTaskId: directive.originTaskId,
692
- tokenId,
693
- },
694
- taskId: task.id,
695
- }));
696
- await savedCandidates.reduce(async (previous, candidate) => {
697
- await previous;
698
- await this.notificationService.createTaskAssignedNotification({
699
- instance,
700
- manager,
701
- node,
702
- task: Object.assign(new task_entity_1.TaskEntity(), task, {
703
- assigneeMemberId: candidate.memberId,
704
- delegationChain: candidate.delegationChain,
705
- originalAssigneeMemberId: candidate.originalMemberId,
706
- }),
707
- });
708
- }, Promise.resolve());
709
- return task;
710
- }
711
- async spawnCountersignTasksForNode(manager, instance, token, node, now) {
712
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
713
- const directives = await directiveRepository.find({
714
- order: { createdAt: 'ASC' },
715
- where: {
716
- instanceId: instance.id,
717
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
718
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.COUNTERSIGN,
719
- },
720
- });
721
- if (directives.length === 0) {
722
- return;
723
- }
724
- await directives.reduce(async (previous, directive) => {
725
- await previous;
726
- try {
727
- await this.createAdhocTaskForDirective(manager, instance, token.id, node, directive, now);
728
- await directiveRepository.save({
729
- ...directive,
730
- consumedAt: now,
731
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CONSUMED,
732
- });
733
- }
734
- catch (error) {
735
- // A countersign target that no longer resolves must not block the
736
- // main flow — cancel the directive and record the failure.
737
- await directiveRepository.save({
738
- ...directive,
739
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CANCELLED,
740
- });
741
- await this.recordAdhocDirectiveActivity(manager, directive, workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_DIRECTIVE_CANCELLED, null, {
742
- error: error instanceof Error
743
- ? error.message
744
- : 'Countersign target resolution failed',
745
- });
746
- }
747
- }, Promise.resolve());
748
- }
749
- async hasOpenTasksForTokenNode(manager, tokenId, nodeId) {
750
- const tasks = await manager.getRepository(task_entity_1.TaskEntity).find({
751
- where: { nodeId, tokenId },
752
- });
753
- return tasks.some((task) => task.tokenId === tokenId &&
754
- task.nodeId === nodeId &&
755
- (task.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
756
- task.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS));
757
- }
758
- async handleAdhocPreApprovalRejection(manager, instance, task, decidedAt, decisionComment) {
759
- if (!task.isAdhoc ||
760
- task.adhocType !== adhoc_enums_1.AdhocDirectiveTypeEnum.PRE_APPROVAL ||
761
- !task.adhocDirectiveId) {
762
- return false;
763
- }
764
- const directive = await manager
765
- .getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity)
766
- .findOne({ where: { id: task.adhocDirectiveId } });
767
- if (directive?.onReject !==
768
- adhoc_enums_1.AdhocPreApprovalRejectBehaviorEnum.RETURN_TO_ORIGIN) {
769
- return false;
770
- }
771
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
772
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
773
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
774
- const originTask = task.adhocOriginTaskId
775
- ? await taskRepository.findOne({ where: { id: task.adhocOriginTaskId } })
776
- : null;
777
- const originTaskIsOpen = originTask?.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
778
- originTask?.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS;
779
- // Safety: when there is nobody to hand the decision back to (origin task
780
- // missing, or already closed without a resolvable assignee), fall back to
781
- // the default rejection path instead of leaving the token stuck WAITING
782
- // with no open task.
783
- if (!originTask || (!originTaskIsOpen && !originTask.assigneeMemberId)) {
784
- return false;
785
- }
786
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
787
- if (!originTaskIsOpen && originTask.assigneeMemberId) {
788
- // The origin approver already decided — reopen a fresh decision task so
789
- // the stage can be re-decided after the rejected pre-approval.
790
- const reopenedTask = await taskRepository.save(taskRepository.create({
791
- assigneeMemberId: originTask.assigneeMemberId,
792
- assignmentType: workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER,
793
- completedAt: null,
794
- createdAt: decidedAt,
795
- decisionPolicySnapshot: { type: 'SINGLE' },
796
- delegationChain: originTask.delegationChain,
797
- instanceId: instance.id,
798
- nodeId: originTask.nodeId,
799
- openedAt: null,
800
- originalAssigneeMemberId: originTask.originalAssigneeMemberId,
801
- slaDueAt: null,
802
- status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
803
- tokenId: originTask.tokenId,
804
- }));
805
- await taskCandidateRepository.save(taskCandidateRepository.create({
806
- claimedAt: null,
807
- createdAt: decidedAt,
808
- decidedAt: null,
809
- delegationChain: originTask.delegationChain,
810
- memberId: originTask.assigneeMemberId,
811
- originalMemberId: originTask.originalAssigneeMemberId ?? originTask.assigneeMemberId,
812
- sourceType: 'DIRECT',
813
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
814
- taskId: reopenedTask.id,
815
- }));
816
- await activityRepository.save(activityRepository.create({
817
- actorMemberId: task.assigneeMemberId,
818
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
819
- instanceId: instance.id,
820
- nodeId: originTask.nodeId,
821
- payload: {
822
- adhocDirectiveId: directive.id,
823
- assigneeMemberId: originTask.assigneeMemberId,
824
- reopenedFromTaskId: originTask.id,
825
- rejectedAdhocTaskId: task.id,
826
- tokenId: originTask.tokenId,
827
- },
828
- taskId: reopenedTask.id,
829
- }));
830
- if (node.type === 'userTask') {
831
- await this.notificationService.createTaskAssignedNotification({
832
- instance,
833
- manager,
834
- node,
835
- task: reopenedTask,
836
- });
837
- }
838
- }
839
- const recipientMemberIds = await this.readOpenTaskRecipientMemberIds(manager, originTask);
840
- if (recipientMemberIds.length > 0) {
841
- await this.notificationService.createAdhocWorkflowNotifications({
842
- channels: null,
843
- instance,
844
- manager,
845
- message: `案件「${instance.title}」的臨時加簽已被拒絕${decisionComment ? `:${decisionComment}` : '。'}`,
846
- payload: {
847
- adhocTaskId: task.id,
848
- directiveId: directive.id,
849
- nodeId: task.nodeId,
850
- originTaskId: originTask.id,
851
- type: 'PRE_APPROVAL_RETURNED',
852
- },
853
- recipientMemberIds,
854
- });
855
- }
856
- await activityRepository.save(activityRepository.create({
857
- actorMemberId: task.assigneeMemberId,
858
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.ADHOC_PRE_APPROVAL_RETURNED,
859
- instanceId: instance.id,
860
- nodeId: task.nodeId,
861
- payload: {
862
- comment: decisionComment,
863
- directiveId: directive.id,
864
- originTaskId: originTask.id,
865
- rejectedAdhocTaskId: task.id,
866
- },
867
- taskId: task.id,
868
- }));
869
- return true;
870
- }
871
- async readOpenTaskRecipientMemberIds(manager, task) {
872
- if (task.assigneeMemberId) {
873
- return [task.assigneeMemberId];
874
- }
875
- const candidates = await manager
876
- .getRepository(task_candidate_entity_1.TaskCandidateEntity)
877
- .find({ where: { taskId: task.id } });
878
- return uniqueTexts(candidates
879
- .filter((candidate) => candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING ||
880
- candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED)
881
- .map((candidate) => candidate.memberId));
882
- }
883
- async dispatchAdhocStageNotifications(manager, instance, nodeId, outcome) {
884
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
885
- const directives = await directiveRepository.find({
886
- order: { createdAt: 'ASC' },
887
- where: {
888
- instanceId: instance.id,
889
- originNodeId: nodeId,
890
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
891
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.STAGE_NOTIFY,
892
- },
893
- });
894
- if (directives.length === 0) {
895
- return;
896
- }
897
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, nodeId);
898
- const nodeLabel = node.data.label;
899
- const outcomeLabel = readAdhocStageOutcomeLabel(outcome);
900
- const consumedAt = new Date();
901
- await directives.reduce(async (previous, directive) => {
902
- await previous;
903
- await this.dispatchAdhocDirectiveNotification(manager, instance, directive, `案件「${instance.title}」的階段「${nodeLabel}」已${outcomeLabel}。`, {
904
- nodeId,
905
- nodeLabel,
906
- outcome,
907
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.STAGE_NOTIFY,
908
- });
909
- await directiveRepository.save({
910
- ...directive,
911
- consumedAt,
912
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CONSUMED,
913
- });
914
- }, Promise.resolve());
915
- }
916
- async dispatchAdhocCompletionNotifications(manager, instance, finalState) {
917
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
918
- const directives = await directiveRepository.find({
919
- order: { createdAt: 'ASC' },
920
- where: {
921
- instanceId: instance.id,
922
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
923
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.COMPLETION_NOTIFY,
924
- },
925
- });
926
- if (directives.length === 0) {
927
- return;
928
- }
929
- const finalStateLabel = readInstanceFinalStateLabel(finalState);
930
- const consumedAt = new Date();
931
- await directives.reduce(async (previous, directive) => {
932
- await previous;
933
- await this.dispatchAdhocDirectiveNotification(manager, instance, directive, `案件「${instance.title}」已結案(${finalStateLabel})。`, {
934
- finalState,
935
- type: adhoc_enums_1.AdhocDirectiveTypeEnum.COMPLETION_NOTIFY,
936
- });
937
- await directiveRepository.save({
938
- ...directive,
939
- consumedAt,
940
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CONSUMED,
941
- });
942
- }, Promise.resolve());
943
- }
944
- async dispatchAdhocDirectiveNotification(manager, instance, directive, message, payload) {
945
- const targetValue = readAdhocTargetValue(directive);
946
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
947
- if (targetValue.kind === adhoc_enums_1.AdhocTargetKindEnum.WEBHOOK) {
948
- const result = await executeAdhocWebhookDispatch(this.serviceTaskDispatcher, targetValue, {
949
- ...payload,
950
- instanceId: instance.id,
951
- instanceTitle: instance.title,
952
- message,
953
- triggeredAt: new Date().toISOString(),
954
- });
955
- await activityRepository.save(activityRepository.create({
956
- actorMemberId: null,
957
- eventType: result.ok
958
- ? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
959
- : workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
960
- instanceId: instance.id,
961
- nodeId: directive.originNodeId,
962
- payload: {
963
- action: 'ADHOC_WEBHOOK',
964
- directiveId: directive.id,
965
- error: result.error,
966
- ok: result.ok,
967
- status: result.status,
968
- url: targetValue.webhookUrl,
969
- },
970
- taskId: null,
971
- }));
972
- return;
973
- }
974
- try {
975
- const recipients = await this.resolveApproverResolver(manager, instance, buildAdhocApproverResolver(targetValue), '臨時通知對象');
976
- await this.notificationService.createAdhocWorkflowNotifications({
977
- channels: directive.channels,
978
- instance,
979
- manager,
980
- message,
981
- payload: { ...payload, directiveId: directive.id },
982
- recipientMemberIds: recipients.map((recipient) => recipient.memberId),
983
- });
984
- }
985
- catch (error) {
986
- // Notification target resolution must never block the workflow
987
- // transition that triggered it — record the failure instead.
988
- await activityRepository.save(activityRepository.create({
989
- actorMemberId: null,
990
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
991
- instanceId: instance.id,
992
- nodeId: directive.originNodeId,
993
- payload: {
994
- action: 'ADHOC_NOTIFY',
995
- directiveId: directive.id,
996
- error: error instanceof Error
997
- ? error.message
998
- : 'Ad-hoc notification dispatch failed',
999
- },
1000
- taskId: null,
1001
- }));
1002
- }
1003
- }
1004
- async cancelPendingAdhocDirectives(manager, instanceId, types) {
1005
- const directiveRepository = manager.getRepository(adhoc_directive_entity_1.AdhocDirectiveEntity);
1006
- const directives = await directiveRepository.find({
1007
- where: {
1008
- instanceId,
1009
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.PENDING,
1010
- ...(types ? { type: (0, typeorm_2.In)([...types]) } : {}),
1011
- },
1012
- });
1013
- if (directives.length === 0) {
1014
- return;
1015
- }
1016
- await directiveRepository.save(directives.map((directive) => ({
1017
- ...directive,
1018
- status: adhoc_enums_1.AdhocDirectiveStatusEnum.CANCELLED,
1019
- })));
1020
- }
1021
- dryRunApprovalWorkflow(input) {
1022
- const workflowDefinition = parseWorkflowDefinition(input.workflowDefinitionJson);
1023
- const formData = parseJsonObject(input.formDataJson, 'formDataJson');
1024
- const initiatorMetadataSnapshot = input.initiatorMetadataSnapshotJson
1025
- ? parseJsonObject(input.initiatorMetadataSnapshotJson, 'initiatorMetadataSnapshotJson')
1026
- : readDefaultInitiatorMetadataSnapshot(input.initiatorMemberId);
1027
- try {
1028
- return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
1029
- errors: [],
1030
- steps: this.simulateWorkflow({
1031
- formData,
1032
- initiatorMemberId: input.initiatorMemberId,
1033
- initiatorMetadataSnapshot,
1034
- workflowDefinition,
1035
- }),
1036
- valid: true,
1037
- });
1038
- }
1039
- catch (error) {
1040
- return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunResultObject(), {
1041
- errors: [error instanceof Error ? error.message : 'Dry run failed'],
1042
- steps: [],
1043
- valid: false,
1044
- });
1045
- }
1046
- }
1047
- simulateWorkflow(input) {
1048
- const startNode = input.workflowDefinition.nodes.find((node) => node.type === 'startEvent');
1049
- if (!startNode) {
1050
- throw new common_1.BadRequestException('Workflow does not include a startEvent');
1051
- }
1052
- return this.simulateWorkflowPath({
1053
- ...input,
1054
- depth: 0,
1055
- incomingEdge: null,
1056
- lastDecision: null,
1057
- nodeId: startNode.id,
1058
- steps: [],
1059
- visitedNodeIds: new Set(),
1060
- });
1061
- }
1062
- simulateWorkflowPath(input) {
1063
- if (input.depth > MAX_PROCESSING_STEPS) {
1064
- throw new common_1.ConflictException('Dry run exceeded maximum processing steps');
1065
- }
1066
- const node = readWorkflowNodeOrThrow(input.workflowDefinition, input.nodeId);
1067
- const visitKey = `${input.nodeId}:${input.incomingEdge?.id ?? 'start'}`;
1068
- const entryCondition = readNodeEntryCondition(node);
1069
- if (input.visitedNodeIds.has(visitKey)) {
1070
- return [
1071
- ...input.steps,
1072
- createDryRunStep({
1073
- assigneeMemberId: null,
1074
- edge: input.incomingEdge,
1075
- edgeMatched: null,
1076
- edgeReason: null,
1077
- entryCondition,
1078
- entryConditionMet: null,
1079
- message: '偵測到循環路徑,已停止此分支。',
1080
- node,
1081
- status: 'STOPPED',
1082
- stepIndex: input.steps.length,
1083
- }),
1084
- ];
1085
- }
1086
- const nextVisitedNodeIds = new Set([...input.visitedNodeIds, visitKey]);
1087
- const entryConditionMet = this.conditionService.evaluateBoolean(entryCondition, buildDryRunExpressionContext(input, input.lastDecision), `workflow.nodes.${node.id}.data.entryCondition`);
1088
- const assigneeMemberId = entryConditionMet && node.type === 'userTask'
1089
- ? this.resolveDryRunAssigneeMemberId(input, node.data.approverResolver, `workflow.nodes.${node.id}.data.approverResolver`)
1090
- : null;
1091
- const nextLastDecision = entryConditionMet && node.type === 'userTask'
1092
- ? {
1093
- action: workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED,
1094
- assigneeMemberId,
1095
- nodeId: node.id,
1096
- }
1097
- : input.lastDecision;
1098
- const nextSteps = [
1099
- ...input.steps,
1100
- createDryRunStep({
1101
- assigneeMemberId,
1102
- edge: input.incomingEdge,
1103
- edgeMatched: input.incomingEdge ? true : null,
1104
- edgeReason: readDryRunEdgeReason(input.incomingEdge),
1105
- entryCondition,
1106
- entryConditionMet,
1107
- message: readDryRunStepMessage(node, entryConditionMet),
1108
- node,
1109
- status: readDryRunStepStatus(node, entryConditionMet),
1110
- stepIndex: input.steps.length,
1111
- }),
1112
- ];
1113
- if (!entryConditionMet || node.type === 'endEvent') {
1114
- if (node.type === 'endEvent') {
1115
- return nextSteps;
1116
- }
1117
- return readOutgoingEdgesOrThrow(input.workflowDefinition, node.id).reduce((steps, edge) => this.simulateWorkflowPath({
1118
- ...input,
1119
- depth: input.depth + 1,
1120
- incomingEdge: edge,
1121
- lastDecision: nextLastDecision,
1122
- nodeId: edge.target,
1123
- steps,
1124
- visitedNodeIds: nextVisitedNodeIds,
1125
- }), nextSteps);
1126
- }
1127
- const outgoingEdges = this.readDryRunAvailableOutgoingEdges(input, node, nextLastDecision);
1128
- return outgoingEdges.reduce((steps, edge) => this.simulateWorkflowPath({
1129
- ...input,
1130
- depth: input.depth + 1,
1131
- incomingEdge: edge,
1132
- lastDecision: nextLastDecision,
1133
- nodeId: edge.target,
1134
- steps,
1135
- visitedNodeIds: nextVisitedNodeIds,
1136
- }), nextSteps);
1137
- }
1138
- readDryRunAvailableOutgoingEdges(input, node, lastDecision) {
1139
- if (node.type === 'endEvent') {
1140
- return [];
1141
- }
1142
- const outgoingEdges = readOutgoingEdgesOrThrow(input.workflowDefinition, node.id);
1143
- const conditionalEdges = outgoingEdges.filter((edge) => !edge.data.isDefault && edgeHasCondition(edge));
1144
- if (conditionalEdges.length === 0) {
1145
- return outgoingEdges;
1146
- }
1147
- const matchingEdges = conditionalEdges.filter((edge) => this.evaluateDryRunEdge(input, edge, lastDecision));
1148
- if (matchingEdges.length > 0) {
1149
- return node.type === 'exclusiveGateway'
1150
- ? [matchingEdges[0]]
1151
- : matchingEdges;
1152
- }
1153
- const defaultEdges = outgoingEdges.filter((edge) => edge.data.isDefault);
1154
- if (defaultEdges.length > 0) {
1155
- return node.type === 'exclusiveGateway'
1156
- ? [defaultEdges[0]]
1157
- : defaultEdges;
1158
- }
1159
- throw new common_1.ConflictException(`Workflow node ${node.id} has no matching outgoing edge`);
1160
- }
1161
- evaluateDryRunEdge(input, edge, lastDecision) {
1162
- const expression = edge.data.condition?.trim();
1163
- if (expression) {
1164
- return this.conditionService.evaluateBoolean(expression, buildDryRunExpressionContext(input, lastDecision), `workflow.edges.${edge.id}.data.condition`);
1165
- }
1166
- return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: input.formData });
1167
- }
1168
- resolveDryRunAssigneeMemberId(input, resolver, label) {
1169
- if (resolver.type === 'DIRECT') {
1170
- return readMemberIdFromValue(resolver.memberIds, label);
1171
- }
1172
- if (resolver.type === 'DYNAMIC_FORM') {
1173
- return readMemberIdFromValue(readValueAtPath(input.formData, resolver.formPath), `${label}.formPath`);
1174
- }
1175
- if (resolver.type === 'EXPRESSION') {
1176
- return readMemberIdFromValue(this.conditionService.evaluateValue(resolver.expression, buildDryRunExpressionContext(input, null), `${label}.expression`), `${label}.expression`);
1177
- }
1178
- if (resolver.type === 'POSITION') {
1179
- return `position:${resolver.positionId}`;
1180
- }
1181
- if (resolver.type === 'ORG_UNIT_MEMBER') {
1182
- return `orgUnitMember:${resolver.orgUnitId}`;
1183
- }
1184
- if (resolver.type === 'ORG_UNIT_POSITION') {
1185
- return `orgUnitPosition:${resolver.orgUnitId}:${resolver.positionId}`;
1186
- }
1187
- if (resolver.type === 'ORG_UNIT_MANAGER') {
1188
- return `orgUnitManager:${resolver.orgUnitId}`;
1189
- }
1190
- return readMemberIdFromValue(readManagerMemberIdFromInitiatorSnapshot(input.initiatorMetadataSnapshot, resolver.levelsUp), label);
1191
- }
1192
- async getApprovalInstance(id, scope) {
1193
- const instance = await this.approvalInstanceRepository.findOne({
1194
- where: { id },
1195
- });
1196
- if (!instance) {
1197
- throw new common_1.NotFoundException(`Approval instance ${id} was not found`);
1198
- }
1199
- if (scope &&
1200
- !canReadAllWorkflows(scope) &&
1201
- !(await this.isApprovalInstanceReadableByMember(instance, scope.memberId))) {
1202
- throw new common_1.NotFoundException(`Approval instance ${id} was not found`);
1203
- }
1204
- return instance;
1205
- }
1206
- async listApprovalInstances(scope, options = {}) {
1207
- const queryBuilder = await this.createFilteredApprovalInstanceQueryBuilder(scope, options);
1208
- if (!queryBuilder) {
1209
- return [];
1210
- }
1211
- queryBuilder.orderBy('"approvalInstance"."created_at"', 'DESC');
1212
- if (shouldPaginateList(options)) {
1213
- queryBuilder
1214
- .skip(readPageOffset(options.page, options.pageSize))
1215
- .take(normalizePageSize(options.pageSize));
1216
- }
1217
- return queryBuilder.getMany();
1218
- }
1219
- async countApprovalInstances(scope, options = {}) {
1220
- const queryBuilder = await this.createFilteredApprovalInstanceQueryBuilder(scope, options);
1221
- return queryBuilder ? queryBuilder.getCount() : 0;
1222
- }
1223
- async readApprovalInstancePageInfo(scope, options = {}) {
1224
- const totalCount = await this.countApprovalInstances(scope, options);
1225
- const page = normalizePage(options.page);
1226
- const pageSize = normalizePageSize(options.pageSize);
1227
- const totalPages = Math.max(Math.ceil(totalCount / pageSize), 1);
1228
- return {
1229
- hasNextPage: page < totalPages,
1230
- hasPreviousPage: page > 1,
1231
- page,
1232
- pageSize,
1233
- totalCount,
1234
- totalPages,
1235
- };
1236
- }
1237
- async createFilteredApprovalInstanceQueryBuilder(scope, options = {}) {
1238
- const queryBuilder = this.approvalInstanceRepository.createQueryBuilder('approvalInstance');
1239
- const hasReadableInstances = this.applyReadableInstanceFilter(queryBuilder, scope, options);
1240
- if (!hasReadableInstances) {
1241
- return null;
1242
- }
1243
- const templateId = normalizeText(options.templateId);
1244
- if (options.state?.length) {
1245
- queryBuilder.andWhere('"approvalInstance"."state" IN (:...states)', {
1246
- states: [...options.state],
1247
- });
1248
- }
1249
- if (templateId) {
1250
- queryBuilder.andWhere('"approvalInstance"."template_id" = :templateId', {
1251
- templateId,
1252
- });
1253
- }
1254
- applyApprovalInstanceSearchFilter(queryBuilder, options.searchText);
1255
- return queryBuilder;
1256
- }
1257
- applyReadableInstanceFilter(queryBuilder, scope, options) {
1258
- if (!scope) {
1259
- return true;
1260
- }
1261
- const view = options.view ?? workflow_engine_enums_1.ApprovalInstanceListViewEnum.ALL;
1262
- if (view === workflow_engine_enums_1.ApprovalInstanceListViewEnum.SENT) {
1263
- queryBuilder.andWhere('"approvalInstance"."initiator_member_id" = :readableMemberId', {
1264
- readableMemberId: scope.memberId,
1265
- });
1266
- return true;
1267
- }
1268
- if (view === workflow_engine_enums_1.ApprovalInstanceListViewEnum.CC) {
1269
- queryBuilder.andWhere(`
1270
- EXISTS (
1271
- SELECT 1
1272
- FROM "notifications" "readableNotification"
1273
- WHERE "readableNotification"."instance_id" = "approvalInstance"."id"
1274
- AND "readableNotification"."recipient_member_id" = :readableMemberId
1275
- )
1276
- `, {
1277
- readableMemberId: scope.memberId,
1278
- });
1279
- return true;
1280
- }
1281
- if (canReadAllWorkflows(scope)) {
1282
- return true;
1283
- }
1284
- queryBuilder.andWhere(`
1285
- (
1286
- "approvalInstance"."initiator_member_id" = :readableMemberId
1287
- OR EXISTS (
1288
- SELECT 1
1289
- FROM "tasks" "readableTask"
1290
- WHERE "readableTask"."instance_id" = "approvalInstance"."id"
1291
- AND (
1292
- "readableTask"."assignee_member_id" = :readableMemberId
1293
- OR "readableTask"."original_assignee_member_id" = :readableMemberId
1294
- )
1295
- )
1296
- OR EXISTS (
1297
- SELECT 1
1298
- FROM "task_candidates" "readableCandidate"
1299
- INNER JOIN "tasks" "readableCandidateTask"
1300
- ON "readableCandidateTask"."id" = "readableCandidate"."task_id"
1301
- WHERE "readableCandidateTask"."instance_id" = "approvalInstance"."id"
1302
- AND (
1303
- "readableCandidate"."member_id" = :readableMemberId
1304
- OR "readableCandidate"."original_member_id" = :readableMemberId
1305
- )
1306
- )
1307
- OR EXISTS (
1308
- SELECT 1
1309
- FROM "task_decisions" "readableDecision"
1310
- INNER JOIN "tasks" "readableDecisionTask"
1311
- ON "readableDecisionTask"."id" = "readableDecision"."task_id"
1312
- WHERE "readableDecisionTask"."instance_id" = "approvalInstance"."id"
1313
- AND "readableDecision"."decided_by_member_id" = :readableMemberId
1314
- )
1315
- )
1316
- `, {
1317
- readableMemberId: scope.memberId,
1318
- });
1319
- return true;
1320
- }
1321
- async readWorkflowDashboardSummary(scope, options = {}) {
1322
- const instances = (await this.listApprovalInstances(scope, {
1323
- view: workflow_engine_enums_1.ApprovalInstanceListViewEnum.ALL,
1324
- })).filter((instance) => isInstanceWithinRange(instance, options));
1325
- const instanceIds = instances.map((instance) => instance.id);
1326
- const openTasks = instanceIds.length
1327
- ? await this.taskRepository.find({
1328
- where: {
1329
- instanceId: (0, typeorm_2.In)(instanceIds),
1330
- status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
1331
- },
1332
- })
1333
- : [];
1334
- const now = Date.now();
1335
- return {
1336
- activeInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING).length,
1337
- completedInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED).length,
1338
- overdueTaskCount: openTasks.filter((task) => task.slaDueAt && task.slaDueAt.getTime() <= now).length,
1339
- pendingTaskCount: openTasks.length,
1340
- rejectedInstanceCount: instances.filter((instance) => instance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED).length,
1341
- totalInstanceCount: instances.length,
1342
- };
1343
- }
1344
- async listLaunchableApprovalTemplates(memberId) {
1345
- const templates = await this.approvalTemplateRepository.find({
1346
- order: { updatedAt: 'DESC' },
1347
- });
1348
- const currentVersionIds = templates
1349
- .map((template) => template.currentVersionId)
1350
- .filter((versionId) => Boolean(versionId));
1351
- if (!currentVersionIds.length) {
1352
- return [];
1353
- }
1354
- const versions = await this.approvalTemplateVersionRepository.find({
1355
- where: {
1356
- id: (0, typeorm_2.In)(currentVersionIds),
1357
- status: template_enums_1.ApprovalTemplateVersionStatusEnum.PUBLISHED,
1358
- },
1359
- });
1360
- const versionById = new Map(versions.map((version) => [
1361
- version.id,
1362
- version,
1363
- ]));
1364
- const initiatorMetadataSnapshot = await this.readDefaultInitiatorMetadataSnapshot(memberId);
1365
- return templates.filter((template) => {
1366
- const currentVersion = template.currentVersionId
1367
- ? (versionById.get(template.currentVersionId) ?? null)
1368
- : null;
1369
- if (!currentVersion?.formDefinitionVersionId) {
1370
- return false;
1371
- }
1372
- return this.conditionService.evaluateBoolean(currentVersion.initiatorPolicyCel, buildInitiatorPolicyContext(initiatorMetadataSnapshot), 'initiatorPolicyCel');
1373
- });
1374
- }
1375
- async listWorkflowTokens(instanceId, scope) {
1376
- await this.getApprovalInstance(instanceId, scope);
1377
- return this.workflowTokenRepository.find({
1378
- order: { createdAt: 'ASC' },
1379
- where: { instanceId },
1380
- });
1381
- }
1382
- async listTasks(instanceId, scope) {
1383
- await this.getApprovalInstance(instanceId, scope);
1384
- return this.attachTaskCandidateSummaries(await this.taskRepository.find({
1385
- order: { createdAt: 'ASC' },
1386
- where: { instanceId },
1387
- }));
1388
- }
1389
- async listInboxTasks(assigneeMemberId) {
1390
- const directTasks = await this.taskRepository.find({
1391
- order: { createdAt: 'DESC' },
1392
- where: [
1393
- {
1394
- assigneeMemberId,
1395
- status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
1396
- },
1397
- ],
1398
- });
1399
- const candidateRows = await this.taskCandidateRepository.find({
1400
- where: {
1401
- memberId: assigneeMemberId,
1402
- status: (0, typeorm_2.In)([
1403
- workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
1404
- workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED,
1405
- ]),
1406
- },
1407
- });
1408
- const candidateTasks = candidateRows.length
1409
- ? await this.taskRepository.find({
1410
- order: { createdAt: 'DESC' },
1411
- where: {
1412
- id: (0, typeorm_2.In)(candidateRows.map((candidate) => candidate.taskId)),
1413
- status: (0, typeorm_2.In)([workflow_engine_enums_1.TaskStatusEnum.PENDING, workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS]),
1414
- },
1415
- })
1416
- : [];
1417
- return this.attachTaskCandidateSummaries(uniqueTasksById([...directTasks, ...candidateTasks]));
1418
- }
1419
- async listApprovalHistoryTasks(assigneeMemberId) {
1420
- const directTasks = await this.taskRepository.find({
1421
- order: { completedAt: 'DESC', createdAt: 'DESC' },
1422
- take: DEFAULT_APPROVAL_HISTORY_TASK_LIMIT,
1423
- where: { assigneeMemberId, status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED },
1424
- });
1425
- const candidateRows = await this.taskCandidateRepository.find({
1426
- where: {
1427
- memberId: assigneeMemberId,
1428
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
1429
- },
1430
- });
1431
- const candidateTasks = candidateRows.length
1432
- ? await this.taskRepository.find({
1433
- order: { completedAt: 'DESC', createdAt: 'DESC' },
1434
- take: DEFAULT_APPROVAL_HISTORY_TASK_LIMIT,
1435
- where: {
1436
- id: (0, typeorm_2.In)(candidateRows.map((candidate) => candidate.taskId)),
1437
- status: workflow_engine_enums_1.TaskStatusEnum.COMPLETED,
1438
- },
1439
- })
1440
- : [];
1441
- return this.attachTaskCandidateSummaries(uniqueTasksById([...directTasks, ...candidateTasks]));
1442
- }
1443
- async listTaskDecisions(taskId, scope) {
1444
- await this.assertTaskReadable(taskId, scope);
1445
- return this.taskDecisionRepository.find({
1446
- order: { decidedAt: 'ASC' },
1447
- where: { taskId },
1448
- });
1449
- }
1450
- async listTaskCandidates(taskId, scope) {
1451
- await this.assertTaskReadable(taskId, scope);
1452
- return this.taskCandidateRepository.find({
1453
- order: { createdAt: 'ASC', id: 'ASC' },
1454
- where: { taskId },
1455
- });
1456
- }
1457
- async attachTaskCandidateSummaries(tasks) {
1458
- if (!tasks.length) {
1459
- return tasks;
1460
- }
1461
- const candidates = await this.taskCandidateRepository.find({
1462
- order: { createdAt: 'ASC', id: 'ASC' },
1463
- where: { taskId: (0, typeorm_2.In)(tasks.map((task) => task.id)) },
1464
- });
1465
- const candidatesByTaskId = candidates.reduce((currentMap, candidate) => {
1466
- const nextMap = new Map(currentMap);
1467
- const memberIds = nextMap.get(candidate.taskId) ?? [];
1468
- nextMap.set(candidate.taskId, [...memberIds, candidate.memberId]);
1469
- return nextMap;
1470
- }, new Map());
1471
- return tasks.map((task) => Object.assign(new task_entity_1.TaskEntity(), task, {
1472
- candidateMemberIds: candidatesByTaskId.get(task.id) ?? [],
1473
- }));
1474
- }
1475
- async readDefaultInitiatorMetadataSnapshot(memberId) {
1476
- const today = toDateOnlyString(new Date());
1477
- const memberships = (await this.approvalInstanceRepository.manager
1478
- .getRepository(membership_entity_1.MembershipEntity)
1479
- .find({ where: { memberId } }))
1480
- .filter((membership) => isDateRangeActive(membership, today))
1481
- .sort(compareMembership);
1482
- const primaryMembership = memberships[0] ?? null;
1483
- const positionIds = uniqueTexts(memberships
1484
- .map((membership) => membership.positionId)
1485
- .filter((positionId) => Boolean(positionId)));
1486
- return {
1487
- customFields: {},
1488
- memberId,
1489
- orgUnitIds: uniqueTexts(memberships.map((membership) => membership.orgUnitId)),
1490
- positionId: primaryMembership?.positionId ?? null,
1491
- positionIds,
1492
- primaryOrgUnitId: primaryMembership?.orgUnitId ?? null,
1493
- };
1494
- }
1495
- async listActivityLogs(instanceId, scope) {
1496
- await this.getApprovalInstance(instanceId, scope);
1497
- return this.activityLogRepository.find({
1498
- order: { createdAt: 'ASC' },
1499
- where: { instanceId },
1500
- });
1501
- }
1502
- async assertTaskReadable(taskId, scope) {
1503
- const task = await this.taskRepository.findOne({ where: { id: taskId } });
1504
- if (!task) {
1505
- throw new common_1.NotFoundException(`Task ${taskId} was not found`);
1506
- }
1507
- await this.getApprovalInstance(task.instanceId, scope);
1508
- return task;
1509
- }
1510
- async isApprovalInstanceReadableByMember(instance, memberId) {
1511
- if (instance.initiatorMemberId === memberId) {
1512
- return true;
1513
- }
1514
- const tasks = await this.taskRepository.find({
1515
- where: { instanceId: instance.id },
1516
- });
1517
- if (tasks.some((task) => isTaskRelatedToMember(task, memberId))) {
1518
- return true;
1519
- }
1520
- const taskIds = tasks.map((task) => task.id);
1521
- if (!taskIds.length) {
1522
- return false;
1523
- }
1524
- const candidates = await this.taskCandidateRepository.find({
1525
- where: { taskId: (0, typeorm_2.In)(taskIds) },
1526
- });
1527
- if (candidates.some((candidate) => candidate.memberId === memberId ||
1528
- candidate.originalMemberId === memberId)) {
1529
- return true;
1530
- }
1531
- const decisions = await this.taskDecisionRepository.find({
1532
- where: { taskId: (0, typeorm_2.In)(taskIds) },
1533
- });
1534
- if (decisions.some((decision) => decision.decidedByMemberId === memberId)) {
1535
- return true;
1536
- }
1537
- // Members who received any notification for this instance (e.g. ad-hoc
1538
- // stage / completion notify recipients, CC'd service-task recipients) may
1539
- // open the instance they were notified about.
1540
- const notification = await this.notificationRepository.findOne({
1541
- where: { instanceId: instance.id, recipientMemberId: memberId },
1542
- });
1543
- return Boolean(notification);
1544
- }
1545
- async processActiveToken(manager, instance, token) {
1546
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, token.currentNodeId);
1547
- if (!(await this.isNodeEntryReady(manager, instance, token, node))) {
1548
- return;
1549
- }
1550
- if (!this.isNodeEntryConditionMet(instance, node)) {
1551
- await this.skipNodeByEntryCondition(manager, instance, token, node);
1552
- return;
1553
- }
1554
- if (node.type === 'startEvent') {
1555
- await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1556
- return;
1557
- }
1558
- if (node.type === 'endEvent') {
1559
- await this.completeTokenAtEndNode(manager, instance, token, node);
1560
- return;
1561
- }
1562
- if (node.type === 'userTask') {
1563
- await this.createUserTaskForToken(manager, instance, token, node);
1564
- return;
1565
- }
1566
- if (node.type === 'serviceTask') {
1567
- await this.executeServiceTask(manager, instance, token, node);
1568
- return;
1569
- }
1570
- if (node.type === 'exclusiveGateway') {
1571
- await this.processExclusiveGateway(manager, instance, token, node);
1572
- return;
1573
- }
1574
- if (node.type === 'parallelGateway') {
1575
- await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1576
- return;
1577
- }
1578
- return;
1579
- }
1580
- async processRunningInstance(manager, instance) {
1581
- for (let step = 0; step < MAX_PROCESSING_STEPS; step += 1) {
1582
- const token = await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).findOne({
1583
- order: { createdAt: 'ASC' },
1584
- where: {
1585
- instanceId: instance.id,
1586
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
1587
- },
1588
- });
1589
- if (!token) {
1590
- await this.completeInstanceIfNoOpenRuntimeState(manager, instance);
1591
- return;
1592
- }
1593
- await this.processActiveToken(manager, instance, token);
1594
- }
1595
- throw new common_1.ConflictException(`Approval instance ${instance.id} exceeded maximum processing steps`);
1596
- }
1597
- async completeInstanceIfNoOpenRuntimeState(manager, instance) {
1598
- if (instance.state !== workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
1599
- return;
1600
- }
1601
- const waitingTokens = await manager
1602
- .getRepository(workflow_token_entity_1.WorkflowTokenEntity)
1603
- .find({
1604
- where: {
1605
- instanceId: instance.id,
1606
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1607
- },
1608
- });
1609
- if (waitingTokens.length > 0) {
1610
- return;
1611
- }
1612
- const completedAt = new Date();
1613
- const completedInstance = await manager
1614
- .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
1615
- .save({
1616
- ...instance,
1617
- completedAt,
1618
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED,
1619
- });
1620
- await this.notificationService.createInstanceCompletedNotification({
1621
- instance: completedInstance,
1622
- manager,
1623
- });
1624
- await this.dispatchAdhocCompletionNotifications(manager, completedInstance, workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED);
1625
- await this.cancelPendingAdhocDirectives(manager, instance.id, null);
1626
- }
1627
- async isNodeEntryReady(manager, instance, token, node) {
1628
- if (node.type === 'startEvent') {
1629
- return true;
1630
- }
1631
- const incomingEdges = readIncomingEdges(instance.workflowSnapshot, node.id);
1632
- if (incomingEdges.length < 2) {
1633
- return true;
1634
- }
1635
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1636
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1637
- const activeOrWaitingTokens = (await tokenRepository.find({
1638
- where: { instanceId: instance.id },
1639
- })).filter((candidate) => candidate.currentNodeId === node.id &&
1640
- (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1641
- candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING));
1642
- const arrivedTokens = activeOrWaitingTokens.some((candidate) => candidate.id === token.id)
1643
- ? activeOrWaitingTokens
1644
- : [...activeOrWaitingTokens, token];
1645
- if ((node.data.triggerMode ?? 'AND') === 'OR') {
1646
- await this.cancelAlternativeTokensForOrTrigger(manager, instance, token, node.id);
1647
- await this.consumeJoinedTokens(manager, arrivedTokens.filter((candidate) => candidate.id !== token.id));
1648
- return true;
1649
- }
1650
- const pendingTokensThatMayReachNode = (await tokenRepository.find({
1651
- where: { instanceId: instance.id },
1652
- })).filter((candidate) => candidate.id !== token.id &&
1653
- !arrivedTokens.some((arrivedToken) => arrivedToken.id === candidate.id) &&
1654
- (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1655
- candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) &&
1656
- hasPathToNode(instance.workflowSnapshot, candidate.currentNodeId, node.id));
1657
- if (arrivedTokens.length < incomingEdges.length &&
1658
- pendingTokensThatMayReachNode.length > 0) {
1659
- await tokenRepository.save({
1660
- ...token,
1661
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1662
- });
1663
- await activityRepository.save(activityRepository.create({
1664
- actorMemberId: null,
1665
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1666
- instanceId: instance.id,
1667
- nodeId: node.id,
1668
- payload: {
1669
- arrivedCount: arrivedTokens.length,
1670
- requiredCount: incomingEdges.length,
1671
- tokenId: token.id,
1672
- triggerMode: 'AND',
1673
- },
1674
- taskId: null,
1675
- }));
1676
- return false;
1677
- }
1678
- await this.consumeJoinedTokens(manager, arrivedTokens.filter((candidate) => candidate.id !== token.id));
1679
- return true;
1680
- }
1681
- async advanceTokenToOutgoingNodes(manager, instance, token, node) {
1682
- const edges = await this.readAvailableOutgoingEdges(manager, instance, node);
1683
- if (edges.length === 1) {
1684
- await this.advanceTokenAlongEdge(manager, instance, token, node, edges[0]);
1685
- return;
1686
- }
1687
- await this.forkTokenAlongEdges(manager, instance, token, node, edges);
1688
- }
1689
- isNodeEntryConditionMet(instance, node) {
1690
- const entryCondition = readNodeEntryCondition(node);
1691
- return this.conditionService.evaluateBoolean(entryCondition, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.entryCondition`);
1692
- }
1693
- async skipNodeByEntryCondition(manager, instance, token, node) {
1694
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
1695
- actorMemberId: null,
1696
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1697
- instanceId: instance.id,
1698
- nodeId: node.id,
1699
- payload: {
1700
- skippedByEntryCondition: true,
1701
- tokenId: token.id,
1702
- },
1703
- taskId: null,
1704
- }));
1705
- await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
1706
- }
1707
- async readAvailableOutgoingEdges(manager, instance, node) {
1708
- const outgoingEdges = readOutgoingEdgesOrThrow(instance.workflowSnapshot, node.id);
1709
- const conditionalEdges = outgoingEdges.filter((edge) => !edge.data.isDefault && edgeHasCondition(edge));
1710
- if (conditionalEdges.length === 0) {
1711
- return outgoingEdges;
1712
- }
1713
- const matchingEdges = (await Promise.all(conditionalEdges.map(async (edge) => (await this.evaluateWorkflowEdge(manager, instance, node, edge))
1714
- ? edge
1715
- : null))).filter((edge) => edge !== null);
1716
- if (matchingEdges.length > 0) {
1717
- return node.type === 'exclusiveGateway'
1718
- ? [matchingEdges[0]]
1719
- : matchingEdges;
1720
- }
1721
- const defaultEdges = outgoingEdges.filter((edge) => edge.data.isDefault);
1722
- if (defaultEdges.length > 0) {
1723
- return node.type === 'exclusiveGateway'
1724
- ? [defaultEdges[0]]
1725
- : defaultEdges;
1726
- }
1727
- throw new common_1.ConflictException(`Workflow node ${node.id} has no matching outgoing edge`);
1728
- }
1729
- async evaluateWorkflowEdge(manager, instance, node, edge) {
1730
- const expression = edge.data.condition?.trim();
1731
- if (expression) {
1732
- return this.conditionService.evaluateBoolean(expression, {
1733
- ...buildWorkflowExpressionContext(instance),
1734
- lastDecision: await this.readLastDecisionContext(manager, instance.id, node.id),
1735
- }, `workflow.edges.${edge.id}.data.condition`);
1736
- }
1737
- return (0, workflow_condition_evaluator_1.evaluateWorkflowEdgeCondition)(edge, { formData: instance.formData });
1738
- }
1739
- async readLastDecisionContext(manager, instanceId, nodeId) {
1740
- const tasks = await manager.getRepository(task_entity_1.TaskEntity).find({
1741
- where: { instanceId, nodeId },
1742
- });
1743
- const taskIds = tasks.map((task) => task.id);
1744
- if (taskIds.length === 0) {
1745
- return null;
1746
- }
1747
- const decision = (await manager.getRepository(task_decision_entity_1.TaskDecisionEntity).find({
1748
- order: { decidedAt: 'DESC' },
1749
- where: { taskId: (0, typeorm_2.In)(taskIds) },
1750
- }))[0];
1751
- if (!decision) {
1752
- return null;
1753
- }
1754
- return {
1755
- action: decision.action,
1756
- comment: decision.comment,
1757
- decidedAt: decision.decidedAt.toISOString(),
1758
- decidedByMemberId: decision.decidedByMemberId,
1759
- returnToNodeId: decision.returnToNodeId,
1760
- taskId: decision.taskId,
1761
- transferToMemberId: decision.transferToMemberId,
1762
- };
1763
- }
1764
- async advanceTokenAlongEdge(manager, instance, token, node, edge) {
1765
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1766
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1767
- const nextToken = await tokenRepository.save({
1768
- ...token,
1769
- currentNodeId: edge.target,
1770
- });
1771
- await activityRepository.save(activityRepository.create({
1772
- actorMemberId: null,
1773
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1774
- instanceId: instance.id,
1775
- nodeId: edge.target,
1776
- payload: {
1777
- edgeId: edge.id,
1778
- fromNodeId: node.id,
1779
- tokenId: nextToken.id,
1780
- toNodeId: edge.target,
1781
- },
1782
- taskId: null,
1783
- }));
1784
- }
1785
- async forkTokenAlongEdges(manager, instance, token, node, edges) {
1786
- const forkedAt = new Date();
1787
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1788
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1789
- await tokenRepository.save({
1790
- ...token,
1791
- consumedAt: forkedAt,
1792
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1793
- });
1794
- const childTokens = await tokenRepository.save(edges.map((edge) => tokenRepository.create({
1795
- consumedAt: null,
1796
- currentNodeId: edge.target,
1797
- instanceId: instance.id,
1798
- parentTokenId: token.id,
1799
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
1800
- })));
1801
- await activityRepository.save([
1802
- activityRepository.create({
1803
- actorMemberId: null,
1804
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1805
- instanceId: instance.id,
1806
- nodeId: node.id,
1807
- payload: {
1808
- edgeIds: edges.map((edge) => edge.id),
1809
- fromNodeId: node.id,
1810
- tokenId: token.id,
1811
- toNodeIds: edges.map((edge) => edge.target),
1812
- },
1813
- taskId: null,
1814
- }),
1815
- ...childTokens.map((childToken, index) => activityRepository.create({
1816
- actorMemberId: null,
1817
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
1818
- instanceId: instance.id,
1819
- nodeId: childToken.currentNodeId,
1820
- payload: {
1821
- edgeId: edges[index]?.id,
1822
- parentTokenId: token.id,
1823
- tokenId: childToken.id,
1824
- },
1825
- taskId: null,
1826
- })),
1827
- ]);
1828
- }
1829
- async completeTokenAtEndNode(manager, instance, token, node) {
1830
- const completedAt = new Date();
1831
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1832
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
1833
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1834
- const endState = node.data.endState ?? 'APPROVED';
1835
- const instanceState = endState === 'REJECTED'
1836
- ? workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED
1837
- : workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED;
1838
- await tokenRepository.save({
1839
- ...token,
1840
- consumedAt: completedAt,
1841
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
1842
- });
1843
- const hasOpenToken = (await tokenRepository.find({
1844
- where: { instanceId: instance.id },
1845
- })).some((candidate) => candidate.id !== token.id &&
1846
- (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
1847
- candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING));
1848
- if (!hasOpenToken) {
1849
- const completedInstance = await instanceRepository.save({
1850
- ...instance,
1851
- completedAt,
1852
- state: instanceState,
1853
- });
1854
- if (instanceState === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED) {
1855
- await this.notificationService.createInstanceCompletedNotification({
1856
- instance: completedInstance,
1857
- manager,
1858
- });
1859
- }
1860
- await this.dispatchAdhocCompletionNotifications(manager, completedInstance, instanceState);
1861
- await this.cancelPendingAdhocDirectives(manager, instance.id, null);
1862
- }
1863
- await activityRepository.save(activityRepository.create({
1864
- actorMemberId: null,
1865
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
1866
- instanceId: instance.id,
1867
- nodeId: node.id,
1868
- payload: {
1869
- endState,
1870
- instanceState,
1871
- tokenId: token.id,
1872
- },
1873
- taskId: null,
1874
- }));
1875
- }
1876
- async createUserTaskForToken(manager, instance, token, node) {
1877
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
1878
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
1879
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
1880
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
1881
- const existingTask = await taskRepository.findOne({
1882
- where: {
1883
- isAdhoc: false,
1884
- nodeId: node.id,
1885
- tokenId: token.id,
1886
- },
1887
- });
1888
- if (existingTask) {
1889
- await tokenRepository.save({
1890
- ...token,
1891
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1892
- });
1893
- return;
1894
- }
1895
- const candidates = await this.resolveRuntimeTaskCandidates(manager, instance, node);
1896
- const primaryCandidate = candidates[0];
1897
- const now = new Date();
1898
- const task = await taskRepository.save(taskRepository.create({
1899
- assigneeMemberId: candidates.length === 1 ? primaryCandidate.memberId : null,
1900
- assignmentType: candidates.length === 1
1901
- ? workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER
1902
- : workflow_engine_enums_1.TaskAssignmentTypeEnum.CANDIDATE_GROUP,
1903
- completedAt: null,
1904
- createdAt: now,
1905
- decisionPolicySnapshot: node.data.decisionPolicy,
1906
- delegationChain: candidates.length === 1 ? primaryCandidate.delegationChain : [],
1907
- instanceId: instance.id,
1908
- nodeId: node.id,
1909
- openedAt: null,
1910
- originalAssigneeMemberId: candidates.length === 1 ? primaryCandidate.originalMemberId : null,
1911
- slaDueAt: (0, notification_service_1.calculateTaskSlaDueAt)({ node, now }),
1912
- status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
1913
- tokenId: token.id,
1914
- }));
1915
- const savedCandidates = await taskCandidateRepository.save(candidates.map((candidate) => taskCandidateRepository.create({
1916
- claimedAt: null,
1917
- createdAt: now,
1918
- decidedAt: null,
1919
- delegationChain: candidate.delegationChain,
1920
- memberId: candidate.memberId,
1921
- originalMemberId: candidate.originalMemberId,
1922
- sourceType: candidate.sourceType,
1923
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
1924
- taskId: task.id,
1925
- })));
1926
- task.candidateMemberIds = savedCandidates.map((candidate) => candidate.memberId);
1927
- const singleCandidate = savedCandidates.length === 1 ? savedCandidates[0] : null;
1928
- await tokenRepository.save({
1929
- ...token,
1930
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING,
1931
- });
1932
- await activityRepository.save(activityRepository.create({
1933
- actorMemberId: null,
1934
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
1935
- instanceId: instance.id,
1936
- nodeId: node.id,
1937
- payload: {
1938
- assignmentType: task.assignmentType,
1939
- assigneeMemberId: singleCandidate?.memberId ?? null,
1940
- candidateMemberIds: savedCandidates.map((candidate) => candidate.memberId),
1941
- originalAssigneeMemberId: singleCandidate?.originalMemberId ?? null,
1942
- tokenId: token.id,
1943
- },
1944
- taskId: task.id,
1945
- }));
1946
- await savedCandidates.reduce(async (previous, candidate) => {
1947
- await previous;
1948
- await this.notificationService.createTaskAssignedNotification({
1949
- instance,
1950
- manager,
1951
- node,
1952
- task: Object.assign(new task_entity_1.TaskEntity(), task, {
1953
- assigneeMemberId: candidate.memberId,
1954
- delegationChain: candidate.delegationChain,
1955
- originalAssigneeMemberId: candidate.originalMemberId,
1956
- }),
1957
- });
1958
- }, Promise.resolve());
1959
- // Pending ad-hoc countersign directives attach to the next user task
1960
- // created after they were requested — spawn their parallel tasks now.
1961
- await this.spawnCountersignTasksForNode(manager, instance, token, node, now);
1962
- }
1963
- async markTaskCandidateDecision({ action, candidate, candidates, decidedAt, manager, task, }) {
1964
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
1965
- if (!candidate && candidates.length === 0 && task.assigneeMemberId) {
1966
- const legacyCandidate = await taskCandidateRepository.save(taskCandidateRepository.create({
1967
- claimedAt: task.openedAt ?? decidedAt,
1968
- createdAt: task.createdAt,
1969
- decidedAt,
1970
- delegationChain: task.delegationChain,
1971
- memberId: task.assigneeMemberId,
1972
- originalMemberId: task.originalAssigneeMemberId ?? task.assigneeMemberId,
1973
- sourceType: 'DIRECT',
1974
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
1975
- taskId: task.id,
1976
- }));
1977
- return [legacyCandidate];
1978
- }
1979
- const completedCandidate = candidate
1980
- ? Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), candidate, {
1981
- claimedAt: candidate.claimedAt ?? decidedAt,
1982
- decidedAt,
1983
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED,
1984
- })
1985
- : null;
1986
- const decisionPolicy = readDecisionPolicySnapshot(task);
1987
- const shouldCancelOpenCandidates = action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED ||
1988
- decisionPolicy.type === 'SINGLE' ||
1989
- decisionPolicy.type === 'PARALLEL_ANY';
1990
- const nextCandidates = candidates.map((currentCandidate) => {
1991
- if (currentCandidate.id === completedCandidate?.id) {
1992
- return completedCandidate;
1993
- }
1994
- if (shouldCancelOpenCandidates &&
1995
- (currentCandidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING ||
1996
- currentCandidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED)) {
1997
- return Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), currentCandidate, {
1998
- decidedAt,
1999
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED,
2000
- });
2001
- }
2002
- return currentCandidate;
2003
- });
2004
- if (nextCandidates.length > 0) {
2005
- await taskCandidateRepository.save(nextCandidates);
2006
- }
2007
- return nextCandidates;
2008
- }
2009
- async transferTask(manager, instance, task, decision, signature, transferToMemberId, decisionComment, decidedAt, actorCandidate) {
2010
- if (!transferToMemberId) {
2011
- throw new common_1.BadRequestException('Transfer target member is required');
2012
- }
2013
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
2014
- const taskCandidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
2015
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
2016
- const transferringMemberId = task.assigneeMemberId;
2017
- if (!transferringMemberId) {
2018
- throw new common_1.ConflictException('Task must be claimed before transfer');
2019
- }
2020
- const nextDelegationChain = [
2021
- ...readDelegationSteps(task.delegationChain),
2022
- {
2023
- from: transferringMemberId,
2024
- reason: 'MANUAL_TRANSFER',
2025
- ruleId: null,
2026
- to: transferToMemberId,
2027
- },
2028
- ];
2029
- const transferredTask = await taskRepository.save({
2030
- ...task,
2031
- completedAt: decidedAt,
2032
- delegationChain: nextDelegationChain,
2033
- status: workflow_engine_enums_1.TaskStatusEnum.TRANSFERRED,
2034
- });
2035
- const nextTask = await taskRepository.save(taskRepository.create({
2036
- assigneeMemberId: transferToMemberId,
2037
- assignmentType: workflow_engine_enums_1.TaskAssignmentTypeEnum.DIRECT_MEMBER,
2038
- completedAt: null,
2039
- decisionPolicySnapshot: { type: 'SINGLE' },
2040
- delegationChain: nextDelegationChain,
2041
- instanceId: task.instanceId,
2042
- nodeId: task.nodeId,
2043
- openedAt: null,
2044
- originalAssigneeMemberId: task.originalAssigneeMemberId,
2045
- slaDueAt: task.slaDueAt,
2046
- status: workflow_engine_enums_1.TaskStatusEnum.PENDING,
2047
- tokenId: task.tokenId,
2048
- }));
2049
- await taskCandidateRepository.save([
2050
- ...(actorCandidate
2051
- ? [
2052
- taskCandidateRepository.create({
2053
- ...actorCandidate,
2054
- decidedAt,
2055
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.TRANSFERRED,
2056
- }),
2057
- ]
2058
- : []),
2059
- taskCandidateRepository.create({
2060
- claimedAt: null,
2061
- createdAt: decidedAt,
2062
- decidedAt: null,
2063
- delegationChain: nextDelegationChain,
2064
- memberId: transferToMemberId,
2065
- originalMemberId: task.originalAssigneeMemberId ?? transferToMemberId,
2066
- sourceType: 'DIRECT',
2067
- status: workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING,
2068
- taskId: nextTask.id,
2069
- }),
2070
- ]);
2071
- await activityRepository.save([
2072
- activityRepository.create({
2073
- actorMemberId: task.assigneeMemberId,
2074
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_DECIDED,
2075
- instanceId: instance.id,
2076
- nodeId: task.nodeId,
2077
- payload: {
2078
- action: workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED,
2079
- comment: decisionComment,
2080
- decisionId: decision.id,
2081
- signatureId: signature.id,
2082
- signedPayloadHash: signature.signedPayloadHash,
2083
- transferToMemberId,
2084
- },
2085
- taskId: transferredTask.id,
2086
- }),
2087
- activityRepository.create({
2088
- actorMemberId: transferringMemberId,
2089
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TASK_CREATED,
2090
- instanceId: instance.id,
2091
- nodeId: task.nodeId,
2092
- payload: {
2093
- assigneeMemberId: transferToMemberId,
2094
- delegationChain: nextDelegationChain,
2095
- originalAssigneeMemberId: task.originalAssigneeMemberId,
2096
- tokenId: task.tokenId,
2097
- transferredFromTaskId: task.id,
2098
- },
2099
- taskId: nextTask.id,
2100
- }),
2101
- ]);
2102
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
2103
- if (node.type === 'userTask') {
2104
- await this.notificationService.createTaskAssignedNotification({
2105
- instance,
2106
- manager,
2107
- node,
2108
- task: nextTask,
2109
- transferred: true,
2110
- });
2111
- }
2112
- }
2113
- async resolveRuntimeTaskCandidates(manager, instance, node) {
2114
- const resolvedCandidates = await this.resolveApproverResolver(manager, instance, node.data.approverResolver, `簽核節點「${node.data.label}」`);
2115
- const uniqueCandidates = await this.applyDelegationToResolvedCandidates(instance, node.id, resolvedCandidates);
2116
- if (uniqueCandidates.length === 0) {
2117
- throw new common_1.ConflictException(`簽核節點「${node.data.label}」 did not resolve to any member id`);
2118
- }
2119
- return uniqueCandidates;
2120
- }
2121
- async applyDelegationToResolvedCandidates(instance, nodeId, resolvedCandidates) {
2122
- const context = {
2123
- formData: instance.formData,
2124
- initiatorMemberId: instance.initiatorMemberId,
2125
- initiatorMetadataSnapshot: instance.initiatorMetadataSnapshot,
2126
- instanceId: instance.id,
2127
- nodeId,
2128
- state: instance.state,
2129
- templateId: instance.templateId,
2130
- templateVersionId: instance.templateVersionId,
2131
- title: instance.title,
2132
- };
2133
- const delegatedCandidates = await Promise.all(resolvedCandidates.map(async (candidate) => {
2134
- const delegationResolution = await this.delegationService.resolveAssignee(candidate.memberId, context);
2135
- return {
2136
- delegationChain: delegationResolution.delegationChain,
2137
- memberId: delegationResolution.finalAssigneeMemberId,
2138
- originalMemberId: candidate.memberId,
2139
- sourceType: candidate.sourceType,
2140
- };
2141
- }));
2142
- return uniqueRuntimeCandidates(delegatedCandidates);
2143
- }
2144
- async resolveApproverResolver(manager, instance, resolver, label) {
2145
- if (resolver.type === 'DIRECT') {
2146
- return readMemberIdsFromValue(resolver.memberIds, label).map((memberId) => ({ memberId, sourceType: resolver.type }));
2147
- }
2148
- if (resolver.type === 'DYNAMIC_FORM') {
2149
- return readMemberIdsFromValue(readValueAtPath(instance.formData, resolver.formPath), `${label}.formPath`).map((memberId) => ({ memberId, sourceType: resolver.type }));
2150
- }
2151
- if (resolver.type === 'EXPRESSION') {
2152
- return readMemberIdsFromValue(this.conditionService.evaluateValue(resolver.expression, buildWorkflowExpressionContext(instance), `${label}.expression`), `${label}.expression`).map((memberId) => ({ memberId, sourceType: resolver.type }));
2153
- }
2154
- if (resolver.type === 'POSITION') {
2155
- return this.resolvePositionCandidates(manager, resolver.positionId, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
2156
- }
2157
- if (resolver.type === 'ORG_UNIT_MEMBER') {
2158
- return this.resolveOrgUnitMemberCandidates(manager, resolver.orgUnitId, Boolean(resolver.includeDescendants), label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
2159
- }
2160
- if (resolver.type === 'ORG_UNIT_POSITION') {
2161
- return this.resolveOrgUnitPositionCandidates(manager, resolver.orgUnitId, resolver.positionId, Boolean(resolver.includeDescendants), label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
2162
- }
2163
- if (resolver.type === 'ORG_UNIT_MANAGER') {
2164
- return this.resolveOrgUnitManagerCandidates(manager, instance, resolver.orgUnitId, resolver.fallback, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
2165
- }
2166
- return this.resolveInitiatorManagerCandidates(manager, instance, resolver.levelsUp, resolver.fallback, label).then((memberIds) => memberIds.map((memberId) => ({ memberId, sourceType: resolver.type })));
2167
- }
2168
- async resolvePositionCandidates(manager, positionId, label) {
2169
- const today = toDateOnlyString(new Date());
2170
- const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
2171
- where: { positionId },
2172
- });
2173
- const activeMemberships = memberships
2174
- .filter((membership) => membership.effectiveFrom <= today &&
2175
- (!membership.effectiveTo || membership.effectiveTo >= today))
2176
- .sort((first, second) => {
2177
- if (first.isPrimary !== second.isPrimary) {
2178
- return first.isPrimary ? -1 : 1;
2179
- }
2180
- return second.effectiveFrom.localeCompare(first.effectiveFrom);
2181
- });
2182
- if (!activeMemberships.length) {
2183
- throw new common_1.ConflictException(`${label} position ${positionId} has no active membership`);
2184
- }
2185
- return uniqueTexts(activeMemberships.map((membership) => membership.memberId));
2186
- }
2187
- async resolveInitiatorManagerCandidates(manager, instance, levelsUp, fallback, label) {
2188
- const normalizedLevelsUp = Math.max(Math.trunc(levelsUp), 1);
2189
- const resolvedManagerMemberIds = await this.resolveManagerMemberIdsFromOrganization(manager, instance.initiatorMemberId, normalizedLevelsUp);
2190
- if (resolvedManagerMemberIds.length) {
2191
- return resolvedManagerMemberIds;
2192
- }
2193
- return [
2194
- this.resolveFallbackAssignee(instance, fallback, label, `找不到發起人的第 ${normalizedLevelsUp} 層主管`),
2195
- ];
2196
- }
2197
- resolveFallbackAssignee(instance, fallback, label, failureReason) {
2198
- if (!fallback || fallback.type === 'NONE') {
2199
- throw new common_1.ConflictException(`${label} 無法建立待簽任務:${failureReason},且未設定改派固定人。請聯絡流程管理員調整主管規則或模板 fallback。`);
2200
- }
2201
- if (!fallback.memberId.trim()) {
2202
- throw new common_1.ConflictException(`${label} 無法建立待簽任務:改派固定人未設定。`);
2203
- }
2204
- if (fallback.memberId === instance.initiatorMemberId &&
2205
- !fallback.allowInitiatorSelfApproval) {
2206
- throw new common_1.ConflictException(`${label} 無法建立待簽任務:改派固定人是申請人本人,且此節點未允許申請人自簽。`);
2207
- }
2208
- return fallback.memberId;
2209
- }
2210
- async resolveManagerMemberIdsFromOrganization(manager, memberId, levelsUp) {
2211
- return this.resolveManagerMemberIdsAtLevel(manager, memberId, toDateOnlyString(new Date()), levelsUp);
2212
- }
2213
- async resolveManagerMemberIdsAtLevel(manager, memberId, date, remainingLevels) {
2214
- const directManagerMemberIds = await this.resolveDirectManagerMemberIds(manager, memberId, date);
2215
- if (remainingLevels <= 1 || directManagerMemberIds.length === 0) {
2216
- return directManagerMemberIds;
2217
- }
2218
- const nextLevelGroups = await Promise.all(directManagerMemberIds.map((managerMemberId) => this.resolveManagerMemberIdsAtLevel(manager, managerMemberId, date, remainingLevels - 1)));
2219
- return uniqueTexts(nextLevelGroups.flat());
2220
- }
2221
- async resolveDirectManagerMemberIds(manager, memberId, date) {
2222
- const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
2223
- where: { memberId },
2224
- });
2225
- const activeMemberships = memberships.filter((membership) => isDateRangeActive(membership, date));
2226
- const directOrgUnitIds = activeMemberships.map((membership) => membership.orgUnitId);
2227
- const positionIds = activeMemberships
2228
- .map((membership) => membership.positionId)
2229
- .filter((positionId) => Boolean(positionId));
2230
- const orgUnitIds = await this.readOrgUnitAndAncestorIds(manager, directOrgUnitIds);
2231
- const candidatePairs = [
2232
- { scopeId: memberId, scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER },
2233
- ...orgUnitIds.map((scopeId) => ({
2234
- scopeId,
2235
- scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
2236
- })),
2237
- ...positionIds.map((scopeId) => ({
2238
- scopeId,
2239
- scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION,
2240
- })),
2241
- ];
2242
- return this.resolveManagerResolutionCandidates(manager, candidatePairs, date);
2243
- }
2244
- async resolveOrgUnitManagerCandidates(manager, instance, orgUnitId, fallback, label) {
2245
- const date = toDateOnlyString(new Date());
2246
- const orgUnitIds = await this.readOrgUnitAndAncestorIds(manager, [
2247
- orgUnitId,
2248
- ]);
2249
- const managerMemberIds = await this.resolveManagerResolutionCandidates(manager, orgUnitIds.map((scopeId) => ({
2250
- scopeId,
2251
- scopeType: organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
2252
- })), date);
2253
- if (!managerMemberIds.length) {
2254
- return [
2255
- this.resolveFallbackAssignee(instance, fallback, label, `找不到指定組織的主管規則`),
2256
- ];
2257
- }
2258
- return managerMemberIds;
2259
- }
2260
- async readOrgUnitAndAncestorIds(manager, orgUnitIds) {
2261
- if (!orgUnitIds.length) {
2262
- return [];
2263
- }
2264
- const orgUnitRepository = manager.getRepository(org_unit_entity_1.OrgUnitEntity);
2265
- const directOrgUnits = await orgUnitRepository.find({
2266
- where: { deletedAt: (0, typeorm_2.IsNull)(), id: (0, typeorm_2.In)([...orgUnitIds]) },
2267
- });
2268
- const ancestorGroups = await Promise.all(directOrgUnits.map((orgUnit) => orgUnitRepository
2269
- .createQueryBuilder('orgUnit')
2270
- .where('orgUnit.deleted_at IS NULL')
2271
- .andWhere('orgUnit.path @> :path', { path: orgUnit.path })
2272
- .getMany()));
2273
- const byId = new Map([...directOrgUnits, ...ancestorGroups.flat()].map((orgUnit) => [orgUnit.id, orgUnit]));
2274
- return [...byId.keys()];
2275
- }
2276
- async resolveManagerResolutionCandidates(manager, candidatePairs, date) {
2277
- const scopeIds = candidatePairs.map((pair) => pair.scopeId);
2278
- if (!scopeIds.length) {
2279
- return [];
2280
- }
2281
- const resolutions = await manager
2282
- .getRepository(manager_resolution_entity_1.ManagerResolutionEntity)
2283
- .find({
2284
- where: {
2285
- scopeId: (0, typeorm_2.In)([...scopeIds]),
2286
- scopeType: (0, typeorm_2.In)([
2287
- organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER,
2288
- organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT,
2289
- organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION,
2290
- ]),
2291
- },
2292
- });
2293
- const active = resolutions
2294
- .filter((resolution) => candidatePairs.some((pair) => pair.scopeId === resolution.scopeId &&
2295
- pair.scopeType === resolution.scopeType))
2296
- .filter((resolution) => isDateRangeActive(resolution, date))
2297
- .sort(compareManagerResolution);
2298
- return uniqueTexts(active.map((resolution) => resolution.managerMemberId));
2299
- }
2300
- async resolveOrgUnitMemberCandidates(manager, orgUnitId, includeDescendants, label) {
2301
- const orgUnitIds = await this.readOrgUnitScopeIds(manager, orgUnitId, includeDescendants);
2302
- const today = toDateOnlyString(new Date());
2303
- const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
2304
- where: { orgUnitId: (0, typeorm_2.In)([...orgUnitIds]) },
2305
- });
2306
- const memberIds = memberships
2307
- .filter((membership) => isDateRangeActive(membership, today))
2308
- .sort(compareMembership)
2309
- .map((membership) => membership.memberId);
2310
- if (memberIds.length === 0) {
2311
- throw new common_1.ConflictException(`${label} orgUnit ${orgUnitId} has no active member`);
2312
- }
2313
- return uniqueTexts(memberIds);
2314
- }
2315
- async resolveOrgUnitPositionCandidates(manager, orgUnitId, positionId, includeDescendants, label) {
2316
- const orgUnitIds = await this.readOrgUnitScopeIds(manager, orgUnitId, includeDescendants);
2317
- const today = toDateOnlyString(new Date());
2318
- const memberships = await manager.getRepository(membership_entity_1.MembershipEntity).find({
2319
- where: { orgUnitId: (0, typeorm_2.In)([...orgUnitIds]), positionId },
2320
- });
2321
- const memberIds = memberships
2322
- .filter((membership) => isDateRangeActive(membership, today))
2323
- .sort(compareMembership)
2324
- .map((membership) => membership.memberId);
2325
- if (memberIds.length === 0) {
2326
- throw new common_1.ConflictException(`${label} orgUnit ${orgUnitId} and position ${positionId} has no active membership`);
2327
- }
2328
- return uniqueTexts(memberIds);
2329
- }
2330
- async readOrgUnitScopeIds(manager, orgUnitId, includeDescendants) {
2331
- const orgUnitRepository = manager.getRepository(org_unit_entity_1.OrgUnitEntity);
2332
- const orgUnit = await orgUnitRepository.findOne({
2333
- where: { deletedAt: (0, typeorm_2.IsNull)(), id: orgUnitId },
2334
- });
2335
- if (!orgUnit) {
2336
- throw new common_1.ConflictException(`Org unit ${orgUnitId} was not found`);
2337
- }
2338
- if (!includeDescendants) {
2339
- return [orgUnit.id];
2340
- }
2341
- const descendants = await orgUnitRepository
2342
- .createQueryBuilder('orgUnit')
2343
- .where('orgUnit.deleted_at IS NULL')
2344
- .andWhere('orgUnit.path <@ :path', { path: orgUnit.path })
2345
- .getMany();
2346
- return uniqueTexts([orgUnit.id, ...descendants.map((unit) => unit.id)]);
2347
- }
2348
- async processExclusiveGateway(manager, instance, token, node) {
2349
- if (node.data.direction !== 'split') {
2350
- await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
2351
- return;
2352
- }
2353
- const edge = (await this.readAvailableOutgoingEdges(manager, instance, node))[0];
2354
- await this.advanceTokenAlongEdge(manager, instance, token, node, edge);
2355
- }
2356
- async executeServiceTask(manager, instance, token, node) {
2357
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
2358
- const action = node.data.action;
2359
- if (action.type === 'WEBHOOK') {
2360
- const payload = typeof action.payload === 'string' && action.payload.trim()
2361
- ? this.conditionService.evaluateValue(action.payload, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.payload`)
2362
- : {
2363
- instance: buildWorkflowExpressionContext(instance).instance,
2364
- triggeredAt: new Date().toISOString(),
2365
- };
2366
- const result = await executeWebhookServiceAction(this.serviceTaskDispatcher, action, payload);
2367
- await activityRepository.save(activityRepository.create({
2368
- actorMemberId: null,
2369
- eventType: result.ok
2370
- ? workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED
2371
- : workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_FAILED,
2372
- instanceId: instance.id,
2373
- nodeId: node.id,
2374
- payload: {
2375
- action: 'WEBHOOK',
2376
- error: result.error,
2377
- ok: result.ok,
2378
- status: result.status,
2379
- tokenId: token.id,
2380
- url: action.url,
2381
- },
2382
- taskId: null,
2383
- }));
2384
- await this.advanceTokenToOutgoingNodes(manager, instance, token, node);
2385
- return;
2386
- }
2387
- if (action.type === 'SET_FORM_FIELD') {
2388
- const value = this.conditionService.evaluateValue(action.value, buildWorkflowExpressionContext(instance), `workflow.nodes.${node.id}.data.action.value`);
2389
- const updatedFormData = writeValueAtPath(instance.formData, action.fieldPath, value);
2390
- const updatedInstance = await manager
2391
- .getRepository(approval_instance_entity_1.ApprovalInstanceEntity)
2392
- .save({
2393
- ...instance,
2394
- formData: updatedFormData,
2395
- });
2396
- Object.assign(instance, updatedInstance);
2397
- await activityRepository.save(activityRepository.create({
2398
- actorMemberId: null,
2399
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.SERVICE_TASK_EXECUTED,
2400
- instanceId: instance.id,
2401
- nodeId: node.id,
2402
- payload: {
2403
- action: 'SET_FORM_FIELD',
2404
- fieldPath: action.fieldPath,
2405
- tokenId: token.id,
2406
- },
2407
- taskId: null,
2408
- }));
2409
- await this.advanceTokenToOutgoingNodes(manager, updatedInstance, token, node);
2410
- return;
2411
- }
2412
- const recipients = await this.resolveApproverResolver(manager, instance, action.recipients, `知會節點「${node.data.label}」`);
2413
- const recipientMemberIds = uniqueTexts(recipients.map((recipient) => recipient.memberId));
2414
- await this.notificationService.createServiceTaskNotifications({
2415
- instance,
2416
- manager,
2417
- node,
2418
- recipientMemberIds,
2419
- });
2420
- await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save({
2421
- ...token,
2422
- consumedAt: new Date(),
2423
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2424
- });
2425
- await activityRepository.save(activityRepository.create({
2426
- actorMemberId: null,
2427
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
2428
- instanceId: instance.id,
2429
- nodeId: node.id,
2430
- payload: {
2431
- action: 'NOTIFY',
2432
- recipientMemberIds,
2433
- tokenId: token.id,
2434
- },
2435
- taskId: null,
2436
- }));
2437
- }
2438
- async rejectInstance(manager, instance, task, rejectedAt) {
2439
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
2440
- // Dispatch pending ad-hoc notifications for the rejecting node before the
2441
- // remaining directives are cancelled by the terminal cleanup below.
2442
- await this.dispatchAdhocStageNotifications(manager, instance, task.nodeId, 'REJECTED');
2443
- // Consume open tokens and cancel any still-open tasks (parallel branches,
2444
- // ad-hoc countersign / pre-approval tasks) so nothing actionable lingers
2445
- // on a rejected instance.
2446
- await this.consumeOpenRuntimeState(manager, instance, rejectedAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
2447
- await this.notificationService.supersedeInstanceTaskNotifications({
2448
- instanceId: instance.id,
2449
- manager,
2450
- });
2451
- const rejectedInstance = await instanceRepository.save({
2452
- ...instance,
2453
- completedAt: rejectedAt,
2454
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED,
2455
- });
2456
- await this.dispatchAdhocCompletionNotifications(manager, rejectedInstance, workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED);
2457
- await this.cancelPendingAdhocDirectives(manager, instance.id, null);
2458
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
2459
- actorMemberId: task.assigneeMemberId,
2460
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_ADVANCED,
2461
- instanceId: instance.id,
2462
- nodeId: task.nodeId,
2463
- payload: {
2464
- action: 'REJECTED',
2465
- taskId: task.id,
2466
- },
2467
- taskId: task.id,
2468
- }));
2469
- }
2470
- async returnInstanceToNode(manager, instance, task, returnToNodeId, returnedAt) {
2471
- if (!returnToNodeId) {
2472
- throw new common_1.ConflictException('Return target node is required');
2473
- }
2474
- const targetNode = readWorkflowNodeOrThrow(instance.workflowSnapshot, returnToNodeId);
2475
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
2476
- const instanceRepository = manager.getRepository(approval_instance_entity_1.ApprovalInstanceEntity);
2477
- const activityRepository = manager.getRepository(activity_log_entity_1.ActivityLogEntity);
2478
- // The returning node's stage has ended (outcome: RETURNED) — dispatch its
2479
- // pending ad-hoc stage notifications, then drop flow-affecting ad-hoc
2480
- // directives so they cannot replay after the return/resubmit cycle.
2481
- await this.dispatchAdhocStageNotifications(manager, instance, task.nodeId, 'RETURNED');
2482
- await this.cancelPendingAdhocDirectives(manager, instance.id, [
2483
- adhoc_enums_1.AdhocDirectiveTypeEnum.COUNTERSIGN,
2484
- adhoc_enums_1.AdhocDirectiveTypeEnum.PRE_APPROVAL,
2485
- ]);
2486
- await this.consumeOpenRuntimeState(manager, instance, returnedAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
2487
- const returnedInstance = await instanceRepository.save({
2488
- ...instance,
2489
- completedAt: null,
2490
- state: targetNode.type === 'startEvent'
2491
- ? workflow_engine_enums_1.ApprovalInstanceStateEnum.RETURNED
2492
- : workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
2493
- });
2494
- const resubmitStrategy = readReturnResubmitStrategy(task, instance);
2495
- const returnToken = await tokenRepository.save(tokenRepository.create({
2496
- consumedAt: null,
2497
- currentNodeId: targetNode.id,
2498
- instanceId: instance.id,
2499
- parentTokenId: null,
2500
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
2501
- }));
2502
- await activityRepository.save([
2503
- activityRepository.create({
2504
- actorMemberId: task.assigneeMemberId,
2505
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RETURNED,
2506
- instanceId: instance.id,
2507
- nodeId: task.nodeId,
2508
- payload: {
2509
- resubmitStrategy,
2510
- returnedFromNodeId: task.nodeId,
2511
- returnToNodeId: targetNode.id,
2512
- taskId: task.id,
2513
- },
2514
- taskId: task.id,
2515
- }),
2516
- activityRepository.create({
2517
- actorMemberId: null,
2518
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
2519
- instanceId: instance.id,
2520
- nodeId: targetNode.id,
2521
- payload: {
2522
- returnedFromNodeId: task.nodeId,
2523
- tokenId: returnToken.id,
2524
- },
2525
- taskId: null,
2526
- }),
2527
- ]);
2528
- if (returnedInstance.state === workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING) {
2529
- await this.processRunningInstance(manager, returnedInstance);
2530
- }
2531
- }
2532
- async readLatestReturnedInstanceContext(manager, instanceId) {
2533
- const activity = await manager.getRepository(activity_log_entity_1.ActivityLogEntity).findOne({
2534
- order: { createdAt: 'DESC' },
2535
- where: {
2536
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.INSTANCE_RETURNED,
2537
- instanceId,
2538
- },
2539
- });
2540
- if (!activity) {
2541
- return null;
2542
- }
2543
- const returnToNodeId = readStringPayloadValue(activity.payload, 'returnToNodeId');
2544
- const returnedFromNodeId = readStringPayloadValue(activity.payload, 'returnedFromNodeId') ??
2545
- activity.nodeId;
2546
- if (!returnToNodeId || !returnedFromNodeId) {
2547
- return null;
2548
- }
2549
- return {
2550
- resubmitStrategy: readReturnResubmitStrategyFromPayload(activity.payload),
2551
- returnedFromNodeId,
2552
- returnToNodeId,
2553
- };
2554
- }
2555
- async resumeReturnedInstanceFromReturnPoint(manager, instance, context, resubmittedAt) {
2556
- const targetNode = readWorkflowNodeOrThrow(instance.workflowSnapshot, context.returnToNodeId);
2557
- if (targetNode.type !== 'startEvent') {
2558
- return instance;
2559
- }
2560
- readWorkflowNodeOrThrow(instance.workflowSnapshot, context.returnedFromNodeId);
2561
- await this.consumeOpenRuntimeState(manager, instance, resubmittedAt, workflow_engine_enums_1.TaskStatusEnum.CANCELLED);
2562
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
2563
- const returnPointToken = await tokenRepository.save(tokenRepository.create({
2564
- consumedAt: null,
2565
- currentNodeId: context.returnedFromNodeId,
2566
- instanceId: instance.id,
2567
- parentTokenId: null,
2568
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE,
2569
- }));
2570
- await manager.getRepository(activity_log_entity_1.ActivityLogEntity).save(manager.getRepository(activity_log_entity_1.ActivityLogEntity).create({
2571
- actorMemberId: null,
2572
- eventType: workflow_engine_enums_1.ActivityLogEventTypeEnum.TOKEN_CREATED,
2573
- instanceId: instance.id,
2574
- nodeId: context.returnedFromNodeId,
2575
- payload: {
2576
- resubmitStrategy: context.resubmitStrategy,
2577
- resumedFromNodeId: context.returnToNodeId,
2578
- tokenId: returnPointToken.id,
2579
- },
2580
- taskId: null,
2581
- }));
2582
- return instance;
2583
- }
2584
- async consumeOpenRuntimeState(manager, instance, consumedAt, taskStatus) {
2585
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
2586
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
2587
- const tokens = await tokenRepository.find({
2588
- where: { instanceId: instance.id },
2589
- });
2590
- const openTokens = tokens.filter((token) => token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
2591
- token.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING);
2592
- const tasks = await taskRepository.find({
2593
- where: { instanceId: instance.id },
2594
- });
2595
- const openTasks = tasks.filter((task) => task.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
2596
- task.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS);
2597
- if (openTokens.length > 0) {
2598
- await tokenRepository.save(openTokens.map((token) => ({
2599
- ...token,
2600
- consumedAt,
2601
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2602
- })));
2603
- }
2604
- if (openTasks.length > 0) {
2605
- await taskRepository.save(openTasks.map((openTask) => Object.assign(new task_entity_1.TaskEntity(), openTask, {
2606
- completedAt: consumedAt,
2607
- status: taskStatus,
2608
- })));
2609
- await this.closeTaskCandidates(manager, openTasks.map((task) => task.id), readCandidateStatusForClosedTask(taskStatus));
2610
- }
2611
- }
2612
- async consumeJoinedTokens(manager, tokens) {
2613
- if (tokens.length === 0) {
2614
- return;
2615
- }
2616
- const consumedAt = new Date();
2617
- await manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity).save(tokens.map((token) => ({
2618
- ...token,
2619
- consumedAt,
2620
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2621
- })));
2622
- }
2623
- async cancelAlternativeTokensForOrTrigger(manager, instance, token, targetNodeId) {
2624
- const tokenRepository = manager.getRepository(workflow_token_entity_1.WorkflowTokenEntity);
2625
- const taskRepository = manager.getRepository(task_entity_1.TaskEntity);
2626
- const consumedAt = new Date();
2627
- const tokens = await tokenRepository.find({
2628
- where: { instanceId: instance.id },
2629
- });
2630
- const alternativeTokens = tokens.filter((candidate) => candidate.id !== token.id &&
2631
- (candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.ACTIVE ||
2632
- candidate.status === workflow_engine_enums_1.WorkflowTokenStatusEnum.WAITING) &&
2633
- hasPathToNode(instance.workflowSnapshot, candidate.currentNodeId, targetNodeId));
2634
- if (alternativeTokens.length === 0) {
2635
- return;
2636
- }
2637
- const alternativeTokenIds = alternativeTokens.map((candidate) => candidate.id);
2638
- const cancellableTasks = (await taskRepository.find({
2639
- where: { instanceId: instance.id },
2640
- })).filter((task) => alternativeTokenIds.includes(task.tokenId) &&
2641
- (task.status === workflow_engine_enums_1.TaskStatusEnum.PENDING ||
2642
- task.status === workflow_engine_enums_1.TaskStatusEnum.IN_PROGRESS));
2643
- await tokenRepository.save(alternativeTokens.map((candidate) => ({
2644
- ...candidate,
2645
- consumedAt,
2646
- status: workflow_engine_enums_1.WorkflowTokenStatusEnum.CONSUMED,
2647
- })));
2648
- if (cancellableTasks.length) {
2649
- await taskRepository.save(cancellableTasks.map((task) => Object.assign(new task_entity_1.TaskEntity(), task, {
2650
- completedAt: consumedAt,
2651
- status: workflow_engine_enums_1.TaskStatusEnum.CANCELLED,
2652
- })));
2653
- await this.closeTaskCandidates(manager, cancellableTasks.map((task) => task.id), workflow_engine_enums_1.TaskCandidateStatusEnum.SUPERSEDED);
2654
- }
2655
- }
2656
- async closeTaskCandidates(manager, taskIds, status) {
2657
- if (taskIds.length === 0) {
2658
- return;
2659
- }
2660
- const candidateRepository = manager.getRepository(task_candidate_entity_1.TaskCandidateEntity);
2661
- const candidates = (await candidateRepository.find({ where: { taskId: (0, typeorm_2.In)([...taskIds]) } })).filter((candidate) => candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.PENDING ||
2662
- candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.CLAIMED);
2663
- if (candidates.length === 0) {
2664
- return;
2665
- }
2666
- await candidateRepository.save(candidates.map((candidate) => Object.assign(new task_candidate_entity_1.TaskCandidateEntity(), candidate, { status })));
2667
- }
2668
- async getTemplateOrThrow(templateId) {
2669
- const template = await this.approvalTemplateRepository.findOne({
2670
- where: { deletedAt: (0, typeorm_2.IsNull)(), id: templateId },
2671
- });
2672
- if (!template) {
2673
- throw new common_1.NotFoundException(`Approval template ${templateId} was not found`);
2674
- }
2675
- return template;
2676
- }
2677
- async getPublishedTemplateVersionOrThrow(versionId) {
2678
- const version = await this.approvalTemplateVersionRepository.findOne({
2679
- where: { id: versionId },
2680
- });
2681
- if (!version) {
2682
- throw new common_1.NotFoundException(`Approval template version ${versionId} was not found`);
2683
- }
2684
- if (version.status !== template_enums_1.ApprovalTemplateVersionStatusEnum.PUBLISHED) {
2685
- throw new common_1.ConflictException('Approval template current version must be published');
2686
- }
2687
- return version;
2688
- }
2689
- async getPublishedFormVersionOrThrow(versionId) {
2690
- if (!versionId) {
2691
- throw new common_1.ConflictException('Approval template version does not bind a form version');
2692
- }
2693
- const version = await this.formDefinitionVersionRepository.findOne({
2694
- where: { id: versionId },
2695
- });
2696
- if (!version) {
2697
- throw new common_1.NotFoundException(`Form definition version ${versionId} was not found`);
2698
- }
2699
- if (version.status !== form_enums_1.FormDefinitionVersionStatusEnum.PUBLISHED) {
2700
- throw new common_1.ConflictException('Approval template must bind a published form version');
2701
- }
2702
- return version;
2703
- }
2704
- };
2705
- exports.WorkflowEngineService = WorkflowEngineService;
2706
- exports.WorkflowEngineService = WorkflowEngineService = tslib_1.__decorate([
2707
- (0, common_1.Injectable)(),
2708
- tslib_1.__param(0, (0, typeorm_1.InjectRepository)(approval_instance_entity_1.ApprovalInstanceEntity)),
2709
- tslib_1.__param(1, (0, typeorm_1.InjectRepository)(workflow_token_entity_1.WorkflowTokenEntity)),
2710
- tslib_1.__param(2, (0, typeorm_1.InjectRepository)(task_entity_1.TaskEntity)),
2711
- tslib_1.__param(3, (0, typeorm_1.InjectRepository)(task_candidate_entity_1.TaskCandidateEntity)),
2712
- tslib_1.__param(4, (0, typeorm_1.InjectRepository)(task_decision_entity_1.TaskDecisionEntity)),
2713
- tslib_1.__param(5, (0, typeorm_1.InjectRepository)(notification_entity_1.NotificationEntity)),
2714
- tslib_1.__param(6, (0, typeorm_1.InjectRepository)(activity_log_entity_1.ActivityLogEntity)),
2715
- tslib_1.__param(7, (0, typeorm_1.InjectRepository)(adhoc_directive_entity_1.AdhocDirectiveEntity)),
2716
- tslib_1.__param(8, (0, typeorm_1.InjectRepository)(approval_template_entity_1.ApprovalTemplateEntity)),
2717
- tslib_1.__param(9, (0, typeorm_1.InjectRepository)(approval_template_version_entity_1.ApprovalTemplateVersionEntity)),
2718
- tslib_1.__param(10, (0, typeorm_1.InjectRepository)(form_definition_version_entity_1.FormDefinitionVersionEntity)),
2719
- tslib_1.__param(16, (0, common_1.Optional)()),
2720
- tslib_1.__param(16, (0, common_1.Inject)(workflow_service_task_dispatcher_token_1.BPM_WORKFLOW_SERVICE_TASK_DISPATCHER)),
2721
- tslib_1.__metadata("design:paramtypes", [typeorm_2.Repository,
2722
- typeorm_2.Repository,
2723
- typeorm_2.Repository,
2724
- typeorm_2.Repository,
2725
- typeorm_2.Repository,
2726
- typeorm_2.Repository,
2727
- typeorm_2.Repository,
2728
- typeorm_2.Repository,
2729
- typeorm_2.Repository,
2730
- typeorm_2.Repository,
2731
- typeorm_2.Repository,
2732
- attachment_service_1.AttachmentService,
2733
- condition_service_1.ConditionService,
2734
- delegation_service_1.DelegationService,
2735
- notification_service_1.NotificationService,
2736
- signature_service_1.SignatureService, Object])
2737
- ], WorkflowEngineService);
2738
- function parseJsonObject(value, label) {
2739
- try {
2740
- const parsedValue = JSON.parse(value);
2741
- if (isRecord(parsedValue)) {
2742
- return parsedValue;
2743
- }
2744
- throw new common_1.BadRequestException(`${label} must be a JSON object`);
2745
- }
2746
- catch (error) {
2747
- if (error instanceof common_1.BadRequestException) {
2748
- throw error;
2749
- }
2750
- throw new common_1.BadRequestException(error instanceof Error ? error.message : `${label} is invalid JSON`);
2751
- }
2752
- }
2753
- function readDefaultInitiatorMetadataSnapshot(memberId) {
2754
- return {
2755
- customFields: {},
2756
- memberId,
2757
- };
2758
- }
2759
- function parseWorkflowDefinition(value) {
2760
- try {
2761
- const parsedValue = JSON.parse(value);
2762
- if (isRecord(parsedValue) &&
2763
- Array.isArray(parsedValue.nodes) &&
2764
- Array.isArray(parsedValue.edges) &&
2765
- isRecord(parsedValue.meta)) {
2766
- return parsedValue;
2767
- }
2768
- throw new common_1.BadRequestException('workflowDefinitionJson must be a workflow definition object');
2769
- }
2770
- catch (error) {
2771
- if (error instanceof common_1.BadRequestException) {
2772
- throw error;
2773
- }
2774
- throw new common_1.BadRequestException(error instanceof Error
2775
- ? error.message
2776
- : 'workflowDefinitionJson is invalid JSON');
2777
- }
2778
- }
2779
- function buildInitiatorPolicyContext(initiatorMetadataSnapshot) {
2780
- return {
2781
- env: {
2782
- now: new Date().toISOString(),
2783
- },
2784
- initiator: initiatorMetadataSnapshot,
2785
- subject: initiatorMetadataSnapshot,
2786
- };
2787
- }
2788
- function buildWorkflowExpressionContext(instance) {
2789
- return {
2790
- env: {
2791
- now: new Date().toISOString(),
2792
- },
2793
- form: instance.formData,
2794
- formData: instance.formData,
2795
- initiator: {
2796
- ...instance.initiatorMetadataSnapshot,
2797
- memberId: instance.initiatorMemberId,
2798
- },
2799
- instance: {
2800
- id: instance.id,
2801
- state: instance.state,
2802
- templateId: instance.templateId,
2803
- templateVersionId: instance.templateVersionId,
2804
- title: instance.title,
2805
- },
2806
- };
2807
- }
2808
- function readDelegationSteps(value) {
2809
- return value
2810
- .map((step) => {
2811
- const from = readStringValue(step.from);
2812
- const to = readStringValue(step.to);
2813
- const reason = readStringValue(step.reason);
2814
- if (!from || !to || !reason) {
2815
- return null;
2816
- }
2817
- return {
2818
- from,
2819
- reason,
2820
- ruleId: readStringValue(step.ruleId),
2821
- to,
2822
- };
2823
- })
2824
- .filter((step) => step !== null);
2825
- }
2826
- function readStringValue(value) {
2827
- return typeof value === 'string' && value.trim() ? value : null;
2828
- }
2829
- function buildDryRunExpressionContext(input, lastDecision) {
2830
- return {
2831
- env: {
2832
- now: new Date().toISOString(),
2833
- },
2834
- form: input.formData,
2835
- formData: input.formData,
2836
- initiator: {
2837
- ...input.initiatorMetadataSnapshot,
2838
- memberId: input.initiatorMemberId,
2839
- },
2840
- instance: {
2841
- id: SYSTEM_DRY_RUN_INSTANCE_ID,
2842
- state: workflow_engine_enums_1.ApprovalInstanceStateEnum.RUNNING,
2843
- title: 'Dry Run',
2844
- },
2845
- lastDecision,
2846
- };
2847
- }
2848
- function isRecord(value) {
2849
- return typeof value === 'object' && value !== null && !Array.isArray(value);
2850
- }
2851
- function readWorkflowNodeOrThrow(workflow, nodeId) {
2852
- const node = workflow.nodes.find((candidate) => candidate.id === nodeId);
2853
- if (!node) {
2854
- throw new common_1.ConflictException(`Workflow node ${nodeId} was not found`);
2855
- }
2856
- return node;
2857
- }
2858
- function readOutgoingEdgesOrThrow(workflow, nodeId) {
2859
- const edges = workflow.edges.filter((edge) => edge.source === nodeId);
2860
- if (edges.length === 0) {
2861
- throw new common_1.ConflictException(`Workflow node ${nodeId} has no outgoing edge`);
2862
- }
2863
- return edges;
2864
- }
2865
- function readIncomingEdges(workflow, nodeId) {
2866
- return workflow.edges.filter((edge) => edge.target === nodeId);
2867
- }
2868
- function hasPathToNode(workflow, sourceNodeId, targetNodeId) {
2869
- return visitPathToNode(workflow, sourceNodeId, targetNodeId, new Set());
2870
- }
2871
- function visitPathToNode(workflow, sourceNodeId, targetNodeId, visitedNodeIds) {
2872
- if (sourceNodeId === targetNodeId) {
2873
- return true;
2874
- }
2875
- if (visitedNodeIds.has(sourceNodeId)) {
2876
- return false;
2877
- }
2878
- const nextVisitedNodeIds = new Set([...visitedNodeIds, sourceNodeId]);
2879
- return workflow.edges
2880
- .filter((edge) => edge.source === sourceNodeId)
2881
- .some((edge) => visitPathToNode(workflow, edge.target, targetNodeId, nextVisitedNodeIds));
2882
- }
2883
- function readReturnTargetNodeId(workflow, currentNodeId, requestedNodeId) {
2884
- const currentNode = readWorkflowNodeOrThrow(workflow, currentNodeId);
2885
- if (currentNode.type !== 'userTask') {
2886
- throw new common_1.ConflictException(`Workflow node ${currentNodeId} does not support return decisions`);
2887
- }
2888
- if (!currentNode.data.returnBehavior.allowReturn) {
2889
- throw new common_1.ConflictException(`User task ${currentNodeId} does not allow return decisions`);
2890
- }
2891
- const trimmedRequestedNodeId = requestedNodeId?.trim() || null;
2892
- const defaultTargetNodeId = readDefaultReturnTargetNodeId(workflow, currentNode);
2893
- if (currentNode.data.returnBehavior.allowedTargets === 'ANY') {
2894
- if (!trimmedRequestedNodeId) {
2895
- throw new common_1.BadRequestException('returnToNodeId is required');
2896
- }
2897
- readWorkflowNodeOrThrow(workflow, trimmedRequestedNodeId);
2898
- return trimmedRequestedNodeId;
2899
- }
2900
- if (trimmedRequestedNodeId &&
2901
- trimmedRequestedNodeId !== defaultTargetNodeId) {
2902
- throw new common_1.ConflictException(`User task ${currentNodeId} can only return to ${defaultTargetNodeId}`);
2903
- }
2904
- return defaultTargetNodeId;
2905
- }
2906
- function readDefaultReturnTargetNodeId(workflow, currentNode) {
2907
- if (currentNode.data.returnBehavior.allowedTargets === 'INITIATOR') {
2908
- const startNode = workflow.nodes.find((node) => node.type === 'startEvent');
2909
- if (!startNode) {
2910
- throw new common_1.ConflictException('Workflow does not include a startEvent');
2911
- }
2912
- return startNode.id;
2913
- }
2914
- const previousNodeId = readIncomingEdges(workflow, currentNode.id)[0]?.source;
2915
- if (!previousNodeId) {
2916
- throw new common_1.ConflictException(`User task ${currentNode.id} does not have a previous node to return to`);
2917
- }
2918
- return previousNodeId;
2919
- }
2920
- function readReturnResubmitStrategy(task, instance) {
2921
- const node = readWorkflowNodeOrThrow(instance.workflowSnapshot, task.nodeId);
2922
- if (node.type !== 'userTask') {
2923
- return 'RESTART';
2924
- }
2925
- return node.data.returnBehavior.resubmitStrategy ?? 'RESTART';
2926
- }
2927
- function readReturnResubmitStrategyFromPayload(payload) {
2928
- return payload.resubmitStrategy === 'FROM_RETURN_POINT'
2929
- ? 'FROM_RETURN_POINT'
2930
- : 'RESTART';
2931
- }
2932
- function readStringPayloadValue(payload, key) {
2933
- const value = payload[key];
2934
- return typeof value === 'string' && value.trim() ? value.trim() : null;
2935
- }
2936
- function readNodeEntryCondition(node) {
2937
- if (node.type === 'userTask' || node.type === 'serviceTask') {
2938
- return node.data.entryCondition?.trim() || null;
2939
- }
2940
- return null;
2941
- }
2942
- function edgeHasCondition(edge) {
2943
- return Boolean(edge.data.condition?.trim() ||
2944
- (edge.data.conditionFieldKey && edge.data.conditionOperator));
2945
- }
2946
- function readMemberIdFromValue(value, label) {
2947
- return readMemberIdsFromValue(value, label)[0] ?? '';
2948
- }
2949
- function readMemberIdsFromValue(value, label) {
2950
- if (typeof value === 'string' && value.trim()) {
2951
- return [value.trim()];
2952
- }
2953
- if (Array.isArray(value)) {
2954
- const memberIds = value
2955
- .filter((candidate) => typeof candidate === 'string' && Boolean(candidate.trim()))
2956
- .map((candidate) => candidate.trim());
2957
- if (memberIds.length > 0) {
2958
- return uniqueTexts(memberIds);
2959
- }
2960
- }
2961
- throw new common_1.ConflictException(`${label} did not resolve to a member id`);
2962
- }
2963
- function uniqueTexts(values) {
2964
- return [...new Set(values.filter((value) => value.trim()))];
2965
- }
2966
- function uniqueRuntimeCandidates(candidates) {
2967
- const seen = new Set();
2968
- return candidates.filter((candidate) => {
2969
- if (seen.has(candidate.memberId)) {
2970
- return false;
2971
- }
2972
- seen.add(candidate.memberId);
2973
- return true;
2974
- });
2975
- }
2976
- function uniqueTasksById(tasks) {
2977
- const seen = new Set();
2978
- return tasks.filter((task) => {
2979
- if (seen.has(task.id)) {
2980
- return false;
2981
- }
2982
- seen.add(task.id);
2983
- return true;
2984
- });
2985
- }
2986
- function normalizeText(value) {
2987
- return typeof value === 'string' && value.trim() ? value.trim() : null;
2988
- }
2989
- function readPageOffset(page, pageSize) {
2990
- const normalizedPageSize = normalizePageSize(pageSize);
2991
- return (normalizePage(page) - 1) * normalizedPageSize;
2992
- }
2993
- function shouldPaginateList(options) {
2994
- return (typeof options.page !== 'undefined' ||
2995
- typeof options.pageSize !== 'undefined');
2996
- }
2997
- function normalizePage(value) {
2998
- return Number.isInteger(value) && Number(value) > 0 ? Number(value) : 1;
2999
- }
3000
- function normalizePageSize(value) {
3001
- if (!Number.isInteger(value) || Number(value) <= 0) {
3002
- return 20;
3003
- }
3004
- return Math.min(Number(value), 100);
3005
- }
3006
- function applyApprovalInstanceSearchFilter(queryBuilder, searchTextValue) {
3007
- const searchText = normalizeText(searchTextValue)?.toLocaleLowerCase();
3008
- if (!searchText) {
3009
- return;
3010
- }
3011
- queryBuilder.andWhere([
3012
- '(',
3013
- 'LOWER("approvalInstance"."title") LIKE :approvalInstanceSearchText',
3014
- 'OR LOWER("approvalInstance"."initiator_member_id") LIKE :approvalInstanceSearchText',
3015
- 'OR LOWER(CAST("approvalInstance"."id" AS text)) LIKE :approvalInstanceSearchText',
3016
- ')',
3017
- ].join(' '), {
3018
- approvalInstanceSearchText: `%${escapeLikePattern(searchText)}%`,
3019
- });
3020
- }
3021
- function escapeLikePattern(value) {
3022
- return value
3023
- .replace(/\\/gu, '\\\\')
3024
- .replace(/%/gu, '\\%')
3025
- .replace(/_/gu, '\\_');
3026
- }
3027
- function isInstanceWithinRange(instance, options) {
3028
- const fromTime = options.from?.getTime() ?? null;
3029
- const toTime = options.to?.getTime() ?? null;
3030
- const candidateTime = (instance.completedAt ??
3031
- instance.startedAt ??
3032
- instance.createdAt).getTime();
3033
- return ((fromTime === null || candidateTime >= fromTime) &&
3034
- (toTime === null || candidateTime <= toTime));
3035
- }
3036
- function readDecisionPolicySnapshot(task) {
3037
- const value = task.decisionPolicySnapshot;
3038
- if (isRecord(value) &&
3039
- typeof value.type === 'string' &&
3040
- (value.type === 'SINGLE' ||
3041
- value.type === 'SEQUENTIAL' ||
3042
- value.type === 'PARALLEL_ALL' ||
3043
- value.type === 'PARALLEL_ANY')) {
3044
- return { type: value.type };
3045
- }
3046
- if (isRecord(value) &&
3047
- value.type === 'QUORUM' &&
3048
- typeof value.threshold === 'number' &&
3049
- (value.thresholdType === 'COUNT' || value.thresholdType === 'PERCENTAGE')) {
3050
- return {
3051
- threshold: value.threshold,
3052
- thresholdType: value.thresholdType,
3053
- type: 'QUORUM',
3054
- };
3055
- }
3056
- return { type: 'SINGLE' };
3057
- }
3058
- function shouldCompleteTaskAfterDecision({ action, candidates, decisionPolicy, }) {
3059
- if (action !== workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED) {
3060
- return true;
3061
- }
3062
- if (decisionPolicy.type === 'SINGLE' ||
3063
- decisionPolicy.type === 'PARALLEL_ANY') {
3064
- return true;
3065
- }
3066
- const completedCount = candidates.filter((candidate) => candidate.status === workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED).length;
3067
- const totalCount = Math.max(candidates.length, 1);
3068
- if (decisionPolicy.type === 'PARALLEL_ALL' ||
3069
- decisionPolicy.type === 'SEQUENTIAL') {
3070
- return completedCount >= totalCount;
3071
- }
3072
- const threshold = decisionPolicy.thresholdType === 'PERCENTAGE'
3073
- ? Math.ceil((totalCount * decisionPolicy.threshold) / 100)
3074
- : decisionPolicy.threshold;
3075
- return completedCount >= Math.max(threshold, 1);
3076
- }
3077
- function readValueAtPath(value, path) {
3078
- return path
3079
- .split('.')
3080
- .filter((segment) => segment.trim())
3081
- .reduce((currentValue, segment) => {
3082
- if (!isRecord(currentValue)) {
3083
- return undefined;
3084
- }
3085
- return currentValue[segment];
3086
- }, value);
3087
- }
3088
- function writeValueAtPath(value, path, nextValue) {
3089
- const segments = normalizeFormFieldPath(path);
3090
- if (segments.length === 0) {
3091
- return value;
3092
- }
3093
- return writeNestedValue(value, segments, nextValue);
3094
- }
3095
- function writeNestedValue(value, segments, nextValue) {
3096
- const [currentSegment, ...remainingSegments] = segments;
3097
- if (!currentSegment) {
3098
- return value;
3099
- }
3100
- if (remainingSegments.length === 0) {
3101
- return { ...value, [currentSegment]: nextValue };
3102
- }
3103
- const currentValue = value[currentSegment];
3104
- const nestedValue = isRecord(currentValue) ? currentValue : {};
3105
- return {
3106
- ...value,
3107
- [currentSegment]: writeNestedValue(nestedValue, remainingSegments, nextValue),
3108
- };
3109
- }
3110
- function normalizeFormFieldPath(path) {
3111
- const segments = path
3112
- .split('.')
3113
- .map((segment) => segment.trim())
3114
- .filter(Boolean);
3115
- if (segments[0] === 'form' || segments[0] === 'formData') {
3116
- return segments.slice(1);
3117
- }
3118
- return segments;
3119
- }
3120
- async function executeWebhookServiceAction(serviceTaskDispatcher, action, payload) {
3121
- try {
3122
- return await serviceTaskDispatcher.dispatchWebhook({
3123
- headers: action.headers,
3124
- payload,
3125
- url: action.url,
3126
- });
3127
- }
3128
- catch (error) {
3129
- return {
3130
- error: error instanceof Error ? error.message : 'Unknown webhook error',
3131
- ok: false,
3132
- status: null,
3133
- };
3134
- }
3135
- }
3136
- async function executeAdhocWebhookDispatch(serviceTaskDispatcher, target, payload) {
3137
- if (!target.webhookUrl) {
3138
- return { error: 'Webhook URL is missing', ok: false, status: null };
3139
- }
3140
- try {
3141
- return await serviceTaskDispatcher.dispatchWebhook({
3142
- headers: target.webhookHeaders,
3143
- payload,
3144
- url: target.webhookUrl,
3145
- });
3146
- }
3147
- catch (error) {
3148
- return {
3149
- error: error instanceof Error ? error.message : 'Unknown webhook error',
3150
- ok: false,
3151
- status: null,
3152
- };
3153
- }
3154
- }
3155
- function buildAdhocTargetValue(input) {
3156
- if (input.kind === adhoc_enums_1.AdhocTargetKindEnum.MEMBER) {
3157
- const memberIds = uniqueTexts([...(input.memberIds ?? [])]);
3158
- if (memberIds.length === 0) {
3159
- throw new common_1.BadRequestException('memberIds is required for a MEMBER ad-hoc target');
3160
- }
3161
- return { kind: input.kind, memberIds };
3162
- }
3163
- if (input.kind === adhoc_enums_1.AdhocTargetKindEnum.POSITION) {
3164
- const positionId = input.positionId?.trim();
3165
- if (!positionId) {
3166
- throw new common_1.BadRequestException('positionId is required for a POSITION ad-hoc target');
3167
- }
3168
- return { kind: input.kind, positionId };
3169
- }
3170
- if (input.kind === adhoc_enums_1.AdhocTargetKindEnum.ORG_UNIT_MEMBER) {
3171
- const orgUnitId = input.orgUnitId?.trim();
3172
- if (!orgUnitId) {
3173
- throw new common_1.BadRequestException('orgUnitId is required for an ORG_UNIT_MEMBER ad-hoc target');
3174
- }
3175
- return {
3176
- includeDescendants: Boolean(input.includeDescendants),
3177
- kind: input.kind,
3178
- orgUnitId,
3179
- };
3180
- }
3181
- const webhookUrl = input.webhookUrl?.trim();
3182
- if (!webhookUrl) {
3183
- throw new common_1.BadRequestException('webhookUrl is required for a WEBHOOK ad-hoc target');
3184
- }
3185
- return {
3186
- kind: input.kind,
3187
- webhookHeaders: input.webhookHeadersJson
3188
- ? parseAdhocWebhookHeaders(input.webhookHeadersJson)
3189
- : undefined,
3190
- webhookUrl,
3191
- };
3192
- }
3193
- function parseAdhocWebhookHeaders(json) {
3194
- try {
3195
- const parsed = JSON.parse(json);
3196
- if (!isRecord(parsed)) {
3197
- throw new common_1.BadRequestException('webhookHeadersJson must be a JSON object of string values');
3198
- }
3199
- return Object.entries(parsed).reduce((accumulator, [key, value]) => ({
3200
- ...accumulator,
3201
- ...(typeof value === 'string' ? { [key]: value } : {}),
3202
- }), {});
3203
- }
3204
- catch (error) {
3205
- if (error instanceof common_1.BadRequestException) {
3206
- throw error;
3207
- }
3208
- throw new common_1.BadRequestException('webhookHeadersJson is not valid JSON');
3209
- }
3210
- }
3211
- function readAdhocTargetValue(directive) {
3212
- const value = directive.targetValue;
3213
- const kind = directive.targetKind;
3214
- if (kind === adhoc_enums_1.AdhocTargetKindEnum.MEMBER) {
3215
- return {
3216
- kind,
3217
- memberIds: Array.isArray(value.memberIds)
3218
- ? value.memberIds.filter((memberId) => typeof memberId === 'string')
3219
- : [],
3220
- };
3221
- }
3222
- if (kind === adhoc_enums_1.AdhocTargetKindEnum.POSITION) {
3223
- return {
3224
- kind,
3225
- positionId: typeof value.positionId === 'string' ? value.positionId : '',
3226
- };
3227
- }
3228
- if (kind === adhoc_enums_1.AdhocTargetKindEnum.ORG_UNIT_MEMBER) {
3229
- return {
3230
- includeDescendants: Boolean(value.includeDescendants),
3231
- kind,
3232
- orgUnitId: typeof value.orgUnitId === 'string' ? value.orgUnitId : '',
3233
- };
3234
- }
3235
- return {
3236
- kind,
3237
- webhookHeaders: isRecord(value.webhookHeaders)
3238
- ? Object.entries(value.webhookHeaders).reduce((accumulator, [key, headerValue]) => ({
3239
- ...accumulator,
3240
- ...(typeof headerValue === 'string'
3241
- ? { [key]: headerValue }
3242
- : {}),
3243
- }), {})
3244
- : undefined,
3245
- webhookUrl: typeof value.webhookUrl === 'string' ? value.webhookUrl : '',
3246
- };
3247
- }
3248
- function buildAdhocApproverResolver(target) {
3249
- if (target.kind === adhoc_enums_1.AdhocTargetKindEnum.MEMBER) {
3250
- return { memberIds: target.memberIds ?? [], type: 'DIRECT' };
3251
- }
3252
- if (target.kind === adhoc_enums_1.AdhocTargetKindEnum.POSITION) {
3253
- return { positionId: target.positionId ?? '', type: 'POSITION' };
3254
- }
3255
- if (target.kind === adhoc_enums_1.AdhocTargetKindEnum.ORG_UNIT_MEMBER) {
3256
- return {
3257
- includeDescendants: target.includeDescendants,
3258
- orgUnitId: target.orgUnitId ?? '',
3259
- type: 'ORG_UNIT_MEMBER',
3260
- };
3261
- }
3262
- throw new common_1.BadRequestException('WEBHOOK ad-hoc targets cannot be used as approvers');
3263
- }
3264
- function readAdhocStageOutcomeLabel(outcome) {
3265
- if (outcome === 'APPROVED') {
3266
- return '通過';
3267
- }
3268
- if (outcome === 'REJECTED') {
3269
- return '拒絕';
3270
- }
3271
- return '退回';
3272
- }
3273
- function readInstanceFinalStateLabel(finalState) {
3274
- if (finalState === workflow_engine_enums_1.ApprovalInstanceStateEnum.APPROVED) {
3275
- return '核准';
3276
- }
3277
- if (finalState === workflow_engine_enums_1.ApprovalInstanceStateEnum.REJECTED) {
3278
- return '拒絕';
3279
- }
3280
- return '取消';
3281
- }
3282
- function readManagerMemberIdFromInitiatorSnapshot(initiatorMetadataSnapshot, levelsUp) {
3283
- const managerChain = initiatorMetadataSnapshot.managerChain;
3284
- if (Array.isArray(managerChain)) {
3285
- return managerChain[Math.max(levelsUp - 1, 0)];
3286
- }
3287
- const managerMemberIds = initiatorMetadataSnapshot.managerMemberIds;
3288
- if (Array.isArray(managerMemberIds)) {
3289
- return managerMemberIds[Math.max(levelsUp - 1, 0)];
3290
- }
3291
- const customFields = initiatorMetadataSnapshot.customFields;
3292
- if (isRecord(customFields)) {
3293
- return customFields.managerMemberId;
3294
- }
3295
- return initiatorMetadataSnapshot.managerMemberId;
3296
- }
3297
- function isDateRangeActive(value, date) {
3298
- return (value.effectiveFrom <= date &&
3299
- (!value.effectiveTo || value.effectiveTo >= date));
3300
- }
3301
- function compareManagerResolution(left, right) {
3302
- const priorityDiff = right.priority - left.priority;
3303
- if (priorityDiff !== 0) {
3304
- return priorityDiff;
3305
- }
3306
- return (readManagerResolutionScopeRank(right.scopeType) -
3307
- readManagerResolutionScopeRank(left.scopeType) ||
3308
- right.effectiveFrom.localeCompare(left.effectiveFrom));
3309
- }
3310
- function readManagerResolutionScopeRank(scopeType) {
3311
- const ranks = {
3312
- [organization_enums_1.ManagerResolutionScopeTypeEnum.MEMBER]: 3,
3313
- [organization_enums_1.ManagerResolutionScopeTypeEnum.ORG_UNIT]: 2,
3314
- [organization_enums_1.ManagerResolutionScopeTypeEnum.POSITION]: 1,
3315
- };
3316
- return ranks[scopeType];
3317
- }
3318
- function compareMembership(left, right) {
3319
- if (left.isPrimary !== right.isPrimary) {
3320
- return left.isPrimary ? -1 : 1;
3321
- }
3322
- return right.effectiveFrom.localeCompare(left.effectiveFrom);
3323
- }
3324
- function toDateOnlyString(value) {
3325
- return value.toISOString().slice(0, 10);
3326
- }
3327
- function canReadAllWorkflows(scope) {
3328
- return (scope.roles.includes('BPM_ADMIN') ||
3329
- scope.permissions.some((permission) => WORKFLOW_READ_ALL_PERMISSIONS.has(permission)));
3330
- }
3331
- function isTaskRelatedToMember(task, memberId) {
3332
- return (task.assigneeMemberId === memberId ||
3333
- task.originalAssigneeMemberId === memberId);
3334
- }
3335
- function readCandidateStatusForClosedTask(taskStatus) {
3336
- if (taskStatus === workflow_engine_enums_1.TaskStatusEnum.TRANSFERRED) {
3337
- return workflow_engine_enums_1.TaskCandidateStatusEnum.TRANSFERRED;
3338
- }
3339
- if (taskStatus === workflow_engine_enums_1.TaskStatusEnum.COMPLETED) {
3340
- return workflow_engine_enums_1.TaskCandidateStatusEnum.COMPLETED;
3341
- }
3342
- return workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED;
3343
- }
3344
- function validateSubmittedFormData(schema, formData) {
3345
- const missingFields = schema.fields.filter((field) => {
3346
- const fieldValue = formData[field.fieldKey];
3347
- return (isSubmittedFormFieldVisible(field, schema.fields, formData) &&
3348
- isSubmittedFormFieldRequired(field, schema.fields, formData) &&
3349
- !isSubmittedFieldValuePresent(fieldValue));
3350
- });
3351
- if (missingFields.length > 0) {
3352
- throw new common_1.BadRequestException(`Form data is missing required fields: ${missingFields
3353
- .map((field) => field.label || field.fieldKey)
3354
- .join(', ')}`);
3355
- }
3356
- }
3357
- function readFormDefinitionSnapshotSchema(snapshot) {
3358
- const schema = snapshot.schema;
3359
- if (isFormDefinitionSchema(schema)) {
3360
- return schema;
3361
- }
3362
- throw new common_1.BadRequestException('Approval instance form schema is invalid');
3363
- }
3364
- function isFormDefinitionSchema(value) {
3365
- return (isRecord(value) && value.schemaVersion === 1 && Array.isArray(value.fields));
3366
- }
3367
- function isSubmittedFormFieldVisible(field, fields, values) {
3368
- return field.visibleWhen
3369
- ? evaluateFormConditionExpression(field.visibleWhen, fields, values, true)
3370
- : true;
3371
- }
3372
- function isSubmittedFormFieldRequired(field, fields, values) {
3373
- return (field.required ||
3374
- Boolean(field.requiredWhen
3375
- ? evaluateFormConditionExpression(field.requiredWhen, fields, values, false)
3376
- : false));
3377
- }
3378
- function evaluateFormConditionExpression(expression, fields, values, fallback) {
3379
- const rule = parseFormConditionRule(expression);
3380
- if (!rule) {
3381
- return fallback;
3382
- }
3383
- const field = fields.find((candidate) => candidate.fieldKey === rule.fieldKey);
3384
- if (!field) {
3385
- return fallback;
3386
- }
3387
- return evaluateFormConditionRule(rule, field, values[field.fieldKey]);
3388
- }
3389
- function parseFormConditionRule(expression) {
3390
- const match = expression
3391
- .trim()
3392
- .match(/^form\.([A-Za-z][A-Za-z0-9_]*)\s*(==|!=|>=|<=|>|<)\s*(.+)$/u);
3393
- if (!match) {
3394
- return null;
3395
- }
3396
- const operator = readFormConditionOperatorFromSymbol(match[2]);
3397
- if (!operator) {
3398
- return null;
3399
- }
3400
- return {
3401
- fieldKey: match[1],
3402
- operator,
3403
- value: parseFormConditionLiteral(match[3]),
3404
- };
3405
- }
3406
- function evaluateFormConditionRule(rule, field, value) {
3407
- if (Array.isArray(value)) {
3408
- return evaluateArrayFormCondition(value, rule);
3409
- }
3410
- if (field.type === 'boolean') {
3411
- return compareFormConditionValues(value === true ? 'true' : 'false', rule.value, rule.operator);
3412
- }
3413
- if (isNumberFormField(field)) {
3414
- return compareNumericFormCondition(value, rule);
3415
- }
3416
- return compareFormConditionValues(typeof value === 'undefined' || value === null ? '' : String(value), rule.value, rule.operator);
3417
- }
3418
- function evaluateArrayFormCondition(value, rule) {
3419
- const stringValues = value.filter((entry) => typeof entry === 'string');
3420
- if (rule.operator === 'equals') {
3421
- return stringValues.includes(rule.value);
3422
- }
3423
- if (rule.operator === 'notEquals') {
3424
- return !stringValues.includes(rule.value);
3425
- }
3426
- return false;
3427
- }
3428
- function compareNumericFormCondition(value, rule) {
3429
- const actualValue = typeof value === 'number' ? value : Number(value);
3430
- const expectedValue = Number(rule.value);
3431
- if (!Number.isFinite(actualValue) || !Number.isFinite(expectedValue)) {
3432
- return false;
3433
- }
3434
- return compareFormConditionValues(actualValue, expectedValue, rule.operator);
3435
- }
3436
- function compareFormConditionValues(actualValue, expectedValue, operator) {
3437
- if (operator === 'equals') {
3438
- return actualValue === expectedValue;
3439
- }
3440
- if (operator === 'notEquals') {
3441
- return actualValue !== expectedValue;
3442
- }
3443
- if (operator === 'greaterThan') {
3444
- return actualValue > expectedValue;
3445
- }
3446
- if (operator === 'greaterThanOrEqual') {
3447
- return actualValue >= expectedValue;
3448
- }
3449
- if (operator === 'lessThan') {
3450
- return actualValue < expectedValue;
3451
- }
3452
- return actualValue <= expectedValue;
3453
- }
3454
- function readFormConditionOperatorFromSymbol(symbol) {
3455
- const operators = {
3456
- '!=': 'notEquals',
3457
- '<': 'lessThan',
3458
- '<=': 'lessThanOrEqual',
3459
- '==': 'equals',
3460
- '>': 'greaterThan',
3461
- '>=': 'greaterThanOrEqual',
3462
- };
3463
- return symbol ? (operators[symbol] ?? null) : null;
3464
- }
3465
- function parseFormConditionLiteral(value) {
3466
- const trimmedValue = value.trim();
3467
- if ((trimmedValue.startsWith('"') && trimmedValue.endsWith('"')) ||
3468
- (trimmedValue.startsWith("'") && trimmedValue.endsWith("'"))) {
3469
- return trimmedValue.slice(1, -1);
3470
- }
3471
- return trimmedValue;
3472
- }
3473
- function isSubmittedFieldValuePresent(value) {
3474
- if (typeof value === 'undefined' || value === null) {
3475
- return false;
3476
- }
3477
- if (typeof value === 'string') {
3478
- return value.trim().length > 0;
3479
- }
3480
- if (Array.isArray(value)) {
3481
- return value.length > 0;
3482
- }
3483
- return true;
3484
- }
3485
- function isNumberFormField(field) {
3486
- return field.type === 'number' || field.type === 'money';
3487
- }
3488
- function createDryRunStep(input) {
3489
- return Object.assign(new workflow_dry_run_object_1.WorkflowDryRunStepObject(), {
3490
- assigneeMemberId: input.assigneeMemberId,
3491
- edgeDefault: input.edge ? Boolean(input.edge.data.isDefault) : null,
3492
- edgeId: input.edge?.id ?? null,
3493
- edgeLabel: input.edge ? readDryRunEdgeLabel(input.edge) : null,
3494
- edgeMatched: input.edgeMatched,
3495
- edgeReason: input.edgeReason,
3496
- entryCondition: input.entryCondition,
3497
- entryConditionMatched: input.entryConditionMet,
3498
- id: `dry-run-step-${input.stepIndex + 1}`,
3499
- message: input.message,
3500
- nodeId: input.node.id,
3501
- nodeLabel: input.node.data.label,
3502
- nodeType: input.node.type,
3503
- status: input.status,
3504
- });
3505
- }
3506
- function readDryRunEdgeLabel(edge) {
3507
- return edge.data.label?.trim() || edge.id;
3508
- }
3509
- function readDryRunEdgeReason(edge) {
3510
- if (!edge) {
3511
- return null;
3512
- }
3513
- if (edge.data.isDefault) {
3514
- return '其他條件不符合時採用預設路徑。';
3515
- }
3516
- const expression = edge.data.condition?.trim();
3517
- if (expression) {
3518
- return `條件成立:${expression}`;
3519
- }
3520
- if (edge.data.conditionFieldKey && edge.data.conditionOperator) {
3521
- return `條件成立:${edge.data.conditionFieldKey} ${edge.data.conditionOperator}${edge.data.conditionValue ? ` ${edge.data.conditionValue}` : ''}`;
3522
- }
3523
- return '無條件路徑。';
3524
- }
3525
- function readDryRunStepStatus(node, entryConditionMet) {
3526
- if (!entryConditionMet) {
3527
- return 'SKIPPED';
3528
- }
3529
- if (node.type === 'userTask') {
3530
- return 'WAITING';
3531
- }
3532
- if (node.type === 'endEvent') {
3533
- return 'COMPLETED';
3534
- }
3535
- return 'PASSED';
3536
- }
3537
- function readDryRunStepMessage(node, entryConditionMet) {
3538
- if (!entryConditionMet) {
3539
- return '進入條件不符合,略過節點。';
3540
- }
3541
- if (node.type === 'userTask') {
3542
- return '將建立待簽任務。';
3543
- }
3544
- if (node.type === 'endEvent') {
3545
- return `流程結束:${node.data.endState ?? 'APPROVED'}`;
3546
- }
3547
- return '節點條件通過。';
3548
- }
3549
- function mapDecisionToResolution(action) {
3550
- switch (action) {
3551
- case workflow_engine_enums_1.TaskDecisionActionEnum.APPROVED:
3552
- return notification_enums_1.NotificationResolutionEnum.APPROVED;
3553
- case workflow_engine_enums_1.TaskDecisionActionEnum.REJECTED:
3554
- return notification_enums_1.NotificationResolutionEnum.REJECTED;
3555
- case workflow_engine_enums_1.TaskDecisionActionEnum.RETURNED:
3556
- return notification_enums_1.NotificationResolutionEnum.RETURNED;
3557
- case workflow_engine_enums_1.TaskDecisionActionEnum.TRANSFERRED:
3558
- return notification_enums_1.NotificationResolutionEnum.TRANSFERRED;
3559
- }
3560
- }
3561
- //# sourceMappingURL=workflow-engine.service.js.map