Automatic reassignment - approval task approver
@since 15.18.0
When the last active approver of a running approval task is deactivated or deleted, the task is reassigned to new approvers resolved by a configurable script (or to default approvers, when no script is configured or the script resolves nobody). A deactivated identity is kept as an approver for history (and is rendered grey on the frontend), a deleted identity's approver records are removed.
On approver deactivation
Whenever an identity transitions from an active to a disabled state (IdentityState.isDisabled()), all its running approval tasks (ApprovalTaskState.RUNNING) are checked. The disabled identity is kept as an approver (task lists render it grey), but when no active approver is left on the task, the task gets new approvers. Terminated tasks are not modified at all - their approvers are kept for history.
Every way an identity can become disabled is covered, because the trigger is the state transition itself, not the action that caused it:
- manual deactivation (dashboard button, bulk action on the users table),
- the last valid contract ends (`validTill` in the past) - identity state `LEFT`,
- all contracts are excluded - identity state `DISABLED`.
An identity with more than one contract stays active (and stays an active approver) until its last valid contract ends.
If the task still has at least one active approver after the transition, nothing else happens - the task is not reassigned yet. Only when the deactivated approver was the last active one is the task reassigned.
When a deactivated approver is activated back, nothing is reassigned (the approvers added in the meantime stay) - the identity just becomes an active approver of its tasks again.
On approver deletion
Identity delete already removes the identity's approver records (referential integrity, IdentityDeleteProcessor). A running task left without any active approver is then reassigned the same way as on deactivation.
Resolving new approvers
- The script configured by idm.sec.core.wf.approval.task.reassign.script is evaluated (see below).
- Null, duplicate and disabled identities are filtered out from the script result.
- When no usable approver remains (script not configured, not found, failed, or returned nothing usable), default approvers are used - valid holders of the substitute fallback role (idm.sec.core.role.substituteFallback). When the property is not set, the admin role is used (idm.sec.core.role.admin, `superAdminRole` by default). When even the fallback role has no valid holder, the task is assigned to the `admin` identity.
- Delegations of the new approvers are applied (a delegate becomes the approver instead of the delegator).
The reassignment does not re-send new task notifications.
Configuration
| Property | Default value | Description |
|---|---|---|
| idm.sec.core.wf.approval.task.reassign.script | Code of the script that is used to resolve new approvers. | |
| idm.sec.core.role.substituteFallback | admin role | Role whose valid holders are used as default approvers (and as substitutes for the role guarantor reassignment), when the script resolves nobody. |
The feature has no enable/disable switch - the configuration is not public and the behavior is always active. The processor implementing it is not disableable either: an approval task must never be left without an active approver.
Interface of reassign scripts
The reassignment scripts are of category SYSTEM and can use the input parameters approvalTask - the task that just ran out of active approvers - and lastApprover - the approver that was just deactivated or deleted (can be null).
The script must return either IdmIdentityDto or List<IdmIdentityDto>. Returning null, empty lists or throwing an exception will assign the task to the default approvers (see `getDefaultApprovers()` in DefaultIdmApprovalTaskApproverService).
Product provided scripts
The product provides three prewritten system scripts to resolve new approvers. None of them is configured by default - the feature falls back to the default approvers until one of them (or a script of your own) is set in idm.sec.core.wf.approval.task.reassign.script.
| Script code | Description |
|---|---|
| `approvalTaskReassignToIdentity` | Reassigns the task to a single identity with a hardcoded username. |
| `approvalTaskReassignToRole` | Reassigns the task to all valid identities with a hardcoded role assigned. |
| `approvalTaskReassignToLastApproverManager` | Reassigns the task to managers of the last approver (found by the last approver's contracts - contract guarantees or managers by tree structure). Falls back to the default approvers when the last approver has no manager. |
Frontend
A warning about possible task reassignment is shown:
- in the confirm dialog of the Deactivate manually dashboard button (identity detail),
- in the Deactivate manually bulk action dialog on the users table (bulk action prevalidation),
- on the contract detail, when `validFrom` / `validTill` is changed so that the user would be deactivated.
Localization key of the warning: `content.identities.action.deactivate.taskReassignWarning`.
Deactivated approvers are rendered grey in the task lists and the approvers dialog of a task shows deactivated approvers too.
Report of running approval tasks without an approver
To find whether there are any approval tasks left without an active approver (typically from before the deployment of this feature or due to some database inconsistencies) and to list them, there is a report that does exactly that. See more about how to use it here.
Implementation notes
| Class | Usage |
|---|---|
| IdmApprovalTaskApproverService | Public contract. reassignWhenNoActiveApproverLeft(task, lastApprover), deleteWithAnyApproverLeftCheck(approver), getReassignApprovers(…), getDefaultApprovers(taskId); holds PROPERTY_REASSIGN_SCRIPT_CODE. |
| DefaultIdmApprovalTaskApproverService | Script evaluation (parameters approvalTask and lastApprover), filtering of the result, the default approver chain (substitute fallback role → admin identity) and the application of delegations through DelegationManager. |
| IdentityDisableReassignApprovalTasksProcessor | Identity UPDATE, order DEFAULT_ORDER + 10. Its conditional fires only when isDisabled() actually changed, so it runs on both directions of the transition; only RUNNING tasks of the identity are loaded, so an identity with a long approval history does not slow the identity update down. Not disableable. |
| IdentityDeleteProcessor | Pre-existing; calls deleteWithAnyApproverLeftCheck for the deleted identity's approver records. |
| IdentityDisableBulkAction#prevalidate() | Returns the frontend info model CoreResultCode.IDENTITY_DISABLE_BULK_ACTION_TASK_REASSIGN. |
| IdmApprovalTaskRepository#touchTask | Bumps the modified timestamp of the task in a single update statement, guarded by ApprovalTaskState.RUNNING so terminated tasks are never touched. The frontend entity cache renders a newly fetched entity only when its modified changed - approver changes would not show up on already loaded tasks otherwise. |
| IdmCoreApprovalTaskReassignToIdentity / …ToRole / …ToLastApproverManager | Shipped scripts (eu/bcvsolutions/idm/scripts/) |