PortiBlog

How to: Document Management Systeem met parallelle goedkeuringsworkflow bouwen

11 juni 2018

Inleiding

Microsoft Flow is een ‘gratis dienst’ van Microsoft die je kan gebruiken om processen te automatiseren. Onze collega Niek Jachimowski schreef hier al eerder een blog over: https://blog.portiva.nl/2016/09/06/aan-de-slag-met-microsoft-flow/

Flow biedt natuurlijk geweldige mogelijkheden en wordt vaak aangehaald als de vervanger voor SharePoint Designer workflows. De standaard voorbeelden voor een goedkeuring op een document binnen SharePoint zijn natuurlijk heel leuk en helpen je al een eind op weg. Maar wanneer je op een kwaliteitsafdeling zit en je moet borgen dat al je kwaliteitsprocedures goedgekeurd worden door verschillende afdelingsmanagers en kwaliteitsmedewerker van de afdeling. Dan wordt de Flow al snel complexer. In deze blog willen we je daarom meenemen in zaken waar je tegen loopt bij het maken van een geavanceerde goedkeuringsworkflow met Microsoft Flow.

  • Hoe pas je parallelle goedkeuring toe
  • Reminders bij acties
  • 30 dagen retentie (looptijd voor de Flow)
  • Rechten voor het starten van een Flow
  • Start knop met JSON voor je Flow
  • Geschiedenislijst (wat werkt wel en wat niet)

Al deze punten gaan we stapsgewijs met jullie door.

Situatieschets

Voor een kwaliteitssysteem is in dit geval gekozen voor een oplossing waarbij de procedures en werkinstructies in een SharePoint bibliotheek worden opgeslagen. De bibliotheek moet voorzien worden van de volgende instellingen:

  • De new “moderne” experience;
  • Een inhoudstype met alle vereiste kenmerken:
    • Type document (managed meta data);
    • Documenteigenaar (People picker);
    • Goedkeurders (People picker meerder waarden toestaan);
    • Organisatieniveau (managed meta data);
    • Laatste reviewdatum;
    • …;
  • Versiebeheer primaire en secundaire versie;
  • Content approval;
  • Alleen gebruikers die items kunnen bewerken;

De kolom met (meerdere) goedkeurders wordt als volgt ingericht.

Kolom instelling
Deze kolom wordt later in het goedkeuringsproces gebruikt zodat alle goedkeurders tegelijkertijd zijn of haar goedkeuring moeten geven.
In SharePoint Designer workflows is deze stap een eitje en kun je ook eenvoudig aangeven of maar 2 van de X goedkeurders moeten bevestigen. Bekijk de onderstaande afbeeldingen maar eens:


Bron: https://collab365.community/sharepoint-2013-approval-workflow-with-3-dinamically-determined/

Deze zaken zijn nog niet mogelijk met Microsoft Flow. Toch gaan we met MS Flow het volgende scenario behandelen: we willen graag dat alle geselecteerde goedkeurders (dus parallel) moeten goedkeuren voordat het document de status goedgekeurd heeft in de documentbibliotheek. Dit betekend dat na de goedkeuring het document ook naar een primaire versie gaat. En dus voor alle gebruikers zichtbaar wordt!

Goedkeurings proces

Microsoft Flow heeft een standaard oplossing voor een goedkeuring genaamd ‘Start an approval’

Gedurende deze handleiding maken we gebruik van deze specifieke koppeling.
Daarnaast noteren we de naam van de goedkeurder niet in de Flow zelf. Het komt vaak voor dat de goedkeurders veranderen en er zelfs meerdere en verschillende goedkeurders zijn per document.

Parallelle goedkeuring

Wanneer de goedkeuring door meerdere personen moet worden gegeven dan kan dat serieel of parallel. Links een serieel proces, rechts een parallel proces

Het voordeel van een parallelle goedkeuring is dat ‘Keuring 2’ niet hoeft te wachten op het resultaat van ‘Keuring 1’. Hierdoor zal het gehele proces eerder voltooid zijn. Je hebt binnen Microsoft Flow meerdere methodes om parallelle acties uit te voeren. De bekendste en makkelijkste is het toevoegen van een parallelle branch:

