@acorex/modules 21.0.0-next.57 → 21.0.0-next.59
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.
- package/fesm2022/{acorex-modules-assessment-management-acorex-modules-assessment-management-DcPnI9fZ.mjs → acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs} +72 -78
- package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-o8aFazwA.mjs.map +1 -0
- package/fesm2022/{acorex-modules-assessment-management-assessment-case.entity-D880d_qz.mjs → acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs} +67 -71
- package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-yrc_Yybq.mjs.map +1 -0
- package/fesm2022/acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs +47 -0
- package/fesm2022/acorex-modules-assessment-management-assessment-session-answers-view.util-BUQa_TSY.mjs.map +1 -0
- package/fesm2022/{acorex-modules-assessment-management-assessment-session.entity-DvbocIN1.mjs → acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs} +5 -41
- package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-Cwf30iuu.mjs.map +1 -0
- package/fesm2022/{acorex-modules-assessment-management-fill-assessment-session.command-BMJ54EPO.mjs → acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs} +32 -7
- package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-CJieLR5g.mjs.map +1 -0
- package/fesm2022/{acorex-modules-assessment-management-index-XdJCIYG3.mjs → acorex-modules-assessment-management-index-D8KjfHAH.mjs} +4 -4
- package/fesm2022/{acorex-modules-assessment-management-index-XdJCIYG3.mjs.map → acorex-modules-assessment-management-index-D8KjfHAH.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-preview-question.command-CO8NfjEb.mjs → acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-preview-question.command-CO8NfjEb.mjs.map → acorex-modules-assessment-management-preview-question.command-D0-FB1HH.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-DSzbtU0e.mjs → acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-preview-questionnaire.command-DSzbtU0e.mjs.map → acorex-modules-assessment-management-preview-questionnaire.command-wqPNgv7q.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DbvxAUgb.mjs → acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DbvxAUgb.mjs.map → acorex-modules-assessment-management-question-bank-interface-editor-widget-edit.component-DYyUhSWd.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DBfQhSKR.mjs → acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-question-bank-item.entity-DBfQhSKR.mjs.map → acorex-modules-assessment-management-question-bank-item.entity-DUf2ceKY.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-questionnaire-calculation.entity-CL9s20qR.mjs → acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-questionnaire-calculation.entity-CL9s20qR.mjs.map → acorex-modules-assessment-management-questionnaire-calculation.entity-CYF0k42_.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-questionnaire-viewer-popup.component-CH_cJlSf.mjs → acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs} +111 -28
- package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-Cy5ElBmU.mjs.map +1 -0
- package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-CrryBMC2.mjs → acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs} +3 -3
- package/fesm2022/{acorex-modules-assessment-management-questionnaire.entity-CrryBMC2.mjs.map → acorex-modules-assessment-management-questionnaire.entity-RWSkzNbA.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-assessment-management-save-questionnaire-questions.command-Dq5VComh.mjs → acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs} +2 -2
- package/fesm2022/{acorex-modules-assessment-management-save-questionnaire-questions.command-Dq5VComh.mjs.map → acorex-modules-assessment-management-save-questionnaire-questions.command-koGmUbNE.mjs.map} +1 -1
- package/fesm2022/acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs +157 -0
- package/fesm2022/acorex-modules-assessment-management-view-case-last-session-answers.command-NTuDdoYk.mjs.map +1 -0
- package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs +172 -0
- package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-0btGV66_.mjs.map +1 -0
- package/fesm2022/acorex-modules-assessment-management.mjs +1 -1
- package/fesm2022/{acorex-modules-auth-acorex-modules-auth-wfCSNwXC.mjs → acorex-modules-auth-acorex-modules-auth-D4IBi_VD.mjs} +39 -30
- package/fesm2022/acorex-modules-auth-acorex-modules-auth-D4IBi_VD.mjs.map +1 -0
- package/fesm2022/{acorex-modules-auth-app-chooser.component-BY281-Cl.mjs → acorex-modules-auth-app-chooser.component-BEktaBWD.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-app-chooser.component-BY281-Cl.mjs.map → acorex-modules-auth-app-chooser.component-BEktaBWD.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-login.module-CjgYlyOv.mjs → acorex-modules-auth-login.module-aAwHzXPj.mjs} +4 -4
- package/fesm2022/{acorex-modules-auth-login.module-CjgYlyOv.mjs.map → acorex-modules-auth-login.module-aAwHzXPj.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-master.layout-Clx8QDYW.mjs → acorex-modules-auth-master.layout-BapDYFZ_.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-master.layout-Clx8QDYW.mjs.map → acorex-modules-auth-master.layout-BapDYFZ_.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-oauth-callback.component-CGnGAPJ2.mjs → acorex-modules-auth-oauth-callback.component-Cyu2vqyK.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-oauth-callback.component-CGnGAPJ2.mjs.map → acorex-modules-auth-oauth-callback.component-Cyu2vqyK.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-password.component-8eEX0lRP.mjs → acorex-modules-auth-password.component-DB-ac0Rn.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-password.component-8eEX0lRP.mjs.map → acorex-modules-auth-password.component-DB-ac0Rn.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-password.component-CUxwThnN.mjs → acorex-modules-auth-password.component-cPmwNpuq.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-password.component-CUxwThnN.mjs.map → acorex-modules-auth-password.component-cPmwNpuq.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-routes-CYLKJm9g.mjs → acorex-modules-auth-routes-r72zIRTa.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-routes-CYLKJm9g.mjs.map → acorex-modules-auth-routes-r72zIRTa.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-tenant-chooser.component-DDK1KW-8.mjs → acorex-modules-auth-tenant-chooser.component-AQA2o6Ty.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-tenant-chooser.component-DDK1KW-8.mjs.map → acorex-modules-auth-tenant-chooser.component-AQA2o6Ty.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-two-factor.module-Iw2U7Cy5.mjs → acorex-modules-auth-two-factor.module-Dx0CC5ic.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-two-factor.module-Iw2U7Cy5.mjs.map → acorex-modules-auth-two-factor.module-Dx0CC5ic.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-auth-user-sessions.component-sKDANprs.mjs → acorex-modules-auth-user-sessions.component-3Wr7DUDQ.mjs} +2 -2
- package/fesm2022/{acorex-modules-auth-user-sessions.component-sKDANprs.mjs.map → acorex-modules-auth-user-sessions.component-3Wr7DUDQ.mjs.map} +1 -1
- package/fesm2022/acorex-modules-auth.mjs +1 -1
- package/fesm2022/acorex-modules-common.mjs +7 -3
- package/fesm2022/acorex-modules-common.mjs.map +1 -1
- package/fesm2022/{acorex-modules-conversation-acorex-modules-conversation-DlteA_jC.mjs → acorex-modules-conversation-acorex-modules-conversation-DkdNLMKa.mjs} +8 -7
- package/fesm2022/acorex-modules-conversation-acorex-modules-conversation-DkdNLMKa.mjs.map +1 -0
- package/fesm2022/{acorex-modules-conversation-assist-delegated-agent-detail-popup.component-BAgBuxlz.mjs → acorex-modules-conversation-assist-delegated-agent-detail-popup.component-VKPbkRqI.mjs} +2 -2
- package/fesm2022/{acorex-modules-conversation-assist-delegated-agent-detail-popup.component-BAgBuxlz.mjs.map → acorex-modules-conversation-assist-delegated-agent-detail-popup.component-VKPbkRqI.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-conversation-comments-page.component-BrwP9l7M.mjs → acorex-modules-conversation-comments-page.component-ehUCjoFD.mjs} +2 -2
- package/fesm2022/{acorex-modules-conversation-comments-page.component-BrwP9l7M.mjs.map → acorex-modules-conversation-comments-page.component-ehUCjoFD.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-conversation-send-assist-chat-message.command-BzKawEoN.mjs → acorex-modules-conversation-send-assist-chat-message.command-ZNcVi54N.mjs} +2 -2
- package/fesm2022/{acorex-modules-conversation-send-assist-chat-message.command-BzKawEoN.mjs.map → acorex-modules-conversation-send-assist-chat-message.command-ZNcVi54N.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-conversation-start-assist-chat.command-BUOGUMl0.mjs → acorex-modules-conversation-start-assist-chat.command-CX6lm1lG.mjs} +2 -2
- package/fesm2022/{acorex-modules-conversation-start-assist-chat.command-BUOGUMl0.mjs.map → acorex-modules-conversation-start-assist-chat.command-CX6lm1lG.mjs.map} +1 -1
- package/fesm2022/acorex-modules-conversation.mjs +1 -1
- package/fesm2022/acorex-modules-customer-management.mjs +2 -0
- package/fesm2022/acorex-modules-customer-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-data-management.mjs +6 -5
- package/fesm2022/acorex-modules-data-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-help-desk-acorex-modules-help-desk-Bfjo-ENr.mjs → acorex-modules-help-desk-acorex-modules-help-desk-DVlO9JZw.mjs} +3 -2
- package/fesm2022/acorex-modules-help-desk-acorex-modules-help-desk-DVlO9JZw.mjs.map +1 -0
- package/fesm2022/{acorex-modules-help-desk-capture-screen.component-CsACFkPq.mjs → acorex-modules-help-desk-capture-screen.component-rNPehEf8.mjs} +2 -2
- package/fesm2022/{acorex-modules-help-desk-capture-screen.component-CsACFkPq.mjs.map → acorex-modules-help-desk-capture-screen.component-rNPehEf8.mjs.map} +1 -1
- package/fesm2022/acorex-modules-help-desk.mjs +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-acorex-modules-human-capital-management-XhV2JQXs.mjs → acorex-modules-human-capital-management-acorex-modules-human-capital-management-BWezAea1.mjs} +131 -112
- package/fesm2022/acorex-modules-human-capital-management-acorex-modules-human-capital-management-BWezAea1.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-Cre30Kp7.mjs → acorex-modules-human-capital-management-approve-leave-request.command-Bh_Bbq5-.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-approve-leave-request.command-Cre30Kp7.mjs.map → acorex-modules-human-capital-management-approve-leave-request.command-Bh_Bbq5-.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-NYgddiaD.mjs → acorex-modules-human-capital-management-assign-position-assignment.command-BpDdDG9i.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-assign-position-assignment.command-NYgddiaD.mjs.map → acorex-modules-human-capital-management-assign-position-assignment.command-BpDdDG9i.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-DPh-KQgN.mjs → acorex-modules-human-capital-management-cancel-leave-request.command-CjtKaKFw.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-cancel-leave-request.command-DPh-KQgN.mjs.map → acorex-modules-human-capital-management-cancel-leave-request.command-CjtKaKFw.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-employee.entity-znCbbQUd.mjs → acorex-modules-human-capital-management-employee.entity-Cx_oY2aK.mjs} +8 -8
- package/fesm2022/acorex-modules-human-capital-management-employee.entity-Cx_oY2aK.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-DUNB3zAB.mjs → acorex-modules-human-capital-management-employment-type.entity-B20Taa5x.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-employment-type.entity-DUNB3zAB.mjs.map → acorex-modules-human-capital-management-employment-type.entity-B20Taa5x.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-JyFrrCGE.mjs → acorex-modules-human-capital-management-leave-request.entity-BO8QXpZE.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-leave-request.entity-JyFrrCGE.mjs.map → acorex-modules-human-capital-management-leave-request.entity-BO8QXpZE.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-B61MiEPu.mjs → acorex-modules-human-capital-management-leave-type.entity-CQwSWqSh.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-leave-type.entity-B61MiEPu.mjs.map → acorex-modules-human-capital-management-leave-type.entity-CQwSWqSh.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-lifecycle-process-type.entity-DJqo2kTG.mjs → acorex-modules-human-capital-management-lifecycle-event-type.entity-BPWJrvbN.mjs} +13 -13
- package/fesm2022/acorex-modules-human-capital-management-lifecycle-event-type.entity-BPWJrvbN.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-employee-lifecycle-process.entity-LX68KORg.mjs → acorex-modules-human-capital-management-lifecycle-event.entity-ZZuPu9KQ.mjs} +45 -56
- package/fesm2022/acorex-modules-human-capital-management-lifecycle-event.entity-ZZuPu9KQ.mjs.map +1 -0
- package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-CKCC6bKg.mjs → acorex-modules-human-capital-management-position-assignment.entity-BN1y0qhi.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-position-assignment.entity-CKCC6bKg.mjs.map → acorex-modules-human-capital-management-position-assignment.entity-BN1y0qhi.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-Blv08UQZ.mjs → acorex-modules-human-capital-management-reject-leave-request.command-LczqOHP-.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-reject-leave-request.command-Blv08UQZ.mjs.map → acorex-modules-human-capital-management-reject-leave-request.command-LczqOHP-.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-lgEAbxYH.mjs → acorex-modules-human-capital-management-revoke-position-assignment.command-Dm8YacDI.mjs} +2 -2
- package/fesm2022/{acorex-modules-human-capital-management-revoke-position-assignment.command-lgEAbxYH.mjs.map → acorex-modules-human-capital-management-revoke-position-assignment.command-Dm8YacDI.mjs.map} +1 -1
- package/fesm2022/acorex-modules-human-capital-management-start-lifecycle-event-flow.command-DPwuyFz2.mjs +237 -0
- package/fesm2022/acorex-modules-human-capital-management-start-lifecycle-event-flow.command-DPwuyFz2.mjs.map +1 -0
- package/fesm2022/acorex-modules-human-capital-management.mjs +1 -1
- package/fesm2022/{acorex-modules-learning-management-enrollment.entity-B53AQq_9.mjs → acorex-modules-learning-management-enrollment.entity-CdJafrUh.mjs} +4 -22
- package/fesm2022/acorex-modules-learning-management-enrollment.entity-CdJafrUh.mjs.map +1 -0
- package/fesm2022/acorex-modules-learning-management.mjs +1 -1
- package/fesm2022/acorex-modules-learning-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-location-management.mjs +1 -0
- package/fesm2022/acorex-modules-location-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-5DJoQkx2.mjs → acorex-modules-platform-management-acorex-modules-platform-management-Cbf1rlEK.mjs} +4 -4
- package/fesm2022/{acorex-modules-platform-management-acorex-modules-platform-management-5DJoQkx2.mjs.map → acorex-modules-platform-management-acorex-modules-platform-management-Cbf1rlEK.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-platform-management-menu-list.component-C3WBYbOt.mjs → acorex-modules-platform-management-menu-list.component-DIf_-oaM.mjs} +2 -2
- package/fesm2022/{acorex-modules-platform-management-menu-list.component-C3WBYbOt.mjs.map → acorex-modules-platform-management-menu-list.component-DIf_-oaM.mjs.map} +1 -1
- package/fesm2022/acorex-modules-platform-management.mjs +1 -1
- package/fesm2022/acorex-modules-security-management.mjs +3 -0
- package/fesm2022/acorex-modules-security-management.mjs.map +1 -1
- package/fesm2022/acorex-modules-supplier-management.mjs +1 -0
- package/fesm2022/acorex-modules-supplier-management.mjs.map +1 -1
- package/fesm2022/{acorex-modules-task-management-acorex-modules-task-management-CrybKNgo.mjs → acorex-modules-task-management-acorex-modules-task-management--mdNdYIs.mjs} +32 -2
- package/fesm2022/acorex-modules-task-management-acorex-modules-task-management--mdNdYIs.mjs.map +1 -0
- package/fesm2022/{acorex-modules-task-management-task-board.page-B2xxXscG.mjs → acorex-modules-task-management-task-board.page-BIZ1qH7m.mjs} +33 -3
- package/fesm2022/acorex-modules-task-management-task-board.page-BIZ1qH7m.mjs.map +1 -0
- package/fesm2022/acorex-modules-task-management.mjs +1 -1
- package/fesm2022/{acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-OLbCD-7P.mjs → acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-B4DLmioy.mjs} +3 -3
- package/fesm2022/{acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-OLbCD-7P.mjs.map → acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-B4DLmioy.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-index-RRzkvrNM.mjs → acorex-modules-workflow-management-index-BhVZ1Bcw.mjs} +3 -3
- package/fesm2022/{acorex-modules-workflow-management-index-RRzkvrNM.mjs.map → acorex-modules-workflow-management-index-BhVZ1Bcw.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-index-Eh5DDK-V.mjs → acorex-modules-workflow-management-index-DC_9M9dk.mjs} +3 -3
- package/fesm2022/{acorex-modules-workflow-management-index-Eh5DDK-V.mjs.map → acorex-modules-workflow-management-index-DC_9M9dk.mjs.map} +1 -1
- package/fesm2022/{acorex-modules-workflow-management-workflow-instance.entity-B_V3uMSI.mjs → acorex-modules-workflow-management-workflow-instance.entity-BnKT3Wgh.mjs} +2 -2
- package/fesm2022/{acorex-modules-workflow-management-workflow-instance.entity-B_V3uMSI.mjs.map → acorex-modules-workflow-management-workflow-instance.entity-BnKT3Wgh.mjs.map} +1 -1
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-DjYDwHWU.mjs +255 -0
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-DjYDwHWU.mjs.map +1 -0
- package/fesm2022/acorex-modules-workflow-management.mjs +1819 -745
- package/fesm2022/acorex-modules-workflow-management.mjs.map +1 -1
- package/package.json +2 -2
- package/types/acorex-modules-assessment-management.d.ts +4 -7
- package/types/acorex-modules-common.d.ts +1 -0
- package/types/acorex-modules-human-capital-management.d.ts +31 -30
- package/types/acorex-modules-learning-management.d.ts +0 -1
- package/types/acorex-modules-task-management.d.ts +41 -2
- package/types/acorex-modules-workflow-management.d.ts +280 -34
- package/fesm2022/acorex-modules-assessment-management-acorex-modules-assessment-management-DcPnI9fZ.mjs.map +0 -1
- package/fesm2022/acorex-modules-assessment-management-assessment-case.entity-D880d_qz.mjs.map +0 -1
- package/fesm2022/acorex-modules-assessment-management-assessment-session.entity-DvbocIN1.mjs.map +0 -1
- package/fesm2022/acorex-modules-assessment-management-fill-assessment-session.command-BMJ54EPO.mjs.map +0 -1
- package/fesm2022/acorex-modules-assessment-management-questionnaire-viewer-popup.component-CH_cJlSf.mjs.map +0 -1
- package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-DpppXekm.mjs +0 -112
- package/fesm2022/acorex-modules-assessment-management-view-session-answers.command-DpppXekm.mjs.map +0 -1
- package/fesm2022/acorex-modules-auth-acorex-modules-auth-wfCSNwXC.mjs.map +0 -1
- package/fesm2022/acorex-modules-conversation-acorex-modules-conversation-DlteA_jC.mjs.map +0 -1
- package/fesm2022/acorex-modules-help-desk-acorex-modules-help-desk-Bfjo-ENr.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-acorex-modules-human-capital-management-XhV2JQXs.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-employee-lifecycle-process.entity-LX68KORg.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-employee.entity-znCbbQUd.mjs.map +0 -1
- package/fesm2022/acorex-modules-human-capital-management-lifecycle-process-type.entity-DJqo2kTG.mjs.map +0 -1
- package/fesm2022/acorex-modules-learning-management-enrollment.entity-B53AQq_9.mjs.map +0 -1
- package/fesm2022/acorex-modules-task-management-acorex-modules-task-management-CrybKNgo.mjs.map +0 -1
- package/fesm2022/acorex-modules-task-management-task-board.page-B2xxXscG.mjs.map +0 -1
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-DMszilef.mjs +0 -356
- package/fesm2022/acorex-modules-workflow-management-workflow-task-popover.component-DMszilef.mjs.map +0 -1
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, NgModule,
|
|
2
|
+
import { Injectable, inject, NgModule, Injector, runInInjectionContext, makeEnvironmentProviders, InjectionToken, Inject } from '@angular/core';
|
|
3
3
|
import { AXPCommonMenuKeys, AXPWidgetsList } from '@acorex/modules/common';
|
|
4
4
|
import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
|
|
5
|
-
import { AXPEntityService, AXPEntityFormBuilderService, AXPEntityDefinitionRegistryService, entityDetailsCrudActions, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
|
|
5
|
+
import { AXPEntityService, AXPEntityFormBuilderService, AXPEntityDefinitionRegistryService, entityDetailsCrudActions, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_DEFINITION_LOADER, AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD } from '@acorex/platform/layout/entity';
|
|
6
6
|
import { firstValueFrom } from 'rxjs';
|
|
7
7
|
import { AXMCalendarManagementModule } from '@acorex/modules/calendar-management';
|
|
8
|
-
import { AXPEntityCommandScope, AXPStatusProvider, AXPSystemStatusType, systemStatusToDefinition,
|
|
9
|
-
import { AXPSystemActionType, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, provideLazyProvider } from '@acorex/platform/core';
|
|
8
|
+
import { AXPEntityCommandScope, AXPStatusProvider, AXPSystemStatusType, systemStatusToDefinition, AXP_STATUS_PROVIDERS, AXP_MENU_PROVIDER, AXPSettingsService, AXPStatusDefinitionProviderService, AXPRegionalSetting, AXPSystemStatuses } from '@acorex/platform/common';
|
|
9
|
+
import { AXPSystemActionType, AXPBroadcastEventService, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, provideLazyProvider } from '@acorex/platform/core';
|
|
10
10
|
import { AXP_PAGE_COMPONENT_PROVIDER } from '@acorex/platform/layout/components';
|
|
11
11
|
import { AXPDomainModule } from '@acorex/platform/domain';
|
|
12
|
-
import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetSerializationHelper, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
|
|
12
|
+
import { AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXPWidgetSerializationHelper, AXPWidgetCoreModule, AXP_WIDGET_DEFINITION_PROVIDER, AXPWidgetsCatalog } from '@acorex/platform/layout/widget-core';
|
|
13
13
|
import { AXPBarChartWidget, AXPHeatmapChartWidget, AXPDonutChartWidget, AXPLineChartWidget, AXPGaugeChartWidget, AXPFunnelChartWidget, AXPKpiDetailsWidget, AXPKpiProgressWidget, AXPKpiSegmentedWidget, AXPKpiStatCardWidget } from '@acorex/modules/dashboard-management';
|
|
14
14
|
import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY } from '@acorex/platform/layout/widgets';
|
|
15
15
|
import { provideCommandSetups } from '@acorex/platform/runtime';
|
|
16
|
-
import { AXPWorkflowManager, AXP_WORKFLOW_ERROR_CODES,
|
|
16
|
+
import { AXPWorkflowDefinitionService, AXP_WORKFLOW_ENGINE, AXPWorkflowManager, AXP_WORKFLOW_SUPPRESS_CONTINUATION_INPUT_KEY, AXP_WORKFLOW_ERROR_CODES, axpIsWorkflowTaskBoardActivityType, AXP_WORKFLOW_PROVIDER, AXP_ACTIVITY_CATEGORY_PROVIDER, AXP_ACTIVITY_PROVIDER, AXP_WORKFLOW_CATEGORY_PROVIDER, AXP_WORKFLOW_CONTINUATION_HOOK } from '@acorex/platform/workflow';
|
|
17
17
|
import { AXDialogService } from '@acorex/components/dialog';
|
|
18
18
|
import { AXTranslationService } from '@acorex/core/translation';
|
|
19
19
|
import { AXPopupService } from '@acorex/components/popup';
|
|
20
20
|
import { AXPDialogRendererComponent, AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
|
|
21
21
|
import { cloneDeep } from 'lodash-es';
|
|
22
22
|
import { AXToastService } from '@acorex/components/toast';
|
|
23
|
-
import { AXPWorkflowTaskProvider, matchesTaskBoardAssigneeUserIdsFilter, filterTasksOverlappingRange
|
|
23
|
+
import { AXP_WORKFLOW_TASK_PROVIDER, AXPWorkflowTaskProvider, matchesTaskBoardAssigneeUserIdsFilter, filterTasksOverlappingRange } from '@acorex/modules/task-management';
|
|
24
|
+
import { AXFormatService } from '@acorex/core/format';
|
|
24
25
|
|
|
25
26
|
const config = {
|
|
26
27
|
i18n: 'workflow-management',
|
|
@@ -167,7 +168,7 @@ class AXMWorkflowManagementModuleEntityProvider {
|
|
|
167
168
|
case RootConfig.entities.workflowDefinition.name:
|
|
168
169
|
return (await import('./acorex-modules-workflow-management-workflow-definition.entity-DISpkWE4.mjs')).factory();
|
|
169
170
|
case RootConfig.entities.workflowInstance.name:
|
|
170
|
-
return (await import('./acorex-modules-workflow-management-workflow-instance.entity-
|
|
171
|
+
return (await import('./acorex-modules-workflow-management-workflow-instance.entity-BnKT3Wgh.mjs')).factory();
|
|
171
172
|
case RootConfig.entities.activityDefinition.name:
|
|
172
173
|
return (await import('./acorex-modules-workflow-management-activity-definition.entity-B3DkgDQb.mjs')).factory();
|
|
173
174
|
case RootConfig.entities.automation.name:
|
|
@@ -996,10 +997,10 @@ const AXMActivityCommandConfiguratorWidget = {
|
|
|
996
997
|
],
|
|
997
998
|
components: {
|
|
998
999
|
edit: {
|
|
999
|
-
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-
|
|
1000
|
+
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-B4DLmioy.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetEditComponent),
|
|
1000
1001
|
},
|
|
1001
1002
|
designer: {
|
|
1002
|
-
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-
|
|
1003
|
+
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-edit.component-B4DLmioy.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetEditComponent),
|
|
1003
1004
|
},
|
|
1004
1005
|
column: {
|
|
1005
1006
|
component: () => import('./acorex-modules-workflow-management-activity-command-configurator-widget-column.component-CDo0QVFy.mjs').then((c) => c.AXMActivityCommandConfiguratorWidgetColumnComponent),
|
|
@@ -2137,6 +2138,8 @@ const automationPlugin = {
|
|
|
2137
2138
|
* This plugin:
|
|
2138
2139
|
* - Adds workflow capability marker to entity extensions
|
|
2139
2140
|
* - Enables workflow trigger middleware to detect workflow-capable entities
|
|
2141
|
+
* - Domain rows link runs via `workflowInfo` on the entity model (see `AXPRecordWorkflowInfo`),
|
|
2142
|
+
* not as entity definition properties
|
|
2140
2143
|
* - Provides UI hooks for workflow-related features (future enhancement)
|
|
2141
2144
|
*
|
|
2142
2145
|
* Usage:
|
|
@@ -2550,6 +2553,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2550
2553
|
type: Injectable
|
|
2551
2554
|
}] });
|
|
2552
2555
|
|
|
2556
|
+
//#region ---- Lazy loader ----
|
|
2557
|
+
/** Code-split chunk for the generic workflow task provider implementation. */
|
|
2558
|
+
function loadGenericWorkflowTaskProviderClass() {
|
|
2559
|
+
return Promise.resolve().then(function () { return genericWorkflowTask_provider; }).then((m) => m.AXPGenericWorkflowTaskProvider);
|
|
2560
|
+
}
|
|
2561
|
+
//#endregion
|
|
2562
|
+
//#region ---- Per-definition registration ----
|
|
2563
|
+
/**
|
|
2564
|
+
* Registers one workflow task provider for a single definition (lazy-loaded).
|
|
2565
|
+
* Use in feature modules that need a dedicated task-board entry per workflow.
|
|
2566
|
+
*/
|
|
2567
|
+
function provideGenericWorkflowTaskProvider(config) {
|
|
2568
|
+
return {
|
|
2569
|
+
provide: AXP_WORKFLOW_TASK_PROVIDER,
|
|
2570
|
+
multi: true,
|
|
2571
|
+
useFactory: (injector) => loadGenericWorkflowTaskProviderClass().then((ProviderClass) => runInInjectionContext(injector, () => new ProviderClass(config))),
|
|
2572
|
+
deps: [Injector],
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
//#endregion
|
|
2576
|
+
//#region ---- Auto-discovery registration ----
|
|
2577
|
+
/**
|
|
2578
|
+
* Registers one task provider per workflow definition (lazy-loaded).
|
|
2579
|
+
* Discovers definitions via {@link AXPWorkflowDefinitionService} at first resolve.
|
|
2580
|
+
*/
|
|
2581
|
+
function provideTaskWorkflow() {
|
|
2582
|
+
return makeEnvironmentProviders([
|
|
2583
|
+
{
|
|
2584
|
+
provide: AXP_WORKFLOW_TASK_PROVIDER,
|
|
2585
|
+
multi: true,
|
|
2586
|
+
useFactory: (injector) => (async () => {
|
|
2587
|
+
const ProviderClass = await loadGenericWorkflowTaskProviderClass();
|
|
2588
|
+
const workflowDefinitionService = injector.get(AXPWorkflowDefinitionService);
|
|
2589
|
+
const multiLanguageResolver = injector.get(AXTranslationService);
|
|
2590
|
+
const categories = await workflowDefinitionService.getCategories();
|
|
2591
|
+
const providers = [];
|
|
2592
|
+
const seenDefinitionNames = new Set();
|
|
2593
|
+
for (const category of categories) {
|
|
2594
|
+
const workflows = await workflowDefinitionService.getWorkflowsByCategoryId(category.id);
|
|
2595
|
+
for (const workflow of workflows) {
|
|
2596
|
+
const key = workflow.name;
|
|
2597
|
+
if (!key || seenDefinitionNames.has(key)) {
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
seenDefinitionNames.add(key);
|
|
2601
|
+
const provider = runInInjectionContext(injector, () => new ProviderClass({
|
|
2602
|
+
name: workflow.name,
|
|
2603
|
+
title: multiLanguageResolver.resolve(workflow.title) ?? workflow.name,
|
|
2604
|
+
icon: 'fa-light fa-diagram-project',
|
|
2605
|
+
definitionId: workflow.name,
|
|
2606
|
+
}));
|
|
2607
|
+
providers.push(provider);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
return providers;
|
|
2611
|
+
})(),
|
|
2612
|
+
deps: [Injector],
|
|
2613
|
+
},
|
|
2614
|
+
]);
|
|
2615
|
+
}
|
|
2616
|
+
//#endregion
|
|
2617
|
+
|
|
2553
2618
|
//#region ---- Imports ----
|
|
2554
2619
|
//#endregion
|
|
2555
2620
|
//#region ---- Permission Helpers ----
|
|
@@ -2563,133 +2628,336 @@ function canReassignWorkflowTaskToSelf(sessionService) {
|
|
|
2563
2628
|
}
|
|
2564
2629
|
//#endregion
|
|
2565
2630
|
|
|
2566
|
-
//#region ----
|
|
2631
|
+
//#region ---- Imports ----
|
|
2632
|
+
//#endregion
|
|
2633
|
+
//#region ---- User id helpers ----
|
|
2634
|
+
/** Normalizes platform user id lists on bookmark payloads. */
|
|
2635
|
+
function normalizeWorkflowTaskUserIds(value) {
|
|
2636
|
+
if (!value || value.length === 0) {
|
|
2637
|
+
return [];
|
|
2638
|
+
}
|
|
2639
|
+
return value.map((id) => String(id)).filter((s) => s.length > 0);
|
|
2640
|
+
}
|
|
2641
|
+
/** Workflow instance creator (task-board "started by"). */
|
|
2642
|
+
function getWorkflowInstanceInitiatorUserId(instance) {
|
|
2643
|
+
const id = instance.auditInfo?.created?.by?.id;
|
|
2644
|
+
return id != null && String(id).length > 0 ? String(id) : null;
|
|
2645
|
+
}
|
|
2646
|
+
/** Demo/platform operator accounts that bypass permission checks in mock auth. */
|
|
2647
|
+
function isWorkflowPlatformRootUser(sessionService) {
|
|
2648
|
+
const username = sessionService.user?.name?.trim();
|
|
2649
|
+
return username === 'root' || username === 'root2' || username === 'super-root';
|
|
2650
|
+
}
|
|
2651
|
+
//#endregion
|
|
2652
|
+
//#region ---- Bookmark payload helpers ----
|
|
2653
|
+
/** Bookmark types rendered on the workflow task board. */
|
|
2654
|
+
function isWorkflowTaskBoardBookmarkPayload(payload) {
|
|
2655
|
+
const t = payload.activityType;
|
|
2656
|
+
return t === 'workflow-activity:human-task' || t === 'workflow-activity:cartable';
|
|
2657
|
+
}
|
|
2658
|
+
/** Pooled task that must be claimed before acting (no direct continuation offer). */
|
|
2659
|
+
function isWorkflowPooledClaimablePayload(payload) {
|
|
2660
|
+
const assigned = normalizeWorkflowTaskUserIds(payload.assignedUserIds);
|
|
2661
|
+
const candidates = normalizeWorkflowTaskUserIds(payload.candidateUserIds);
|
|
2662
|
+
if (payload.activityType === 'workflow-activity:cartable') {
|
|
2663
|
+
return assigned.length === 0 && candidates.length > 0;
|
|
2664
|
+
}
|
|
2665
|
+
return payload.activityType === 'workflow-activity:human-task' && assigned.length === 0 && candidates.length > 0;
|
|
2666
|
+
}
|
|
2667
|
+
/** Human-task assigned to another user; may be taken over when permitted. */
|
|
2668
|
+
function isWorkflowReassignableHumanTaskPayload(payload) {
|
|
2669
|
+
if (payload.activityType !== 'workflow-activity:human-task') {
|
|
2670
|
+
return false;
|
|
2671
|
+
}
|
|
2672
|
+
const assigned = normalizeWorkflowTaskUserIds(payload.assignedUserIds);
|
|
2673
|
+
if (assigned.length === 0) {
|
|
2674
|
+
return false;
|
|
2675
|
+
}
|
|
2676
|
+
return true;
|
|
2677
|
+
}
|
|
2678
|
+
/** Parses bookmark payload from entity storage. */
|
|
2679
|
+
function parseWorkflowTaskContinuationPayload(bookmark) {
|
|
2680
|
+
try {
|
|
2681
|
+
const raw = bookmark.payload ?? (bookmark.payloadJson ? JSON.parse(bookmark.payloadJson) : null);
|
|
2682
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
2683
|
+
return null;
|
|
2684
|
+
}
|
|
2685
|
+
return raw;
|
|
2686
|
+
}
|
|
2687
|
+
catch {
|
|
2688
|
+
return null;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
//#endregion
|
|
2692
|
+
//#region ---- Step advancement gate ----
|
|
2693
|
+
/** Resume outcomes that do not advance the workflow graph (draft save, cancel, loop back). */
|
|
2694
|
+
const NON_ADVANCING_RESUME_OUTCOMES = new Set(['saved', 'cancelled', 'cancel', 'save', 'draft']);
|
|
2567
2695
|
/**
|
|
2568
|
-
*
|
|
2696
|
+
* Whether the workflow moved forward enough to prompt "continue to next step".
|
|
2697
|
+
* Skips draft save / cancel on questionnaire (same human-task bookmark remains).
|
|
2698
|
+
*/
|
|
2699
|
+
function shouldOfferWorkflowContinuationAfterStep(context, activeBookmarkActivityIds) {
|
|
2700
|
+
const outcome = context.resumeOutcome?.trim().toLowerCase();
|
|
2701
|
+
if (outcome && NON_ADVANCING_RESUME_OUTCOMES.has(outcome)) {
|
|
2702
|
+
return false;
|
|
2703
|
+
}
|
|
2704
|
+
const pendingActivityId = context.pendingNextTask?.activityId;
|
|
2705
|
+
const completedActivityId = context.completedActivityId;
|
|
2706
|
+
if (pendingActivityId && completedActivityId && pendingActivityId === completedActivityId) {
|
|
2707
|
+
return false;
|
|
2708
|
+
}
|
|
2709
|
+
if (activeBookmarkActivityIds.length === 0) {
|
|
2710
|
+
return false;
|
|
2711
|
+
}
|
|
2712
|
+
if (!completedActivityId) {
|
|
2713
|
+
return true;
|
|
2714
|
+
}
|
|
2715
|
+
return activeBookmarkActivityIds.some((id) => id !== completedActivityId);
|
|
2716
|
+
}
|
|
2717
|
+
//#endregion
|
|
2718
|
+
//#region ---- Continuation eligibility ----
|
|
2719
|
+
/**
|
|
2720
|
+
* Whether the UI should offer "continue to next step" after a successful resume.
|
|
2569
2721
|
*
|
|
2570
|
-
*
|
|
2571
|
-
*
|
|
2572
|
-
*
|
|
2573
|
-
* "leaveRequestId": "{{ variables.leaveRequestId }}",
|
|
2574
|
-
* "_taskI18n": {
|
|
2575
|
-
* "employeeDisplayName": "{{ variables.employeeDisplayName }}",
|
|
2576
|
-
* "leaveTypeTitle": "{{ variables.leaveTypeTitle }}"
|
|
2577
|
-
* },
|
|
2578
|
-
* "_taskTitleParts": [
|
|
2579
|
-
* "{{ variables.employeeDisplayName }}",
|
|
2580
|
-
* "{{ variables.leaveTypeTitle }}"
|
|
2581
|
-
* ]
|
|
2582
|
-
* }
|
|
2583
|
-
* ```
|
|
2722
|
+
* - Current user is the assignee (sequential tasks for the same person, e.g. line manager).
|
|
2723
|
+
* - Workflow initiator and assignee are the same person and that person is the current user.
|
|
2724
|
+
* - Current user may take over a human-task assigned to someone else.
|
|
2584
2725
|
*/
|
|
2585
|
-
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
|
|
2726
|
+
function shouldOfferWorkflowTaskContinuation(ctx) {
|
|
2727
|
+
const currentUserId = ctx.sessionService.user?.id;
|
|
2728
|
+
if (currentUserId == null) {
|
|
2729
|
+
return false;
|
|
2730
|
+
}
|
|
2731
|
+
const current = String(currentUserId);
|
|
2732
|
+
if (!isWorkflowTaskBoardBookmarkPayload(ctx.payload)) {
|
|
2733
|
+
return false;
|
|
2734
|
+
}
|
|
2735
|
+
if (isWorkflowPooledClaimablePayload(ctx.payload)) {
|
|
2736
|
+
return false;
|
|
2737
|
+
}
|
|
2738
|
+
const assigned = normalizeWorkflowTaskUserIds(ctx.payload.assignedUserIds);
|
|
2739
|
+
const assigneeId = assigned[0] ?? null;
|
|
2740
|
+
const initiatorId = getWorkflowInstanceInitiatorUserId(ctx.instance);
|
|
2741
|
+
const isPlatformRoot = isWorkflowPlatformRootUser(ctx.sessionService);
|
|
2742
|
+
const isInitiator = initiatorId != null && current === initiatorId;
|
|
2743
|
+
if (assigneeId && current === assigneeId) {
|
|
2744
|
+
return true;
|
|
2745
|
+
}
|
|
2746
|
+
if (initiatorId && assigneeId && initiatorId === assigneeId && current === initiatorId) {
|
|
2747
|
+
return true;
|
|
2748
|
+
}
|
|
2749
|
+
/** User who started the workflow may continue (take over when not assignee). */
|
|
2750
|
+
if (isInitiator && assigneeId) {
|
|
2751
|
+
return true;
|
|
2752
|
+
}
|
|
2753
|
+
/** Platform operators (e.g. super-root) may continue any pending human task. */
|
|
2754
|
+
if (isPlatformRoot) {
|
|
2755
|
+
return true;
|
|
2756
|
+
}
|
|
2757
|
+
if (!canReassignWorkflowTaskToSelf(ctx.sessionService) && !isPlatformRoot) {
|
|
2758
|
+
return false;
|
|
2759
|
+
}
|
|
2760
|
+
if (!isWorkflowReassignableHumanTaskPayload(ctx.payload)) {
|
|
2761
|
+
return false;
|
|
2762
|
+
}
|
|
2763
|
+
if (!assigneeId || current === assigneeId) {
|
|
2764
|
+
return false;
|
|
2765
|
+
}
|
|
2766
|
+
return true;
|
|
2767
|
+
}
|
|
2768
|
+
/** True when the user must take over the next bookmark before acting on it. */
|
|
2769
|
+
function needsTakeOverForWorkflowTaskContinuation(ctx) {
|
|
2770
|
+
const currentUserId = ctx.sessionService.user?.id;
|
|
2771
|
+
if (currentUserId == null) {
|
|
2772
|
+
return false;
|
|
2773
|
+
}
|
|
2774
|
+
const current = String(currentUserId);
|
|
2775
|
+
const assigned = normalizeWorkflowTaskUserIds(ctx.payload.assignedUserIds);
|
|
2776
|
+
const assigneeId = assigned[0] ?? null;
|
|
2777
|
+
if (!assigneeId || current === assigneeId) {
|
|
2778
|
+
return false;
|
|
2779
|
+
}
|
|
2780
|
+
const canTakeOver = canReassignWorkflowTaskToSelf(ctx.sessionService) || isWorkflowPlatformRootUser(ctx.sessionService);
|
|
2781
|
+
return canTakeOver && isWorkflowReassignableHumanTaskPayload(ctx.payload);
|
|
2782
|
+
}
|
|
2589
2783
|
//#endregion
|
|
2590
2784
|
|
|
2785
|
+
//#region ---- Imports ----
|
|
2786
|
+
//#endregion
|
|
2787
|
+
//#region ---- Access factory ----
|
|
2788
|
+
function getActiveBookmarks(instance) {
|
|
2789
|
+
const wi = instance.workflowInstance;
|
|
2790
|
+
const bookmarks = wi?.bookmarks ?? [];
|
|
2791
|
+
return bookmarks.filter((b) => !b.isConsumed && !b.consumed);
|
|
2792
|
+
}
|
|
2591
2793
|
/**
|
|
2592
|
-
*
|
|
2593
|
-
* When creating via useFactory: () => new AXPGenericWorkflowTaskProvider({ ... }), config is passed in the closure.
|
|
2794
|
+
* Loads workflow instance entity rows (including embedded bookmarks).
|
|
2594
2795
|
*/
|
|
2595
|
-
|
|
2796
|
+
function createWorkflowInstanceBookmarkAccess(entityService = inject(AXPEntityService)) {
|
|
2797
|
+
const workflowInstanceData = entityService
|
|
2798
|
+
.withEntity('WorkflowManagement', 'WorkflowInstance')
|
|
2799
|
+
.data();
|
|
2800
|
+
return {
|
|
2801
|
+
async loadInstance(instanceId) {
|
|
2802
|
+
let instance = await workflowInstanceData.byKey(instanceId);
|
|
2803
|
+
if (instance && getActiveBookmarks(instance).length > 0) {
|
|
2804
|
+
return instance;
|
|
2805
|
+
}
|
|
2806
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
2807
|
+
instance = await workflowInstanceData.byKey(instanceId);
|
|
2808
|
+
return instance ?? null;
|
|
2809
|
+
},
|
|
2810
|
+
getActiveBookmarks,
|
|
2811
|
+
async findActiveBookmark(instanceId, activityId) {
|
|
2812
|
+
const instance = await this.loadInstance(instanceId);
|
|
2813
|
+
if (!instance) {
|
|
2814
|
+
return null;
|
|
2815
|
+
}
|
|
2816
|
+
const bookmark = getActiveBookmarks(instance).find((b) => b.activityId === activityId);
|
|
2817
|
+
if (!bookmark) {
|
|
2818
|
+
return null;
|
|
2819
|
+
}
|
|
2820
|
+
return { instance, bookmark };
|
|
2821
|
+
},
|
|
2822
|
+
parseHumanTaskPayload(bookmark) {
|
|
2823
|
+
const parsed = parseWorkflowTaskContinuationPayload(bookmark);
|
|
2824
|
+
if (!parsed) {
|
|
2825
|
+
return null;
|
|
2826
|
+
}
|
|
2827
|
+
return parsed;
|
|
2828
|
+
},
|
|
2829
|
+
};
|
|
2830
|
+
}
|
|
2831
|
+
//#endregion
|
|
2832
|
+
|
|
2596
2833
|
//#endregion
|
|
2834
|
+
//#region ---- Service ----
|
|
2597
2835
|
/**
|
|
2598
|
-
*
|
|
2599
|
-
*
|
|
2600
|
-
* Provides tasks from Workflow Instances directly (no Work Items).
|
|
2601
|
-
* Queries suspended workflow instances and extracts tasks from bookmarks.
|
|
2602
|
-
*
|
|
2603
|
-
* Create one provider per workflow definition by passing a config (name, title, definitionId).
|
|
2604
|
-
* E.g. leave request: { name: 'leave-request-workflow-tasks', title: 'Leave Request', definitionId: 'LeaveRequestWorkflow' }
|
|
2605
|
-
*
|
|
2606
|
-
* Architecture:
|
|
2607
|
-
* - Queries Workflow Instances with subStatus = 'Suspended'
|
|
2608
|
-
* - Filters by config.definitionId when set (one provider = one definition)
|
|
2609
|
-
* - Extracts active bookmarks for each instance
|
|
2610
|
-
* - Parses bookmark payload for assignment/actions
|
|
2611
|
-
* - Maps to task board format
|
|
2836
|
+
* Workflow-instance human-task operations (resume, take-over, primary action).
|
|
2837
|
+
* Independent of task board / popover — task board delegates here for the same behavior.
|
|
2612
2838
|
*/
|
|
2613
|
-
class
|
|
2614
|
-
|
|
2615
|
-
//#region ---- Constructor ----
|
|
2616
|
-
constructor(config) {
|
|
2617
|
-
super();
|
|
2618
|
-
//#endregion
|
|
2839
|
+
class AXPWorkflowInstanceHumanTaskService {
|
|
2840
|
+
constructor() {
|
|
2619
2841
|
//#region ---- Services & Dependencies ----
|
|
2620
|
-
this.
|
|
2621
|
-
this.workflowInstanceData = this.entityService
|
|
2622
|
-
.withEntity('WorkflowManagement', 'WorkflowInstance')
|
|
2623
|
-
.data();
|
|
2624
|
-
this.userEntityData = this.entityService.withEntity('SecurityManagement', 'User').data();
|
|
2625
|
-
this.translationService = inject(AXTranslationService);
|
|
2626
|
-
this.workflowManager = inject(AXPWorkflowManager);
|
|
2842
|
+
this.injector = inject(Injector);
|
|
2627
2843
|
this.sessionService = inject(AXPSessionService);
|
|
2628
|
-
this.
|
|
2844
|
+
this.translationService = inject(AXTranslationService);
|
|
2629
2845
|
this.dialogService = inject(AXDialogService);
|
|
2630
|
-
this.
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
get name() {
|
|
2635
|
-
return this.config.name;
|
|
2636
|
-
}
|
|
2637
|
-
get title() {
|
|
2638
|
-
return this.config.title;
|
|
2846
|
+
this.toastService = inject(AXToastService);
|
|
2847
|
+
this.eventService = inject(AXPBroadcastEventService);
|
|
2848
|
+
this.workflowEngine = inject(AXP_WORKFLOW_ENGINE);
|
|
2849
|
+
this.bookmarkAccess = createWorkflowInstanceBookmarkAccess();
|
|
2639
2850
|
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2851
|
+
/** Resolved lazily to avoid DI cycle with {@link AXPWorkflowManager} continuation hook. */
|
|
2852
|
+
get workflowManager() {
|
|
2853
|
+
return this.injector.get(AXPWorkflowManager);
|
|
2642
2854
|
}
|
|
2643
2855
|
//#endregion
|
|
2644
|
-
//#region ----
|
|
2856
|
+
//#region ---- Resume & take-over ----
|
|
2645
2857
|
/**
|
|
2646
|
-
*
|
|
2858
|
+
* Resumes a suspended human-task bookmark (runs interactive activities via the workflow manager).
|
|
2647
2859
|
*/
|
|
2648
|
-
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2860
|
+
async resumeSuspendedHumanTask(request) {
|
|
2861
|
+
const userInput = {
|
|
2862
|
+
...(request.userInput ?? {}),
|
|
2863
|
+
bookmarkId: request.bookmarkId,
|
|
2864
|
+
};
|
|
2865
|
+
if (request.suppressContinuation) {
|
|
2866
|
+
userInput[AXP_WORKFLOW_SUPPRESS_CONTINUATION_INPUT_KEY] = true;
|
|
2867
|
+
}
|
|
2868
|
+
let result = await this.workflowManager.resume(request.instanceId, request.activityId, request.outcome, userInput, request.taskToken);
|
|
2869
|
+
if (result.success || result.errorCode !== AXP_WORKFLOW_ERROR_CODES.TASK_NOT_ASSIGNEE || !this.canTakeOver()) {
|
|
2870
|
+
if (!result.success) {
|
|
2871
|
+
await this.showResumeError(result);
|
|
2872
|
+
}
|
|
2873
|
+
return result;
|
|
2874
|
+
}
|
|
2875
|
+
const confirmed = await this.confirmReassignToSelf();
|
|
2876
|
+
if (!confirmed) {
|
|
2877
|
+
return { success: false, instanceId: request.instanceId, error: result.error, errorCode: result.errorCode };
|
|
2878
|
+
}
|
|
2879
|
+
const reassigned = await this.reassignBookmarkToCurrentUser(request.instanceId, request.bookmarkId, request.activityId);
|
|
2880
|
+
if (!reassigned) {
|
|
2881
|
+
return { success: false, instanceId: request.instanceId, error: result.error, errorCode: result.errorCode };
|
|
2882
|
+
}
|
|
2883
|
+
result = await this.workflowManager.resume(request.instanceId, request.activityId, request.outcome, userInput, request.taskToken);
|
|
2884
|
+
if (!result.success) {
|
|
2885
|
+
await this.showResumeError(result);
|
|
2886
|
+
}
|
|
2887
|
+
return result;
|
|
2651
2888
|
}
|
|
2652
2889
|
/**
|
|
2653
|
-
*
|
|
2890
|
+
* Runs the first configured action on a suspended human-task bookmark (e.g. start-fill → questionnaire).
|
|
2654
2891
|
*/
|
|
2655
|
-
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
return assigned.length === 0 && candidates.length > 0;
|
|
2892
|
+
async executePrimaryHumanTaskAction(instanceId, activityId, options) {
|
|
2893
|
+
const found = await this.bookmarkAccess.findActiveBookmark(instanceId, activityId);
|
|
2894
|
+
if (!found) {
|
|
2895
|
+
return null;
|
|
2660
2896
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2897
|
+
const payload = this.bookmarkAccess.parseHumanTaskPayload(found.bookmark);
|
|
2898
|
+
if (!payload?.taskToken) {
|
|
2899
|
+
return null;
|
|
2900
|
+
}
|
|
2901
|
+
const outcome = this.resolvePrimaryTaskOutcome(payload);
|
|
2902
|
+
const result = await this.resumeSuspendedHumanTask({
|
|
2903
|
+
instanceId,
|
|
2904
|
+
bookmarkId: found.bookmark.id,
|
|
2905
|
+
activityId,
|
|
2906
|
+
taskToken: payload.taskToken,
|
|
2907
|
+
outcome,
|
|
2908
|
+
suppressContinuation: options?.suppressContinuation ?? true,
|
|
2909
|
+
});
|
|
2910
|
+
if (options?.notifyViews !== false) {
|
|
2911
|
+
this.notifyWorkflowTaskViewsChanged();
|
|
2912
|
+
}
|
|
2913
|
+
return result;
|
|
2667
2914
|
}
|
|
2668
2915
|
/**
|
|
2669
|
-
*
|
|
2916
|
+
* Reassigns a human-task bookmark to the current user (take over) without advancing the workflow.
|
|
2670
2917
|
*/
|
|
2671
|
-
|
|
2672
|
-
|
|
2918
|
+
async reassignBookmarkToCurrentUser(instanceId, bookmarkId, activityId) {
|
|
2919
|
+
const reassign = this.workflowEngine.reassignTaskToSelf?.bind(this.workflowEngine);
|
|
2920
|
+
if (!reassign) {
|
|
2673
2921
|
return false;
|
|
2674
2922
|
}
|
|
2675
|
-
const
|
|
2676
|
-
if (
|
|
2923
|
+
const result = await reassign({ instanceId, bookmarkId, stepId: activityId });
|
|
2924
|
+
if (!result.success) {
|
|
2925
|
+
await this.showToast('@workflow-management:tasks.errors.reassign-failed');
|
|
2677
2926
|
return false;
|
|
2678
2927
|
}
|
|
2679
|
-
|
|
2680
|
-
|
|
2928
|
+
return true;
|
|
2929
|
+
}
|
|
2930
|
+
/**
|
|
2931
|
+
* Take over with confirmation dialog (task board "Take Over" action).
|
|
2932
|
+
*/
|
|
2933
|
+
async reassignBookmarkToCurrentUserWithConfirm(instanceId, bookmarkId, activityId) {
|
|
2934
|
+
if (!this.canTakeOver()) {
|
|
2935
|
+
await this.showToast('@workflow-management:tasks.errors.not-assignee');
|
|
2681
2936
|
return false;
|
|
2682
2937
|
}
|
|
2683
|
-
|
|
2938
|
+
const confirmed = await this.confirmReassignToSelf();
|
|
2939
|
+
if (!confirmed) {
|
|
2940
|
+
return true;
|
|
2941
|
+
}
|
|
2942
|
+
return this.reassignBookmarkToCurrentUser(instanceId, bookmarkId, activityId);
|
|
2684
2943
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2944
|
+
//#endregion
|
|
2945
|
+
//#region ---- Outcome resolution ----
|
|
2946
|
+
/** First action command name on the human-task bookmark (e.g. start-fill, approve). */
|
|
2947
|
+
resolvePrimaryTaskOutcome(payload) {
|
|
2948
|
+
const actions = [...(payload.actions?.prefix ?? []), ...(payload.actions?.suffix ?? [])];
|
|
2949
|
+
for (const action of actions) {
|
|
2950
|
+
const outcome = action.command?.name ?? action.name;
|
|
2951
|
+
if (outcome && outcome !== 'unknown') {
|
|
2952
|
+
return outcome;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
return 'submit';
|
|
2687
2956
|
}
|
|
2688
|
-
//#
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
this.toastService.show({ color, title, content });
|
|
2957
|
+
//#endregion
|
|
2958
|
+
//#region ---- UI helpers ----
|
|
2959
|
+
canTakeOver() {
|
|
2960
|
+
return canReassignWorkflowTaskToSelf(this.sessionService);
|
|
2693
2961
|
}
|
|
2694
2962
|
async confirmReassignToSelf() {
|
|
2695
2963
|
const title = await this.translationService.translateAsync('@workflow-management:tasks.dialogs.reassign-to-self.title');
|
|
@@ -2697,626 +2965,199 @@ class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
|
2697
2965
|
const dialogResult = await this.dialogService.confirm(title, message, 'warning', 'horizontal', false, 'cancel');
|
|
2698
2966
|
return dialogResult.result === true;
|
|
2699
2967
|
}
|
|
2700
|
-
async
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
}
|
|
2708
|
-
async resumeWorkflowTask(instanceId, bookmarkId, activityId, outcome, userInput, taskToken) {
|
|
2709
|
-
let result = await this.workflowManager.resume(instanceId, activityId, outcome, userInput, taskToken);
|
|
2710
|
-
if (result.success || result.errorCode !== AXP_WORKFLOW_ERROR_CODES.TASK_NOT_ASSIGNEE || !this.canTakeOverTask()) {
|
|
2711
|
-
if (!result.success) {
|
|
2712
|
-
const toastKey = result.errorCode === AXP_WORKFLOW_ERROR_CODES.TASK_NOT_ASSIGNEE
|
|
2713
|
-
? '@workflow-management:tasks.errors.not-assignee'
|
|
2714
|
-
: undefined;
|
|
2715
|
-
if (toastKey) {
|
|
2716
|
-
await this.showWorkflowTaskToast(toastKey);
|
|
2717
|
-
}
|
|
2718
|
-
else if (result.error) {
|
|
2719
|
-
this.toastService.show({
|
|
2720
|
-
color: 'danger',
|
|
2721
|
-
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
2722
|
-
content: result.error,
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
}
|
|
2726
|
-
return result;
|
|
2727
|
-
}
|
|
2728
|
-
const confirmed = await this.confirmReassignToSelf();
|
|
2729
|
-
if (!confirmed) {
|
|
2730
|
-
return { success: false, instanceId, error: result.error, errorCode: result.errorCode };
|
|
2731
|
-
}
|
|
2732
|
-
const reassigned = await this.reassignTaskToSelf(instanceId, bookmarkId, activityId);
|
|
2733
|
-
if (!reassigned) {
|
|
2734
|
-
return { success: false, instanceId, error: result.error, errorCode: result.errorCode };
|
|
2968
|
+
async showResumeError(result) {
|
|
2969
|
+
const toastKey = result.errorCode === AXP_WORKFLOW_ERROR_CODES.TASK_NOT_ASSIGNEE
|
|
2970
|
+
? '@workflow-management:tasks.errors.not-assignee'
|
|
2971
|
+
: undefined;
|
|
2972
|
+
if (toastKey) {
|
|
2973
|
+
await this.showToast(toastKey);
|
|
2974
|
+
return;
|
|
2735
2975
|
}
|
|
2736
|
-
result
|
|
2737
|
-
if (!result.success) {
|
|
2976
|
+
if (result.error) {
|
|
2738
2977
|
this.toastService.show({
|
|
2739
2978
|
color: 'danger',
|
|
2740
2979
|
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
2741
|
-
content: result.error
|
|
2980
|
+
content: result.error,
|
|
2742
2981
|
});
|
|
2743
2982
|
}
|
|
2744
|
-
|
|
2983
|
+
}
|
|
2984
|
+
async showToast(contentKey, color = 'danger') {
|
|
2985
|
+
const title = await this.translationService.translateAsync('@general:messages.generic.error.title');
|
|
2986
|
+
const content = await this.translationService.translateAsync(contentKey);
|
|
2987
|
+
this.toastService.show({ color, title, content });
|
|
2988
|
+
}
|
|
2989
|
+
/** Optional broadcast so open task-board views refresh; does not require task-board module at runtime. */
|
|
2990
|
+
notifyWorkflowTaskViewsChanged(provider) {
|
|
2991
|
+
this.eventService.publish('task-board.refresh', { provider });
|
|
2992
|
+
}
|
|
2993
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowInstanceHumanTaskService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2994
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowInstanceHumanTaskService, providedIn: 'root' }); }
|
|
2995
|
+
}
|
|
2996
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowInstanceHumanTaskService, decorators: [{
|
|
2997
|
+
type: Injectable,
|
|
2998
|
+
args: [{ providedIn: 'root' }]
|
|
2999
|
+
}] });
|
|
3000
|
+
|
|
3001
|
+
//#region ---- Imports ----
|
|
3002
|
+
//#endregion
|
|
3003
|
+
//#region ---- Service ----
|
|
3004
|
+
/**
|
|
3005
|
+
* Offers "continue to next step" / take-over after workflow start, resume, and interactive chains.
|
|
3006
|
+
* Uses {@link AXPWorkflowInstanceHumanTaskService} only — not task board or popover.
|
|
3007
|
+
*/
|
|
3008
|
+
class AXPWorkflowTaskContinuationService {
|
|
3009
|
+
constructor() {
|
|
3010
|
+
//#region ---- Services & Dependencies ----
|
|
3011
|
+
this.sessionService = inject(AXPSessionService);
|
|
3012
|
+
this.translationService = inject(AXTranslationService);
|
|
3013
|
+
this.dialogService = inject(AXDialogService);
|
|
3014
|
+
this.humanTaskService = inject(AXPWorkflowInstanceHumanTaskService);
|
|
3015
|
+
this.bookmarkAccess = createWorkflowInstanceBookmarkAccess();
|
|
2745
3016
|
}
|
|
2746
3017
|
//#endregion
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
* - definitionId (if provided in filter)
|
|
2754
|
-
* - assignee/claimant (if filter provided)
|
|
2755
|
-
*
|
|
2756
|
-
* Then extracts active bookmarks and maps to task board format.
|
|
2757
|
-
*/
|
|
2758
|
-
async getTasks(options) {
|
|
2759
|
-
// Build filter for workflow instances
|
|
2760
|
-
const baseFilter = { logic: 'and', filters: [] };
|
|
2761
|
-
// Filter by subStatus = 'Suspended' (only suspended workflows have pending tasks)
|
|
2762
|
-
baseFilter.filters?.push({
|
|
2763
|
-
field: 'subStatus',
|
|
2764
|
-
operator: { type: 'equal' },
|
|
2765
|
-
value: 'Suspended',
|
|
2766
|
-
});
|
|
2767
|
-
// Scope this provider to one workflow definition
|
|
2768
|
-
if (this.config.definitionId) {
|
|
2769
|
-
baseFilter.filters?.push({
|
|
2770
|
-
field: 'definitionId',
|
|
2771
|
-
operator: { type: 'equal' },
|
|
2772
|
-
value: this.config.definitionId,
|
|
2773
|
-
});
|
|
2774
|
-
}
|
|
2775
|
-
// Date range is applied after mapping bookmarks (task start/end overlap), not on instance createdAt.
|
|
2776
|
-
// Query workflow instances
|
|
2777
|
-
const { items: instances } = await this.workflowInstanceData.query({
|
|
2778
|
-
skip: options?.skip ?? 0,
|
|
2779
|
-
take: options?.take ?? 20,
|
|
2780
|
-
filter: baseFilter,
|
|
2781
|
-
});
|
|
2782
|
-
// Extract tasks from bookmarks for each instance
|
|
2783
|
-
const tasks = [];
|
|
2784
|
-
for (const instance of instances) {
|
|
2785
|
-
try {
|
|
2786
|
-
// Get active bookmarks from instance entity (workflowInstance.bookmarks)
|
|
2787
|
-
const bookmarks = this.getActiveBookmarksFromInstance(instance);
|
|
2788
|
-
// Process each bookmark (typically one per suspended activity)
|
|
2789
|
-
for (const bookmark of bookmarks) {
|
|
2790
|
-
try {
|
|
2791
|
-
// Get payload from entity (payload object or parse payloadJson)
|
|
2792
|
-
let payload;
|
|
2793
|
-
try {
|
|
2794
|
-
const raw = bookmark.payload ?? (bookmark.payloadJson ? JSON.parse(bookmark.payloadJson) : null);
|
|
2795
|
-
payload =
|
|
2796
|
-
typeof raw === 'object' && raw !== null
|
|
2797
|
-
? raw
|
|
2798
|
-
: {};
|
|
2799
|
-
}
|
|
2800
|
-
catch {
|
|
2801
|
-
continue;
|
|
2802
|
-
}
|
|
2803
|
-
// Task board: human-task and cartable (pooled); exclude other suspended UI bookmarks
|
|
2804
|
-
if (!this.isTaskBoardBookmarkPayload(payload)) {
|
|
2805
|
-
continue;
|
|
2806
|
-
}
|
|
2807
|
-
// Check assignment filter if provided
|
|
2808
|
-
if (options?.assigneeIds && options.assigneeIds.length > 0) {
|
|
2809
|
-
const assignedIds = payload.assignedUserIds || [];
|
|
2810
|
-
const candidateIds = payload.candidateUserIds || [];
|
|
2811
|
-
const allUserIds = [...assignedIds, ...candidateIds].map(String);
|
|
2812
|
-
// TODO: Also check candidateRoleIds by resolving role members
|
|
2813
|
-
if (!matchesTaskBoardAssigneeUserIdsFilter(allUserIds, options.assigneeIds)) {
|
|
2814
|
-
continue;
|
|
2815
|
-
}
|
|
2816
|
-
}
|
|
2817
|
-
// Map bookmark to task
|
|
2818
|
-
const task = await this.mapBookmarkToTask(instance, bookmark, payload);
|
|
2819
|
-
tasks.push(task);
|
|
2820
|
-
}
|
|
2821
|
-
catch {
|
|
2822
|
-
// Skip this bookmark and continue
|
|
2823
|
-
}
|
|
2824
|
-
}
|
|
3018
|
+
//#region ---- Hook ----
|
|
3019
|
+
async offerAfterWorkflowStep(context) {
|
|
3020
|
+
try {
|
|
3021
|
+
const instance = await this.bookmarkAccess.loadInstance(context.instanceId);
|
|
3022
|
+
if (!instance) {
|
|
3023
|
+
return;
|
|
2825
3024
|
}
|
|
2826
|
-
|
|
2827
|
-
|
|
3025
|
+
const activeActivityIds = this.bookmarkAccess
|
|
3026
|
+
.getActiveBookmarks(instance)
|
|
3027
|
+
.map((b) => b.activityId);
|
|
3028
|
+
if (!shouldOfferWorkflowContinuationAfterStep(context, activeActivityIds)) {
|
|
3029
|
+
return;
|
|
2828
3030
|
}
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
return { items, total };
|
|
2836
|
-
}
|
|
2837
|
-
/**
|
|
2838
|
-
* Get active (non-consumed) bookmarks from workflow instance entity.
|
|
2839
|
-
* Reads from instance.workflowInstance?.bookmarks — no direct DB access.
|
|
2840
|
-
*/
|
|
2841
|
-
getActiveBookmarksFromInstance(instance) {
|
|
2842
|
-
const wi = instance?.workflowInstance;
|
|
2843
|
-
const bookmarks = wi?.bookmarks ?? [];
|
|
2844
|
-
const active = bookmarks.filter((b) => !b.isConsumed && !b.consumed);
|
|
2845
|
-
return active;
|
|
2846
|
-
}
|
|
2847
|
-
/**
|
|
2848
|
-
* Task-board reporter: signed-in user who is viewing the app (workflow initiator context).
|
|
2849
|
-
*/
|
|
2850
|
-
buildReporterFromSession(instance) {
|
|
2851
|
-
const sessionUser = this.sessionService.user;
|
|
2852
|
-
if (sessionUser?.id) {
|
|
2853
|
-
const label = sessionUser.title?.trim() || sessionUser.name?.trim() || sessionUser.id;
|
|
2854
|
-
return { id: sessionUser.id, type: 'user', fullName: label };
|
|
2855
|
-
}
|
|
2856
|
-
const audit = instance.auditInfo?.created?.by;
|
|
2857
|
-
return {
|
|
2858
|
-
id: audit?.id ?? '',
|
|
2859
|
-
type: 'user',
|
|
2860
|
-
fullName: audit?.fullName ?? '',
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
/**
|
|
2864
|
-
* Builds interpolation params from `context._taskI18n` (workflow-defined; provider stays domain-agnostic).
|
|
2865
|
-
*/
|
|
2866
|
-
buildTaskTranslationParams(context) {
|
|
2867
|
-
const params = {};
|
|
2868
|
-
const i18n = context?.[AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY];
|
|
2869
|
-
if (!i18n || typeof i18n !== 'object' || Array.isArray(i18n)) {
|
|
2870
|
-
return params;
|
|
2871
|
-
}
|
|
2872
|
-
for (const [key, value] of Object.entries(i18n)) {
|
|
2873
|
-
const text = this.toTaskTranslationParam(value);
|
|
2874
|
-
if (text.length > 0) {
|
|
2875
|
-
params[key] = text;
|
|
3031
|
+
if (context.pendingNextTask &&
|
|
3032
|
+
axpIsWorkflowTaskBoardActivityType(context.pendingNextTask.activityType)) {
|
|
3033
|
+
const offered = await this.offerFromPendingNextTask(instance, context, context.pendingNextTask);
|
|
3034
|
+
if (offered) {
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
2876
3037
|
}
|
|
3038
|
+
await this.offerFromInstanceBookmarks(instance, context);
|
|
2877
3039
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
/**
|
|
2881
|
-
* Normalizes bookmark context values for i18n interpolation (strings or multi-language maps).
|
|
2882
|
-
*/
|
|
2883
|
-
toTaskTranslationParam(value) {
|
|
2884
|
-
if (value == null) {
|
|
2885
|
-
return '';
|
|
2886
|
-
}
|
|
2887
|
-
if (typeof value === 'string') {
|
|
2888
|
-
return value.trim();
|
|
2889
|
-
}
|
|
2890
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
2891
|
-
return String(value);
|
|
2892
|
-
}
|
|
2893
|
-
if (typeof value === 'object') {
|
|
2894
|
-
const resolved = this.translationService.resolve(value);
|
|
2895
|
-
return resolved?.trim() ?? '';
|
|
3040
|
+
catch (error) {
|
|
3041
|
+
console.warn('[AXPWorkflowTaskContinuationService] offerAfterWorkflowStep failed', error);
|
|
2896
3042
|
}
|
|
2897
|
-
return String(value).trim();
|
|
2898
3043
|
}
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
const
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
3044
|
+
//#endregion
|
|
3045
|
+
//#region ---- Offer paths ----
|
|
3046
|
+
async offerFromPendingNextTask(instance, context, nextTask) {
|
|
3047
|
+
const payload = this.buildPayloadFromWorkflowTask(nextTask);
|
|
3048
|
+
const bookmark = this.bookmarkAccess
|
|
3049
|
+
.getActiveBookmarks(instance)
|
|
3050
|
+
.find((b) => b.activityId === nextTask.activityId);
|
|
3051
|
+
const continuationCtx = {
|
|
3052
|
+
instance,
|
|
3053
|
+
bookmark: bookmark ?? { id: '', activityId: nextTask.activityId, type: 'user-task' },
|
|
3054
|
+
payload,
|
|
3055
|
+
sessionService: this.sessionService,
|
|
3056
|
+
completedBookmarkId: context.completedBookmarkId,
|
|
3057
|
+
completedActivityId: context.completedActivityId,
|
|
3058
|
+
};
|
|
3059
|
+
if (!shouldOfferWorkflowTaskContinuation(continuationCtx)) {
|
|
3060
|
+
return false;
|
|
2909
3061
|
}
|
|
2910
|
-
return this.
|
|
3062
|
+
return this.confirmAndAdvance(instance.id, context, continuationCtx, nextTask.activityId, bookmark?.id);
|
|
2911
3063
|
}
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
3064
|
+
async offerFromInstanceBookmarks(instance, context) {
|
|
3065
|
+
for (const bookmark of this.bookmarkAccess.getActiveBookmarks(instance)) {
|
|
3066
|
+
if (this.isCompletedBookmark(bookmark, context)) {
|
|
3067
|
+
continue;
|
|
3068
|
+
}
|
|
3069
|
+
const payload = parseWorkflowTaskContinuationPayload(bookmark);
|
|
3070
|
+
if (!payload) {
|
|
3071
|
+
continue;
|
|
3072
|
+
}
|
|
3073
|
+
const continuationCtx = {
|
|
3074
|
+
instance,
|
|
3075
|
+
bookmark,
|
|
3076
|
+
payload,
|
|
3077
|
+
sessionService: this.sessionService,
|
|
3078
|
+
completedBookmarkId: context.completedBookmarkId,
|
|
3079
|
+
completedActivityId: context.completedActivityId,
|
|
3080
|
+
};
|
|
3081
|
+
if (!shouldOfferWorkflowTaskContinuation(continuationCtx)) {
|
|
3082
|
+
continue;
|
|
3083
|
+
}
|
|
3084
|
+
await this.confirmAndAdvance(instance.id, context, continuationCtx, bookmark.activityId, bookmark.id);
|
|
3085
|
+
return;
|
|
2919
3086
|
}
|
|
2920
|
-
return parts
|
|
2921
|
-
.map((part) => this.toTaskTranslationParam(part))
|
|
2922
|
-
.filter((segment) => segment.length > 0)
|
|
2923
|
-
.join(' — ');
|
|
2924
|
-
}
|
|
2925
|
-
/**
|
|
2926
|
-
* True when bookmark title is the generic activity label, not workflow-specific copy.
|
|
2927
|
-
*/
|
|
2928
|
-
isGenericHumanTaskTitle(title) {
|
|
2929
|
-
const normalized = title?.trim().toLowerCase();
|
|
2930
|
-
return (!normalized ||
|
|
2931
|
-
normalized === 'human task' ||
|
|
2932
|
-
normalized === 'cartable task' ||
|
|
2933
|
-
normalized === 'workflow-activity:human-task' ||
|
|
2934
|
-
normalized === 'workflow-activity:cartable' ||
|
|
2935
|
-
normalized === 'workflow task');
|
|
2936
3087
|
}
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
if (!id) {
|
|
2943
|
-
return undefined;
|
|
3088
|
+
async confirmAndAdvance(instanceId, context, continuationCtx, nextActivityId, nextBookmarkId) {
|
|
3089
|
+
const takeOver = needsTakeOverForWorkflowTaskContinuation(continuationCtx);
|
|
3090
|
+
const confirmed = await this.confirmContinueToNextStep(takeOver);
|
|
3091
|
+
if (!confirmed) {
|
|
3092
|
+
return true;
|
|
2944
3093
|
}
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
if (!
|
|
2948
|
-
|
|
3094
|
+
if (takeOver) {
|
|
3095
|
+
let bookmarkIdToUse = nextBookmarkId;
|
|
3096
|
+
if (!bookmarkIdToUse) {
|
|
3097
|
+
const found = await this.bookmarkAccess.findActiveBookmark(instanceId, nextActivityId);
|
|
3098
|
+
bookmarkIdToUse = found?.bookmark.id;
|
|
2949
3099
|
}
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
const
|
|
2954
|
-
if (
|
|
2955
|
-
|
|
3100
|
+
if (!bookmarkIdToUse) {
|
|
3101
|
+
return true;
|
|
3102
|
+
}
|
|
3103
|
+
const reassigned = await this.humanTaskService.reassignBookmarkToCurrentUser(instanceId, bookmarkIdToUse, nextActivityId);
|
|
3104
|
+
if (!reassigned) {
|
|
3105
|
+
return true;
|
|
2956
3106
|
}
|
|
2957
|
-
return ref;
|
|
2958
|
-
}
|
|
2959
|
-
catch {
|
|
2960
|
-
return { id, type: 'user', fullName: id };
|
|
2961
3107
|
}
|
|
3108
|
+
await this.humanTaskService.executePrimaryHumanTaskAction(instanceId, nextActivityId, {
|
|
3109
|
+
suppressContinuation: true,
|
|
3110
|
+
notifyViews: true,
|
|
3111
|
+
});
|
|
3112
|
+
return true;
|
|
2962
3113
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
const dueDate = payload.dueDate
|
|
2969
|
-
? typeof payload.dueDate === 'string'
|
|
2970
|
-
? new Date(payload.dueDate)
|
|
2971
|
-
: payload.dueDate
|
|
2972
|
-
: instance.createdAt
|
|
2973
|
-
? new Date(instance.createdAt)
|
|
2974
|
-
: new Date();
|
|
2975
|
-
const startDate = instance.createdAt ? new Date(instance.createdAt) : dueDate;
|
|
2976
|
-
// If startDate and endDate are the same, set endDate to one day after startDate
|
|
2977
|
-
let endDate = dueDate;
|
|
2978
|
-
if (startDate.getTime() === dueDate.getTime()) {
|
|
2979
|
-
endDate = new Date(startDate);
|
|
2980
|
-
endDate.setDate(endDate.getDate() + 1);
|
|
3114
|
+
//#endregion
|
|
3115
|
+
//#region ---- Helpers ----
|
|
3116
|
+
isCompletedBookmark(bookmark, context) {
|
|
3117
|
+
if (context.completedBookmarkId && bookmark.id === context.completedBookmarkId) {
|
|
3118
|
+
return true;
|
|
2981
3119
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
const taskTranslationParams = this.buildTaskTranslationParams(payload.context);
|
|
2985
|
-
let titleSource = payload.title || payload.activityName || 'Workflow Task';
|
|
2986
|
-
if (this.isGenericHumanTaskTitle(titleSource)) {
|
|
2987
|
-
const composed = this.composeTaskTitleFromContext(payload.context);
|
|
2988
|
-
if (composed) {
|
|
2989
|
-
titleSource = composed;
|
|
2990
|
-
}
|
|
3120
|
+
if (context.completedActivityId && bookmark.activityId === context.completedActivityId) {
|
|
3121
|
+
return true;
|
|
2991
3122
|
}
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
const
|
|
3123
|
+
return false;
|
|
3124
|
+
}
|
|
3125
|
+
buildPayloadFromWorkflowTask(task) {
|
|
3126
|
+
const input = task.input ?? {};
|
|
3127
|
+
const assigned = input['assignedUserIds'];
|
|
3128
|
+
const assignedUserIds = Array.isArray(assigned)
|
|
3129
|
+
? assigned.map(String)
|
|
3130
|
+
: assigned != null && String(assigned).length > 0
|
|
3131
|
+
? [String(assigned)]
|
|
3132
|
+
: null;
|
|
2996
3133
|
return {
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
id: AXPSystemStatuses.Pending.name,
|
|
3007
|
-
title: this.translationService.translateSync('@workflow-management:tasks.states.pending'),
|
|
3008
|
-
},
|
|
3009
|
-
priority,
|
|
3010
|
-
reporter: this.buildReporterFromSession(instance),
|
|
3011
|
-
data: {
|
|
3012
|
-
// Store workflow instance and bookmark metadata
|
|
3013
|
-
instanceId: instance.id,
|
|
3014
|
-
bookmarkId: bookmark.id,
|
|
3015
|
-
activityId: bookmark.activityId,
|
|
3016
|
-
activityType: payload.activityType,
|
|
3017
|
-
activityName: payload.activityName,
|
|
3018
|
-
taskToken: payload.taskToken,
|
|
3019
|
-
entityRefId: instance.entityRefId,
|
|
3020
|
-
entityRefType: instance.entityRefType,
|
|
3021
|
-
definitionId: instance.definitionId,
|
|
3022
|
-
// Store full payload for actions
|
|
3023
|
-
payload: payload,
|
|
3024
|
-
},
|
|
3134
|
+
activityType: task.activityType,
|
|
3135
|
+
activityName: task.activityName ?? task.activityType,
|
|
3136
|
+
taskToken: task.taskToken,
|
|
3137
|
+
assignedUserIds,
|
|
3138
|
+
candidateUserIds: input['candidateUserIds'],
|
|
3139
|
+
title: input['title'],
|
|
3140
|
+
description: input['description'],
|
|
3141
|
+
actions: input['actions'],
|
|
3142
|
+
context: input['context'],
|
|
3025
3143
|
};
|
|
3026
3144
|
}
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
return 'high';
|
|
3036
|
-
case 'Normal':
|
|
3037
|
-
return 'medium';
|
|
3038
|
-
case 'Low':
|
|
3039
|
-
return 'low';
|
|
3040
|
-
default:
|
|
3041
|
-
return 'medium';
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
3044
|
-
//#endregion
|
|
3045
|
-
//#region ---- Task Updates ----
|
|
3046
|
-
/**
|
|
3047
|
-
* Update tasks (not applicable for workflow instances - they are read-only from task provider perspective).
|
|
3048
|
-
*/
|
|
3049
|
-
async updateTasks(tasksToUpdate) {
|
|
3050
|
-
// Workflow instances are managed by workflow engine
|
|
3051
|
-
// Task updates should be done through workflow resume, not direct updates
|
|
3052
|
-
return tasksToUpdate;
|
|
3053
|
-
}
|
|
3054
|
-
//#endregion
|
|
3055
|
-
//#region ---- Command Execution ----
|
|
3056
|
-
/**
|
|
3057
|
-
* Execute commands on tasks.
|
|
3058
|
-
*
|
|
3059
|
-
* Supports:
|
|
3060
|
-
* - Workflow resume commands (Complete, Cancel)
|
|
3061
|
-
*/
|
|
3062
|
-
async executeCommand(command) {
|
|
3063
|
-
if (!command?.name)
|
|
3064
|
-
return { success: true };
|
|
3065
|
-
const taskData = command.options?.['data'];
|
|
3066
|
-
const instanceId = taskData?.instanceId ?? command.options?.['instanceId'] ?? '';
|
|
3067
|
-
const bookmarkId = taskData?.bookmarkId ?? command.options?.['bookmarkId'] ?? '';
|
|
3068
|
-
const taskToken = taskData?.taskToken ?? command.options?.['taskToken'] ?? '';
|
|
3069
|
-
const activityId = taskData?.activityId ?? command.options?.['activityId'] ?? command.options?.['stepId'] ?? '';
|
|
3070
|
-
const outcome = command.options?.['outcome'] ?? 'Done';
|
|
3071
|
-
const userInput = command.options?.['userInput'] ?? {};
|
|
3072
|
-
if (command.name === 'WorkflowManagement.WorkflowInstance:ClaimTask') {
|
|
3073
|
-
if (!instanceId || !bookmarkId || !activityId) {
|
|
3074
|
-
return {
|
|
3075
|
-
success: false,
|
|
3076
|
-
message: {
|
|
3077
|
-
text: 'Missing required parameters: instanceId, bookmarkId, activityId',
|
|
3078
|
-
},
|
|
3079
|
-
};
|
|
3080
|
-
}
|
|
3081
|
-
const claimResult = await this.workflowManager.claimTask(instanceId, bookmarkId, activityId);
|
|
3082
|
-
if (!claimResult.success && claimResult.error) {
|
|
3083
|
-
this.toastService.show({
|
|
3084
|
-
color: 'danger',
|
|
3085
|
-
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
3086
|
-
content: claimResult.error,
|
|
3087
|
-
});
|
|
3088
|
-
}
|
|
3089
|
-
return {
|
|
3090
|
-
success: claimResult.success,
|
|
3091
|
-
message: claimResult.error
|
|
3092
|
-
? {
|
|
3093
|
-
text: claimResult.error,
|
|
3094
|
-
}
|
|
3095
|
-
: undefined,
|
|
3096
|
-
};
|
|
3097
|
-
}
|
|
3098
|
-
if (command.name === 'WorkflowManagement.WorkflowInstance:ReassignToSelf') {
|
|
3099
|
-
if (!instanceId || !bookmarkId || !activityId) {
|
|
3100
|
-
return {
|
|
3101
|
-
success: false,
|
|
3102
|
-
message: {
|
|
3103
|
-
text: 'Missing required parameters: instanceId, bookmarkId, activityId',
|
|
3104
|
-
},
|
|
3105
|
-
};
|
|
3106
|
-
}
|
|
3107
|
-
if (!this.canTakeOverTask()) {
|
|
3108
|
-
await this.showWorkflowTaskToast('@workflow-management:tasks.errors.not-assignee');
|
|
3109
|
-
return { success: false };
|
|
3110
|
-
}
|
|
3111
|
-
const confirmed = await this.confirmReassignToSelf();
|
|
3112
|
-
if (!confirmed) {
|
|
3113
|
-
return { success: true };
|
|
3114
|
-
}
|
|
3115
|
-
const reassigned = await this.reassignTaskToSelf(instanceId, bookmarkId, activityId);
|
|
3116
|
-
return { success: reassigned };
|
|
3117
|
-
}
|
|
3118
|
-
// Handle workflow resume commands
|
|
3119
|
-
if (command.name === 'WorkflowManagement.WorkflowInstance:Resume' || command.name === 'resume-workflow') {
|
|
3120
|
-
if (!instanceId || !taskToken || !activityId) {
|
|
3121
|
-
return {
|
|
3122
|
-
success: false,
|
|
3123
|
-
message: {
|
|
3124
|
-
text: 'Missing required parameters: instanceId, taskToken, activityId',
|
|
3125
|
-
},
|
|
3126
|
-
};
|
|
3127
|
-
}
|
|
3128
|
-
const result = await this.resumeWorkflowTask(instanceId, bookmarkId, activityId, outcome, userInput, taskToken);
|
|
3129
|
-
return {
|
|
3130
|
-
success: result.success,
|
|
3131
|
-
message: result.error
|
|
3132
|
-
? {
|
|
3133
|
-
text: result.error,
|
|
3134
|
-
}
|
|
3135
|
-
: undefined,
|
|
3136
|
-
};
|
|
3137
|
-
}
|
|
3138
|
-
return { success: true };
|
|
3139
|
-
}
|
|
3140
|
-
//#endregion
|
|
3141
|
-
//#region ---- Actions & Statuses ----
|
|
3142
|
-
/**
|
|
3143
|
-
* Get available actions for a task.
|
|
3144
|
-
*
|
|
3145
|
-
* Extracts actions from bookmark payload.
|
|
3146
|
-
*/
|
|
3147
|
-
async getActions(task) {
|
|
3148
|
-
const actions = [];
|
|
3149
|
-
if (!task)
|
|
3150
|
-
return actions;
|
|
3151
|
-
const taskData = task.data;
|
|
3152
|
-
const payload = taskData?.payload;
|
|
3153
|
-
if (!payload)
|
|
3154
|
-
return actions;
|
|
3155
|
-
if (this.isPooledClaimablePayload(payload)) {
|
|
3156
|
-
actions.push({
|
|
3157
|
-
name: 'claim',
|
|
3158
|
-
title: this.translationService.translateSync('@workflow-management:tasks.actions.claim.title'),
|
|
3159
|
-
icon: 'fa-light fa-hand',
|
|
3160
|
-
color: 'primary',
|
|
3161
|
-
priority: 'primary',
|
|
3162
|
-
command: {
|
|
3163
|
-
name: 'WorkflowManagement.WorkflowInstance:ClaimTask',
|
|
3164
|
-
options: {
|
|
3165
|
-
instanceId: taskData.instanceId,
|
|
3166
|
-
bookmarkId: taskData.bookmarkId,
|
|
3167
|
-
activityId: taskData.activityId,
|
|
3168
|
-
},
|
|
3169
|
-
},
|
|
3170
|
-
});
|
|
3171
|
-
return actions;
|
|
3172
|
-
}
|
|
3173
|
-
if (this.isReassignableHumanTaskPayload(payload) && this.canTakeOverTask()) {
|
|
3174
|
-
actions.push({
|
|
3175
|
-
name: 'reassign-to-self',
|
|
3176
|
-
title: this.translationService.translateSync('@workflow-management:tasks.actions.reassign-to-self.title'),
|
|
3177
|
-
icon: 'fa-light fa-user-check',
|
|
3178
|
-
color: 'warning',
|
|
3179
|
-
priority: 'secondary',
|
|
3180
|
-
command: {
|
|
3181
|
-
name: 'WorkflowManagement.WorkflowInstance:ReassignToSelf',
|
|
3182
|
-
options: {
|
|
3183
|
-
instanceId: taskData.instanceId,
|
|
3184
|
-
bookmarkId: taskData.bookmarkId,
|
|
3185
|
-
activityId: taskData.activityId,
|
|
3186
|
-
},
|
|
3187
|
-
},
|
|
3188
|
-
});
|
|
3189
|
-
}
|
|
3190
|
-
// Extract actions from payload
|
|
3191
|
-
if (payload.actions) {
|
|
3192
|
-
const allActions = [...(payload.actions.prefix || []), ...(payload.actions.suffix || [])];
|
|
3193
|
-
for (const action of allActions) {
|
|
3194
|
-
const actionName = action.command?.name || action.name || 'unknown';
|
|
3195
|
-
actions.push({
|
|
3196
|
-
name: actionName,
|
|
3197
|
-
title: action.title || actionName,
|
|
3198
|
-
icon: action.icon,
|
|
3199
|
-
color: action.color,
|
|
3200
|
-
priority: 'primary',
|
|
3201
|
-
command: {
|
|
3202
|
-
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
3203
|
-
options: {
|
|
3204
|
-
instanceId: taskData.instanceId,
|
|
3205
|
-
bookmarkId: taskData.bookmarkId,
|
|
3206
|
-
activityId: taskData.activityId,
|
|
3207
|
-
taskToken: taskData.taskToken,
|
|
3208
|
-
outcome: actionName,
|
|
3209
|
-
userInput: {},
|
|
3210
|
-
},
|
|
3211
|
-
},
|
|
3212
|
-
});
|
|
3213
|
-
}
|
|
3214
|
-
}
|
|
3215
|
-
else {
|
|
3216
|
-
// Default actions if none provided
|
|
3217
|
-
actions.push({
|
|
3218
|
-
name: 'submit',
|
|
3219
|
-
title: this.translationService.translateSync('@general:actions.submit.title'),
|
|
3220
|
-
icon: 'fa-light fa-check',
|
|
3221
|
-
color: 'primary',
|
|
3222
|
-
priority: 'primary',
|
|
3223
|
-
command: {
|
|
3224
|
-
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
3225
|
-
options: {
|
|
3226
|
-
instanceId: taskData.instanceId,
|
|
3227
|
-
bookmarkId: taskData.bookmarkId,
|
|
3228
|
-
activityId: taskData.activityId,
|
|
3229
|
-
taskToken: taskData.taskToken,
|
|
3230
|
-
outcome: 'submit',
|
|
3231
|
-
userInput: {},
|
|
3232
|
-
},
|
|
3233
|
-
},
|
|
3234
|
-
});
|
|
3235
|
-
actions.push({
|
|
3236
|
-
name: 'cancel',
|
|
3237
|
-
title: this.translationService.translateSync('@general:actions.cancel.title'),
|
|
3238
|
-
icon: 'fa-light fa-times',
|
|
3239
|
-
color: 'default',
|
|
3240
|
-
priority: 'secondary',
|
|
3241
|
-
command: {
|
|
3242
|
-
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
3243
|
-
options: {
|
|
3244
|
-
instanceId: taskData.instanceId,
|
|
3245
|
-
bookmarkId: taskData.bookmarkId,
|
|
3246
|
-
activityId: taskData.activityId,
|
|
3247
|
-
taskToken: taskData.taskToken,
|
|
3248
|
-
outcome: 'cancel',
|
|
3249
|
-
userInput: {},
|
|
3250
|
-
},
|
|
3251
|
-
},
|
|
3252
|
-
});
|
|
3253
|
-
}
|
|
3254
|
-
return actions;
|
|
3255
|
-
}
|
|
3256
|
-
async getExtraFields() {
|
|
3257
|
-
return [];
|
|
3258
|
-
}
|
|
3259
|
-
async getStatuses() {
|
|
3260
|
-
return [
|
|
3261
|
-
{
|
|
3262
|
-
index: 0,
|
|
3263
|
-
key: AXPSystemStatuses.Pending.name,
|
|
3264
|
-
title: await this.translationService.translateAsync('@workflow-management:tasks.states.pending'),
|
|
3265
|
-
color: 'warning',
|
|
3266
|
-
},
|
|
3267
|
-
];
|
|
3268
|
-
}
|
|
3269
|
-
//#endregion
|
|
3270
|
-
//#region ---- Component ----
|
|
3271
|
-
getComponent() {
|
|
3272
|
-
return () => import('./acorex-modules-workflow-management-workflow-task-popover.component-DMszilef.mjs').then((m) => m.AXMWorkflowTaskPopoverComponent);
|
|
3145
|
+
async confirmContinueToNextStep(requiresTakeOver) {
|
|
3146
|
+
const title = await this.translationService.translateAsync('@workflow-management:tasks.dialogs.continue-next-step.title');
|
|
3147
|
+
const messageKey = requiresTakeOver
|
|
3148
|
+
? '@workflow-management:tasks.dialogs.continue-next-step.take-over-message'
|
|
3149
|
+
: '@workflow-management:tasks.dialogs.continue-next-step.message';
|
|
3150
|
+
const message = await this.translationService.translateAsync(messageKey);
|
|
3151
|
+
const dialogResult = await this.dialogService.confirm(title, message, 'primary', 'horizontal', false, 'cancel');
|
|
3152
|
+
return dialogResult.result === true;
|
|
3273
3153
|
}
|
|
3274
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
3275
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
3276
|
-
}
|
|
3277
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider, decorators: [{
|
|
3278
|
-
type: Injectable
|
|
3279
|
-
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
3280
|
-
type: Inject,
|
|
3281
|
-
args: [AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG]
|
|
3282
|
-
}] }] });
|
|
3283
|
-
|
|
3284
|
-
function provideTaskWorkflow() {
|
|
3285
|
-
return makeEnvironmentProviders([
|
|
3286
|
-
{
|
|
3287
|
-
provide: AXP_WORKFLOW_TASK_PROVIDER,
|
|
3288
|
-
multi: true,
|
|
3289
|
-
useFactory: (injector) => {
|
|
3290
|
-
return (async () => {
|
|
3291
|
-
const workflowDefinitionService = injector.get(AXPWorkflowDefinitionService);
|
|
3292
|
-
const multiLanguageResolver = injector.get(AXTranslationService);
|
|
3293
|
-
const categories = await workflowDefinitionService.getCategories();
|
|
3294
|
-
const providers = [];
|
|
3295
|
-
const seenDefinitionNames = new Set();
|
|
3296
|
-
for (const category of categories) {
|
|
3297
|
-
const workflows = await workflowDefinitionService.getWorkflowsByCategoryId(category.id);
|
|
3298
|
-
for (const workflow of workflows) {
|
|
3299
|
-
const key = workflow.name;
|
|
3300
|
-
if (!key || seenDefinitionNames.has(key)) {
|
|
3301
|
-
continue;
|
|
3302
|
-
}
|
|
3303
|
-
seenDefinitionNames.add(key);
|
|
3304
|
-
const provider = runInInjectionContext(injector, () => new AXPGenericWorkflowTaskProvider({
|
|
3305
|
-
name: workflow.name,
|
|
3306
|
-
title: multiLanguageResolver.resolve(workflow.title) ?? workflow.name,
|
|
3307
|
-
icon: 'fa-light fa-diagram-project',
|
|
3308
|
-
definitionId: workflow.name,
|
|
3309
|
-
}));
|
|
3310
|
-
providers.push(provider);
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
return providers;
|
|
3314
|
-
})();
|
|
3315
|
-
},
|
|
3316
|
-
deps: [Injector],
|
|
3317
|
-
},
|
|
3318
|
-
]);
|
|
3154
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowTaskContinuationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3155
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowTaskContinuationService, providedIn: 'root' }); }
|
|
3319
3156
|
}
|
|
3157
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWorkflowTaskContinuationService, decorators: [{
|
|
3158
|
+
type: Injectable,
|
|
3159
|
+
args: [{ providedIn: 'root' }]
|
|
3160
|
+
}] });
|
|
3320
3161
|
|
|
3321
3162
|
class AXMWorkflowManagementModule {
|
|
3322
3163
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMWorkflowManagementModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
@@ -3380,8 +3221,8 @@ class AXMWorkflowManagementModule {
|
|
|
3380
3221
|
useClass: AXMWorkflowManagementModuleEntityProvider,
|
|
3381
3222
|
multi: true,
|
|
3382
3223
|
},
|
|
3383
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3384
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3224
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-DC_9M9dk.mjs').then((m) => m.AXMWorkflowInstanceActivityInstancesPageComponentProvider)),
|
|
3225
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-BhVZ1Bcw.mjs').then((m) => m.AXMWorkflowDefinitionActivitiesPageComponentProvider)),
|
|
3385
3226
|
// Automation condition builder: default automation.rules(refType, refId); modules override for their refTypes
|
|
3386
3227
|
{
|
|
3387
3228
|
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
@@ -3440,29 +3281,18 @@ class AXMWorkflowManagementModule {
|
|
|
3440
3281
|
CartableTaskActivity,
|
|
3441
3282
|
ShowToastActivity,
|
|
3442
3283
|
CollectSignatureActivity,
|
|
3443
|
-
//
|
|
3444
|
-
//
|
|
3445
|
-
//
|
|
3446
|
-
//
|
|
3447
|
-
//
|
|
3448
|
-
//
|
|
3449
|
-
//
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
// definitionId: 'createLeaveRequest',
|
|
3456
|
-
// } as AXPGenericWorkflowTaskProviderConfig,
|
|
3457
|
-
// },
|
|
3458
|
-
// {
|
|
3459
|
-
// provide: AXP_WORKFLOW_TASK_PROVIDER,
|
|
3460
|
-
// multi: true,
|
|
3461
|
-
// useFactory: (config: AXPGenericWorkflowTaskProviderConfig) =>
|
|
3462
|
-
// new AXPGenericWorkflowTaskProvider(config),
|
|
3463
|
-
// deps: [AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG],
|
|
3464
|
-
// },
|
|
3465
|
-
provideTaskWorkflow()
|
|
3284
|
+
// Per-workflow task provider (lazy-loaded). Import provideGenericWorkflowTaskProvider from provider.task.ts:
|
|
3285
|
+
// provideGenericWorkflowTaskProvider({
|
|
3286
|
+
// name: 'leave-request',
|
|
3287
|
+
// title: 'Leave Request Tasks',
|
|
3288
|
+
// icon: 'fa-light fa-diagram-project',
|
|
3289
|
+
// definitionId: 'createLeaveRequest',
|
|
3290
|
+
// }),
|
|
3291
|
+
provideTaskWorkflow(),
|
|
3292
|
+
{
|
|
3293
|
+
provide: AXP_WORKFLOW_CONTINUATION_HOOK,
|
|
3294
|
+
useExisting: AXPWorkflowTaskContinuationService,
|
|
3295
|
+
},
|
|
3466
3296
|
], imports: [AXMCalendarManagementModule,
|
|
3467
3297
|
AXPDomainModule,
|
|
3468
3298
|
AXMWorkflowPluginModule, // Register workflow plugin
|
|
@@ -3533,8 +3363,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3533
3363
|
useClass: AXMWorkflowManagementModuleEntityProvider,
|
|
3534
3364
|
multi: true,
|
|
3535
3365
|
},
|
|
3536
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3537
|
-
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-
|
|
3366
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-DC_9M9dk.mjs').then((m) => m.AXMWorkflowInstanceActivityInstancesPageComponentProvider)),
|
|
3367
|
+
provideLazyProvider(AXP_PAGE_COMPONENT_PROVIDER, () => import('./acorex-modules-workflow-management-index-BhVZ1Bcw.mjs').then((m) => m.AXMWorkflowDefinitionActivitiesPageComponentProvider)),
|
|
3538
3368
|
// Automation condition builder: default automation.rules(refType, refId); modules override for their refTypes
|
|
3539
3369
|
{
|
|
3540
3370
|
provide: AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER,
|
|
@@ -3593,29 +3423,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3593
3423
|
CartableTaskActivity,
|
|
3594
3424
|
ShowToastActivity,
|
|
3595
3425
|
CollectSignatureActivity,
|
|
3596
|
-
//
|
|
3597
|
-
//
|
|
3598
|
-
//
|
|
3599
|
-
//
|
|
3600
|
-
//
|
|
3601
|
-
//
|
|
3602
|
-
//
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
// definitionId: 'createLeaveRequest',
|
|
3609
|
-
// } as AXPGenericWorkflowTaskProviderConfig,
|
|
3610
|
-
// },
|
|
3611
|
-
// {
|
|
3612
|
-
// provide: AXP_WORKFLOW_TASK_PROVIDER,
|
|
3613
|
-
// multi: true,
|
|
3614
|
-
// useFactory: (config: AXPGenericWorkflowTaskProviderConfig) =>
|
|
3615
|
-
// new AXPGenericWorkflowTaskProvider(config),
|
|
3616
|
-
// deps: [AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG],
|
|
3617
|
-
// },
|
|
3618
|
-
provideTaskWorkflow()
|
|
3426
|
+
// Per-workflow task provider (lazy-loaded). Import provideGenericWorkflowTaskProvider from provider.task.ts:
|
|
3427
|
+
// provideGenericWorkflowTaskProvider({
|
|
3428
|
+
// name: 'leave-request',
|
|
3429
|
+
// title: 'Leave Request Tasks',
|
|
3430
|
+
// icon: 'fa-light fa-diagram-project',
|
|
3431
|
+
// definitionId: 'createLeaveRequest',
|
|
3432
|
+
// }),
|
|
3433
|
+
provideTaskWorkflow(),
|
|
3434
|
+
{
|
|
3435
|
+
provide: AXP_WORKFLOW_CONTINUATION_HOOK,
|
|
3436
|
+
useExisting: AXPWorkflowTaskContinuationService,
|
|
3437
|
+
},
|
|
3619
3438
|
],
|
|
3620
3439
|
}]
|
|
3621
3440
|
}] });
|
|
@@ -3626,9 +3445,1264 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3626
3445
|
// Entity exports - services and modules removed, handled by middleware
|
|
3627
3446
|
// Exporting entity types for use in feature components
|
|
3628
3447
|
|
|
3448
|
+
//#region ---- Workflow task context (reserved keys) ----
|
|
3449
|
+
/**
|
|
3450
|
+
* Human-task / cartable `context` payload: technical ids at the root; display metadata under reserved keys.
|
|
3451
|
+
*
|
|
3452
|
+
* @example
|
|
3453
|
+
* ```json
|
|
3454
|
+
* {
|
|
3455
|
+
* "leaveRequestId": "{{ variables.leaveRequestId }}",
|
|
3456
|
+
* "_taskI18n": {
|
|
3457
|
+
* "employeeDisplayName": "{{ variables.employeeDisplayName }}",
|
|
3458
|
+
* "leaveTypeTitle": "{{ variables.leaveTypeTitle }}"
|
|
3459
|
+
* },
|
|
3460
|
+
* "_taskTitleParts": [
|
|
3461
|
+
* "{{ variables.employeeDisplayName }}",
|
|
3462
|
+
* "{{ variables.leaveTypeTitle }}"
|
|
3463
|
+
* ]
|
|
3464
|
+
* }
|
|
3465
|
+
* ```
|
|
3466
|
+
*/
|
|
3467
|
+
/** Interpolation map for `@scope:key` task title and description strings. */
|
|
3468
|
+
const AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY = '_taskI18n';
|
|
3469
|
+
/** Ordered segments joined with " — " when bookmark title is still a generic activity label. */
|
|
3470
|
+
const AXP_WORKFLOW_TASK_CONTEXT_TITLE_PARTS_KEY = '_taskTitleParts';
|
|
3471
|
+
//#endregion
|
|
3472
|
+
|
|
3473
|
+
/**
|
|
3474
|
+
* Injection token for provider config. Use when registering via DI (provide token + useFactory with deps).
|
|
3475
|
+
* Prefer {@link provideGenericWorkflowTaskProvider} (dynamic import) instead of a static class import in NgModule providers.
|
|
3476
|
+
*/
|
|
3477
|
+
const AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG = new InjectionToken('AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG');
|
|
3478
|
+
//#endregion
|
|
3479
|
+
//#region ---- Provider Class ----
|
|
3480
|
+
/**
|
|
3481
|
+
* Generic Workflow Task Provider
|
|
3482
|
+
*
|
|
3483
|
+
* Provides tasks from Workflow Instances directly (no Work Items).
|
|
3484
|
+
* Queries suspended workflow instances and extracts tasks from bookmarks.
|
|
3485
|
+
*
|
|
3486
|
+
* Create one provider per workflow definition by passing a config (name, title, definitionId).
|
|
3487
|
+
* E.g. leave request: { name: 'leave-request-workflow-tasks', title: 'Leave Request', definitionId: 'LeaveRequestWorkflow' }
|
|
3488
|
+
*
|
|
3489
|
+
* Architecture:
|
|
3490
|
+
* - Queries Workflow Instances with subStatus = 'Suspended'
|
|
3491
|
+
* - Filters by config.definitionId when set (one provider = one definition)
|
|
3492
|
+
* - Extracts active bookmarks for each instance
|
|
3493
|
+
* - Parses bookmark payload for assignment/actions
|
|
3494
|
+
* - Maps to task board format
|
|
3495
|
+
*/
|
|
3496
|
+
class AXPGenericWorkflowTaskProvider extends AXPWorkflowTaskProvider {
|
|
3497
|
+
//#endregion
|
|
3498
|
+
//#region ---- Constructor ----
|
|
3499
|
+
constructor(config) {
|
|
3500
|
+
super();
|
|
3501
|
+
//#endregion
|
|
3502
|
+
//#region ---- Services & Dependencies ----
|
|
3503
|
+
this.entityService = inject(AXPEntityService);
|
|
3504
|
+
this.workflowInstanceData = this.entityService
|
|
3505
|
+
.withEntity('WorkflowManagement', 'WorkflowInstance')
|
|
3506
|
+
.data();
|
|
3507
|
+
this.userEntityData = this.entityService.withEntity('SecurityManagement', 'User').data();
|
|
3508
|
+
this.translationService = inject(AXTranslationService);
|
|
3509
|
+
this.formatService = inject(AXFormatService);
|
|
3510
|
+
this.settingsService = inject(AXPSettingsService);
|
|
3511
|
+
this.entityRegistry = inject(AXPEntityDefinitionRegistryService);
|
|
3512
|
+
this.statusDefinitionService = inject(AXPStatusDefinitionProviderService);
|
|
3513
|
+
this.workflowDefinitionService = inject(AXPWorkflowDefinitionService);
|
|
3514
|
+
this.workflowManager = inject(AXPWorkflowManager);
|
|
3515
|
+
this.humanTaskService = inject(AXPWorkflowInstanceHumanTaskService);
|
|
3516
|
+
this.sessionService = inject(AXPSessionService);
|
|
3517
|
+
this.toastService = inject(AXToastService);
|
|
3518
|
+
/** Per-provider caches (one instance per workflow definition). */
|
|
3519
|
+
this.entityDefinitionCache = new Map();
|
|
3520
|
+
this.statusDefinitionKeyByEntityType = new Map();
|
|
3521
|
+
this.statusProviderByDefinitionKey = new Map();
|
|
3522
|
+
this.userReferenceCache = new Map();
|
|
3523
|
+
this.entityStatusIdCache = new Map();
|
|
3524
|
+
this.config = config;
|
|
3525
|
+
}
|
|
3526
|
+
//#endregion
|
|
3527
|
+
//#region ---- Provider Metadata ----
|
|
3528
|
+
get name() {
|
|
3529
|
+
return this.config.name;
|
|
3530
|
+
}
|
|
3531
|
+
get title() {
|
|
3532
|
+
return this.config.title;
|
|
3533
|
+
}
|
|
3534
|
+
get icon() {
|
|
3535
|
+
return this.config.icon ?? 'fa-light fa-diagram-project';
|
|
3536
|
+
}
|
|
3537
|
+
//#endregion
|
|
3538
|
+
//#region ---- Bookmark Helpers ----
|
|
3539
|
+
/** Bookmark types shown on the workflow task board. */
|
|
3540
|
+
isTaskBoardBookmarkPayload(payload) {
|
|
3541
|
+
const t = payload.activityType;
|
|
3542
|
+
return t === 'workflow-activity:human-task' || t === 'workflow-activity:cartable';
|
|
3543
|
+
}
|
|
3544
|
+
/** Pooled task: must be claimed before submit/cancel (cartable, or human-task with candidates only). */
|
|
3545
|
+
isPooledClaimablePayload(payload) {
|
|
3546
|
+
const assigned = this.normalizeUserIdList(payload.assignedUserIds);
|
|
3547
|
+
const candidates = this.normalizeUserIdList(payload.candidateUserIds);
|
|
3548
|
+
if (payload.activityType === 'workflow-activity:cartable') {
|
|
3549
|
+
return assigned.length === 0 && candidates.length > 0;
|
|
3550
|
+
}
|
|
3551
|
+
return payload.activityType === 'workflow-activity:human-task' && assigned.length === 0 && candidates.length > 0;
|
|
3552
|
+
}
|
|
3553
|
+
normalizeUserIdList(value) {
|
|
3554
|
+
if (!value || value.length === 0)
|
|
3555
|
+
return [];
|
|
3556
|
+
return value.map((id) => String(id)).filter((s) => s.length > 0);
|
|
3557
|
+
}
|
|
3558
|
+
/** Human-task assigned to another user; current user may take over when permitted. */
|
|
3559
|
+
isReassignableHumanTaskPayload(payload) {
|
|
3560
|
+
if (payload.activityType !== 'workflow-activity:human-task') {
|
|
3561
|
+
return false;
|
|
3562
|
+
}
|
|
3563
|
+
const assigned = this.normalizeUserIdList(payload.assignedUserIds);
|
|
3564
|
+
if (assigned.length === 0) {
|
|
3565
|
+
return false;
|
|
3566
|
+
}
|
|
3567
|
+
const currentUserId = this.sessionService.user?.id;
|
|
3568
|
+
if (currentUserId == null) {
|
|
3569
|
+
return false;
|
|
3570
|
+
}
|
|
3571
|
+
return !assigned.includes(String(currentUserId));
|
|
3572
|
+
}
|
|
3573
|
+
canTakeOverTask() {
|
|
3574
|
+
return canReassignWorkflowTaskToSelf(this.sessionService);
|
|
3575
|
+
}
|
|
3576
|
+
//#endregion
|
|
3577
|
+
//#region ---- Task Retrieval ----
|
|
3578
|
+
/**
|
|
3579
|
+
* Get tasks from suspended workflow instances.
|
|
3580
|
+
*
|
|
3581
|
+
* Queries workflow instances filtered by:
|
|
3582
|
+
* - subStatus = 'Suspended'
|
|
3583
|
+
* - entityRefType (if provided in filter)
|
|
3584
|
+
* - definitionId (if provided in filter)
|
|
3585
|
+
* - assignee/claimant (if filter provided)
|
|
3586
|
+
*
|
|
3587
|
+
* Then extracts active bookmarks and maps to task board format.
|
|
3588
|
+
*/
|
|
3589
|
+
async getTasks(options) {
|
|
3590
|
+
// Build filter for workflow instances
|
|
3591
|
+
const baseFilter = { logic: 'and', filters: [] };
|
|
3592
|
+
// Filter by subStatus = 'Suspended' (only suspended workflows have pending tasks)
|
|
3593
|
+
baseFilter.filters?.push({
|
|
3594
|
+
field: 'subStatus',
|
|
3595
|
+
operator: { type: 'equal' },
|
|
3596
|
+
value: 'Suspended',
|
|
3597
|
+
});
|
|
3598
|
+
// Scope this provider to one workflow definition
|
|
3599
|
+
if (this.config.definitionId) {
|
|
3600
|
+
baseFilter.filters?.push({
|
|
3601
|
+
field: 'definitionId',
|
|
3602
|
+
operator: { type: 'equal' },
|
|
3603
|
+
value: this.config.definitionId,
|
|
3604
|
+
});
|
|
3605
|
+
}
|
|
3606
|
+
// Date range is applied after mapping bookmarks (task start/end overlap), not on instance createdAt.
|
|
3607
|
+
// Query workflow instances
|
|
3608
|
+
const { items: instances } = await this.workflowInstanceData.query({
|
|
3609
|
+
skip: options?.skip ?? 0,
|
|
3610
|
+
take: options?.take ?? 20,
|
|
3611
|
+
filter: baseFilter,
|
|
3612
|
+
});
|
|
3613
|
+
// Extract tasks from bookmarks for each instance
|
|
3614
|
+
const tasks = [];
|
|
3615
|
+
for (const instance of instances) {
|
|
3616
|
+
try {
|
|
3617
|
+
// Get active bookmarks from instance entity (workflowInstance.bookmarks)
|
|
3618
|
+
const bookmarks = this.getActiveBookmarksFromInstance(instance);
|
|
3619
|
+
// Process each bookmark (typically one per suspended activity)
|
|
3620
|
+
for (const bookmark of bookmarks) {
|
|
3621
|
+
try {
|
|
3622
|
+
// Get payload from entity (payload object or parse payloadJson)
|
|
3623
|
+
let payload;
|
|
3624
|
+
try {
|
|
3625
|
+
const raw = bookmark.payload ?? (bookmark.payloadJson ? JSON.parse(bookmark.payloadJson) : null);
|
|
3626
|
+
payload =
|
|
3627
|
+
typeof raw === 'object' && raw !== null
|
|
3628
|
+
? raw
|
|
3629
|
+
: {};
|
|
3630
|
+
}
|
|
3631
|
+
catch {
|
|
3632
|
+
continue;
|
|
3633
|
+
}
|
|
3634
|
+
// Task board: human-task and cartable (pooled); exclude other suspended UI bookmarks
|
|
3635
|
+
if (!this.isTaskBoardBookmarkPayload(payload)) {
|
|
3636
|
+
continue;
|
|
3637
|
+
}
|
|
3638
|
+
// Check assignment filter if provided
|
|
3639
|
+
if (options?.assigneeIds && options.assigneeIds.length > 0) {
|
|
3640
|
+
const assignedIds = payload.assignedUserIds || [];
|
|
3641
|
+
const candidateIds = payload.candidateUserIds || [];
|
|
3642
|
+
const allUserIds = [...assignedIds, ...candidateIds].map(String);
|
|
3643
|
+
// TODO: Also check candidateRoleIds by resolving role members
|
|
3644
|
+
if (!matchesTaskBoardAssigneeUserIdsFilter(allUserIds, options.assigneeIds)) {
|
|
3645
|
+
continue;
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
// Map bookmark to task
|
|
3649
|
+
const task = await this.mapBookmarkToTask(instance, bookmark, payload);
|
|
3650
|
+
tasks.push(task);
|
|
3651
|
+
}
|
|
3652
|
+
catch {
|
|
3653
|
+
// Skip this bookmark and continue
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
}
|
|
3657
|
+
catch {
|
|
3658
|
+
// Skip this instance and continue
|
|
3659
|
+
}
|
|
3660
|
+
}
|
|
3661
|
+
const rangeFiltered = filterTasksOverlappingRange(tasks, options?.range);
|
|
3662
|
+
const total = rangeFiltered.length;
|
|
3663
|
+
const skip = options?.skip ?? 0;
|
|
3664
|
+
const take = options?.take ?? total;
|
|
3665
|
+
const items = rangeFiltered.slice(skip, skip + take);
|
|
3666
|
+
return { items, total };
|
|
3667
|
+
}
|
|
3668
|
+
/**
|
|
3669
|
+
* Get active (non-consumed) bookmarks from workflow instance entity.
|
|
3670
|
+
* Reads from instance.workflowInstance?.bookmarks — no direct DB access.
|
|
3671
|
+
*/
|
|
3672
|
+
getActiveBookmarksFromInstance(instance) {
|
|
3673
|
+
const wi = instance?.workflowInstance;
|
|
3674
|
+
const bookmarks = wi?.bookmarks ?? [];
|
|
3675
|
+
const active = bookmarks.filter((b) => !b.isConsumed && !b.consumed);
|
|
3676
|
+
return active;
|
|
3677
|
+
}
|
|
3678
|
+
//#endregion
|
|
3679
|
+
//#region ---- Task Display & Translation ----
|
|
3680
|
+
/** Task-board "started by": workflow instance creator (initiator), not the current viewer. */
|
|
3681
|
+
buildStartedByFromInstance(instance) {
|
|
3682
|
+
const audit = instance.auditInfo?.created?.by;
|
|
3683
|
+
if (audit?.id) {
|
|
3684
|
+
return {
|
|
3685
|
+
id: audit.id,
|
|
3686
|
+
type: 'user',
|
|
3687
|
+
fullName: audit.fullName?.trim() || audit.id,
|
|
3688
|
+
};
|
|
3689
|
+
}
|
|
3690
|
+
const sessionUser = this.sessionService.user;
|
|
3691
|
+
if (sessionUser?.id) {
|
|
3692
|
+
const label = sessionUser.title?.trim() || sessionUser.name?.trim() || sessionUser.id;
|
|
3693
|
+
return { id: sessionUser.id, type: 'user', fullName: label };
|
|
3694
|
+
}
|
|
3695
|
+
return { id: '', type: 'user', fullName: '' };
|
|
3696
|
+
}
|
|
3697
|
+
/**
|
|
3698
|
+
* Builds interpolation params from `context._taskI18n` (workflow-defined; provider stays domain-agnostic).
|
|
3699
|
+
*/
|
|
3700
|
+
async buildTaskTranslationParams(context) {
|
|
3701
|
+
const params = {};
|
|
3702
|
+
const i18n = context?.[AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY];
|
|
3703
|
+
if (!i18n || typeof i18n !== 'object' || Array.isArray(i18n)) {
|
|
3704
|
+
return params;
|
|
3705
|
+
}
|
|
3706
|
+
for (const [key, value] of Object.entries(i18n)) {
|
|
3707
|
+
const text = await this.toTaskTranslationParam(value);
|
|
3708
|
+
if (text.length > 0) {
|
|
3709
|
+
params[key] = text;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
return params;
|
|
3713
|
+
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Normalizes bookmark context values for i18n interpolation (strings or multi-language maps).
|
|
3716
|
+
*/
|
|
3717
|
+
async toTaskTranslationParam(value) {
|
|
3718
|
+
if (value == null) {
|
|
3719
|
+
return '';
|
|
3720
|
+
}
|
|
3721
|
+
if (value instanceof Date) {
|
|
3722
|
+
return this.formatDateForTaskDisplay(value);
|
|
3723
|
+
}
|
|
3724
|
+
if (typeof value === 'string') {
|
|
3725
|
+
const trimmed = value.trim();
|
|
3726
|
+
if (!trimmed) {
|
|
3727
|
+
return '';
|
|
3728
|
+
}
|
|
3729
|
+
if (this.isDateLikeString(trimmed)) {
|
|
3730
|
+
return this.formatDateForTaskDisplay(trimmed);
|
|
3731
|
+
}
|
|
3732
|
+
return trimmed;
|
|
3733
|
+
}
|
|
3734
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
3735
|
+
return String(value);
|
|
3736
|
+
}
|
|
3737
|
+
if (typeof value === 'object') {
|
|
3738
|
+
const resolved = this.translationService.resolve(value);
|
|
3739
|
+
return resolved?.trim() ?? '';
|
|
3740
|
+
}
|
|
3741
|
+
return String(value).trim();
|
|
3742
|
+
}
|
|
3743
|
+
/**
|
|
3744
|
+
* Formats dates for task title/description interpolation using regional short-date settings.
|
|
3745
|
+
*/
|
|
3746
|
+
async formatDateForTaskDisplay(value) {
|
|
3747
|
+
const date = value instanceof Date
|
|
3748
|
+
? value
|
|
3749
|
+
: this.parseDateOnlyString(value) ?? this.parseIsoDateString(value);
|
|
3750
|
+
if (!date || Number.isNaN(date.getTime())) {
|
|
3751
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
3752
|
+
}
|
|
3753
|
+
const format = await this.settingsService.get(AXPRegionalSetting.ShortDate);
|
|
3754
|
+
const formatted = this.formatService.format(date, 'date', { format });
|
|
3755
|
+
return typeof formatted === 'string' ? formatted : String(formatted ?? '');
|
|
3756
|
+
}
|
|
3757
|
+
isDateLikeString(value) {
|
|
3758
|
+
return /^\d{4}-\d{2}-\d{2}(?:T|$)/.test(value.trim());
|
|
3759
|
+
}
|
|
3760
|
+
parseIsoDateString(value) {
|
|
3761
|
+
const parsed = new Date(value);
|
|
3762
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Resolves task copy: literal strings pass through; keys starting with `@` are translated with context params.
|
|
3766
|
+
*/
|
|
3767
|
+
async resolveTaskDisplayText(value, params) {
|
|
3768
|
+
const raw = value?.trim();
|
|
3769
|
+
if (!raw) {
|
|
3770
|
+
return '';
|
|
3771
|
+
}
|
|
3772
|
+
if (!raw.startsWith('@')) {
|
|
3773
|
+
return raw;
|
|
3774
|
+
}
|
|
3775
|
+
return this.translationService.translateAsync(raw, { params });
|
|
3776
|
+
}
|
|
3777
|
+
/**
|
|
3778
|
+
* Builds a display title from `context._taskTitleParts` when bookmark title is still generic.
|
|
3779
|
+
*/
|
|
3780
|
+
async composeTaskTitleFromContext(context) {
|
|
3781
|
+
const parts = context?.[AXP_WORKFLOW_TASK_CONTEXT_TITLE_PARTS_KEY];
|
|
3782
|
+
if (!Array.isArray(parts)) {
|
|
3783
|
+
return '';
|
|
3784
|
+
}
|
|
3785
|
+
const segments = [];
|
|
3786
|
+
for (const part of parts) {
|
|
3787
|
+
const segment = await this.toTaskTranslationParam(part);
|
|
3788
|
+
if (segment.length > 0) {
|
|
3789
|
+
segments.push(segment);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
return segments.join(' — ');
|
|
3793
|
+
}
|
|
3794
|
+
/**
|
|
3795
|
+
* True when bookmark title is the generic activity label, not workflow-specific copy.
|
|
3796
|
+
*/
|
|
3797
|
+
isGenericHumanTaskTitle(title) {
|
|
3798
|
+
const normalized = title?.trim().toLowerCase();
|
|
3799
|
+
return (!normalized ||
|
|
3800
|
+
normalized === 'human task' ||
|
|
3801
|
+
normalized === 'cartable task' ||
|
|
3802
|
+
normalized === 'workflow-activity:human-task' ||
|
|
3803
|
+
normalized === 'workflow-activity:cartable' ||
|
|
3804
|
+
normalized === 'workflow task');
|
|
3805
|
+
}
|
|
3806
|
+
//#endregion
|
|
3807
|
+
//#region ---- User Resolution ----
|
|
3808
|
+
/** Resolve platform user id to a display label for the task board grid. */
|
|
3809
|
+
async resolveAssigneeReference(userId) {
|
|
3810
|
+
const id = userId?.trim();
|
|
3811
|
+
if (!id) {
|
|
3812
|
+
return undefined;
|
|
3813
|
+
}
|
|
3814
|
+
const cached = this.userReferenceCache.get(id);
|
|
3815
|
+
if (cached) {
|
|
3816
|
+
return cached;
|
|
3817
|
+
}
|
|
3818
|
+
const load = this.loadUserReference(id);
|
|
3819
|
+
this.userReferenceCache.set(id, load);
|
|
3820
|
+
return load;
|
|
3821
|
+
}
|
|
3822
|
+
async loadUserReference(id) {
|
|
3823
|
+
try {
|
|
3824
|
+
const row = await this.userEntityData.byKey(id);
|
|
3825
|
+
if (!row || typeof row !== 'object') {
|
|
3826
|
+
return { id, type: 'user', fullName: id };
|
|
3827
|
+
}
|
|
3828
|
+
const displayName = 'displayName' in row ? String(row.displayName ?? '').trim() : '';
|
|
3829
|
+
const username = 'username' in row ? String(row.username ?? '').trim() : '';
|
|
3830
|
+
const fullName = displayName || username || id;
|
|
3831
|
+
const ref = { id, type: 'user', fullName };
|
|
3832
|
+
if (username) {
|
|
3833
|
+
ref.username = username;
|
|
3834
|
+
}
|
|
3835
|
+
return ref;
|
|
3836
|
+
}
|
|
3837
|
+
catch {
|
|
3838
|
+
return { id, type: 'user', fullName: id };
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
//#endregion
|
|
3842
|
+
//#region ---- Task Mapping ----
|
|
3843
|
+
/** Map bookmark to task board task format. */
|
|
3844
|
+
async mapBookmarkToTask(instance, bookmark, payload) {
|
|
3845
|
+
// Extract dates
|
|
3846
|
+
const dueDate = payload.dueDate
|
|
3847
|
+
? typeof payload.dueDate === 'string'
|
|
3848
|
+
? new Date(payload.dueDate)
|
|
3849
|
+
: payload.dueDate
|
|
3850
|
+
: instance.createdAt
|
|
3851
|
+
? new Date(instance.createdAt)
|
|
3852
|
+
: new Date();
|
|
3853
|
+
const startDate = instance.createdAt ? new Date(instance.createdAt) : dueDate;
|
|
3854
|
+
// If startDate and endDate are the same, set endDate to one day after startDate
|
|
3855
|
+
let endDate = dueDate;
|
|
3856
|
+
if (startDate.getTime() === dueDate.getTime()) {
|
|
3857
|
+
endDate = new Date(startDate);
|
|
3858
|
+
endDate.setDate(endDate.getDate() + 1);
|
|
3859
|
+
}
|
|
3860
|
+
// Map priority
|
|
3861
|
+
const priority = this.mapPriorityToTaskPriority(payload.priority || 'Normal');
|
|
3862
|
+
const taskTranslationParams = await this.buildTaskTranslationParams(payload.context);
|
|
3863
|
+
let titleSource = payload.title || payload.activityName || 'Workflow Task';
|
|
3864
|
+
if (this.isGenericHumanTaskTitle(titleSource)) {
|
|
3865
|
+
const composed = await this.composeTaskTitleFromContext(payload.context);
|
|
3866
|
+
if (composed) {
|
|
3867
|
+
titleSource = composed;
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
const title = await this.resolveTaskDisplayText(titleSource, taskTranslationParams);
|
|
3871
|
+
const description = await this.resolveTaskDisplayText(payload.description || `Action required: ${payload.activityName}`, taskTranslationParams);
|
|
3872
|
+
const assigneeId = payload.assignedUserIds?.[0];
|
|
3873
|
+
const assignee = await this.resolveAssigneeReference(assigneeId);
|
|
3874
|
+
const instanceCreatedAt = instance.createdAt ? new Date(instance.createdAt) : startDate;
|
|
3875
|
+
const resolvedStatus = await this.resolveTaskStatus(payload, instance);
|
|
3876
|
+
return {
|
|
3877
|
+
id: bookmark.id, // Use bookmark ID as task ID
|
|
3878
|
+
title,
|
|
3879
|
+
description,
|
|
3880
|
+
startDate,
|
|
3881
|
+
endDate,
|
|
3882
|
+
allDay: false,
|
|
3883
|
+
assignee,
|
|
3884
|
+
index: 2,
|
|
3885
|
+
createdAt: instanceCreatedAt,
|
|
3886
|
+
updatedAt: instance.updatedAt ? new Date(instance.updatedAt) : instanceCreatedAt,
|
|
3887
|
+
status: {
|
|
3888
|
+
id: resolvedStatus.id,
|
|
3889
|
+
title: resolvedStatus.title,
|
|
3890
|
+
},
|
|
3891
|
+
priority,
|
|
3892
|
+
reporter: this.buildStartedByFromInstance(instance),
|
|
3893
|
+
data: {
|
|
3894
|
+
// Store workflow instance and bookmark metadata
|
|
3895
|
+
instanceId: instance.id,
|
|
3896
|
+
bookmarkId: bookmark.id,
|
|
3897
|
+
activityId: bookmark.activityId,
|
|
3898
|
+
activityType: payload.activityType,
|
|
3899
|
+
activityName: payload.activityName,
|
|
3900
|
+
taskToken: payload.taskToken,
|
|
3901
|
+
entityRefId: instance.entityRefId,
|
|
3902
|
+
entityRefType: instance.entityRefType,
|
|
3903
|
+
definitionId: instance.definitionId,
|
|
3904
|
+
statusColor: resolvedStatus.color,
|
|
3905
|
+
entityStatusDefinition: resolvedStatus.definition,
|
|
3906
|
+
entityStatusDefinitionKey: resolvedStatus.definitionKey,
|
|
3907
|
+
// Store full payload for actions
|
|
3908
|
+
payload: payload,
|
|
3909
|
+
},
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3912
|
+
/** Map workflow bookmark priority to task board priority. */
|
|
3913
|
+
mapPriorityToTaskPriority(priority) {
|
|
3914
|
+
switch (priority) {
|
|
3915
|
+
case 'Urgent':
|
|
3916
|
+
return 'highest';
|
|
3917
|
+
case 'High':
|
|
3918
|
+
return 'high';
|
|
3919
|
+
case 'Normal':
|
|
3920
|
+
return 'medium';
|
|
3921
|
+
case 'Low':
|
|
3922
|
+
return 'low';
|
|
3923
|
+
default:
|
|
3924
|
+
return 'medium';
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
//#endregion
|
|
3928
|
+
//#region ---- Task Updates ----
|
|
3929
|
+
/**
|
|
3930
|
+
* Update tasks (not applicable for workflow instances - they are read-only from task provider perspective).
|
|
3931
|
+
*/
|
|
3932
|
+
async updateTasks(tasksToUpdate) {
|
|
3933
|
+
// Workflow instances are managed by workflow engine
|
|
3934
|
+
// Task updates should be done through workflow resume, not direct updates
|
|
3935
|
+
return tasksToUpdate;
|
|
3936
|
+
}
|
|
3937
|
+
//#endregion
|
|
3938
|
+
//#region ---- Command Execution ----
|
|
3939
|
+
/**
|
|
3940
|
+
* Execute commands on tasks.
|
|
3941
|
+
*
|
|
3942
|
+
* Supports:
|
|
3943
|
+
* - Workflow resume commands (Complete, Cancel)
|
|
3944
|
+
*/
|
|
3945
|
+
async executeCommand(command) {
|
|
3946
|
+
if (!command?.name)
|
|
3947
|
+
return { success: true };
|
|
3948
|
+
const taskData = command.options?.['data'];
|
|
3949
|
+
const instanceId = taskData?.instanceId ?? command.options?.['instanceId'] ?? '';
|
|
3950
|
+
const bookmarkId = taskData?.bookmarkId ?? command.options?.['bookmarkId'] ?? '';
|
|
3951
|
+
const taskToken = taskData?.taskToken ?? command.options?.['taskToken'] ?? '';
|
|
3952
|
+
const activityId = taskData?.activityId ?? command.options?.['activityId'] ?? command.options?.['stepId'] ?? '';
|
|
3953
|
+
const outcome = command.options?.['outcome'] ?? 'Done';
|
|
3954
|
+
const userInput = command.options?.['userInput'] ?? {};
|
|
3955
|
+
if (command.name === 'WorkflowManagement.WorkflowInstance:ClaimTask') {
|
|
3956
|
+
if (!instanceId || !bookmarkId || !activityId) {
|
|
3957
|
+
return {
|
|
3958
|
+
success: false,
|
|
3959
|
+
message: {
|
|
3960
|
+
text: 'Missing required parameters: instanceId, bookmarkId, activityId',
|
|
3961
|
+
},
|
|
3962
|
+
};
|
|
3963
|
+
}
|
|
3964
|
+
const claimResult = await this.workflowManager.claimTask(instanceId, bookmarkId, activityId);
|
|
3965
|
+
if (!claimResult.success && claimResult.error) {
|
|
3966
|
+
this.toastService.show({
|
|
3967
|
+
color: 'danger',
|
|
3968
|
+
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
3969
|
+
content: claimResult.error,
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
return {
|
|
3973
|
+
success: claimResult.success,
|
|
3974
|
+
message: claimResult.error
|
|
3975
|
+
? {
|
|
3976
|
+
text: claimResult.error,
|
|
3977
|
+
}
|
|
3978
|
+
: undefined,
|
|
3979
|
+
};
|
|
3980
|
+
}
|
|
3981
|
+
if (command.name === 'WorkflowManagement.WorkflowInstance:ReassignToSelf') {
|
|
3982
|
+
if (!instanceId || !bookmarkId || !activityId) {
|
|
3983
|
+
return {
|
|
3984
|
+
success: false,
|
|
3985
|
+
message: {
|
|
3986
|
+
text: 'Missing required parameters: instanceId, bookmarkId, activityId',
|
|
3987
|
+
},
|
|
3988
|
+
};
|
|
3989
|
+
}
|
|
3990
|
+
const reassigned = await this.humanTaskService.reassignBookmarkToCurrentUserWithConfirm(instanceId, bookmarkId, activityId);
|
|
3991
|
+
return { success: reassigned };
|
|
3992
|
+
}
|
|
3993
|
+
// Handle workflow resume commands
|
|
3994
|
+
if (command.name === 'WorkflowManagement.WorkflowInstance:Resume' || command.name === 'resume-workflow') {
|
|
3995
|
+
if (!instanceId || !taskToken || !activityId) {
|
|
3996
|
+
return {
|
|
3997
|
+
success: false,
|
|
3998
|
+
message: {
|
|
3999
|
+
text: 'Missing required parameters: instanceId, taskToken, activityId',
|
|
4000
|
+
},
|
|
4001
|
+
};
|
|
4002
|
+
}
|
|
4003
|
+
const result = await this.humanTaskService.resumeSuspendedHumanTask({
|
|
4004
|
+
instanceId,
|
|
4005
|
+
bookmarkId,
|
|
4006
|
+
activityId,
|
|
4007
|
+
taskToken,
|
|
4008
|
+
outcome,
|
|
4009
|
+
userInput: { ...userInput, bookmarkId },
|
|
4010
|
+
});
|
|
4011
|
+
if (result.success) {
|
|
4012
|
+
this.refresh();
|
|
4013
|
+
}
|
|
4014
|
+
return {
|
|
4015
|
+
success: result.success,
|
|
4016
|
+
message: result.error
|
|
4017
|
+
? {
|
|
4018
|
+
text: result.error,
|
|
4019
|
+
}
|
|
4020
|
+
: undefined,
|
|
4021
|
+
};
|
|
4022
|
+
}
|
|
4023
|
+
return { success: true };
|
|
4024
|
+
}
|
|
4025
|
+
//#endregion
|
|
4026
|
+
//#region ---- Actions & Statuses ----
|
|
4027
|
+
/**
|
|
4028
|
+
* Get available actions for a task.
|
|
4029
|
+
*
|
|
4030
|
+
* Extracts actions from bookmark payload.
|
|
4031
|
+
*/
|
|
4032
|
+
async getActions(task) {
|
|
4033
|
+
const actions = [];
|
|
4034
|
+
if (!task)
|
|
4035
|
+
return actions;
|
|
4036
|
+
const taskData = task.data;
|
|
4037
|
+
const payload = taskData?.payload;
|
|
4038
|
+
if (!payload)
|
|
4039
|
+
return actions;
|
|
4040
|
+
if (this.isPooledClaimablePayload(payload)) {
|
|
4041
|
+
actions.push({
|
|
4042
|
+
name: 'claim',
|
|
4043
|
+
title: this.translationService.translateSync('@workflow-management:tasks.actions.claim.title'),
|
|
4044
|
+
icon: 'fa-light fa-hand',
|
|
4045
|
+
color: 'primary',
|
|
4046
|
+
priority: 'primary',
|
|
4047
|
+
command: {
|
|
4048
|
+
name: 'WorkflowManagement.WorkflowInstance:ClaimTask',
|
|
4049
|
+
options: {
|
|
4050
|
+
instanceId: taskData.instanceId,
|
|
4051
|
+
bookmarkId: taskData.bookmarkId,
|
|
4052
|
+
activityId: taskData.activityId,
|
|
4053
|
+
},
|
|
4054
|
+
},
|
|
4055
|
+
});
|
|
4056
|
+
return actions;
|
|
4057
|
+
}
|
|
4058
|
+
// if (this.isReassignableHumanTaskPayload(payload) && this.canTakeOverTask()) {
|
|
4059
|
+
// actions.push({
|
|
4060
|
+
// name: 'reassign-to-self',
|
|
4061
|
+
// title: this.translationService.translateSync('@workflow-management:tasks.actions.reassign-to-self.title'),
|
|
4062
|
+
// icon: 'fa-light fa-user-check',
|
|
4063
|
+
// color: 'warning',
|
|
4064
|
+
// priority: 'secondary',
|
|
4065
|
+
// command: {
|
|
4066
|
+
// name: 'WorkflowManagement.WorkflowInstance:ReassignToSelf',
|
|
4067
|
+
// options: {
|
|
4068
|
+
// instanceId: taskData.instanceId,
|
|
4069
|
+
// bookmarkId: taskData.bookmarkId,
|
|
4070
|
+
// activityId: taskData.activityId,
|
|
4071
|
+
// },
|
|
4072
|
+
// },
|
|
4073
|
+
// });
|
|
4074
|
+
// }
|
|
4075
|
+
// Extract actions from payload
|
|
4076
|
+
if (payload.actions) {
|
|
4077
|
+
const allActions = [...(payload.actions.prefix || []), ...(payload.actions.suffix || [])];
|
|
4078
|
+
for (const action of allActions) {
|
|
4079
|
+
const actionName = action.command?.name || action.name || 'unknown';
|
|
4080
|
+
actions.push({
|
|
4081
|
+
name: actionName,
|
|
4082
|
+
title: action.title || actionName,
|
|
4083
|
+
icon: action.icon,
|
|
4084
|
+
color: action.color,
|
|
4085
|
+
priority: 'primary',
|
|
4086
|
+
command: {
|
|
4087
|
+
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
4088
|
+
options: {
|
|
4089
|
+
instanceId: taskData.instanceId,
|
|
4090
|
+
bookmarkId: taskData.bookmarkId,
|
|
4091
|
+
activityId: taskData.activityId,
|
|
4092
|
+
taskToken: taskData.taskToken,
|
|
4093
|
+
outcome: actionName,
|
|
4094
|
+
userInput: {},
|
|
4095
|
+
},
|
|
4096
|
+
},
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
else {
|
|
4101
|
+
// Default actions if none provided
|
|
4102
|
+
actions.push({
|
|
4103
|
+
name: 'submit',
|
|
4104
|
+
title: this.translationService.translateSync('@general:actions.submit.title'),
|
|
4105
|
+
icon: 'fa-light fa-check',
|
|
4106
|
+
color: 'primary',
|
|
4107
|
+
priority: 'primary',
|
|
4108
|
+
command: {
|
|
4109
|
+
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
4110
|
+
options: {
|
|
4111
|
+
instanceId: taskData.instanceId,
|
|
4112
|
+
bookmarkId: taskData.bookmarkId,
|
|
4113
|
+
activityId: taskData.activityId,
|
|
4114
|
+
taskToken: taskData.taskToken,
|
|
4115
|
+
outcome: 'submit',
|
|
4116
|
+
userInput: {},
|
|
4117
|
+
},
|
|
4118
|
+
},
|
|
4119
|
+
});
|
|
4120
|
+
actions.push({
|
|
4121
|
+
name: 'cancel',
|
|
4122
|
+
title: this.translationService.translateSync('@general:actions.cancel.title'),
|
|
4123
|
+
icon: 'fa-light fa-times',
|
|
4124
|
+
color: 'default',
|
|
4125
|
+
priority: 'secondary',
|
|
4126
|
+
command: {
|
|
4127
|
+
name: 'WorkflowManagement.WorkflowInstance:Resume',
|
|
4128
|
+
options: {
|
|
4129
|
+
instanceId: taskData.instanceId,
|
|
4130
|
+
bookmarkId: taskData.bookmarkId,
|
|
4131
|
+
activityId: taskData.activityId,
|
|
4132
|
+
taskToken: taskData.taskToken,
|
|
4133
|
+
outcome: 'cancel',
|
|
4134
|
+
userInput: {},
|
|
4135
|
+
},
|
|
4136
|
+
},
|
|
4137
|
+
});
|
|
4138
|
+
}
|
|
4139
|
+
return actions;
|
|
4140
|
+
}
|
|
4141
|
+
async getExtraFields() {
|
|
4142
|
+
return [];
|
|
4143
|
+
}
|
|
4144
|
+
async getStatuses() {
|
|
4145
|
+
const entityType = await this.resolvePrimaryEntityTypeForProvider();
|
|
4146
|
+
if (entityType) {
|
|
4147
|
+
const provider = await this.getEntityStatusProvider(entityType);
|
|
4148
|
+
if (provider) {
|
|
4149
|
+
return this.mapStatusProviderToTaskStatuses(provider);
|
|
4150
|
+
}
|
|
4151
|
+
}
|
|
4152
|
+
return this.getWorkflowTaskStatuses();
|
|
4153
|
+
}
|
|
4154
|
+
//#endregion
|
|
4155
|
+
//#region ---- Entity Status Resolution ----
|
|
4156
|
+
/** Resolves task status from bookmark context `statusId` and entity status plugin; falls back to workflow Pending. */
|
|
4157
|
+
async resolveTaskStatus(payload, instance) {
|
|
4158
|
+
const context = payload.context;
|
|
4159
|
+
const entityType = await this.resolveEntityTypeForTask(context, instance);
|
|
4160
|
+
if (!entityType) {
|
|
4161
|
+
return this.getWorkflowPendingStatus();
|
|
4162
|
+
}
|
|
4163
|
+
const definitionKey = this.getStatusDefinitionKeyForEntityType(entityType);
|
|
4164
|
+
if (!definitionKey) {
|
|
4165
|
+
return this.getWorkflowPendingStatus();
|
|
4166
|
+
}
|
|
4167
|
+
const provider = await this.getStatusProviderByDefinitionKey(definitionKey);
|
|
4168
|
+
if (!provider) {
|
|
4169
|
+
return this.getWorkflowPendingStatus();
|
|
4170
|
+
}
|
|
4171
|
+
let statusId = this.readContextString(context, 'statusId');
|
|
4172
|
+
if (this.isUnresolvedWorkflowExpression(statusId)) {
|
|
4173
|
+
statusId = null;
|
|
4174
|
+
}
|
|
4175
|
+
if (!statusId) {
|
|
4176
|
+
statusId = await this.loadEntityStatusIdFromContext(context, entityType, instance);
|
|
4177
|
+
}
|
|
4178
|
+
if (!statusId) {
|
|
4179
|
+
statusId = await this.loadEntityStatusIdByWorkflowInstance(entityType, instance.id);
|
|
4180
|
+
}
|
|
4181
|
+
if (!statusId) {
|
|
4182
|
+
const defaultStatus = provider.defaultStatus;
|
|
4183
|
+
statusId = typeof defaultStatus === 'string' && defaultStatus.trim().length > 0 ? defaultStatus.trim() : null;
|
|
4184
|
+
}
|
|
4185
|
+
if (statusId) {
|
|
4186
|
+
const definition = provider.statuses.find((status) => status.name === statusId);
|
|
4187
|
+
if (definition) {
|
|
4188
|
+
return {
|
|
4189
|
+
id: definition.name,
|
|
4190
|
+
title: await this.translateStatusTitle(definition.title),
|
|
4191
|
+
color: definition.color,
|
|
4192
|
+
definition,
|
|
4193
|
+
definitionKey,
|
|
4194
|
+
};
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
return this.getWorkflowPendingStatus();
|
|
4198
|
+
}
|
|
4199
|
+
async resolveEntityTypeForTask(context, instance) {
|
|
4200
|
+
const fromContext = this.readContextString(context, 'entityType');
|
|
4201
|
+
if (fromContext && (await this.entityTypeHasStatusPlugin(fromContext))) {
|
|
4202
|
+
return fromContext;
|
|
4203
|
+
}
|
|
4204
|
+
const refType = instance.entityRefType?.trim();
|
|
4205
|
+
if (refType && (await this.entityTypeHasStatusPlugin(refType))) {
|
|
4206
|
+
return refType;
|
|
4207
|
+
}
|
|
4208
|
+
return this.resolvePrimaryEntityTypeForProvider();
|
|
4209
|
+
}
|
|
4210
|
+
/** True when a bookmark value was stored without expression evaluation. */
|
|
4211
|
+
isUnresolvedWorkflowExpression(value) {
|
|
4212
|
+
if (!value) {
|
|
4213
|
+
return false;
|
|
4214
|
+
}
|
|
4215
|
+
return /\{\{/.test(value) || /\}\}/.test(value);
|
|
4216
|
+
}
|
|
4217
|
+
/**
|
|
4218
|
+
* Loads `statusId` from the linked entity row when bookmark context omits or stale-fails it.
|
|
4219
|
+
*/
|
|
4220
|
+
async loadEntityStatusIdFromContext(context, entityType, instance) {
|
|
4221
|
+
const contextEntityId = this.readContextString(context, 'lifecycleEventId');
|
|
4222
|
+
const refType = instance.entityRefType?.trim();
|
|
4223
|
+
const refId = instance.entityRefId?.trim();
|
|
4224
|
+
const entityId = contextEntityId ??
|
|
4225
|
+
(refType === entityType && refId ? refId : null);
|
|
4226
|
+
if (!entityId) {
|
|
4227
|
+
return null;
|
|
4228
|
+
}
|
|
4229
|
+
return this.loadEntityStatusId(entityType, entityId);
|
|
4230
|
+
}
|
|
4231
|
+
/**
|
|
4232
|
+
* Finds a row of `entityType` linked to this workflow instance (e.g. LifecycleEvent.workflowInfo.instanceId).
|
|
4233
|
+
*/
|
|
4234
|
+
async loadEntityStatusIdByWorkflowInstance(entityType, workflowInstanceId) {
|
|
4235
|
+
const instanceId = workflowInstanceId?.trim();
|
|
4236
|
+
if (!instanceId) {
|
|
4237
|
+
return null;
|
|
4238
|
+
}
|
|
4239
|
+
const cacheKey = `${entityType}::wi::${instanceId}`;
|
|
4240
|
+
const cached = this.entityStatusIdCache.get(cacheKey);
|
|
4241
|
+
if (cached) {
|
|
4242
|
+
return cached;
|
|
4243
|
+
}
|
|
4244
|
+
const load = this.fetchEntityStatusIdByWorkflowInstance(entityType, instanceId);
|
|
4245
|
+
this.entityStatusIdCache.set(cacheKey, load);
|
|
4246
|
+
return load;
|
|
4247
|
+
}
|
|
4248
|
+
async fetchEntityStatusIdByWorkflowInstance(entityType, instanceId) {
|
|
4249
|
+
const parsed = this.parseQualifiedEntityType(entityType);
|
|
4250
|
+
if (!parsed) {
|
|
4251
|
+
return null;
|
|
4252
|
+
}
|
|
4253
|
+
try {
|
|
4254
|
+
const { items } = await this.entityService.withEntity(parsed.module, parsed.name).data().query({
|
|
4255
|
+
skip: 0,
|
|
4256
|
+
take: 1,
|
|
4257
|
+
filter: {
|
|
4258
|
+
logic: 'and',
|
|
4259
|
+
filters: [
|
|
4260
|
+
{ field: AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD, operator: { type: 'equal' }, value: instanceId },
|
|
4261
|
+
],
|
|
4262
|
+
},
|
|
4263
|
+
});
|
|
4264
|
+
const row = items?.[0];
|
|
4265
|
+
if (!row || typeof row !== 'object') {
|
|
4266
|
+
return null;
|
|
4267
|
+
}
|
|
4268
|
+
const raw = row.statusId;
|
|
4269
|
+
if (raw == null) {
|
|
4270
|
+
return null;
|
|
4271
|
+
}
|
|
4272
|
+
const text = String(raw).trim();
|
|
4273
|
+
return text.length > 0 ? text : null;
|
|
4274
|
+
}
|
|
4275
|
+
catch {
|
|
4276
|
+
return null;
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
async loadEntityStatusId(entityType, entityId) {
|
|
4280
|
+
const cacheKey = `${entityType}::${entityId}`;
|
|
4281
|
+
const cached = this.entityStatusIdCache.get(cacheKey);
|
|
4282
|
+
if (cached) {
|
|
4283
|
+
return cached;
|
|
4284
|
+
}
|
|
4285
|
+
const load = this.fetchEntityStatusIdByKey(entityType, entityId);
|
|
4286
|
+
this.entityStatusIdCache.set(cacheKey, load);
|
|
4287
|
+
return load;
|
|
4288
|
+
}
|
|
4289
|
+
async fetchEntityStatusIdByKey(entityType, entityId) {
|
|
4290
|
+
const parsed = this.parseQualifiedEntityType(entityType);
|
|
4291
|
+
if (!parsed) {
|
|
4292
|
+
return null;
|
|
4293
|
+
}
|
|
4294
|
+
try {
|
|
4295
|
+
const row = await this.entityService.withEntity(parsed.module, parsed.name).data().byKey(entityId);
|
|
4296
|
+
if (!row || typeof row !== 'object') {
|
|
4297
|
+
return null;
|
|
4298
|
+
}
|
|
4299
|
+
const raw = row.statusId;
|
|
4300
|
+
if (raw == null) {
|
|
4301
|
+
return null;
|
|
4302
|
+
}
|
|
4303
|
+
const text = String(raw).trim();
|
|
4304
|
+
return text.length > 0 ? text : null;
|
|
4305
|
+
}
|
|
4306
|
+
catch {
|
|
4307
|
+
return null;
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
//#endregion
|
|
4311
|
+
//#region ---- Task Board Status Labels ----
|
|
4312
|
+
/** Fallback workflow task-board status when no entity status plugin applies. */
|
|
4313
|
+
getWorkflowPendingStatus() {
|
|
4314
|
+
return {
|
|
4315
|
+
id: AXPSystemStatuses.Pending.name,
|
|
4316
|
+
title: this.translationService.translateSync('@workflow-management:tasks.states.pending'),
|
|
4317
|
+
color: 'warning',
|
|
4318
|
+
};
|
|
4319
|
+
}
|
|
4320
|
+
async getWorkflowTaskStatuses() {
|
|
4321
|
+
const pending = this.getWorkflowPendingStatus();
|
|
4322
|
+
return [
|
|
4323
|
+
{
|
|
4324
|
+
index: 0,
|
|
4325
|
+
key: pending.id,
|
|
4326
|
+
title: pending.title,
|
|
4327
|
+
color: pending.color,
|
|
4328
|
+
},
|
|
4329
|
+
];
|
|
4330
|
+
}
|
|
4331
|
+
async mapStatusProviderToTaskStatuses(provider) {
|
|
4332
|
+
const sorted = [...provider.statuses].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
4333
|
+
const statuses = [];
|
|
4334
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
4335
|
+
const status = sorted[index];
|
|
4336
|
+
statuses.push({
|
|
4337
|
+
index,
|
|
4338
|
+
key: status.name,
|
|
4339
|
+
title: await this.translateStatusTitle(status.title),
|
|
4340
|
+
color: status.color,
|
|
4341
|
+
});
|
|
4342
|
+
}
|
|
4343
|
+
return statuses;
|
|
4344
|
+
}
|
|
4345
|
+
async translateStatusTitle(title) {
|
|
4346
|
+
const raw = title?.trim();
|
|
4347
|
+
if (!raw) {
|
|
4348
|
+
return '';
|
|
4349
|
+
}
|
|
4350
|
+
if (raw.startsWith('@')) {
|
|
4351
|
+
return this.translationService.translateAsync(raw);
|
|
4352
|
+
}
|
|
4353
|
+
return raw;
|
|
4354
|
+
}
|
|
4355
|
+
//#endregion
|
|
4356
|
+
//#region ---- Entity Status Cache & Registry ----
|
|
4357
|
+
readContextString(context, key) {
|
|
4358
|
+
const value = context?.[key];
|
|
4359
|
+
if (value == null) {
|
|
4360
|
+
return null;
|
|
4361
|
+
}
|
|
4362
|
+
const text = String(value).trim();
|
|
4363
|
+
return text.length > 0 ? text : null;
|
|
4364
|
+
}
|
|
4365
|
+
parseQualifiedEntityType(entityType) {
|
|
4366
|
+
const trimmed = entityType.trim();
|
|
4367
|
+
const dot = trimmed.indexOf('.');
|
|
4368
|
+
if (dot <= 0 || dot >= trimmed.length - 1) {
|
|
4369
|
+
return null;
|
|
4370
|
+
}
|
|
4371
|
+
return { module: trimmed.slice(0, dot), name: trimmed.slice(dot + 1) };
|
|
4372
|
+
}
|
|
4373
|
+
async resolveEntityDefinition(entityType) {
|
|
4374
|
+
const key = entityType.trim();
|
|
4375
|
+
if (!key) {
|
|
4376
|
+
return null;
|
|
4377
|
+
}
|
|
4378
|
+
const existing = this.entityDefinitionCache.get(key);
|
|
4379
|
+
if (existing) {
|
|
4380
|
+
return existing;
|
|
4381
|
+
}
|
|
4382
|
+
const load = this.fetchEntityDefinition(key);
|
|
4383
|
+
this.entityDefinitionCache.set(key, load);
|
|
4384
|
+
return load;
|
|
4385
|
+
}
|
|
4386
|
+
async fetchEntityDefinition(entityType) {
|
|
4387
|
+
const parsed = this.parseQualifiedEntityType(entityType);
|
|
4388
|
+
if (!parsed) {
|
|
4389
|
+
return null;
|
|
4390
|
+
}
|
|
4391
|
+
try {
|
|
4392
|
+
return await this.entityRegistry.resolve(parsed.module, parsed.name);
|
|
4393
|
+
}
|
|
4394
|
+
catch {
|
|
4395
|
+
return null;
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
getStatusPluginDefinitionKey(entity) {
|
|
4399
|
+
const plugin = entity.plugins?.find((p) => p.name === 'status');
|
|
4400
|
+
const definition = plugin?.options?.['definition'];
|
|
4401
|
+
return typeof definition === 'string' && definition.trim().length > 0 ? definition.trim() : null;
|
|
4402
|
+
}
|
|
4403
|
+
async entityTypeHasStatusPlugin(entityType) {
|
|
4404
|
+
if (!entityType?.trim()) {
|
|
4405
|
+
return false;
|
|
4406
|
+
}
|
|
4407
|
+
await this.warmStatusDefinitionKeyCache(entityType);
|
|
4408
|
+
return this.getStatusDefinitionKeyForEntityType(entityType) != null;
|
|
4409
|
+
}
|
|
4410
|
+
getStatusDefinitionKeyForEntityType(entityType) {
|
|
4411
|
+
const key = entityType.trim();
|
|
4412
|
+
if (!key) {
|
|
4413
|
+
return null;
|
|
4414
|
+
}
|
|
4415
|
+
if (this.statusDefinitionKeyByEntityType.has(key)) {
|
|
4416
|
+
return this.statusDefinitionKeyByEntityType.get(key) ?? null;
|
|
4417
|
+
}
|
|
4418
|
+
return null;
|
|
4419
|
+
}
|
|
4420
|
+
/**
|
|
4421
|
+
* Warms status-definition-key cache for an entity type (call after resolveEntityDefinition).
|
|
4422
|
+
*/
|
|
4423
|
+
rememberStatusDefinitionKeyForEntityType(entityType, entity) {
|
|
4424
|
+
const key = entityType.trim();
|
|
4425
|
+
if (!key || this.statusDefinitionKeyByEntityType.has(key)) {
|
|
4426
|
+
return;
|
|
4427
|
+
}
|
|
4428
|
+
const definitionKey = entity ? this.getStatusPluginDefinitionKey(entity) : null;
|
|
4429
|
+
this.statusDefinitionKeyByEntityType.set(key, definitionKey);
|
|
4430
|
+
}
|
|
4431
|
+
async getEntityStatusProvider(entityType) {
|
|
4432
|
+
let definitionKey = this.getStatusDefinitionKeyForEntityType(entityType);
|
|
4433
|
+
if (!definitionKey) {
|
|
4434
|
+
const entity = await this.resolveEntityDefinition(entityType);
|
|
4435
|
+
this.rememberStatusDefinitionKeyForEntityType(entityType, entity);
|
|
4436
|
+
definitionKey = this.getStatusDefinitionKeyForEntityType(entityType);
|
|
4437
|
+
}
|
|
4438
|
+
if (!definitionKey) {
|
|
4439
|
+
return undefined;
|
|
4440
|
+
}
|
|
4441
|
+
return this.getStatusProviderByDefinitionKey(definitionKey);
|
|
4442
|
+
}
|
|
4443
|
+
getStatusProviderByDefinitionKey(definitionKey) {
|
|
4444
|
+
const key = definitionKey.trim();
|
|
4445
|
+
const existing = this.statusProviderByDefinitionKey.get(key);
|
|
4446
|
+
if (existing) {
|
|
4447
|
+
return existing;
|
|
4448
|
+
}
|
|
4449
|
+
const load = this.statusDefinitionService.getStatus(key);
|
|
4450
|
+
this.statusProviderByDefinitionKey.set(key, load);
|
|
4451
|
+
return load;
|
|
4452
|
+
}
|
|
4453
|
+
//#endregion
|
|
4454
|
+
//#region ---- Entity Status Presentation ----
|
|
4455
|
+
/** Maps entity status semantic colors to badge-layout tokens (`info` → `accent3`). */
|
|
4456
|
+
mapEntityStatusColorForBadge(color, statusId) {
|
|
4457
|
+
const normalized = color?.trim().toLowerCase();
|
|
4458
|
+
if (normalized === 'info') {
|
|
4459
|
+
return 'accent3';
|
|
4460
|
+
}
|
|
4461
|
+
const badgeColors = new Set([
|
|
4462
|
+
'primary',
|
|
4463
|
+
'secondary',
|
|
4464
|
+
'success',
|
|
4465
|
+
'warning',
|
|
4466
|
+
'danger',
|
|
4467
|
+
'neutral',
|
|
4468
|
+
'ghost',
|
|
4469
|
+
'accent1',
|
|
4470
|
+
'accent2',
|
|
4471
|
+
'accent3',
|
|
4472
|
+
]);
|
|
4473
|
+
if (normalized && badgeColors.has(normalized)) {
|
|
4474
|
+
return normalized;
|
|
4475
|
+
}
|
|
4476
|
+
return this.resolveTaskStatusColor(statusId);
|
|
4477
|
+
}
|
|
4478
|
+
async findStatusDefinition(entityType, statusId) {
|
|
4479
|
+
const provider = await this.getEntityStatusProvider(entityType);
|
|
4480
|
+
return provider?.statuses.find((status) => status.name === statusId);
|
|
4481
|
+
}
|
|
4482
|
+
async warmStatusDefinitionKeyCache(entityType) {
|
|
4483
|
+
if (this.statusDefinitionKeyByEntityType.has(entityType.trim())) {
|
|
4484
|
+
return;
|
|
4485
|
+
}
|
|
4486
|
+
const entity = await this.resolveEntityDefinition(entityType);
|
|
4487
|
+
this.rememberStatusDefinitionKeyForEntityType(entityType, entity);
|
|
4488
|
+
}
|
|
4489
|
+
/**
|
|
4490
|
+
* First `entity-create` target in this workflow whose entity has a status plugin (cached per provider).
|
|
4491
|
+
*/
|
|
4492
|
+
async resolvePrimaryEntityTypeForProvider() {
|
|
4493
|
+
if (this.primaryEntityTypeWithStatus !== undefined) {
|
|
4494
|
+
return this.primaryEntityTypeWithStatus;
|
|
4495
|
+
}
|
|
4496
|
+
let resolved = null;
|
|
4497
|
+
const definitionId = this.config.definitionId?.trim();
|
|
4498
|
+
if (definitionId) {
|
|
4499
|
+
const definition = await this.workflowDefinitionService.getWorkflowByName(definitionId);
|
|
4500
|
+
for (const activity of definition?.graph?.activities ?? []) {
|
|
4501
|
+
if (activity.name !== 'workflow-activity:entity-create') {
|
|
4502
|
+
continue;
|
|
4503
|
+
}
|
|
4504
|
+
const entity = activity.inputs?.['entity'];
|
|
4505
|
+
if (typeof entity !== 'string' || !entity.trim()) {
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4508
|
+
const entityType = entity.trim();
|
|
4509
|
+
await this.warmStatusDefinitionKeyCache(entityType);
|
|
4510
|
+
if (this.getStatusDefinitionKeyForEntityType(entityType)) {
|
|
4511
|
+
resolved = entityType;
|
|
4512
|
+
break;
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
}
|
|
4516
|
+
this.primaryEntityTypeWithStatus = resolved;
|
|
4517
|
+
return resolved;
|
|
4518
|
+
}
|
|
4519
|
+
//#endregion
|
|
4520
|
+
//#region ---- Popover Metadata ----
|
|
4521
|
+
async getPopoverMetadata(task) {
|
|
4522
|
+
return this.buildPopoverMetadata(task);
|
|
4523
|
+
}
|
|
4524
|
+
/**
|
|
4525
|
+
* Builds label/value metadata rows for the workflow task popover (view-mode widgets).
|
|
4526
|
+
*/
|
|
4527
|
+
async buildPopoverMetadata(task) {
|
|
4528
|
+
const context = {};
|
|
4529
|
+
const fields = [];
|
|
4530
|
+
const payload = task.data?.payload;
|
|
4531
|
+
const pushField = (name, title, widgetType, value, options = {}, allowEmpty = false) => {
|
|
4532
|
+
if (!allowEmpty && !this.hasMeaningfulPopoverValue(value)) {
|
|
4533
|
+
return;
|
|
4534
|
+
}
|
|
4535
|
+
context[name] = value;
|
|
4536
|
+
fields.push({
|
|
4537
|
+
name,
|
|
4538
|
+
title,
|
|
4539
|
+
node: {
|
|
4540
|
+
type: widgetType,
|
|
4541
|
+
name,
|
|
4542
|
+
path: name,
|
|
4543
|
+
mode: 'view',
|
|
4544
|
+
options,
|
|
4545
|
+
},
|
|
4546
|
+
});
|
|
4547
|
+
};
|
|
4548
|
+
if (task.status?.id != null && String(task.status.id).trim()) {
|
|
4549
|
+
const taskData = task.data;
|
|
4550
|
+
const statusId = String(task.status.id);
|
|
4551
|
+
const statusDefinition = taskData?.entityStatusDefinition;
|
|
4552
|
+
const statusDefinitionKey = taskData?.entityStatusDefinitionKey;
|
|
4553
|
+
if (statusDefinitionKey && statusDefinition) {
|
|
4554
|
+
// Pass the full status definition so the chip renders before async provider load completes.
|
|
4555
|
+
pushField('status', '@general:terms.status.title', AXPWidgetsList.Advanced.Status, statusDefinition, {
|
|
4556
|
+
definitionKey: statusDefinitionKey,
|
|
4557
|
+
readonly: true,
|
|
4558
|
+
});
|
|
4559
|
+
}
|
|
4560
|
+
else {
|
|
4561
|
+
const statusColor = this.mapEntityStatusColorForBadge(statusDefinition?.color ??
|
|
4562
|
+
task.data?.statusColor, task.status.id);
|
|
4563
|
+
pushField('status', '@general:terms.status.title', AXPWidgetsList.Layouts.BadgeLayout, {
|
|
4564
|
+
text: task.status.title,
|
|
4565
|
+
color: statusColor,
|
|
4566
|
+
icon: statusDefinition?.icon ?? 'fa-light fa-flag',
|
|
4567
|
+
}, { look: 'twotone' });
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
pushField('assignee', '@workflow-management:tasks.popover.assignee', AXPWidgetsCatalog.text, this.formatUserReferenceLabel(task.assignee), {}, true);
|
|
4571
|
+
pushField('startedBy', '@workflow-management:tasks.popover.started-by', AXPWidgetsCatalog.text, this.formatUserReferenceLabel(task.reporter), {}, true);
|
|
4572
|
+
const createdAt = task.createdAt ?? task.startDate;
|
|
4573
|
+
if (createdAt) {
|
|
4574
|
+
pushField('createdAt', '@workflow-management:tasks.popover.created', AXPWidgetsCatalog.dateTime, createdAt, {
|
|
4575
|
+
showTime: false,
|
|
4576
|
+
});
|
|
4577
|
+
}
|
|
4578
|
+
const effectiveDate = this.resolveEffectiveDateValue(payload?.context);
|
|
4579
|
+
if (effectiveDate.kind === 'date') {
|
|
4580
|
+
pushField('effectiveDate', '@workflow-management:tasks.popover.effective-date', AXPWidgetsCatalog.dateTime, effectiveDate.value, { showTime: false });
|
|
4581
|
+
}
|
|
4582
|
+
else if (effectiveDate.kind === 'text') {
|
|
4583
|
+
pushField('effectiveDate', '@workflow-management:tasks.popover.effective-date', AXPWidgetsCatalog.text, effectiveDate.value);
|
|
4584
|
+
}
|
|
4585
|
+
if (task.priority) {
|
|
4586
|
+
pushField('priority', '@workflow-management:tasks.popover.priority', AXPWidgetsList.Layouts.BadgeLayout, {
|
|
4587
|
+
text: task.priority.charAt(0).toUpperCase() + task.priority.slice(1),
|
|
4588
|
+
color: this.resolveTaskPriorityColor(task.priority),
|
|
4589
|
+
icon: 'fa-light fa-exclamation-circle',
|
|
4590
|
+
}, { look: 'twotone' });
|
|
4591
|
+
}
|
|
4592
|
+
return { context, fields };
|
|
4593
|
+
}
|
|
4594
|
+
//#endregion
|
|
4595
|
+
//#region ---- Popover Helpers ----
|
|
4596
|
+
hasMeaningfulPopoverValue(value) {
|
|
4597
|
+
if (value === null || value === undefined) {
|
|
4598
|
+
return false;
|
|
4599
|
+
}
|
|
4600
|
+
if (typeof value === 'string') {
|
|
4601
|
+
const trimmed = value.trim();
|
|
4602
|
+
return trimmed.length > 0 && trimmed !== '—';
|
|
4603
|
+
}
|
|
4604
|
+
if (value instanceof Date) {
|
|
4605
|
+
return !Number.isNaN(value.getTime());
|
|
4606
|
+
}
|
|
4607
|
+
return true;
|
|
4608
|
+
}
|
|
4609
|
+
formatUserReferenceLabel(user) {
|
|
4610
|
+
return user?.fullName?.trim() || user?.username?.trim() || '—';
|
|
4611
|
+
}
|
|
4612
|
+
resolveDueDate(raw) {
|
|
4613
|
+
if (raw == null || raw === '') {
|
|
4614
|
+
return null;
|
|
4615
|
+
}
|
|
4616
|
+
const date = typeof raw === 'string' ? new Date(raw) : raw;
|
|
4617
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
4618
|
+
}
|
|
4619
|
+
resolveEffectiveDateValue(context) {
|
|
4620
|
+
if (!context || typeof context !== 'object') {
|
|
4621
|
+
return { kind: 'none' };
|
|
4622
|
+
}
|
|
4623
|
+
const i18n = context[AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY];
|
|
4624
|
+
const rawFromI18n = i18n && typeof i18n === 'object' && !Array.isArray(i18n)
|
|
4625
|
+
? i18n['effectiveDate']
|
|
4626
|
+
: undefined;
|
|
4627
|
+
const raw = rawFromI18n ??
|
|
4628
|
+
context['effectiveDate'] ??
|
|
4629
|
+
(context['result'] && typeof context['result'] === 'object' && !Array.isArray(context['result'])
|
|
4630
|
+
? context['result']['effectiveDate']
|
|
4631
|
+
: undefined);
|
|
4632
|
+
if (raw instanceof Date) {
|
|
4633
|
+
return Number.isNaN(raw.getTime()) ? { kind: 'none' } : { kind: 'date', value: raw };
|
|
4634
|
+
}
|
|
4635
|
+
if (typeof raw !== 'string' || !raw.trim()) {
|
|
4636
|
+
return { kind: 'none' };
|
|
4637
|
+
}
|
|
4638
|
+
const trimmed = raw.trim();
|
|
4639
|
+
const parsed = this.parseDateOnlyString(trimmed);
|
|
4640
|
+
if (parsed) {
|
|
4641
|
+
return { kind: 'date', value: parsed };
|
|
4642
|
+
}
|
|
4643
|
+
return { kind: 'text', value: trimmed };
|
|
4644
|
+
}
|
|
4645
|
+
/** Parses `yyyy-mm-dd` (and ISO date prefixes) in local time to avoid UTC day shifts in the date widget. */
|
|
4646
|
+
parseDateOnlyString(value) {
|
|
4647
|
+
const dateOnlyMatch = /^(\d{4})-(\d{2})-(\d{2})/.exec(value.trim());
|
|
4648
|
+
if (!dateOnlyMatch) {
|
|
4649
|
+
return null;
|
|
4650
|
+
}
|
|
4651
|
+
const year = Number(dateOnlyMatch[1]);
|
|
4652
|
+
const month = Number(dateOnlyMatch[2]);
|
|
4653
|
+
const day = Number(dateOnlyMatch[3]);
|
|
4654
|
+
if (!year || month < 1 || month > 12 || day < 1 || day > 31) {
|
|
4655
|
+
return null;
|
|
4656
|
+
}
|
|
4657
|
+
const local = new Date(year, month - 1, day);
|
|
4658
|
+
return Number.isNaN(local.getTime()) ? null : local;
|
|
4659
|
+
}
|
|
4660
|
+
resolveTaskStatusColor(statusId) {
|
|
4661
|
+
const id = String(statusId);
|
|
4662
|
+
if (id === AXPSystemStatuses.Pending.name) {
|
|
4663
|
+
return 'warning';
|
|
4664
|
+
}
|
|
4665
|
+
return 'primary';
|
|
4666
|
+
}
|
|
4667
|
+
resolveTaskPriorityColor(priority) {
|
|
4668
|
+
switch (priority) {
|
|
4669
|
+
case 'highest':
|
|
4670
|
+
return 'danger';
|
|
4671
|
+
case 'high':
|
|
4672
|
+
return 'warning';
|
|
4673
|
+
case 'medium':
|
|
4674
|
+
return 'secondary';
|
|
4675
|
+
case 'low':
|
|
4676
|
+
case 'lowest':
|
|
4677
|
+
return 'neutral';
|
|
4678
|
+
default:
|
|
4679
|
+
return 'primary';
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
//#endregion
|
|
4683
|
+
//#region ---- Component ----
|
|
4684
|
+
getComponent() {
|
|
4685
|
+
return () => import('./acorex-modules-workflow-management-workflow-task-popover.component-DjYDwHWU.mjs').then((m) => m.AXMWorkflowTaskPopoverComponent);
|
|
4686
|
+
}
|
|
4687
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider, deps: [{ token: AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4688
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider }); }
|
|
4689
|
+
}
|
|
4690
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGenericWorkflowTaskProvider, decorators: [{
|
|
4691
|
+
type: Injectable
|
|
4692
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
4693
|
+
type: Inject,
|
|
4694
|
+
args: [AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG]
|
|
4695
|
+
}] }] });
|
|
4696
|
+
|
|
4697
|
+
var genericWorkflowTask_provider = /*#__PURE__*/Object.freeze({
|
|
4698
|
+
__proto__: null,
|
|
4699
|
+
AXPGenericWorkflowTaskProvider: AXPGenericWorkflowTaskProvider,
|
|
4700
|
+
AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG: AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG
|
|
4701
|
+
});
|
|
4702
|
+
|
|
3629
4703
|
/**
|
|
3630
4704
|
* Generated bundle index. Do not edit.
|
|
3631
4705
|
*/
|
|
3632
4706
|
|
|
3633
|
-
export { AXCCategoryProvider, AXMMenuProvider, AXMPermissionsKeys, AXMWorkflowActivitiesDefinitionProvider, AXMWorkflowManagementModule, AXMWorkflowManagementModuleEntityProvider, AXPCreateEntityActivity, AXPGenericWorkflowTaskProvider, AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG, AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY, AXP_WORKFLOW_TASK_CONTEXT_TITLE_PARTS_KEY, CartableTaskActivity, CollectSignatureActivity, CreateEntityFormActivity, HumanTaskActivity, RootConfig, ShowConfirmPopupActivity, ShowLayoutPopupActivity, ShowToastActivity };
|
|
4707
|
+
export { AXCCategoryProvider, AXMMenuProvider, AXMPermissionsKeys, AXMWorkflowActivitiesDefinitionProvider, AXMWorkflowManagementModule, AXMWorkflowManagementModuleEntityProvider, AXPCreateEntityActivity, AXPGenericWorkflowTaskProvider, AXPWorkflowInstanceHumanTaskService, AXPWorkflowTaskContinuationService, AXP_GENERIC_WORKFLOW_TASK_PROVIDER_CONFIG, AXP_WORKFLOW_TASK_CONTEXT_I18N_KEY, AXP_WORKFLOW_TASK_CONTEXT_TITLE_PARTS_KEY, CartableTaskActivity, CollectSignatureActivity, CreateEntityFormActivity, HumanTaskActivity, RootConfig, ShowConfirmPopupActivity, ShowLayoutPopupActivity, ShowToastActivity, createWorkflowInstanceBookmarkAccess, getWorkflowInstanceInitiatorUserId, isWorkflowPlatformRootUser, isWorkflowPooledClaimablePayload, isWorkflowReassignableHumanTaskPayload, isWorkflowTaskBoardBookmarkPayload, needsTakeOverForWorkflowTaskContinuation, normalizeWorkflowTaskUserIds, parseWorkflowTaskContinuationPayload, provideGenericWorkflowTaskProvider, provideTaskWorkflow, shouldOfferWorkflowContinuationAfterStep, shouldOfferWorkflowTaskContinuation };
|
|
3634
4708
|
//# sourceMappingURL=acorex-modules-workflow-management.mjs.map
|