Zu Content springen
Deutsch
  • Es gibt keine Vorschläge, da das Suchfeld leer ist.

MS Power Automate Flow - Connecting Planner and ValueStreamer - Backup

Diesr Artikel Dient als Backup für den Artikel 

 
The flows described on the following subpages are all used to create some conncetion between Planner and ValueStreamer and synchronize data between both systems.

 

All these flows use one or more dataverse tables described in: 

How to rebuild / implement a flow in another enviroment 

 

How to rebuild / implement a flow in another enviroment 

 

Introduction

In general the PowerAutomate Flows are used to connect Planner and ValueStreamer via http-Reqests and dataverse tables. The dataverse tables store task-data from both systems and synchronize that data.

Dataverse-Tables

Two Dataverse-Tables are needed:

  1. User-Table

    1. This will match the Users of Valuestreamer to the Users of Planner

  2. Task-Table

    1. This will be the transformation table. Tasks from Valuestreamer will be written into it and send to Planner.

 

 
 
 

Flow

 

needed Dataverse-Table

 

02_Get tasks from Valuestreamer to Planner

User-Table

Task-Table

03_Update ValueStreamer when Planner task is completed

Task-Table

 
 
 

Import a table

If a table already exists in a different enviroment, then an admin can export the table and import it to another enviroment (Screenshot_Import_Table). In this case the existing data in the table should be deleted after the import.

The settings for each column have to be checked (and corrected or you adapt certain fields in the flow later):

  • It may happen that the internal name of a colum is translated to german if your user-language is also german

  • In general the smallest possible datatype is used for a column

    • If your original table had a column with DateTime and the value is only a Date, then the import would use only Date as datatype

(Screenshot_Import_Table)

 

Create a table

If the table does not already exist, then the table has to be created (Screenshot-Create_Table).

Some columns have to be created manually by the user. These columns get automatically the internal name cr4b3_ColumnName (Screenshot_User_Table). For example the column Assignee is internally called cr4b3_Assignee. cr4b3 has some unknown system meaning for dataverse.

Columns without c34b3 are automatically generated when creating a table.

 

(Screenshot-Create_Table)

 
grafik-20240126-121514.png
 
grafik-20240126-123230.png

 

Create User-Table

The User-Table needs the following columns:

(Screenshot_User_Table)

 
grafik-20240130-063919.png
 
grafik-20240130-063954.png

 

Some columns and names have to be added manually:

  • VS_EjotPoC_User → as tablename

  • UUID → as first column, it will get the addition “Primäre Namensspalte” (can also be set via table-settings “Tabelleneigenschaften bearbeiten”)

  • Other columns as seen in the screenshot (Screenshot_Manual_Columns_User_Table)

 

(Screenshot_Manual_Columns_User_Table)

 
grafik-20240129-130258.png

 

Import User-Data

Once the table is created the User-Data has to be entered manually (Screenshot_New_Entry). Open the table, wait unitl it is fully loaded and add new data in the row at the bottom.

UUID = UUID of the User in the ValueStreamer-System

E-Mail = E-mail of the User, the User must have the same email in Valuestreamer and in Planner

 

(Screenshot_New_Entry)

 
grafik-20240130-102532.png

 

Create Task-Table

The task table needs the following columns:

(Screenshot_Task_Table)

 
grafik-20240126-121257.png
 
grafik-20240126-121330.png

