Deploy Ladill Care / deploy (push) Failing after 13s
Healthcare management app: patients, appointments, consultations, lab, pharmacy inventory, billing, and reports at care.ladill.com. Co-authored-by: Cursor <cursoragent@cursor.com>
190 lines
6.9 KiB
PHP
190 lines
6.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Care;
|
|
|
|
use App\Models\Consultation;
|
|
use App\Models\Prescription;
|
|
use App\Models\PrescriptionItem;
|
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use InvalidArgumentException;
|
|
|
|
class PrescriptionService
|
|
{
|
|
public function queryForOrganization(string $ownerRef, int $organizationId): Builder
|
|
{
|
|
return Prescription::owned($ownerRef)->where('organization_id', $organizationId);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $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<int, Prescription>
|
|
*/
|
|
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<string, mixed> $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<string, mixed> $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<int, array<string, mixed>> $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}.");
|
|
}
|
|
}
|
|
}
|