where('organization_id', $organizationId); } /** * @param array $filters */ public function list(string $ownerRef, int $organizationId, array $filters = [], ?int $branchId = null): LengthAwarePaginator { $query = $this->queryForOrganization($ownerRef, $organizationId) ->with(['patient', 'practitioner', 'items']) ->orderByDesc('created_at'); if ($status = $filters['status'] ?? null) { $query->where('status', $status); } if ($patientId = $filters['patient_id'] ?? null) { $query->where('patient_id', $patientId); } if ($branchId !== null) { $query->whereHas('visit', fn (Builder $q) => $q->where('branch_id', $branchId)); } return $query->paginate((int) ($filters['per_page'] ?? 20))->withQueryString(); } /** * @return Collection */ public function pharmacyQueue(string $ownerRef, int $organizationId): Collection { return $this->queryForOrganization($ownerRef, $organizationId) ->where('status', Prescription::STATUS_ACTIVE) ->with(['patient', 'practitioner', 'items', 'visit.branch']) ->orderBy('created_at') ->get(); } /** * @param array $data */ public function createFromConsultation( Consultation $consultation, string $ownerRef, array $data, ?string $actorRef = null, ): Prescription { $consultation->loadMissing('visit'); $status = ($data['activate'] ?? true) ? Prescription::STATUS_ACTIVE : Prescription::STATUS_DRAFT; $prescription = Prescription::create([ 'owner_ref' => $ownerRef, 'organization_id' => $consultation->visit->organization_id, 'visit_id' => $consultation->visit_id, 'consultation_id' => $consultation->id, 'patient_id' => $consultation->patient_id, 'practitioner_id' => $data['practitioner_id'] ?? $consultation->practitioner_id, 'status' => $status, 'notes' => $data['notes'] ?? null, 'prescribed_by' => $actorRef, ]); $this->syncItems($prescription, $ownerRef, $data['items'] ?? []); AuditLogger::record( $ownerRef, 'prescription.created', $consultation->visit->organization_id, $actorRef, Prescription::class, $prescription->id, ); return $prescription->fresh(['items', 'patient', 'practitioner']); } /** * @param array $data */ public function update(Prescription $prescription, string $ownerRef, array $data, ?string $actorRef = null): Prescription { if ($prescription->status !== Prescription::STATUS_DRAFT) { throw new InvalidArgumentException('Only draft prescriptions can be edited.'); } $prescription->update([ 'practitioner_id' => $data['practitioner_id'] ?? $prescription->practitioner_id, 'notes' => $data['notes'] ?? $prescription->notes, ]); if (array_key_exists('items', $data)) { $this->syncItems($prescription, $ownerRef, $data['items']); } AuditLogger::record($ownerRef, 'prescription.updated', $prescription->organization_id, $actorRef, Prescription::class, $prescription->id); return $prescription->fresh(['items', 'patient', 'practitioner']); } public function activate(Prescription $prescription, string $ownerRef, ?string $actorRef = null): Prescription { $this->assertStatus($prescription, Prescription::STATUS_DRAFT); $prescription->update(['status' => Prescription::STATUS_ACTIVE]); AuditLogger::record($ownerRef, 'prescription.activated', $prescription->organization_id, $actorRef, Prescription::class, $prescription->id); return $prescription->fresh(['items', 'patient']); } public function dispense(Prescription $prescription, string $ownerRef, ?string $actorRef = null): Prescription { $this->assertStatus($prescription, Prescription::STATUS_ACTIVE); $prescription->update([ 'status' => Prescription::STATUS_DISPENSED, 'dispensed_at' => now(), 'dispensed_by' => $actorRef, ]); AuditLogger::record($ownerRef, 'prescription.dispensed', $prescription->organization_id, $actorRef, Prescription::class, $prescription->id); return $prescription->fresh(['items', 'patient']); } public function cancel(Prescription $prescription, string $ownerRef, ?string $actorRef = null): Prescription { if (in_array($prescription->status, [Prescription::STATUS_DISPENSED, Prescription::STATUS_CANCELLED], true)) { throw new InvalidArgumentException('Prescription cannot be cancelled.'); } $prescription->update(['status' => Prescription::STATUS_CANCELLED]); AuditLogger::record($ownerRef, 'prescription.cancelled', $prescription->organization_id, $actorRef, Prescription::class, $prescription->id); return $prescription->fresh(['items', 'patient']); } /** * @param array> $items */ protected function syncItems(Prescription $prescription, string $ownerRef, array $items): void { $prescription->items()->delete(); foreach ($items as $index => $row) { if (empty($row['name'])) { continue; } PrescriptionItem::create([ 'owner_ref' => $ownerRef, 'prescription_id' => $prescription->id, 'is_procedure' => (bool) ($row['is_procedure'] ?? false), 'name' => $row['name'], 'dosage' => $row['dosage'] ?? null, 'frequency' => $row['frequency'] ?? null, 'duration' => $row['duration'] ?? null, 'route' => $row['route'] ?? null, 'quantity' => $row['quantity'] ?? null, 'instructions' => $row['instructions'] ?? null, 'sort_order' => $index, ]); } } protected function assertStatus(Prescription $prescription, string ...$allowed): void { if (! in_array($prescription->status, $allowed, true)) { throw new InvalidArgumentException("Cannot transition from {$prescription->status}."); } } }