Activate Care workflows across check-in and financial clearance.
Deploy Ladill Care / deploy (push) Failing after 1m9s
Deploy Ladill Care / deploy (push) Failing after 1m9s
Enforce service-queue gates, cashier settlements, and clinical stage progression so patient journeys cannot bypass configured payment or authorization rules. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -40,22 +40,26 @@ class WorkflowEngine
|
||||
*/
|
||||
public function start(Visit $visit, ?FacilityWorkflow $workflow = null): ?VisitStageAdvance
|
||||
{
|
||||
$workflow ??= $this->workflowFor($visit);
|
||||
if ($workflow === null) {
|
||||
return null;
|
||||
}
|
||||
return DB::transaction(function () use ($visit, $workflow) {
|
||||
Visit::query()->whereKey($visit->id)->lockForUpdate()->first();
|
||||
|
||||
$existing = $this->currentAdvance($visit);
|
||||
if ($existing !== null) {
|
||||
return $existing;
|
||||
}
|
||||
$workflow ??= $this->workflowFor($visit);
|
||||
if ($workflow === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$first = $workflow->firstStage();
|
||||
if ($first === null) {
|
||||
return null;
|
||||
}
|
||||
$existing = $this->currentAdvance($visit);
|
||||
if ($existing !== null) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
return $this->enterStage($visit, $workflow, $first);
|
||||
$first = $workflow->firstStage();
|
||||
if ($first === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->enterStage($visit, $workflow, $first);
|
||||
});
|
||||
}
|
||||
|
||||
public function currentAdvance(Visit $visit): ?VisitStageAdvance
|
||||
@@ -79,9 +83,31 @@ class WorkflowEngine
|
||||
*/
|
||||
public function canAdvance(Visit $visit, ?string $toCode = null): bool
|
||||
{
|
||||
$current = $this->refreshGate($visit);
|
||||
if ($current === null || $current->isBlocked()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$target = $this->resolveTarget($visit, $toCode);
|
||||
|
||||
return $target !== null && $this->gate->isSatisfied($visit, $target);
|
||||
return $target !== null;
|
||||
}
|
||||
|
||||
public function canManuallyAdvance(Visit $visit): bool
|
||||
{
|
||||
$stage = $this->refreshGate($visit)?->stage;
|
||||
if ($stage === null || in_array($stage->type, [
|
||||
'consultation',
|
||||
'laboratory',
|
||||
'imaging',
|
||||
'procedure',
|
||||
'pharmacy',
|
||||
'payment',
|
||||
], true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->canAdvance($visit);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +119,8 @@ class WorkflowEngine
|
||||
public function advance(Visit $visit, ?string $toCode = null): ?VisitStageAdvance
|
||||
{
|
||||
return DB::transaction(function () use ($visit, $toCode) {
|
||||
Visit::query()->whereKey($visit->id)->lockForUpdate()->first();
|
||||
|
||||
$workflow = $this->workflowFor($visit);
|
||||
if ($workflow === null) {
|
||||
return null;
|
||||
@@ -103,7 +131,11 @@ class WorkflowEngine
|
||||
return null;
|
||||
}
|
||||
|
||||
$current = $this->currentAdvance($visit);
|
||||
$current = $this->refreshGate($visit);
|
||||
if ($current?->isBlocked()) {
|
||||
throw new \DomainException('Clear the current stage financial gate before advancing this visit.');
|
||||
}
|
||||
|
||||
if ($current !== null) {
|
||||
$current->forceFill([
|
||||
'status' => VisitStageAdvance::STATUS_COMPLETED,
|
||||
@@ -140,12 +172,21 @@ class WorkflowEngine
|
||||
return $advance;
|
||||
}
|
||||
|
||||
if ($advance->isBlocked() && $this->gate->isSatisfied($visit, $advance->stage)) {
|
||||
$this->gate->obligationFor($visit, $advance->stage);
|
||||
$satisfied = $this->gate->isSatisfied($visit, $advance->stage);
|
||||
|
||||
if ($advance->isBlocked() && $satisfied) {
|
||||
$advance->forceFill([
|
||||
'status' => VisitStageAdvance::STATUS_ACTIVE,
|
||||
'blocked_reason' => null,
|
||||
'cleared_at' => now(),
|
||||
])->save();
|
||||
} elseif (! $advance->isBlocked() && ! $satisfied) {
|
||||
$advance->forceFill([
|
||||
'status' => VisitStageAdvance::STATUS_BLOCKED,
|
||||
'blocked_reason' => $this->gate->blockedReason($visit, $advance->stage),
|
||||
'cleared_at' => null,
|
||||
])->save();
|
||||
}
|
||||
|
||||
return $advance->fresh();
|
||||
@@ -178,7 +219,16 @@ class WorkflowEngine
|
||||
}
|
||||
|
||||
if ($toCode !== null && $toCode !== '') {
|
||||
return $workflow->stage($toCode);
|
||||
$target = $workflow->stage($toCode);
|
||||
$current = $this->currentAdvance($visit)?->stage;
|
||||
if ($target === null || $current === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$isConfiguredNext = $current->default_next === $target->code;
|
||||
$mayBranch = $current->creates_child || $current->can_return || $current->can_skip;
|
||||
|
||||
return $isConfiguredNext || $mayBranch ? $target : null;
|
||||
}
|
||||
|
||||
$current = $this->currentAdvance($visit)?->stage;
|
||||
|
||||
Reference in New Issue
Block a user