Przewodnik po Flowee - jak budować aplikacje procesowe przy użyciu Flowee Help

Wysłanie listy dokumentów na portal

Checklista, która wyświetlana jest na liście dokumentów jest budowana dynamicznie.
Możemy z jej poziomu zarówno pobierać dokumenty jak i je dodawać albo jedno i drugie w zależności od typu dokumentu.

Możemy również grupować listę dokumentów wg potrzeb biznesowych.
(Aktualnie dokumenty podzielone są na grupy i typy.) Grupą jest np. Formularze ogólne, a typem Ankieta ESG.

Tworzysz regułę i umieszczasz ją w procesie:

Obraz zawierający tekst, linia, Czcionka, numer Zawartość wygenerowana przez AI może być niepoprawna.
Image79

Istnieją dwa sposoby przekazywania dokumentów na Portal:

Sposób nr 1 – statycznie dodanie wszystkich dokumentów procesowych z kodami (reguła), później w skrypcie (script task) wskazanie konkretnych dokumentów i integracja (External task) do przekazania listy dokumentów, które mają być widoczne na Portalu do dołączenia:
-> Stwórz Script task

Przykład konfiguracji został opisany w rozdziale Budowa list dokumentów procesowych oraz zasilenie checklisty (dla dowolnego taska manualnego)

Sposób nr 2 – dynamicznie:

Mamy nową akcję, gdzie po kliknięciu przez użytkownika w tę akcję, nadal zostajemy w głównym procesie, natomiast równolegle startujemy Event sub-proces, gdzie system przelicza dokumenty na podstawie głównej reguły oraz reguł w sub procesie i na końcu wywołujemy External taska do przekazania listy dokumentów na Portal

Cel mechanizmu

Dynamiczna checklista dokumentów służy do automatycznego określania:

  • jakie dokumenty są wymagane w sprawie,

  • które są obowiązkowe, a które opcjonalne,

  • w jakim kontekście występują (np. Wniosek, Zabezpieczenia, Uruchomienie),

  • czy dokument jest tylko do odczytu, do uzupełnienia, czy do pobrania.

Checklista nie jest zdefiniowana „na sztywno” w BPMN.
Jest przeliczana dynamicznie na podstawie danych wniosku oraz reguł biznesowych (DMN).

Architektura rozwiązania

Mechanizm składa się z 5 logicznych etapów:

  1. Wyzwolenie przeliczenia checklisty => Akcja na zadaniu z prawidłowo skonfigurowanym messageName

  2. Mapowanie danych procesu do danych decyzyjnych

  3. Wywołanie reguły DMN

  4. Scalenie wyniku reguły z definicjami dokumentów

  5. Utworzenie / aktualizacja checklisty w systemie zewnętrznym

Schemat:

Zdarzenie → Mapowanie danych → DMN → Skrypt łączenia → External Task → System checklist

1. Event SubProcess – wyzwalanie przeliczenia checklisty

Utwórz Event sub process zgodnie z rozdziałem Tworzenie Event Sub Process

Znaczenie:

  • jest to nieprzerywający Event SubProcess

  • uruchamia się po akcji zdefiniowanej jako event: saveTaskAndSendMessage

  • działa równolegle do głównego procesu,

  • służy wyłącznie do przeliczenia checklisty.

Dzięki temu checklista może być aktualizowana wiele razy w trakcie życia sprawy.

Obraz zawierający tekst, zrzut ekranu, Czcionka, dokument Zawartość wygenerowana przez AI może być niepoprawna.

Metadane:

Obraz zawierający tekst, zrzut ekranu, numer, Czcionka Zawartość wygenerowana przez AI może być niepoprawna.