Some columns and names have to be added manually:

  • VS_EJOT_PoC_Tasks → as tablename

  • UUID → as first column, it will get the addition “Primäre Namensspalte” (can also be set via table-settings “Tabelleneigenschaften bearbeiten”

  • Other columns as seen in the screenshot (Screenshot_Manual_Columns_User_Table)

 

(Screenshot_Manual_Columns_Task_Table)

 
grafik-20240129-131814.png

 

PowerAutomate

After the dataverse-tables have been created the power automate flow has to be created or imported from another enviroment.

+++++++++++++++++++++++++++++++++++++++++++++++++++++++

Get tasks from Valuestreamer to Planner

These pages are used to document the “Get task from ValueStreamer”-Flow.

This flow can be used to:

  • get tasks from a certain ValueStreamer-Team to the Teams-Planner-View

  • update tasks that are already in the Teams-Planner-View with the latest data from ValueStreamer (for example if the assignee has been changed)

  • if user gets a task assigned due to this flow, then the user is informed via an automatically teams-message

 

How does the flow work (High-Level-View):

A user-table matches the users from ValueStreamer and Planner.

A Task-Table matches the data about tasks from ValueStreamer and Planner.

If the flow is triggered all tasks from ( a certain Team in) ValueStreamer are compared with the Task-Table and either a new row is created or an existing row is updated in the Task-Table. Once that is done the data from the Task-Table is used to update Planner.

More detailed information can be found on the sub-site: Technical Process (detailed) of flow "Get tasks from Valuestreamer to Planner"

 
Get Task from VS to Planner Overview.png

 

 

Technical Process (detailed) of flow "Get tasks from Valuestreamer to Planner"

  
 

This page shows the process of the flow with all details.

 

Process Overview

 

 
 
 

Step

 

Screenshot

 

1-7

 
grafik-20240205-110302.png

 

8-15

 
grafik-20240205-110353.png

 

15-15.1.1.4 / 15.1.2.3

 
grafik-20240205-110517.png

 

15-15.2.1.6.1.1 / 15.2.1.6.2.3

 

 
grafik-20240205-111035.png

 

15-15.2.2.6

 
grafik-20240205-110722.png

 

 
 
 

Step by step

 
 
 

Step

 

Description

 

Power Automate Function:

Connector,

Display-Name

 

Variables, Parameters, Formula

 
  1.  

Flow is triggered.

Trigger:

No Connector,

Manually Trigger a flow

-

  1.  

Request data from Valuestreamer containing the tasks from a certain team.

HTTP:

HTTP,

HTTP reqeust to GET all tasks from VS Team

 
Parameters
 
  1.  

Parse the response of step 2 in a JSON-Object.

Parse JSON:

Data Operations,

Parse JSON for tasks

 
Parameter
 
 
JSON-Schema
 
  1.  

Create new variable called userID with datatype String and no value.

Initialize Variable:

Variables,

Initialize Variable userID

 
Parameter
 
  1.  

Create new variable called userEmail with datatype String and no value.

Initialize Variable:

Variables,

Initialize Variable userEmail

 
Parameter
 
  1.  

Create new variable called HelpGetPlannerUserId with datatype String and no value.

Initialize Variable:

Variables,

HelpGetPlannerUserID

 
Parameter
 
  1.  

For-each-Loop: Iterate over all returned tasks from VS, using the JSON-Object from step 3.

For each:

Control,

For each

 
Iterate over
 
  1.  

Set variable UserID to Assignee from current task.

Set variable:

Variables,

Set variable userId to JSON response

 
Parameter
 
  1.  

Set variable HelpGetPlannerUserId to null (to delete values from previous iterations)

Set variable:

Variables,

Set helperVariable to null

 
Parameter
 
  1.  

Set variable userEmail to null (to delete values from previous iterations)

Set variable:

Variables,

Set userEmail to null

 
Parameter
 
  1.  

List rows from User-Table that belong to the current UserID (the variable was set in step 8)

List rows
Microsoft Dataverse,

List rows of user table

 
Parameter
 
  1.  

For-each-Loop: Iterate over all returned rows from step 11.

In step 11 only one row should be returned, because each user exists only once in the table. However PowerAutomate does not know this and therefore the For-Each-Loop is needed.

For each:

Control

Iterate over returned rows of user table

 
Iterate over
 
  1.  

Compare current UserID from step 8 to userId from the returned rows in step 11. If equal, then task from Valuestreamer has an assignee and that assignee exists in user table (true).

If true: Go to step 13.1.1

If false: Go to step 13.2.1

Condition:

Control

Condition userId is equal to userId of user table

 
Condition
 

13.1.1

For-each-Loop: Iterate over all returned rows from step 11.

For each:

Control

Iterate over returned rows of user table (2)

 
Iterate over
 

13.1.2

Set variable userEmail to email of current iteration of step 13.1.1

Set variable:

Variables,

Set varaible userEmail

 
Parameter
 

13.2.1

Set variable userEmail to null.

Set variable:

Variables,

Set variable userEmail to null

 
Parameter
 

14.

List rows from Task-Table that belong to the current UserID (the variable UserID was set in step 8)

List rows
Microsoft Dataverse,

List rows of task table

 
Parameter
 

15.

Compare the amount of rows returned in step 14 to 0.

If the amount is 0, then the task does not exist in the task table (true).

If the amount is 1 or higher, then the task does exist in the task table (false).

If true: Go to step 15.1.1

If false: Go to step 15.2.1

For each:

Control,

Condition Task does not exist in table yet

 
Condition
 

15.1.1

Compare the value of userID (set in step 8) to 0.

If the value is not 0, then it is not empty and the task does have an assignee (true).

If the value is 0, then it is empty and the task does not have an assignee (false).

If true: Go to step 15.1.1.1

If false: Go to step 15.1.1.2

For each:

Control,

Condition UserId is not empty

 
Condition
 

15.1.1.1

Current situation: Case 1:
Task only exists in Valuestreamer
Task in Valuestreamer is assigend


Task has to be created in task table. Therefore a new row is added, with the values of the current task of the iteration of step 7

Add a new row:

Microsoft Dataverse,

Add new row with user

 
Parameters
 

15.1.1.2

Task has to be created in Planner.

Create a task:

Planner,

Create a task

 
Parameters
 

15.1.1.3

ID from newly created Planner task (step 15.1.1.2) has to be written into task table.

Update a row:

Microsoft Dataverse,

Update a row

 
Parameter
 

15.1.1.4

Inform user via teams that a new task has been created in planner and assigned to him.


End of iteration - jump back to step 7

Post message in a chat or channel:

Microsoft Teams,

Post message in a chat or channel

 
Parameter
 
 
Message
 

15.1.2.1

Current Situation: Case 2:
Task only exists in Valuestreamer
Task in Valuestreamer is NOT assigend


Task has to be created in task table. Therefore a new row is added, with the values of the current task of the iteration of step 7. The task has no assignee, therefore the user remains empty.

Add a new row:

Microsoft Dataverse,

Add new row with assignee = null

 
Parameters
 

15.1.2.2

Task has to be created in Planner.

Create a task:

Planner,

Create a task with assignee = null

 
Parameters
 

15.1.2.3

ID from newly created Planner task (step 15.1.2.2) has to be written into task table.


End of iteration - jump back to step 7.

Update a row:

Microsoft Dataverse,

Update a row 2

 
Parameter
 

15.2.1

Compare the value of userID (set in step 8) to 0.

If the value is not 0, then it is not empty and the task does have an assignee (true).

If the value is 0, then it is empty and the task does not have an assignee (false).

If true: Go to step 15.2.1.1

If false: Go to step 15.2.2.1

Condition:

Control,

Condition UserId is not empty

 
Condition
 

15.2.1.1

Situation: Case 3:
Task exists in Valuestreamer and Planner.
Task is assigned in Valuestreamer


For-each-Loop: Iterate over returned rows in task-table from step 14.

 

For each:

Control,

Iterate over returned rows of task table

 
Iterate over
 

15.2.1.2

Task has to be updated in task table.

Update a row:

Microsoft Dataverse,

Updating an existing row

 
Parameters
 

15.2.1.3

Task table has to be synchronized with tasks in Planner.

Get a task:

Microsoft Planner,

Get a task

 
Parameter
 

15.2.1.4

Set variable HelpGetPlannerUserId to userId of task in Planner

(Loop will be created automatically, when value is set to userID)

Set Variable:

Variables,

Set variable HelpGetPlannerUserId

 
Parameter
 

15.2.1.5

Compare the value of HelpGetPlannerUserId with 0.

If greater than 0, HelpGetPlannerUserId is not empty (true).

If not greater, HelpGetPlannerUserId is empty (false).

If true: Go to step 15.2.1.5.1.1

If false: Go to step 15.2.1.5.2.1

Condition:

Control,

Planner task has assignee

 
Condition
 

15.2.1.5.1.1

Get User profile from Office365-App

Get user profile (V2):
Office 365 Users,

Get user profile (V2)

 
Parameter
 

15.2.1.5.1.2

Set variable HelpGetPlannerUserId to mail from returned value of step 15.2.1.5.1.1

Set variable:
Variables,

Set variable HelpGetPlannerUserId (2)

 
Parameter
 

15.2.1.5.2.1

Set variable HelpGetPlannerUserId to null.

Set variable:
Variables,

Set variable HelpGetPlannerUserId (3)

 
Parameter
 

15.2.1.6

Compare the value of userEmail to HelpGetPlannerUserId.

If equal, then both tasks have the same assignee (true).

If not equal, then assignees are different and the assignee in planner has to be changed (false).

If true: Go to step 15.2.1.6.1.1

If false: Go to step 15.2.1.6.2.1

Condition:

Control,

Planner assignee is same as valuestreamer task assignee

 
Condition
 

15.2.1.6.1.1

Update task in planner with values from valuestreamer.


End of iteration - jump back to step 7.

Update a task:
Microsoft Planner,

Update a task

 
Parameter
 

15.2.1.6.2.1

Update task in planner to add a new assignee.

Update a task:
Microsoft Planner,

Update a task and adding a new assignee

 
Parameter
 

15.2.1.6.2.2

Remove old assignees from task.

This has to be done by an extra action. The action “Update task in planner” also has the option to remove an assignee -however this did not work while programming this flow (Jan2024)

Remove old assignee from a task:
Microsoft Planner,

Remove assingees from a task

 

 
Parameter
 

15.2.1.6.2.3

Inform user via teams that a task has been assigned to him.


End of iteration - jump back to step 7

Post message in a chat or channel:

Microsoft Teams,

Post message in a chat or channel

 
Parameter
 
 
Message
 

15.2.2.1

Situation: Case 4:
Task exists in Valuestreamer and Planner.
Task is NOT assigned in Valuestreamer


For-Each-Loop: Iterate over all returned tasks from step 14.

 

For each:

Control,

Iterate over all rows from task table

 
Iterate
 

15.2.2.2

Update a row in task table.

Update a row:
Microsoft Dataverse

Update a row in task-table

 
Parameter
 

15.2.2.3

Get task from Planner.

 

Get a task:
Microsoft Planner,

Get a task (2)

 
Parameter
 

15.2.2.4

Set variable HelpGetPlannerUserId to userID from task in planner (step 15.2.2.3). (Creates automatically a loop).

Set Variable:
Variables,

Set variable HelpGetPlannerUserId to userID from task in planner

 
Parameter
 

15.2.2.5

Update task in planner.

Update a task:
Microsoft Dataverse,

Update a task (2)

 

 
Parameter
 

15.2.2.6

Remove assignee from task in planner.


End of iteration - jump back to step 7

Remove assignee from a task:
Microsoft Planner,

Remove assignees from a task (2)

 
Parameter
 
 
 
 

 

Steps as screenshots

Following table shows some Screenshots which belong to a certain step from above.

 
 
 

Step

 

Screenshot

 
  1.  

 
GetTaskFromVS_Step_2.png

 

3.

 
grafik-20240205-104246.png

 

7.

 
grafik-20240205-104349.png

 

8.

 
grafik-20240205-104436.png

 

11.

 
grafik-20240205-104626.png

 

13.

 
grafik-20240205-105011.png

 

 

 

 

 

 
 
 

 

 

 

 
Get Task from VS to Planner.drawio.png

 

Draw.IO-File with Diagram-data:

 
Get Task from VS to Planner.drawio
19 Feb. 2024, 05:00 PM



+++++++++++++++++++++++++++++++++++++++++++++++++++++++

Update ValueStreamer when Planner task is completed

 

These pages are used to document the “Update ValueStreamer task when Planner task is completed”-Flow.

This flow can be used to:

  • update a task in ValueStreamer, when the corresponding task in Planner is completed

  • creates a new T-card in ValueStreamer, if the task in ValueStreamer was T-Card and was completed in Planner

How does the flow work (High-Level-View):

A Task-Table matches the data about tasks from ValueStreamer and Planner.

When a task in Planner is set to done, then the flow is triggered.
The flow updates the task-table and sends several HTTP-Requests to update the task in ValueStreamer.
If the task has the property tcard=true, then an identical task is created in ValueStreamer.

 

 
Update Valuestreamer task when Planner task is completed.drawio.png


 

Technical process (detailed) of flow "Update ValueStreamer task when Planner task is completed"

 

This page shows the process of the flow with all details.



Process Overview

 
 
 

Step

 

Screenshot

 

1-7

 
grafik-20240216-125233.png



7-11.1.2

 
grafik-20240216-125321.png


 
 
 

Step by step

 

Step

 

Description

 

Power Automate Function:

Connector,

Display-Name

 

Variables, Parameters, Formula

 
 

Step

 

Description

 

Power Automate Function:

Connector,

Display-Name

 

Variables, Parameters, Formula

 


When a task is completed in planner, then trigger this flow.

Trigger:
Planner,

When a task is completed



 
Parameter
 
 

Key Value
Group Id: YourGroupId
Plan Id: YourPlanId



2.

Get the completed task in planner from the task table.

List rows:
Microsoft Dataverse,

List rows of task table, based on planner task ID



 
Parameter
 
 

Key Value

Table Name: YourTaskTable
Filter Rows: cr4b3_planner eq triggerOutputs()?['body/id']



3.

Create a new variable called valuestreamerTaskId of type String and with no value.

Initialize Variable:
Variables,

Initialize Variable valuestreamerTaskId



 
Parameter
 
 

Key Value

Name: valuestreamerTaskId

Type: String



4.

For-Each-Loop to iterate over all returned rows from step 2. Power Automate does not know that there always will be only one returned row in this usecase.

For each:
Control,

For each



 
Iterate over
 
 

"@outputs('List_rows_of_task_table,_based_on_planner_task_ID')?['body/value']"



5.

Set variable valuestreamerTaskID to UUID from task table of the current loop.

Set variable:
Variables,

Set variable valuestreamerTaskId to UUID from table



 
Parameter
 
 

Key Value

Name: valuestramerTaskID

Value: @items('For_each')?['cr4b3_uuid']



6.

Send HTTP-Request (GET) to Valuestreamer, filtered by the value of valuestreamerTaskId, to get the data of the task in the ValueStreamer.

HTTP:
HTTP,

HTTP - GET task infos



 
Parameter
 
 

Key Value

Uri: YourDomain/api/exchange/tasks/variables('valuestreamerTaskID')
Method: Get

Headers:
Authorization Basic YOURTOKEN
Accept application/json

Queries:
include All
team YourTeam-ID
progress CREATED,PLANNED,INPROGRESS





7.

Parse the response of step 6 in a JSON-Object.

Parse JSON:
Data Operations,

Parse JSON from GET Task



 
Parameter
 
 

Content:
body('HTTP_reqeust_to_GET_all_tasks_from_VS_Team')



 
JSON-Schema
 
 

{
"type": "object",
"properties": {
"id": {
"type": "string"
},
"actualEffort": {
"type": "integer"
},
"actualEndDate": {},
"actualStartDate": {},
"archivedDate": {},
"category": {
"type": "string"
},
"createdBy": {
"type": "string"
},
"createdByTeam": {
"type": "string"
},
"createdDate": {
"type": "string"
},
"label": {},
"linkedToProcess": {},
"linkedToProcessitem": {},
"linkedToTeam": {},
"modifiedBy": {},
"modifiedDate": {},
"priority": {
"type": "boolean"
},
"progress": {
"type": "string"
},
"responsiblePerson": {},
"responsibleTeam": {
"type": "string"
},
"status": {
"type": "integer"
},
"statusMessage": {},
"targetEffort": {
"type": "number"
},
"targetEndDate": {
"type": "string"
},
"targetStartDate": {},
"title": {
"type": "string"
},
"tCard": {
"type": "boolean"
}
}
}



8.

Send HTTP-Request (Put) to ValueStreamer to set the task in ValueStreamer to done.

HTTP:
HTTP,

HTTP - PUT task to done



 
Parameter
 
 

Key Value

Uri: /api/exchange/tasks/variables('valuestreamerTaskID')
Method: PUT

Headers
Authorization Basic YOURTOKEN
Accept application/json



Body:

 
 
{ "progress": "DONE", "targetEndDate": "@{body('Parse_JSON_from_GET_Task')?['targetEndDate']}", "targetEffort": "@body('Parse_JSON_from_GET_Task')?['targetEffort']", "category": "@{body('Parse_JSON_from_GET_Task')?['category']}", "priority": "@body('Parse_JSON_from_GET_Task')?['priority']", "responsibleTeam": "@{body('Parse_JSON_from_GET_Task')?['responsibleTeam']}", "responsiblePerson": "@{body('Parse_JSON_from_GET_Task')?['responsiblePerson']}", "targetStartDate": "@{body('Parse_JSON_from_GET_Task')?['targetStartDate']}", "tCard": "@{body('Parse_JSON_from_GET_Task')?['tCard']}", "label": "@{body('Parse_JSON_from_GET_Task')?['label']}", "actualEffort": "@{body('Parse_JSON_from_GET_Task')?['actualEffort']}", "status": "@body('Parse_JSON_from_GET_Task')?['status']", "title": "@{body('Parse_JSON_from_GET_Task')?['title']}" }





9.

For-Each-Loop to iterate over all returned rows from step 2. Power Automate does not know that there always will be only one returned row in this usecase.

For each:
Control,

For each



 
Iterate over
 
 

"@outputs('List_rows_of_task_table,_based_on_planner_task_ID')?['body/value']"



10.

Update rows in task table by setting progress to done.



Update a row:
Micorosoft Dataverse,

Update a row



 
Parameter
 
 

Key Value

Table Name: YourTaskTable

Row_ID:

"@items('For_each_1')?['cr4b3_vs_ejotpoc_tasksid']"

Progress: DONE



11.

Check if the task is a tcard in the ValueStreamer.

If the card is a tcard, then the condition is true.

Otherwise the condition is false.

If true: Go to step 11.1.1

If false: There is nothing to do - end flow.

Condition:

Control,

Condition Check if task is tcard



 
Condition
 
 

body('Parse_JSON_fromGet_Task')?['tCard'] is equal to true



11.1.1

Initialize new variable with Name newDueDate of Datatype String and a value.

This variable will be used to create a new task in valuestreamer (Step 11.1.2).
Be aware that there might be errors if the value is earlier than today.
Depending on your usecase the value can be different.

Usecase 1:
The targetEndDate of the new task is one week later from today.

Usecase 2:
The targetEndDate of the new task is one week later than the old targetEndDate.

Initialize Variable:
Variables,

Initialize variable newDueDate and set value



 
Parameter
 
 

Key Value

Name: newDueDate

Type: String

Value: (Usecase 1) getFutureTime(7, ‘Day’, ‘yyyy-MM-dd’)

Value: (Usecase 2)
addDays(body('Parse_JSON_from_GET_Task')?['targetEndDate'], 7, ‘yyyy-MM-dd’)



11.1.2

The completed task is a tcard. Therefore an identical task has to be created in the ValueStreamer.

Send a HTTP-Request (Post) to ValueStreamer to create an identical task with a value for “targetEndDate” using the variable newDueDate from step 11.1.1.

End of flow.

HTTP:
HTTP,

HTTP - Post a new task in ValueStreamer



 
Parameter
 
 

Key Value

Uri: /api/exchange/tasks
Method: Post

Headers
Authorization Basic YOURTOKEN
Accept application/json


Body:

 
 
{ "targetEndDate": "@{variables('newDueDate')}", "targetEffort": @{body('Parse_JSON_from_GET_Task')?['targetEffort']}, "category": "@{body('Parse_JSON_from_GET_Task')?['category']}", "priority": @{body('Parse_JSON_from_GET_Task')?['priority']}, "responsibleTeam": "@{body('Parse_JSON_from_GET_Task')?['responsibleTeam']}", "responsiblePerson": "@{body('Parse_JSON_from_GET_Task')?['responsiblePerson']}", "tCard": "@{body('Parse_JSON_from_GET_Task')?['tCard']}", "label": "@{body('Parse_JSON_from_GET_Task')?['label']}", "status": @{body('Parse_JSON_from_GET_Task')?['status']}, "title": "@{body('Parse_JSON_from_GET_Task')?['title']}" }

 

 
 
 



Steps as screenshots

Following table shows some Screenshots which belong to a certain step from above.

 
 
 

Step

 

Screenshot

 

1.

 
grafik-20240219-105827.png



2.

 
grafik-20240216-125827.png



3.

 
grafik-20240216-125659.png



4.

 
grafik-20240216-125743.png



5

 
grafik-20240216-125904.png



6.

 
grafik-20240216-130145.png



7.

 
grafik-20240216-130839.png



8.

 
grafik-20240219-105558.png


9.

 
grafik-20240216-130931.png



10.

 
grafik-20240216-131003.png



11.

 
grafik-20240216-131033.png



11.1.1 (Usecase 2)

 
grafik-20240216-131102.png



11.1.2

 
grafik-20240216-131748.png