Deze methode heeft echter enkele nadelen:

  • Bij te veel parallelle acties wordt de Flow editor erg traag;
  • Iedere parallelle actie moet apart geconfigureerd worden;
  • Deze methode kan niet (standaard) gebruik maken van één veld met daarin meerdere goedkeurders.

Het is ook mogelijk om maar één actie aan te maken die meerdere keren parallel wordt uitgevoerd. Maak hiervoor een kolom aan in de bibliotheek waarin de goedkeurders worden vastgesteld en zorg ervoor dat deze kolom meerdere personen kan bevatten:

Kolom instelling

Selecteer deze SharePoint kolom voor de ‘Toegewezen aan’ optie in de Approval:

Vervolgens zal je zien dat er automatisch een ‘Apply to each’ blok wordt toegepast op de approval:

Dit gebeurdt automatisch omdat de kolom ’Goedkeurder’ in dit geval meerdere personen kan bevatten. Hierdoor zal het goedkeuringsverzoek naar alle personen gestuurd wordt die in de kolom ‘Goedkeurder’ zijn ingevuld.

Ga vervolgens naar de instellingen van het ‘Apply to each’ blok:

Zet daar ‘Override Default’ aan en zet ‘Degree of Parallelism’ op de hoogste stand (50):

Wanneer de Flow nu start zullen alle gebruikers tegelijkertijd het verzoek tot goedkeuring ontvangen via mail.

30 dagen Flow retentie

Op het moment van schrijven heeft Microsoft Flow een retentie periode van 30 dagen. Dit houdt in dat Flows na 30 dagen stoppen, zelfs als er een approval actief is. Het is belangrijk hiervan bewust te blijven bij het ontwerpen van een goedkeuring workflow. Er zijn meerdere opties om dit probleem te verzachten, bijvoorbeeld door de Flow in meerdere Flows op te splitsen.

Wanneer het hele proces in één Flow wordt gebouwd kan het interessant zijn om de Flow initiator een mailtje te sturen wanneer de 30 dagen bijna verlopen indien nog niet iedereen heeft gereageerd. Dit is simpel te realiseren door de volgende methode:

Voer hiervoor de volgende stappen in de Flow uit:

  1. Voeg aan het begin van de Flow een parallelle actie toe;
  2. Selecteer de actie “Delay” en vul daarin de gewenste waarschuwingsperiode in;
  3. Voeg een ‘Mail’-actie toe en zet de Flow starter in het “To:” veld
  4. Voeg een “Terminate” actie toe aan de goedkeuringskant van de workflow zodat de Flow daadwerkelijk stopt wanneer iedereen het document heeft gekeurd. Hierdoor zal de Flow niet blijven wachten totdat de 25 dagen voorbij zijn.

Dit proces is ook te gebruiken voor bijvoorbeeld reminders of escalaties.

De geschiedenislijst

In het geval van goedkeuringen is het vaak belangrijk om ook achteraf te zien wat er is gebeurd met de goedkeuringen. Wie heeft wat goedgekeurd of afgekeurd. Middels Flow is deze informatie makkelijk weg te schrijven in SharePoint. Dit kan in een kolom bijbehorend bij het document zelf of in een andere lijst.

Beide hebben voor en nadelen:

 

Kolom bij het document

Afzonderlijke lijst

Voordelen

  • Makkelijk
  • Geschiedenis direct gekoppeld aan document
  • De geschiedenis blijft behouden wanneer het document wordt verplaatst/ verwijderd
  • Flow kan geschiedenis bijwerken terwijl iemand het document open heeft.

Nadelen

  • Flow kan de geschiedenis niet bijwerken wanneer iemand het document open heeft
  • Geschiedenis raakt verloren wanneer het document verplaatst wordt
  • Moeilijkere optie (aanmaken extra lijst en extra stappen in Flow)
  • JSON nodig om link naar geschiedenis “mooi” te maken

Met de volgende stappen laten we zien hoe het mogelijk om de goedkeuringsproces geschiedenis in een afzonderlijke lijst te bewaren.

  1. Maak een aparte lijst aan op hetzelfde niveau als de documenten bibliotheek. Vul de lijst vervolgens aan met de volgende instellingen en kolommen:
  • Versiegeschiedenis aanzetten
  • Hyperlink kolom toevoegen met de naam “Link”
  • ‘Meerdere regels tekst’ kolom toevoegen met type tekst “Plain” en “Append Changes to Existing Text” op “Yes”


