NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

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 kryptomien

Odoslaní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.