Altinn / app-frontend-react

Altinn application React frontend
BSD 3-Clause "New" or "Revised" License
18 stars 31 forks source link

End user navigation between process tasks #271

Closed lvbachmann closed 1 year ago

lvbachmann commented 5 years ago

Description

When we give the app developers more possible tasks in a process, including the ability to define what tasks are possible to move between, we need to show the end user using the app in altinn.no how to navigate between the tasks.

Ideas

In scope

Out of scope

This analysis should not define new possible tasks in a process - use tasks already identified at the start of the analysis as foundation.

Consideration

Return back to previous task

We need to use BPMN to design the process that supports a return to a previous task. In BPMN this is the current template with only "one way"

image

<bpmn2:definitions id="Altinn_Data_Confirmation_Process_Definition">
  <bpmn2:process id="Altinn_Data_Confirmation_Process" isExecutable="false">
    <bpmn2:startEvent id="StartEvent_1">
      <bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
    </bpmn2:startEvent>
    <bpmn2:task id="Task_1" name="Fyll ut skjema" altinn:tasktype="data">
      <bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
    </bpmn2:task>
    <bpmn2:task id="Task_2" name="Bekreft skjemadata" altinn:tasktype="confirmation">
      <bpmn2:incoming>SequenceFlow_2</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_3</bpmn2:outgoing>
    </bpmn2:task>
    <bpmn2:endEvent id="EndEvent_1">
      <bpmn2:incoming>SequenceFlow_3</bpmn2:incoming>
    </bpmn2:endEvent>
    <bpmn2:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="Task_1" />
    <bpmn2:sequenceFlow id="SequenceFlow_2" sourceRef="Task_1" targetRef="Task_2" />
    <bpmn2:sequenceFlow id="SequenceFlow_3" sourceRef="Task_2" targetRef="EndEvent_1" />
  </bpmn2:process>
 </bpmn2:definitions>

To support return the BPMN process need to add explicit flows for the return. The below diagram and BPMN file describe the above process with added return flow from Task 2 to Task 1. To implement

image

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions id="Definitions_10dklqb">
  <bpmn:process id="Process_1so39gt" isExecutable="false">
    <bpmn:startEvent id="StartEvent_1s67p7b" />
    <bpmn:startEvent id="Event_Start">
      <bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:task id="Task_1" name="Fyll ut skjema" altinn:tasktype="data">
      <bpmn:incoming>SequenceFlow_1</bpmn:incoming>
      <bpmn:incoming>SequenceFlow_3</bpmn:incoming>
      <bpmn:outgoing>SequenceFlow_2</bpmn:outgoing>
    </bpmn:task>
    <bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="Task_1" targetRef="Task_2" />
    <bpmn:task id="Task_2" name="Bekreft skjemadata" altinn:tasktype="confirmation">
      <bpmn:incoming>SequenceFlow_2</bpmn:incoming>
      <bpmn:outgoing>SequenceFlow_3</bpmn:outgoing>
    </bpmn:task>
    <bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="Task_1" targetRef="Task_2" />
    <bpmn:exclusiveGateway id="Gateway_1">
      <bpmn:incoming>SequenceFlow_3</bpmn:incoming>
      <bpmn:outgoing>SequenceFlow_4</bpmn:outgoing>
      <bpmn:outgoing>SequenceFlow_5</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="SequenceFlow_3" sourceRef="Task_2" targetRef="Gateway_1" />
    <bpmn:sequenceFlow id="SequenceFlow_5" sourceRef="Gateway_1" targetRef="Task_1"  altinn:taskaction:leave="true" />
    <bpmn:endEvent id="Event_End">
      <bpmn:incoming>SequenceFlow_4</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="SequenceFlow_4" sourceRef="Gateway_1" targetRef="Event_End" />
  </bpmn:process>
  </bpmn:definitions>

Optional / conditional tasks

A common scenario is to skip tasks based on business rules. The following diagram shows a flow with a optional signing step. This is a common scenario where the business rule could be based on data in form. (example minimum sales amount)

image

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1y7mtx0" targetNamespace="http://bpmn.io/schema/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="8.7.2">
  <bpmn:process id="Process_04vnahs" isExecutable="false">
    <bpmn:startEvent id="StartEvent">
      <bpmn:outgoing>Flow1</bpmn:outgoing>
    </bpmn:startEvent>
    <bpmn:task id="Task1">
      <bpmn:incoming>Flow1</bpmn:incoming>
      <bpmn:incoming>Flow5</bpmn:incoming>
      <bpmn:incoming>Flow7</bpmn:incoming>
      <bpmn:outgoing>Flow2</bpmn:outgoing>
    </bpmn:task>
    <bpmn:task id="Task2">
      <bpmn:incoming>Flow2</bpmn:incoming>
      <bpmn:outgoing>Flow3</bpmn:outgoing>
    </bpmn:task>
    <bpmn:exclusiveGateway id="GateWay1">
      <bpmn:incoming>Flow3</bpmn:incoming>
      <bpmn:outgoing>Flow5</bpmn:outgoing>
      <bpmn:outgoing>Flow4</bpmn:outgoing>
      <bpmn:outgoing>Flow8</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:task id="Task3">
      <bpmn:incoming>Flow4</bpmn:incoming>
      <bpmn:outgoing>Flow6</bpmn:outgoing>
    </bpmn:task>
    <bpmn:exclusiveGateway id="Gateway2">
      <bpmn:incoming>Flow6</bpmn:incoming>
      <bpmn:outgoing>Flow7</bpmn:outgoing>
      <bpmn:outgoing>Flow9</bpmn:outgoing>
    </bpmn:exclusiveGateway>
    <bpmn:sequenceFlow id="Flow1" sourceRef="StartEvent" targetRef="Task1" />
    <bpmn:sequenceFlow id="Flow2" sourceRef="Task1" targetRef="Task2" />
    <bpmn:sequenceFlow id="Flow3" sourceRef="Task2" targetRef="GateWay1" />
    <bpmn:sequenceFlow id="Flow5" sourceRef="GateWay1" targetRef="Task1" />
    <bpmn:sequenceFlow id="Flow4" sourceRef="GateWay1" targetRef="Task3" />
    <bpmn:sequenceFlow id="Flow6" sourceRef="Task3" targetRef="Gateway2" />
    <bpmn:sequenceFlow id="Flow7" sourceRef="Gateway2" targetRef="Task1" />
    <bpmn:endEvent id="EndEvent">
      <bpmn:incoming>Flow9</bpmn:incoming>
      <bpmn:incoming>Flow8</bpmn:incoming>
    </bpmn:endEvent>
    <bpmn:sequenceFlow id="Flow8" sourceRef="GateWay1" targetRef="EndEvent" />
    <bpmn:sequenceFlow id="Flow9" sourceRef="Gateway2" targetRef="EndEvent" />
  </bpmn:process>
