Definicja procesu (4) – anulowanie zadań

Dzisiejszy artykuł będzie o sposobach anulowania zadań w procesie – to znaczy o tym, jak spowodować aby jakieś zadanie które jest w trakcie realizacji zostało anulowane po wystąpieniu określonego zdarzenia. Może nie jest to oczywiste na pierwszy rzut oka, ale taka funkcjonalność jest bardzo często potrzebna. Jednym z takich przypadków jest decyzja post factum (deferred choice), omówiona w tym artykule. Deferred choice jest jednak dość szczególnym przypadkiem stosowanym w określonej sytuacji, a w NGinn mamy do dyspozycji trochę więcej konstrukcji z anulowaniem.

Jako przykład na dziś weźmy sobie taki scenariusz ‘biznesowy’:

Do firmy wpływają reklamacje od klientów. Każda reklamacja powinna zostać obsłużona w ciągu 30 dni. Reklamacja jest wciągana do systemu ‘Trouble Ticketing’ i w tym systemie przypisywana do odpowiedniej jednostki organizacyjnej. Chcemy aby system automatycznie przypominał tym ludziom że zbliża się termin na rozpatrzenie reklamacji – na przykład poprzez wysłanie wiadomości email na 10 dni przed terminem rozpatrzenia, potem na 3 dni przed terminem no i  w momencie upływu terminu rozpatrzenia – wtedy z kopią do szefa działu obsługi reklamacji. Kiedy reklamacja zostanie rozpatrzona (niezależnie od tego z jakim wynikiem), system powinien zaprzestać wysyłania e-maili z eskalacjami.

Jest to prosty przykład i można go zaimplementować na wiele różnych sposobów – na przykład tak:

 

Proces reklamacji

Proces reklamacji

W momencie startu tego procesu wykonywane jest zadanie ‘fork’ – nie robi ono nic oprócz uruchomienia czterech równoległych ścieżek w procesie. Pierwsza ścieżka kończy się zadaniem ‘Obsłuż reklamację’ – jest to zlecenie obsługi reklamacji przekazywane do Działu Obsługi Reklamacji. Pozostałe trzy ścieżki odpowiadają za eskalacje. Na każdej z nich jest zadanie ‘Timeout’ odliczające odpowiedni okres czasu (odpowiednio 20, 27 i 30 dni od daty startu), po upływie tego czasu uruchamiane są zadania eskalacyjne wysyłające odpowiedniej treści email.

Bez użycia anulowania ścieżki te wykonują się niezależnie od siebie, czyli eskalacje będą wysyłane nawet gdy zadanie ‘Obsłuż reklamację’ się zakończy. Aby przerwać wysyłanie eskalacji po zakończeniu obsługi reklamacji wprowadzamy przejścia anulujące, oznaczone na rysunku czerwoną przerywaną linią. Każde takie przejście po zakończeniu zadania ‘Obsłuż reklamację’ zabiera wszystkie tokeny z miejsca do którego prowadzi (o ile te tokeny tam są) – w tym przypadku chodzi o miejsca e1, e2 i e3. Zabranie tokenów z miejsc e1, e2 i e3 oznacza że jeśli któreś z zadań ‘Timeout’ jest aktywne to straci ono wymagany token na wejściu i zostanie anulowane. Oczywiście jeśli któryś z Timeoutów już się wykonał to zjadł token ze swojego wejścia, wtedy przejście anulujące nie będzie miało żadnego efektu.  Osiągamy w ten sposób swój cel – po zakończeniu zadania ‘Obsłuż reklamację’, jeśli były jakieś oczekujące eskalacje to zostaną anulowane a proces będzie się mógł zakończyć bo już nie będzie tokenów poza miejscem końcowym.

Definicja naszego procesu wygląda tak:

<?xml version="1.0" encoding="windows-1250"?>
<process version="1" name="Reklamacje" xmlns="http://www.nginn.org/WorkflowDefinition.1_0.xsd" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <dataTypes>
    </dataTypes>
    <variables>
		<variable name="v1" type="int" dir="In" required="true" />
    </variables>
    <body>
        <places>
            <place id="start" type="Start" />
            <place id="end" type="End" />
			<place id="e1" />
			<place id="e2" />
			<place id="e3" />
        </places>
        <tasks>
            <task id="T0" type="Empty" splitType="AND" label="fork">
            </task>

			<task id="T1" type="Manual" label="Obsluz reklamacje">
				<parameters>
				</parameters>
            </task>

			<task id="TE1" type="Timer" label="Timeout - 10 dni">
				<parameters>
					<param variable="ExpirationDate"><expr>DateTime.Now.AddDays(20)</expr></param>
				</parameters>
            </task>

			<task id="TE2" type="Timer" label="Timeout - 3 dni">
				<parameters>
					<param variable="ExpirationDate"><expr>DateTime.Now.AddDays(27)</expr></param>
				</parameters>
            </task>

			<task id="TE3" type="Timer" label="Timeout">
				<parameters>
					<param variable="ExpirationDate"><expr>DateTime.Now.AddDays(30)</expr></param>
				</parameters>
            </task>

            <task id="ESK1" label="Eskalacja 1" type="SendNotification" splitType="AND">
			</task>

			<task id="ESK2" label="Eskalacja 2" type="SendNotification" splitType="AND">
			</task>

			<task id="ESK3" label="Eskalacja 3" type="SendNotification" splitType="AND">
			</task>

        </tasks>
        <flows>
            <flow from="start" to="T0" />
            <flow from="T0" to="T1" />
			<flow from="T0" to="e1" />
			<flow from="T0" to="e2" />
			<flow from="T0" to="e3" />
			<flow from="T1" to="e1" cancelling="true" />
			<flow from="T1" to="e2" cancelling="true" />
			<flow from="T1" to="e3" cancelling="true" />
			<flow from="e1" to="TE1" />
			<flow from="e2" to="TE2" />
			<flow from="e3" to="TE3" />

			<flow from="TE1" to="ESK1" />
			<flow from="TE2" to="ESK2" />
			<flow from="TE3" to="ESK3" />
			<flow from="ESK1" to="end" />
			<flow from="ESK2" to="end" />
			<flow from="ESK3" to="end" />
			<flow from="T1" to="end" />
        </flows>
    </body>
</process>

Przejścia anulujące są zapisane w sekcji ‘flows’, to te z atrybutem cancelling=”true”. Mam nadzieję że ten przykład odpowiednio ilustruje przeznaczenie przejść anulujących.

Dzisiejszy przykład pokazuje jeszcze jedną fajną rzecz. Wyobraźmy sobie że zamiast zadania ‘Obsłuż reklamację’ mamy tam cały podproces obsługi reklamacji – z etapami, przechodzeniem przez różne działy, wysyłaniem pism itp. Widać wtedy, że gdy chcemy do takiego procesu dodać eskalacje możemy to zrobić w ogóle w niego nie ingerując – procedura eskalacyjna jest zupełnie zewnętrzna w stosunku do procesu nią objętego.

One Response “Definicja procesu (4) – anulowanie zadań”

  1. nginn mówi:

    PS: będą kolejne części na temat anulowania zadań – temat nie jest wyczerpany.