Refund Approval
Alpha notice: This walkthrough mirrors the hosted demo. Workflow keys, node names, and demo statuses may change between demo releases.
This is the recommended first example for learning DBFlow end to end. It combines a real JSON workflow definition, Core runtime calls, host Filament resource action patterns, hooks, and Standard UI pages.
What you will build
A refund dispute moves from investigation into a formal resolution approval. High refund amounts require an extra reviewer.
Investigation → Submit for resolution review
→ Support Lead Review → Amount Gate
→ [refund_amount >= 500] Risk Reviewer Review → Approved
→ [default] → Approved
Terminal workflow rejection maps the dispute to a lost outcome through hooks. Approval maps to won.
Workflow key and constants
| Item | Value |
|---|---|
| Workflow key | refund_dispute_approval |
| Model | App\Models\RefundDispute |
| Constants class | App\DBFlow\RefundDispute\RefundDisputeWorkflow |
Key node keys from the demo:
support_lead_reviewamount_gate(condition; routing viatransitions[].condition)risk_reviewer_reviewend_approved
Amount condition: refund_amount >= 500 (RefundDisputeWorkflow::CONDITION_HIGH_AMOUNT).
Where the demo code lives
| Area | Path |
|---|---|
| Model | app/Models/RefundDispute.php |
| Workflow constants | app/DBFlow/RefundDispute/RefundDisputeWorkflow.php |
| Hooks | app/DBFlow/RefundDispute/RefundDisputeWorkflowHooks.php |
| Definition seeder | database/seeders/Demo/RefundDisputeWorkflowSeeder.php |
| Resource actions | app/Filament/Resources/RefundDisputeResource/Concerns/HasLifecycleActions.php |
| Workflow presenter | app/Filament/Resources/RefundDisputeResource/Support/RefundDisputeWorkflowPresenter.php |
| Instance page override | app/DBFlow/Filament/Pages/DemoViewWorkflowInstance.php |
| Integration test | tests/Feature/RefundDisputeDbflowIntegrationTest.php |
Model integration
RefundDispute uses HasWorkflow plus Workflowable, WorkflowContextInterface, and WorkflowRouteResolvable:
public function getWorkflowVariables(): array
{
return [
'refund_amount' => (float) $this->refund_amount,
'risk_score' => (int) $this->risk_score,
// ...
];
}
Variables feed transition conditions such as refund_amount >= 500.
Workflow definition source
The demo builds a JSON definition in RefundDisputeWorkflowSeeder using WorkflowBuilderNodeFactory, validates with WorkflowDefinitionValidator, then publishes via CreateWorkflowDraft and PublishWorkflowDraft.
You can also express the same graph through a WorkflowDefinitionProvider and SyncWorkflowDefinitions — see Code-defined Workflows.
Register hooks
DBFlow::registerWorkflowHooks(
app(WorkflowHooksRegistry::class),
RefundDisputeWorkflow::KEY,
RefundDisputeWorkflowHooks::class,
);
RefundDisputeWorkflowHooks::onApproved() sets host status to won. onRejected() sets lost. Investigation states (open, investigating, needs_evidence) remain host-owned until submit.
Start the workflow
From the Filament resource, Submit for resolution review calls:
DBFlow::start(
RefundDisputeWorkflow::KEY,
$record,
Auth::user(),
);
Visibility requires investigating/needs-evidence status, DBFlow enabled, no running workflow, and record not closed.
Approve and reject tasks
Assignees work from My Tasks (/admin/dbflow/my-workflow-tasks). The table actions call MyWorkflowTaskActionRunner, which wraps:
DBFlow::approve($task, $actor, $comment);
DBFlow::reject($task, $actor, $comment, RejectStrategy::End);
Programmatically, resolve the pending task from the running instance:
$task = $dispute->runningWorkflowInstance(RefundDisputeWorkflow::KEY)
?->tasks()
->where('status', WorkflowTaskStatus::Pending)
->first();
Filament resource integration
RefundDisputeResource uses HasLifecycleActions for:
- DBFlow submit action (
submitThroughDbflow) - Host-only investigation buttons when DBFlow is off
- Notifications on
WorkflowAlreadyRunningException
RefundDisputeWorkflowPresenter adds workflow status, pending task counts, and links to My Tasks / instance detail on the resource view.
Timeline and instance detail
Open Workflow Instances → select the running instance, or follow the link from the resource presenter.
ViewWorkflowInstance (demo subclass DemoViewWorkflowInstance) renders the timeline via WorkflowInstanceTimelinePresenter. See Workflow Timeline.
Why start here
- Single clear workflow key and amount branch
- Extensive demo tests (
RefundDisputeAmountBranchingTest,RefundDisputeDbflowIntegrationTest) - Shows separation between host investigation lifecycle and DBFlow resolution approval
- Covers hooks, presenters, and Filament actions without ERP complexity
Alpha caveats
- Assignee user IDs in the seeder are environment-specific demo users
roleassignee type is schema-only — usecallback+registerAssigneeResolver()for dynamic approverspermissionassignee type is a resolver key alias, not Spatie Permission — see Assignee resolution- Pro visual editing is separate; this example uses published JSON from seeders or sync
- Re-run sync or demo seeders after resetting the database to restore published definitions
What's next
- Filament Resource Actions → — general action patterns
- Purchase Request Approval → — second demo scenario
- Approve and Reject → — runtime API reference