$overrides */ public function obligationFor(Visit $visit, WorkflowStage $stage, array $overrides = []): ?FinancialObligation { if (! $stage->requires_payment) { return null; } $existing = FinancialObligation::query() ->where('visit_id', $visit->id) ->where('workflow_stage_id', $stage->id) ->whereNull('deleted_at') ->first(); if ($existing !== null) { return $existing; } return FinancialObligation::create(array_merge([ 'owner_ref' => $visit->owner_ref, 'organization_id' => $visit->organization_id, 'branch_id' => $visit->branch_id, 'visit_id' => $visit->id, 'patient_id' => $visit->patient_id, 'workflow_stage_id' => $stage->id, 'stage_code' => $stage->code, 'charge_code' => $stage->charge_code, 'label' => $stage->charge_label ?? $stage->name, 'amount_minor' => $stage->default_amount_minor ?? $this->defaultAmountFor($stage), 'status' => FinancialObligation::STATUS_PENDING, ], $overrides)); } /** * Whether the visit may enter the stage. Stages with a "before" payment * gate require a cleared obligation; everything else is allowed. */ public function isSatisfied(Visit $visit, WorkflowStage $stage): bool { if (! $stage->gatesBeforeService()) { return true; } $obligation = FinancialObligation::query() ->where('visit_id', $visit->id) ->where('workflow_stage_id', $stage->id) ->whereNull('deleted_at') ->first(); return $obligation !== null && $obligation->isCleared(); } public function blockedReason(Visit $visit, WorkflowStage $stage): ?string { if ($this->isSatisfied($visit, $stage)) { return null; } return 'awaiting_payment'; } /** * Clear an obligation (paid / insurance authorized / waived / credit). */ public function clear( FinancialObligation $obligation, string $method, ?string $actorRef = null, ?string $paymentMode = null, ?string $externalReference = null, ): FinancialObligation { $status = match ($method) { 'insurance' => FinancialObligation::STATUS_AUTHORIZED, 'waiver' => FinancialObligation::STATUS_WAIVED, 'credit' => FinancialObligation::STATUS_DEFERRED, default => FinancialObligation::STATUS_PAID, }; $obligation->forceFill([ 'status' => $status, 'clearance_method' => $method, 'payment_mode' => $paymentMode, 'external_reference' => $externalReference, 'cleared_by' => $actorRef, 'cleared_at' => now(), ])->save(); return $obligation; } public function void(FinancialObligation $obligation): FinancialObligation { $obligation->forceFill(['status' => FinancialObligation::STATUS_VOID])->save(); return $obligation; } protected function defaultAmountFor(WorkflowStage $stage): int { if ($stage->charge_code === 'consultation') { return (int) config('care.billing.consultation_fee_minor', 0); } return 0; } }