Päť kompletných workflowov, každý ako súbor v obálke šablóny, ktorý načíta workflows:sync (alebo importuje versions()->import()). Každý recept je zároveň testovacím fixture balíka, ktorý sa publikuje a prejde do zdokumentovaného konečného stavu. Keďže ide o doslovné fixtures, každý recept používa jediný registrovaný typ subjektu testovacej aplikácie, "subject_type": "document", s poľami title a amount (preto kontrola „vyriešené“ v SLA recepte číta subject.title), a recepty pre onboarding a nočný plán používajú vlastný krok acme.create_task. Vo svojej aplikácii nastavte subject_type na kľúč, pod ktorým ste zaregistrovali skutočný model (napríklad purchase_request alebo ticket), a odkazy na polia nasmerujte na registrované polia tohto typu; validácia pri publikovaní nahlási každé pole, ktoré typ subjektu nemá.
Schválenie nákupu s eskaláciou
Podaná žiadosť o nákup nad 5 000 ide jej schvaľovateľovi; po dvoch pracovných dňoch bez rozhodnutia sa schvaľovanie preradí na jeho nadriadeného a po piatich vyprší. Malé žiadosti schvaľovanie preskočia. Potrebuje approvals-for-laravel a notifications-for-laravel:
{
"schema": "roundly.workflows.template/v1",
"definition": {
"key": "purchase-approval",
"name": "Purchase approval",
"subject_type": "document",
"description": null
},
"graph": {
"schema": "roundly.workflows.graph/v1",
"settings": {"timezone": "Europe/Bratislava", "calendar": "weekdays"},
"nodes": [
{
"id": "start",
"type": "trigger",
"config": {"trigger": "event", "event": "purchase_request.submitted"}
},
{
"id": "big",
"type": "condition",
"config": {"when": {"field": "subject.amount", "op": "gt", "value": "5000"}}
},
{
"id": "approve",
"type": "approval",
"config": {
"approvers": [{"kind": "field", "field": "subject.approver"}],
"rule": "any",
"expires_in": {"amount": 5, "unit": "business_days"}
},
"escalations": [
{
"after": {"amount": 2, "unit": "business_days"},
"actions": [
{
"type": "reassign",
"to": [{"kind": "field", "field": "subject.approver_manager"}]
}
]
}
]
},
{
"id": "mark",
"type": "set_field",
"config": {"field": "subject.priority", "value": "approved"}
},
{
"id": "tell",
"type": "notify",
"config": {
"notification": "purchase.rejected",
"recipients": [{"kind": "field", "field": "subject.requester"}]
}
},
{"id": "done", "type": "end"}
],
"edges": [
{"from": "start", "to": "big"},
{"from": "big", "to": "approve", "outcome": "true"},
{"from": "big", "to": "mark", "outcome": "false"},
{"from": "approve", "to": "mark", "outcome": "approved"},
{"from": "approve", "to": "tell", "outcome": "rejected"},
{"from": "approve", "to": "tell", "outcome": "expired"},
{"from": "mark", "to": "done"},
{"from": "tell", "to": "done"}
]
}
}Slučka prepracovania
Cyklus je povolený len cez uzol delay alebo loop, takže „schválenie → prepracovanie → znova schválenie“ ide cez loop, ktorý obmedzí počet kôl. Každý prechod cez approve otvorí nové kolo s novým idempotentným kľúčom. Potrebuje approvals-for-laravel:
{
"schema": "roundly.workflows.template/v1",
"definition": {"key": "rework-loop", "name": "Rework loop", "subject_type": "document", "description": null},
"graph": {
"schema": "roundly.workflows.graph/v1",
"nodes": [
{
"id": "start",
"type": "trigger",
"config": {"trigger": "event", "event": "purchase_request.submitted"}
},
{"id": "round", "type": "loop", "config": {"max": 3}},
{
"id": "approve",
"type": "approval",
"config": {"approvers": [{"kind": "field", "field": "subject.approver"}], "rule": "any"}
},
{
"id": "rework",
"type": "wait",
"config": {"event": "purchase_request.resubmitted", "timeout": "P14D"}
},
{"id": "done", "type": "end"},
{
"id": "gave_up",
"type": "end",
"config": {"status": "failed", "reason": "too many rounds"}
}
],
"edges": [
{"from": "start", "to": "round"},
{"from": "round", "to": "approve", "outcome": "continue"},
{"from": "round", "to": "gave_up", "outcome": "exhausted"},
{"from": "approve", "to": "done", "outcome": "approved"},
{"from": "approve", "to": "rework", "outcome": "rejected"},
{"from": "approve", "to": "gave_up", "outcome": "expired"},
{"from": "rework", "to": "round", "outcome": "received"},
{"from": "rework", "to": "gave_up", "outcome": "timed_out"}
]
}
}Kontrolný zoznam pri nástupe
Ručný workflow bez subjektu: dva vlastné kroky a podriadený workflow, na ktorý čaká. Dieťa welcome je akákoľvek ručná definícia bez subjektu u toho istého vlastníka; start_workflow odmietne takú, ktorej povinné vstupy nepokrýva:
{
"schema": "roundly.workflows.template/v1",
"definition": {
"key": "onboarding",
"name": "Onboarding checklist",
"subject_type": null,
"description": null
},
"graph": {
"schema": "roundly.workflows.graph/v1",
"nodes": [
{
"id": "start",
"type": "trigger",
"config": {
"trigger": "manual",
"inputs": [{"name": "employee", "type": "string", "label": "Employee", "required": true}]
}
},
{"id": "laptop", "type": "acme.create_task", "config": {"title": "Order a laptop"}},
{"id": "accounts", "type": "acme.create_task", "config": {"title": "Create the accounts"}},
{
"id": "welcome",
"type": "start_workflow",
"config": {"definition": "welcome", "subject": null, "wait": true}
},
{"id": "done", "type": "end"},
{
"id": "failed",
"type": "end",
"config": {"status": "failed", "reason": "The welcome did not finish"}
}
],
"edges": [
{"from": "start", "to": "laptop"},
{"from": "laptop", "to": "accounts"},
{"from": "accounts", "to": "welcome"},
{"from": "welcome", "to": "done", "outcome": "completed"},
{"from": "welcome", "to": "failed", "outcome": "failed"}
]
}
}SLA pripomienka
Čaká, kým sa tiket nevyrieši — kontroluje sa pri každej zmene subjektu (trait HasWorkflows) a každých 15 minút — a po štyroch pracovných hodinách eskaluje spustením workflowu sla-chase. Po piatich dňoch je SLA porušené:
{
"schema": "roundly.workflows.template/v1",
"definition": {
"key": "sla-reminder",
"name": "SLA reminder",
"subject_type": "document",
"description": null
},
"graph": {
"schema": "roundly.workflows.graph/v1",
"settings": {"timezone": "UTC", "calendar": "weekdays"},
"nodes": [
{
"id": "start",
"type": "trigger",
"config": {"trigger": "event", "event": "ticket.opened"}
},
{
"id": "resolved",
"type": "wait",
"config": {
"condition": {"field": "subject.title", "op": "equals", "value": "Resolved"},
"poll": "PT15M",
"timeout": "P5D"
},
"escalations": [
{
"after": {"amount": 4, "unit": "business_hours"},
"actions": [{"type": "start_workflow", "definition": "sla-chase"}]
}
]
},
{"id": "done", "type": "end"},
{
"id": "breached",
"type": "end",
"config": {"status": "failed", "reason": "SLA breached"}
}
],
"edges": [
{"from": "start", "to": "resolved"},
{"from": "resolved", "to": "done", "outcome": "met"},
{"from": "resolved", "to": "breached", "outcome": "timed_out"}
]
}
}Nočný plán
Plán bez subjektu: každú noc o 02:00 bratislavského času jedna inštancia. Zmeškané noci v okne schedule.catch_up sa spustia raz; opakovaná hodina pri prechode času beží raz:
{
"schema": "roundly.workflows.template/v1",
"definition": {"key": "nightly", "name": "Nightly clean-up", "subject_type": null, "description": null},
"graph": {
"schema": "roundly.workflows.graph/v1",
"nodes": [
{
"id": "start",
"type": "trigger",
"config": {"trigger": "schedule", "cron": "0 2 * * *", "timezone": "Europe/Bratislava"}
},
{"id": "report", "type": "acme.create_task", "config": {"title": "Nightly report"}},
{"id": "done", "type": "end"}
],
"edges": [{"from": "start", "to": "report"}, {"from": "report", "to": "done"}]
}
}Overenie v teste
uses(InteractsWithWorkflows::class);
$this->fakeStep('approval', fn (StepContext $c) => StepResult::complete($c->visit() === 1 ? 'rejected' : 'approved'),
outcomes: ['approved', 'rejected', 'expired']);
Workflows::definitions()->sync(resource_path('workflows'), publish: true);
Workflows::emit('purchase_request.submitted', $request);
Workflows::emit('purchase_request.resubmitted', $request);
expect($request->workflowInstances()->sole())->toHaveCompletedVia(['start', 'round', 'approve', 'rework', 'round', 'approve', 'done']);Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.