Kluczową rolę odgrywa metadana messageName, po której następuje wywołanie Event sub procesu „Przeliczenie checklisty”. W tym przypadku przeliczenie checklisty nastąpi w momencie zapisania danych na wniosku oraz wysłaniu wniosku do procesu.
<messageName> musi być równa z tym co umieścisz w konfiguracji Message Start Event (tj. jak na przykładzie: W tasku Wniosek kredytowy konfigurujesz akcje w tasku Wniosek kredytowy:

Obraz zawierający tekst, zrzut ekranu, diagram, Czcionka Zawartość wygenerowana przez AI może być niepoprawna.

Następnie w Message Start Event Twojego Event Sub Process umieszczasz tą samą wartość:

Obraz zawierający tekst, zrzut ekranu, Czcionka, linia Zawartość wygenerowana przez AI może być niepoprawna.

2. Mapowanie danych procesu do danych decyzyjnych

Obraz zawierający zrzut ekranu, tekst, Prostokąt, design Zawartość wygenerowana przez AI może być niepoprawna.

Ten krok:

✔ pobiera dane z repozytorium procesu
✔ buduje obiekt wejściowy dla DMN

Przykładowe obszary logiki:

  • wyciąganie produktów i zabezpieczeń,

  • budowanie list:

    • rodzajów zabezpieczeń,

    • produktów,

    • kwot finansowania,

  • obliczenia biznesowe (np. wiek danych).

➤ Konfiguracja taska

➤ Script =\> skrypt należy dostosować do inputów do reguły DOCUMENTS_LIST, w zależności od danych biznesowych wykorzystywanych do przeliczenia checklisty

import groovy.json.JsonSlurper import groovy.json.JsonOutput import groovy.time.TimeCategory import java.text.SimpleDateFormat def slurper = new JsonSlurper() def jsonOuput = new JsonOutput() def processInstanceId = execution.getProcessInstanceId() def formData = slurper.parseText(dataDelegate.getValueByPath(processInstanceId, 'formData')) def ckkDate = formData.public.client.dataModifyDate; def formioDate = formData.public.dataZlozeniaWniosku; def ckkDataAge = -1; if (ckkDate && formioDate) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") def parsedCkkDate = sdf.parse(ckkDate); def parsedFormioDate = sdf.parse(formioDate); def duration = groovy.time.TimeCategory.minus( parsedFormioDate, parsedCkkDate ); ckkDataAge = duration.days / 365 } def produktyGrid = (formData.public.produktyDataGrid ?: []) def czyPoreczycielemBedzieFirma = produktyGrid.collectMany { produkt -> (produkt.zabezpieczeniaDataGrid ?: []).collect { it.czyPoreczycielemBedzieFirma } }.findAll { it != null } def rodzajZabezpieczenia = produktyGrid.collectMany { produkt -> (produkt.zabezpieczeniaDataGrid ?: []).collect { it.pomocniczeDoZabezpieczen } }.findAll { it != null } def produkty = produktyGrid.collect { it.produkt }.findAll { it != null } def czyKaucjaZabezpiecza100KwotyFinansowania = produktyGrid .collectMany { produkt -> (produkt.zabezpieczeniaDataGrid ?: []).collect { it.czyKaucjaZabezpiecza100KwotyFinansowania} } .findAll { it != null } def okresKredytowania = produktyGrid .collect { it.okresKredytowania } .findAll { it != null } def kwotaFinansowania = produktyGrid .collect { it.kwotaFinansowania } .findAll { it != null } def kwotaGwarancji = produktyGrid .collect { it.gwarancja?.kwotaGwarancji } .findAll { it != null } return [ 'legalForm': formData.public.client.legalForm, 'czyPoreczycielemBedzieFirma': czyPoreczycielemBedzieFirma, 'riskLevel': formData.public.client.riskLevel, 'ckkDataAge': ckkDataAge, 'rodzajZabezpieczenia': rodzajZabezpieczenia, 'czyKaucjaZabezpiecza100KwotyFinansowania': czyKaucjaZabezpiecza100KwotyFinansowania, 'produkt': produkty, 'okresKredytowania': okresKredytowania, 'czyProwadzonaFirmaZagrazaNormomSrodowiskowym': formData.public.czyProwadzonaFirmaZagrazaNormomSrodowiskowym ?: '', 'czyIstniejaPodmiotyPowiazane': formData.public.czyIstniejaPodmiotyPowiazane ?: '', 'kwotaFinansowania': [*kwotaFinansowania, *kwotaGwarancji] ];

Efekt działania

  • jeden spójny obiekt jako Result Variable (creditApplicationRuleData):

creditApplicationRuleData = { legalForm, riskLevel, produkt, rodzajZabezpieczenia, kwotaFinansowania, ckkDataAge, ... }

Ten krok jest kluczowym buforem pomiędzy strukturą formularza a strukturą reguł DMN.

3. Reguła DMN – wybór wymaganych dokumentów

Obraz zawierający tekst, zrzut ekranu, Prostokąt, design Zawartość wygenerowana przez AI może być niepoprawna.

Reguła DMN (Documents_LIST) jest centralnym elementem decyzyjnym mechanizmu checklisty.

To w niej zapisana jest biznesowa wiedza o tym:

👉 jakie dokumenty są wymagane w zależności od parametrów wniosku takich jak np. forma prawna klienta, produkt, kwota produktu.

Proces nie „wie”, jakie dokumenty są potrzebne – on jedynie:

  • zbiera dane,

  • przekazuje je do reguły,

  • interpretuje wynik.

Wejście do reguły (Input)

Wejście pochodzi w całości z obiektu creditApplicationRuleData, przygotowanego w poprzednim ScriptTasku.

Inputy do reguły (biznesowe warunki brzegowe takie jak np. forma prawna klienta, produkt, kwota etc.)

Variable Name

Type

Value

legalForm

String or expression

${creditApplicationRuleData.legalForm}

czyPoreczycielemBedzieFirma

String or expression

${creditApplicationRuleData.czyPoreczycielemBedzieFirma}

riskLevel

String or expression

${creditApplicationRuleData.riskLevel}

ckkDataAge

String or expression

${creditApplicationRuleData.ckkDataAge}

rodzajZabezpieczenia

String or expression

${creditApplicationRuleData.rodzajZabezpieczenia}

czyKaucjaZabezpiecza100KwotyFinansowania

String or expression

${creditApplicationRuleData.czyKaucjaZabezpiecza100KwotyFinansowania}

produkt

String or expression

${creditApplicationRuleData.produkt}

okresKredytowania

String or expression

${creditApplicationRuleData.okresKredytowania}

czyProwadzonaFirmaZagrazaNormomSrodowiskowym

String or expression

${creditApplicationRuleData.czyProwadzonaFirmaZagrazaNormomSrodowiskowym}

czyIstniejaPodmiotyPowiazane

String or expression

${creditApplicationRuleData.czyIstniejaPodmiotyPowiazane}

kwotaFinansowania

String or expression

${creditApplicationRuleData.kwotaFinansowania}

Przykładowy wynik reguły:

[ { "type": [ { "id": "KRS", "required": true }, { "id": "DO", "required": false } ] } ]

4. Łączenie reguły z definicjami dokumentów

Obraz zawierający zrzut ekranu, tekst, Prostokąt, design Zawartość wygenerowana przez AI może być niepoprawna.

Ten krok łączy 3 źródła:

  1. wynik reguły (creditAppChecklist)

  2. słownik dokumentów (documentDefinitionsV2)

  3. wcześniej zapisane pliki (documentFileStorageIds)

Cele:

  • zamiana typów dokumentów na pełne definicje,

  • nadanie flag:

    • required

    • readonly

    • predefinedFile

  • określenie trybu obsługi dokumentu:

Tryb

Znaczenie

read-write-required

plik istnieje, wymagany, można podmienić

write-only-required

brak pliku, wymagany

read-only

plik istnieje, tylko do pobrania

write-only-not-required

opcjonalny upload

➤ Konfiguracja taska

➤ Script

import groovy.json.JsonOutput def jsonOutput = new JsonOutput() def documentDefinitions = execution.getVariable('documentDefinitionsV2') def documentFileStorageIds = execution.getVariable('documentFileStorageIds') def creditAppChecklist = execution.getVariable('creditAppChecklist') // Mapowanie checklisty z dodaniem pola `required` def mappedChecklist = creditAppChecklist .collectMany { result -> result.type } .collect { type -> def docDefinition = documentDefinitions.find { document -> document.metadata.type == type.id } if (docDefinition) { docDefinition.required = type.required docDefinition.metadata.required = type.required } return docDefinition; }.findAll { it != null } // Usuwa `null`, jeśli nie znaleziono dopasowania // Dodanie dodatkowych pól do każdego dokumentu def finalDocs = mappedChecklist.collect { doc -> def predefinedFile = documentFileStorageIds.find { it -> it.type == doc.metadata.type }?.fileStorageId def downloadableOnly = doc.metadata.readonly def required = doc.required ?: false // Określenie wartości `definitionId` def definitionId if (required && predefinedFile) { definitionId = "read-write-required" } else if (!required && predefinedFile && !downloadableOnly) { definitionId = "read-write-not-required" } else if (required && !predefinedFile) { definitionId = "write-only-required" } else if (downloadableOnly && predefinedFile) { definitionId = "read-only" } else if (!required && !predefinedFile) { definitionId = "write-only-not-required" } return [ metadata: doc.metadata, predefinedFile: predefinedFile, definitionId: definitionId, context: "Wniosek" ] } // Konwersja do JSON i zwrócenie wyniku return S(jsonOutput.toJson(finalDocs))

Efekt działania

  • lista gotowa do wysłania do checklisty:

{ "metadata": {...}, "predefinedFile": "...", "definitionId": "read-write-required", "context": "Wniosek" }

Ten krok jest adapterem technicznym między regułą biznesową a systemem checklist.

5. External Task – aktualizacja checklisty

Obraz zawierający zrzut ekranu, tekst, Prostokąt, design Zawartość wygenerowana przez AI może być niepoprawna.

Zadanie przekazuje:

  • nazwę checklisty,

  • alias instancji,

  • listę elementów do dodania,

  • kontekst.

Worker:

📌 BPMN orkiestruje, a system checklist wykonuje fizyczne operacje.

➤Konfiguracja taska

Implementation:

Element

Znaczenie

type="external"

Zadanie będzie obsługiwane przez zewnętrznego workera

topic=" UpdateChecklistItemsListV2"

Kanał komunikacji – worker subskrybuje ten topic

asyncBefore="true"

Osobna transakcja (bezpieczne retry, brak blokad)

inputParameter

Dane wejściowe przekazywane do workera

Dane przekazywane do workera

Do workera trafiają następujące zmienne:

Variable

Type

Value

baseDefinitionName

String or expression

${checklistDefinitionName}

checklistInstanceAlias

String or expression

${checklistAlias}

itemsToAdd

String or expression

${checklistMergedWniosekV2}

contexts

List

Wniosek

6. Event Sub Process – wysłanie wiadomości systemowej na Portal Klienta po aktualizacji zmiany statusów pozycji z checklisty

Obraz zawierający tekst, Prostokąt, Produkty papierowe Zawartość wygenerowana przez AI może być niepoprawna.

Ten fragment procesu realizuje mechanizm automatycznej reakcji na zmianę statusów elementów checklisty dokumentów i wygenerowania wiadomości systemowej do użytkownika na Portalu Klienta.

Subproces jest uruchamiany zdarzeniem typu Message wysyłanym z modułu checklisty.

Oznacza, że:

  • subproces nie jest uruchamiany przez sequence flow

  • jest „podpięty” do głównego procesu

  • reaguje na zdarzenia asynchroniczne w trakcie życia instancji

Czyli:

👉 proces główny cały czas „nasłuchuje” na określony komunikat.

➤Konfiguracja taska

Implementation:

Element

Znaczenie

type="message start event"

Zadanie będzie obsługiwane przez zewnętrznego workera

Global message reference="CHECKLIST_ITEMS_STATUS_MODIFIED"

Kanał komunikacji – proces główny nasłuchuje na komunikat CHECKLIST_ITEMS_STATUS_MODIFIED

asyncBefore="true"

Osobna transakcja (bezpieczne retry, brak blokad)

inputParameter

Dane wejściowe przekazywane do workera

➤Scrypt task => realizuje zapis nowej wiadomości do struktury komunikacji procesu

➤ Script

import groovy.json.JsonSlurper import groovy.json.JsonOutput import java.time.ZonedDateTime import java.time.format.DateTimeFormatter def slurper = new JsonSlurper() def jsonOuput = new JsonOutput() def processInstanceId = execution.getProcessInstanceId() def communication = dataDelegate.getValueByPath(processInstanceId, 'communication.system.messages') if (communication == '-') { return; } def messages = slurper.parseText(communication) messages << [ content: content, system: "system", sender: "", creationTime: ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT) ] def valuesMap = [ 'communication.system.messages': jsonOuput.toJson(messages) ] dataDelegate.setValuesByPath(processInstanceId, valuesMap)