2. Maak in de documentenbibliotheek een kolom (type nummer) aan genaamd ‘GeschiedenisID’ en een kolom (type hyperlink) genaamd ‘Goedkeuringsgeschiedenis’.

3. Flow updaten zodat deze de geschiedenis wegschrijft in de nieuwe lijst:

Voeg een ‘Initialize variable’-actie toe (type Integer) met de naam GoedkeuringsGeschiedenisID:

Voeg voor het goedkeuringsproces een scope toe genaamd ‘Goedkeuringsgeschiedenis’ met daarin een condition met de volgende ‘geavanceerde’ vergelijking:

@empty(string(body('Get_item')?['GeschiedenisID']))

Deze code kijkt of het GeschiedenisID is gevuld. Dit is niet het geval wanneer de Flow nog niet eerder heeft gelopen.

In het ‘No’-gedeelte van de conditie voeg je de volgende acties toe:

  • 'Get item' om het ID van de GoedkeuringsGeschiedenis te achterhalen
  • Bij ID vullen we de volgende expressie in:
    int(body('Get_item')?['GeschiedenisID'])Dit is nodig omdat GeschiedenisID geen integer is terwijl Get item ID alleen integers accepteert.
  • ‘Set variable’ om het ID tijdelijk weg te schrijven in de variabel ‘GoedkeuringsGeschiedenisID’.
  • een ‘Update item’ om de eerste info in de geschiedenis weg te schrijven

Omdat de ‘Naam’ verplicht is om in deze actie in te vullen vul je deze met de naam die al in het item stond.

De No kant (niet gevulde GeschiedenisID) is hiermee afgerond.
In de ‘Yes’ sectie voeg je de volgende acties toe:

  • ‘Create item’ om een nieuw Goedkeuringsgeschiedenis item te maken

    Als naam gebruik je de naam van het bestand inclusief de extentie. Als Link gebruik je de link naar het bestand. Vul hierin meteen de eerste info in die je in de geschiedenis wilt bijhouden.
  • ‘Update file properties’ om het document te linken met het nieuwe Goedkeuringsgeschiedenis item.
    Bij ‘Id’ vul je het Id van ‘For selected item’ in. Bij Link vul je hier de URL van het display form van de Goedkeuringsgeschiedenis lijst in met als ID het ID van de ‘Create item’-actie. Ditzelfde ID gebruik je ook voor ‘GeschiedenisID’.

  • Een ‘Set variable’ om de ‘GoedkeuringsGeschiedenisID’ te vullen om het ‘Goedkeuringsgeschiedenis’-item later te identificeren. (deze stap is noodzakelijk omdat SharePoint nummers als float opslaat in een number field en niet als integer).

De conditie is hiermee volledig en zou er ongeveer als volgt moeten uitzien:

Tussen de ‘Goedkeuringsgeschiedenis’-scope en de ‘Goedkeuringsproces’-actie voeg je een ‘Get item’-actie toe om nogmaals de gegevens van het ‘Goedkeuringsgeschiedenis’-item op te halen. Je gebruikt hiervoor het ID dat is opgeslagen in de variabel ‘GoedkeuringsGeschiedenisID’

Het is handig wanneer je deze actie een makkelijk te herkennen naam geeft zoals in het voorbeeld hierboven. Deze stap is noodzakelijk omdat je vooraf niet weet of het een bestaande of nieuwe goedkeuringsgeschiedenis koppeling betreft.

Voeg tenslotte in de Flow op de plaatsten waar het nodig is Goedkeuringsgeschiedenis updates toe wanneer informatie weggeschreven moet worden door middel van de actie ‘Update item’. Bijvoorbeeld wanneer iemand het document goed of afkeurt:

Let hierbij op dat je de Goedkeuringsgeschiedenis lijst selecteert en daarbij het ID & Naam gebruikt die je hebt opgehaald met de laatste ‘Get item’-actie (in dit geval met de naam ‘Get item Goedkeuringsgeschiedenis’).

De Flow is nu gereed en zou er als volgt uit moeten zien:

JSON Column formatter

Zo we hebben nu een flow gemaakt, de status van het document staat in de bibliotheek en we hebben een kolom met een lelijke url voor de werkstroomgeschiedenis.

