Purchase Request Approval
Alpha notice: This walkthrough mirrors the hosted demo. Status names and thresholds are demo-specific.
This example shows procurement approval with amount-based escalation, host status mapping through hooks, and guarded downstream actions. It builds on the same patterns as Refund Approval with a clearer draft → review → approved lifecycle.
What you will build
Draft / Rejected → Submit for internal review
→ Procurement Manager Review → Amount Gate
→ [amount >= 10000] Finance Review → Approved
→ [default] → Approved
After approval, host actions such as archive become available. ERP, PO creation, and payment logic stay outside DBFlow Core.
Workflow key and constants
| Item | Value |
|---|---|
| Workflow key | procurement_request_approval |
| Model | App\Models\ProcurementRequest |
| Constants class | App\DBFlow\ProcurementRequest\ProcurementRequestWorkflow |
Node keys:
procurement_manager_reviewamount_gatefinance_review(high amount only)end_approved
Amount condition: amount >= 10000 (ProcurementRequestWorkflow::CONDITION_HIGH_AMOUNT).
Host status mapping
Demo business status (ProcurementRequestStatus enum):
| Status | Meaning |
|---|---|
draft |
Editable request, not in review |
pending_review |
Submitted; workflow running or awaiting completion |
approved |
Workflow approved; ready for downstream purchasing steps |
rejected |
Workflow rejected; may return to draft |
archived |
Host archival after approval (not a DBFlow terminal state) |
Hooks keep DBFlow and host columns aligned:
| Hook | Host update |
|---|---|
onStarted |
draft or rejected → pending_review, set submitted_at |
onApproved |
→ approved, set reviewed_at |
onRejected |
→ rejected, set reviewed_at |
onCancelled |
conservative return to draft (unless already archived) |
Implementation: app/DBFlow/ProcurementRequest/ProcurementRequestWorkflowHooks.php.
Where the demo code lives
| Area | Path |
|---|---|
| Model | app/Models/ProcurementRequest.php |
| Workflow constants | app/DBFlow/ProcurementRequest/ProcurementRequestWorkflow.php |
| Hooks | app/DBFlow/ProcurementRequest/ProcurementRequestWorkflowHooks.php |
| Status guard (optional) | app/DBFlow/ProcurementRequest/ProcurementRequestStatusGuard.php |
| Definition seeder | database/seeders/Demo/ProcurementRequestWorkflowSeeder.php |
| Resource actions | app/Filament/Resources/ProcurementRequestResource/Concerns/HasLifecycleActions.php |
| Presenter | app/Filament/Resources/ProcurementRequestResource/Support/ProcurementRequestWorkflowPresenter.php |
| Integration test | tests/Feature/ProcurementRequestDbflowIntegrationTest.php |
Model integration
ProcurementRequest implements Workflowable and WorkflowContextInterface:
public function getWorkflowVariables(): array
{
return [
'amount' => (float) $this->amount,
'department' => $this->department,
// ...
];
}
Workflow definition source
ProcurementRequestWorkflowSeeder builds the JSON graph, validates, and publishes — same pipeline as refund disputes. Constants in ProcurementRequestWorkflow keep keys and conditions centralized.
Start the workflow
Submit for internal review is visible when status is draft or rejected, no running workflow exists, and the record is not archived.
With DBFlow enabled:
DBFlow::start(
ProcurementRequestWorkflow::KEY,
$record,
Auth::user(),
);
With DBFlow disabled, the demo falls back to setting pending_review directly — useful for baseline comparisons only.
Approvals
Assignees use My Tasks. Finance reviewers receive tasks only when amount >= 10000 routes through finance_review.
Guard downstream actions
Archive and other post-approval operations must check host status — not workflow internals:
->visible(fn (ProcurementRequest $record): bool =>
$record->status === ProcurementRequestStatus::Approved
)
DBFlow does not ship ERP adapters. If you sync to an external system, invoke that from WorkflowHooks::onApproved() or a host job — keep integration code in app/, not in Core.
Filament resource integration
ProcurementRequestResource composes HasLifecycleActions for submit, return-to-draft, and archive actions. ProcurementRequestWorkflowPresenter surfaces workflow metadata on the resource view.
Timeline and instance detail
Use Workflow Instances or the presenter link to inspect dbflow_workflow_logs through the Standard timeline. See Workflow Timeline.
Alpha caveats
- Threshold (
10000) and assignee user IDs are demo constants archivedis a host-only state after approval — not an DBFlow end node- Cancel hook rolls back to
draft; adjust for your production policy - Published definition must exist before
DBFlow::start()— run Host Integration sync on deploy permission/callbackassignees requireregisterAssigneeResolver()— not Laravel permission names
What's next
- Workflow Hooks → —
onStarted/onApproved/onRejected/onCancelledreference - Eloquent Models — traits and hooks overview
- Filament Resource Actions — action visibility patterns
- Refund Approval — simpler first example