Dzięki temu Portal Klienta może odczytać nową wiadomość i wyświetlić ją użytkownikowi.

➤ Inputs

7. Event Sub Process – wysłanie wiadomości systemowej na Portal Klienta po dodaniu nowej pozycji do checklisty -\> zlecenie dokumentów ad-hoc

Obraz zawierający tekst, Prostokąt, Produkty papierowe Zawartość wygenerowana przez AI może być niepoprawna.

Ten fragment procesu realizuje mechanizm automatycznej reakcji na dodanie elementów do checklisty dokumentów i wygenerowania wiadomości systemowej do użytkownika na Portalu Klienta.

Subproces jest uruchamiany zdarzeniem typu Message wysyłanym z modułu checklisty.

Oznacza, że:

  • subproces nie jest uruchamiany przez sequence flow

  • jest „podpięty” do głównego procesu

  • reaguje na zdarzenia asynchroniczne w trakcie życia instancji

Czyli:

👉 proces główny cały czas „nasłuchuje” na określony komunikat.

➤Konfiguracja taska

Implementation:

Element

Znaczenie

type="message start event"

Zadanie będzie obsługiwane przez zewnętrznego workera

Global message reference="ITEM_ADDED"

Kanał komunikacji – proces główny nasłuchuje na komunikat ITEM_ADDED