</bpmn:definitions>

If we use an exclusive gateway the next method could identify that there are multiple flows and trigger a new App interface method that the app developer controls. That method could then be populated with logic that decided what the next task is.

Task validation

The current behavior is to validate if a task can be ended. That includes validating if the required attachment is attached and all data models connected to the task are valid. But this is not relevant if you are going back in the process.

We need to update the code so when the process is going backward this validation is not required. The BPMN needs to define which flows that does not require validation. See suggestion in XML

Valid scenarios

We need to define what kind of scenarios Altinn Apps really want to support.

image

The following flows would be invalid to set up in an Altinn Apps application

Technical flows

Return to previous task

Technical flow when the instance is in Task 2 and user want to go back to Task1 image

Optional

In this process, the user does not dictate the next task, but the business logic decides based on the instance, possible the data and other aspects which task is next.

image

To support this we need a new method in App interface that let app developer build custom code to define which route from a gateway. Input to this needs to be instance and the process/gateway info.

In this way the logic can rely on the instanceValues or the data from the instance (using storage API)

Process navigation in frontend

The current views just expect that the only way to move in the process is forward. When there are multiple ways to go from a current task frontend needs to present the options to the users that they can go back to the defined

image

Frontend needs a way to get the Process options from current task. For that we need a new API that presented the current options, the title on that option and some indication which direction it is.

Task Authorization

We need to support the authorization of the user that chooses to move the process backward. It is defined for each task type what is required to perform forward movement of process but it is not described what is required to backward.

Suggestion: User is authorized for Read at current task.

Default flows

We need to consider defining default flows if there are multiple flows out from a gateway. This to simplyfi API calls and to make it possible to trigger CompleteProcess.

See suggested example.

Coded Gateways.

Specification task

Development task

Alternative technical approach

In the current implementation, lots of business logic is located inside the process controller and the BMPN reader just for information about the process. The process is not responsible for doing any work.

Another approach is to introduce and ProcessEngine that is responsible for performing all business logic and trigger all other components that need to do work related to BPMN process.

See Altinn/altinn-studio#7067 for details

TBA

Analysis

Points discussed during a meeting Thursday 9. September:

  1. Being able to navigate backwards in the process should require an explicit sequence flow going out of the current task and back to a earlier task in the process. It should not be possible to go back in a process if there are no such sequence flow declared in the BPMN. It should already be possible to perform the "back" sequence by providing the id of the task the user want to go to as input to the "go next" API. The process "engine" will validate and ensure that it is valid by checking for a sequence flow.
  2. One major hurdle is to unlock locked data elements. If the current task is a confirm "step", data elements have been locked, preventing any updates even if the process goes back to an earlier task. The data elements must bee opened again at some point. Possible solutions:
    1. Logic attached to a sequence flow performs the unlock. The logic could be triggered based on a naming convention of a sequence flow or a custom, Altinn specific, attribute on the sequence flow declaration.
    2. Declaring a new type of task that can be automatically executed by the App. This is almost identical to the first suggestion. Instead of having the sequence flow point directly back to the earlier task it points to the automated "unlock task" which in turn points to the earlier process task. This is more complex, but we avoid attaching business logic to a sequence flow. The issue here is the visual representation and added complexity for the app developer when working with the process. "Why do we need this weird task?" A tiny remedy could be to also have a separate task for locking, to pair up with unlocking.
    3. We no longer lock the data elements when moving into a confirm task. Instead we prevent changes from being performed through authorization rules.
  3. Instead of letting external systems (like the frontend) "navigate" the process by calling next, the decision making is moved to the process itself. The idea is that tasks declare themselves complete with some sort of end state and give control to the process that provides a new task (or end event) based on the situation.
    1. The tricky bit here would be to provide the process the necessary information to determine what to do. Gateways can be coded and allowed access to instances that could be expanded with additional properties if needed. The logic could also get access to the data elements and even the actual data if needed.

Conclusion

TBD

Tasks

RonnyB71 commented 1 year ago

Resolved by https://github.com/Altinn/app-lib-dotnet/issues/192