3 punten die super zijn om met een column formatter te gaan doen:

  1. Startknop maken voor de Flow;
  2. Status van het document met kleurtjes afhankelijk van de laatste reviewdatum – 3 jaar;
  3. De lelijke geschiedenis url die door Flow gemaakt wordt netjes maken;

 

1.      Startknop voor Flow:

We passen hier de stappen toe die standaard hier beschreven staan dit is niet zo spannend, maar het effect voor de eindgebruiker is natuurlijk superhandig. Geen dropdowns of menu’s meer door om het goedkeuringsproces te starten.

2.      Status van het document met kleurtjes afhankelijk van de laatste reviewdatum – 3 jaar;

In het begin van het document werd gevraagd om een laatste reviewdatum aan te maken deze datum wordt na goedkeuring door de workflow gezet op -3 jaar na goedkeuring. Nu is er ook de standaard content approval kolom in SharePoint. Deze is niet te beïnvloeden en heeft geen status vervallen.

Het is een combinatie van de volgende scripts:

  1. https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/column-formatting#conditional-formatting-based-on-the-value-in-a-text-or-choice-field-advanced
  2. https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/column-formatting#apply-formatting-based-on-date-ranges

Hieronder is het eindresultaat te zien:


{
    "$schema": "http://columnformatting.sharepointpnp.com/columnFormattingSchema.json",
    "debugMode": true,
    "elmType": "div",
    "attributes": {
      "class": {
        "operator": "?",
        "operands": [
          {
            "operator": "&&",
            "operands": [
              {
                "operator": "!=",
                "operands": [
                  "",
                  {
                    "operator": "toString()",
                    "operands": [
                      "[$Laatste%20reviewdatum]"
                    ]
                  }
                ]
              },
              {
                "operator": "<=",
                "operands": [
                  "[$ Laatste%20reviewdatum]",
                  {
                    "operator": "-",
                    "operands": [
                      "@now",
                      94608000000
                    ]
                  }
                ]
              }
            ]
          },
          "sp-field-severity--blocked",
          {
            "operator": "?",
            "operands": [
              {
                "operator": "||",
                "operands": [
                  {
                    "operator": "==",
                    "operands": [
                      {
                                  "operator": "toString()",
                                  "operands": [
                                    "@currentField"
                                  ]
                                },
                                "Concept"
                    ]
                  },
                  {
                    "operator": "==",
                    "operands": [
                      {
                                  "operator": "toString()",
                                  "operands": [
                                    "@currentField"
                                  ]
                                },
                                "Draft"
                    ]
                  }
                ]
              },
              "sp-field-severity--warning",
              {
                "operator": "?",
                "operands": [
                  {
                    "operator": "||",
                    "operands": [
                      {
                        "operator": "==",
                        "operands": [
                          {
                                      "operator": "toString()",
                                      "operands": [
                                        "@currentField"
                                      ]
                                    },
                                    "Goedgekeurd"
                        ]
                      },
                      {
                        "operator": "==",
                        "operands": [
                          {
                                      "operator": "toString()",
                                      "operands": [
                                        "@currentField"
                                      ]
                                    },
                                    "Approved"
                        ]
                      }
                    ]
                  },
                  "sp-field-severity--good",
                  "sp-field-severity--blocked"
                ]
              }
            ]
          }
        ]
      }
    },
    "children": [
      {
        "elmType": "span",
        "style": {
          "display": "inline-block",
          "padding": "0 4px"
        },
        "attributes": {
          "iconName": {
            "operator": "?",
            "operands": [
              {
                "operator": "&&",
                "operands": [
                  {
                    "operator": "!=",
                    "operands": [
                      "",
                      {
                        "operator": "toString()",
                        "operands": [
                          "[$Laatste%20reviewdatum]"
                        ]
                      }
                    ]
                  },
                  {
                    "operator": "<=",
                    "operands": [
                      "[$Laatste%20reviewdatum]",
                      {
                        "operator": "-",
                        "operands": [
                          "@now",
                          94608000000
                        ]
                      }
                    ]
                  }
                ]
              },
              "ErrorBadge",
              {
                "operator": "?",
                "operands": [
                  {
                    "operator": "||",
                    "operands": [
                      {
                        "operator": "==",
                        "operands": [
                          {
                                      "operator": "toString()",
                                      "operands": [
                                        "@currentField"
                                      ]
                                    },
                                    "Goedgekeurd"
                        ]
                      },
                      {
                        "operator": "==",
                        "operands": [
                          {
                                      "operator": "toString()",
                                      "operands": [
                                        "@currentField"
                                      ]
                                    },
                                    "Approved"
                        ]
                      }
                    ]
                  },
                  "CheckMark",
                  {
                    "operator": "?",
                    "operands": [
                      {
                        "operator": "||",
                        "operands": [
                          {
                            "operator": "==",
                            "operands": [
                              {
                                          "operator": "toString()",
                                          "operands": [
                                            "@currentField"
                                          ]
                                        },
                                        "Concept"
                            ]
                          },
                          {
                            "operator": "==",
                            "operands": [
                              {
                                          "operator": "toString()",
                                          "operands": [
                                            "@currentField"
                                          ]
                                        },
                                        "Draft"
                            ]
                          }
                        ]
                      },
                      "Forward",
                      "ErrorBadge"
                    ]
                  }
                ]
              }
            ]
          }
        }
      },
      {
        "elmType": "span",
        "txtContent": {
          "operator": "?",
          "operands": [
            {
              "operator": "==",
              "operands": [
                "",
                {
                  "operator": "toString()",
                  "operands": [
                    "[$Laatste%20reviewdatum]"
                  ]
                }
              ]
            },
            "@currentField",
            {
              "operator": "?",
              "operands": [
                {
                  "operator": "<=",
                  "operands": [
                    "[$Laatste%20reviewdatum]",
                    {
                      "operator": "-",
                      "operands": [
                        "@now",
                        94608000000
                      ]
                    }
                  ]
                },
                "Verlopen",
                "@currentField"
              ]
            }
          ]
        }
      }
    ]
  }