asyncBefore="true"

Osobna transakcja (bezpieczne retry, brak blokad)

inputParameter

Dane wejściowe przekazywane do workera

➤Scrypt task => realizuje zapis nowej wiadomości do struktury komunikacji procesu

➤ Script

import groovy.json.JsonSlurper import groovy.json.JsonOutput import java.time.ZonedDateTime import java.time.format.DateTimeFormatter def slurper = new JsonSlurper() def jsonOuput = new JsonOutput() def processInstanceId = execution.getProcessInstanceId() def communication = dataDelegate.getValueByPath(processInstanceId, 'communication.system.messages') if (communication == '-') { return; } def messages = slurper.parseText(communication) messages << [ content: content, system: "system", sender: "", creationTime: ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT) ] def valuesMap = [ 'communication.system.messages': jsonOuput.toJson(messages) ] dataDelegate.setValuesByPath(processInstanceId, valuesMap)

Dzięki temu Portal Klienta może odczytać nową wiadomość i wyświetlić ją użytkownikowi.

➤ Inputs

8. Event Sub Process – wysłanie wiadomości systemowej na Portal Klienta po usunięciu dodanej nowej pozycji do checklisty -\> wycofanie zlecenia dokumentów ad-hoc

Obraz zawierający tekst, Prostokąt, Produkty papierowe Zawartość wygenerowana przez AI może być niepoprawna.

