$payload */ public function stageFromRequest(array $payload): string { $crossmatch = strtolower((string) ($payload['crossmatch_status'] ?? '')); $issued = (int) ($payload['issued_units'] ?? 0); $urgency = strtolower((string) ($payload['urgency'] ?? '')); if ($issued > 0 || $crossmatch === 'issued') { return self::STAGE_ISSUE; } if (in_array($crossmatch, ['compatible', 'incompatible', 'in progress'], true)) { return self::STAGE_CROSSMATCH; } if (str_contains($urgency, 'emergency') || str_contains($urgency, 'massive')) { return self::STAGE_CROSSMATCH; } return self::STAGE_REQUEST; } /** * Whether a transfusion payload should close the visit episode. * * @param array $payload */ public function shouldCompleteVisit(array $payload): bool { $outcome = strtolower((string) ($payload['outcome'] ?? '')); return $outcome !== '' && ( str_contains($outcome, 'completed') || str_contains($outcome, 'stopped') || str_contains($outcome, 'complete') ); } /** * Map product label to catalog service code for billing. */ public function serviceCodeForProduct(?string $product): ?string { $normalized = strtolower(trim((string) $product)); return match (true) { str_contains($normalized, 'whole') => 'bb.whole_blood', str_contains($normalized, 'packed') || str_contains($normalized, 'rbc') => 'bb.packed_rbc', str_contains($normalized, 'platelet') => 'bb.platelets', str_contains($normalized, 'ffp') || str_contains($normalized, 'plasma') => 'bb.ffp', str_contains($normalized, 'cryo') => 'bb.cryo', default => null, }; } /** * Default stage progression for action buttons. * * @return array */ public function stageFlow(): array { return [ self::STAGE_REQUEST => ['next' => self::STAGE_CROSSMATCH, 'label' => 'Start cross-match'], self::STAGE_CROSSMATCH => ['next' => self::STAGE_ISSUE, 'label' => 'Ready to issue'], self::STAGE_ISSUE => ['next' => self::STAGE_TRANSFUSION, 'label' => 'Start transfusion'], self::STAGE_TRANSFUSION => ['next' => self::STAGE_COMPLETED, 'label' => 'Complete visit'], self::STAGE_COMPLETED => ['next' => self::STAGE_COMPLETED, 'label' => 'Completed'], ]; } }