Het probleem was vooral om te controleren wanneer er nog geen laatste reviewdatum is, dit hebben we als volgt opgelost:


"operator": "?", ß If statement in JSON
        "operands": [
          {
            "operator": "&&", ß AND
            "operands": [
              {
                "operator": "!=", ß Niet gelijk aan
                "operands": [
                  "",
                  {
                    "operator": "toString()", ß omzetten naar een String van de laatste reviewdatum, we kunnen anders niet controleren of deze leeg is.
                    "operands": [
                      "[$Laatste%20reviewdatum]"
                    ]
                  }
                ]
    

3.      De lelijke Geschiedenis url die door Flow gemaakt wordt netjes maken;

Zo ziet de hyperlink er zonder formatting uit met Flow, je kan de omschrijving nog niet rechtstreeks meenemen op een hyperlink veld in SharePoint.

Met het volgende script gebaseerd op: https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/column-formatting#turn-field-values-into-hyperlinks-basic

Alleen zit hier weer de controle bij of de URL leeg is:


{
    "$schema": "http://columnformatting.sharepointpnp.com/columnFormattingSchema.json",
    "elmType": "a",
               
    "txtContent": {
          "operator": "?",
          "operands": [
            {
              "operator": "==",
              "operands": [
                "",
                {
                  "operator": "toString()",
                  "operands": [
                    "@currentField"
                  ]
                }
              ]
            },
            "@currentField",
                                                "Flow geschiedenis"
                                               
           
              ]
            }
                ,
    "attributes": {
        "target": "_blank",
        "href": {
            "operator": "+",
            "operands": [
                "",
                                                                "@currentField"
            ]
        }
    }
}

 

Eindresultaat

Zo dit ziet er al beter uit dan zonder de column formatting.

Conclusie

Je moet een heleboel stappen doorlopen voordat je een goedkeuringsworkflow gemaakt hebt. Nog niet alles kan met Flow of je hebt een heleboel stappen nodig om bijvoorbeeld parallel goed te keuren of een geschiedenislijst op te zetten.

Maar de eindgebruikers ervaring van de Approval taken uit Flow is veel beter dan de oude taken met SharePoint. Plus de zaken die je met de custom column formatter kunt doen leveren toegevoegde waarde voor de eindgebruiker.


Submit a comment

Portiva Logo

Jean-Paul van den Bogert

Functional Consultant