Ten fragment procesu realizuje mechanizm automatycznej reakcji na usunięcie dodanych ad-hoc elementów do checklisty dokumentów i wygenerowania wiadomości systemowej do użytkownika na Portalu Klienta.

Subproces jest uruchamiany zdarzeniem typu Message wysyłanym z modułu checklisty.

Oznacza, że:

  • subproces nie jest uruchamiany przez sequence flow

  • jest „podpięty” do głównego procesu

  • reaguje na zdarzenia asynchroniczne w trakcie życia instancji

Czyli:

👉 proces główny cały czas „nasłuchuje” na określony komunikat.

➤Konfiguracja taska

Implementation:

Element

Znaczenie

type="message start event"

Zadanie będzie obsługiwane przez zewnętrznego workera

Global message reference="ITEM_REMOVED"

Kanał komunikacji – proces główny nasłuchuje na komunikat ITEM_REMOVED

asyncBefore="true"

Osobna transakcja (bezpieczne retry, brak blokad)

inputParameter

Dane wejściowe przekazywane do workera

➤Scrypt task => realizuje zapis nowej wiadomości do struktury komunikacji procesu

➤ Script

import groovy.json.JsonSlurper import groovy.json.JsonOutput import java.time.ZonedDateTime import java.time.format.DateTimeFormatter def slurper = new JsonSlurper() def jsonOuput = new JsonOutput() def processInstanceId = execution.getProcessInstanceId() def communication = dataDelegate.getValueByPath(processInstanceId, 'communication.system.messages') if (communication == '-') { return; } def messages = slurper.parseText(communication) messages << [ content: content, system: "system", sender: "", creationTime: ZonedDateTime.now().format(DateTimeFormatter.ISO_INSTANT) ] def valuesMap = [ 'communication.system.messages': jsonOuput.toJson(messages) ] dataDelegate.setValuesByPath(processInstanceId, valuesMap)

Dzięki temu Portal Klienta może odczytać nową wiadomość i wyświetlić ją użytkownikowi.

➤ Inputs

Cechy rozwiązania stosowania implementacji dynamicznych checklist

✅ pełna dynamika
✅ brak checklist „na sztywno”
✅ logika biznesowa w DMN
✅ możliwość wielokrotnego przeliczania
✅ odporność na zmiany formularzy
✅ łatwe wersjonowanie reguł

Rekomendowany standard projektowy

W każdym procesie checklistowym powinny istnieć:

  1. Event SubProcess „Przelicz checklistę”

  2. ScriptTask „Mapowanie danych do reguł”

  3. BusinessRuleTask (DMN)

  4. ScriptTask „Scalenie z definicjami”

  5. External Task „Aktualizacja checklisty”

09 lutego 2026