Initial Ladill Care release.
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>
This commit is contained in:
isaacclad
2026-06-29 11:36:22 +00:00
co-authored by Cursor
commit 6c9c742ed8
300 changed files with 30741 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Appointment extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_SCHEDULED = 'scheduled';
public const STATUS_CHECKED_IN = 'checked_in';
public const STATUS_WAITING = 'waiting';
public const STATUS_IN_CONSULTATION = 'in_consultation';
public const STATUS_COMPLETED = 'completed';
public const STATUS_CANCELLED = 'cancelled';
public const STATUS_NO_SHOW = 'no_show';
public const TYPE_SCHEDULED = 'scheduled';
public const TYPE_WALK_IN = 'walk_in';
protected $table = 'care_appointments';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'branch_id', 'patient_id',
'practitioner_id', 'department_id', 'visit_id', 'type', 'status',
'scheduled_at', 'checked_in_at', 'waiting_at', 'started_at',
'completed_at', 'cancelled_at', 'queue_position', 'reason', 'notes', 'created_by',
];
protected function casts(): array
{
return [
'scheduled_at' => 'datetime',
'checked_in_at' => 'datetime',
'waiting_at' => 'datetime',
'started_at' => 'datetime',
'completed_at' => 'datetime',
'cancelled_at' => 'datetime',
];
}
protected static function booted(): void
{
static::creating(function (Appointment $appointment) {
if (! $appointment->uuid) {
$appointment->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function practitioner(): BelongsTo
{
return $this->belongsTo(Practitioner::class, 'practitioner_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function department(): BelongsTo
{
return $this->belongsTo(Department::class, 'department_id');
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function visit(): BelongsTo
{
return $this->belongsTo(Visit::class, 'visit_id');
}
public function consultation(): HasOne
{
return $this->hasOne(Consultation::class, 'appointment_id');
}
/** @return list<string> */
public static function activeStatuses(): array
{
return [
self::STATUS_SCHEDULED,
self::STATUS_CHECKED_IN,
self::STATUS_WAITING,
self::STATUS_IN_CONSULTATION,
];
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AuditLog extends Model
{
use BelongsToOwner;
protected $table = 'care_audit_logs';
public $timestamps = false;
protected $fillable = [
'owner_ref', 'organization_id', 'actor_ref', 'action',
'subject_type', 'subject_id', 'metadata', 'ip_address', 'created_at',
];
protected function casts(): array
{
return [
'metadata' => 'array',
'created_at' => 'datetime',
];
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public static function record(
string $ownerRef,
string $action,
?int $organizationId = null,
?string $actorRef = null,
?string $subjectType = null,
?int $subjectId = null,
?array $metadata = null,
): self {
return static::create([
'owner_ref' => $ownerRef,
'organization_id' => $organizationId,
'actor_ref' => $actorRef,
'action' => $action,
'subject_type' => $subjectType,
'subject_id' => $subjectId,
'metadata' => $metadata,
'ip_address' => request()?->ip(),
'created_at' => now(),
]);
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Bill extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_DRAFT = 'draft';
public const STATUS_OPEN = 'open';
public const STATUS_PARTIAL = 'partial';
public const STATUS_PAID = 'paid';
public const STATUS_VOID = 'void';
protected $table = 'care_bills';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'branch_id', 'visit_id', 'patient_id',
'invoice_number', 'status', 'subtotal_minor', 'discount_minor', 'tax_minor',
'total_minor', 'amount_paid_minor', 'balance_minor', 'notes', 'created_by', 'finalized_at',
];
protected function casts(): array
{
return ['finalized_at' => 'datetime'];
}
protected static function booted(): void
{
static::creating(function (Bill $bill) {
if (! $bill->uuid) {
$bill->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function visit(): BelongsTo
{
return $this->belongsTo(Visit::class, 'visit_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function lineItems(): HasMany
{
return $this->hasMany(BillLineItem::class, 'bill_id');
}
public function payments(): HasMany
{
return $this->hasMany(Payment::class, 'bill_id');
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BillLineItem extends Model
{
use BelongsToOwner;
protected $table = 'care_bill_line_items';
protected $fillable = [
'owner_ref', 'bill_id', 'type', 'description', 'quantity',
'unit_price_minor', 'total_minor', 'source_type', 'source_id',
];
public function bill(): BelongsTo
{
return $this->belongsTo(Bill::class, 'bill_id');
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Branch extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_branches';
protected $fillable = [
'owner_ref', 'organization_id', 'name', 'code', 'address', 'phone', 'is_active',
];
protected function casts(): array
{
return ['is_active' => 'boolean'];
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function departments(): HasMany
{
return $this->hasMany(Department::class, 'branch_id');
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace App\Models\Concerns;
use Illuminate\Database\Eloquent\Builder;
trait BelongsToOwner
{
public function scopeOwned(Builder $query, string $ownerRef): Builder
{
return $query->where($this->getTable().'.owner_ref', $ownerRef);
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class Consultation extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_DRAFT = 'draft';
public const STATUS_COMPLETED = 'completed';
protected $table = 'care_consultations';
protected $fillable = [
'uuid', 'owner_ref', 'visit_id', 'appointment_id', 'practitioner_id',
'patient_id', 'status', 'symptoms', 'clinical_notes',
'started_at', 'completed_at', 'completed_by',
];
protected function casts(): array
{
return [
'started_at' => 'datetime',
'completed_at' => 'datetime',
];
}
protected static function booted(): void
{
static::creating(function (Consultation $consultation) {
if (! $consultation->uuid) {
$consultation->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function visit(): BelongsTo
{
return $this->belongsTo(Visit::class, 'visit_id');
}
public function appointment(): BelongsTo
{
return $this->belongsTo(Appointment::class, 'appointment_id');
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function practitioner(): BelongsTo
{
return $this->belongsTo(Practitioner::class, 'practitioner_id');
}
public function vitalSigns(): HasMany
{
return $this->hasMany(VitalSign::class, 'consultation_id');
}
public function diagnoses(): HasMany
{
return $this->hasMany(Diagnosis::class, 'consultation_id');
}
public function documents(): HasMany
{
return $this->hasMany(ConsultationDocument::class, 'consultation_id');
}
public function investigationRequests(): HasMany
{
return $this->hasMany(InvestigationRequest::class, 'consultation_id');
}
public function prescriptions(): HasMany
{
return $this->hasMany(Prescription::class, 'consultation_id');
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Storage;
class ConsultationDocument extends Model
{
use BelongsToOwner;
protected $table = 'care_consultation_documents';
protected $fillable = [
'owner_ref', 'consultation_id', 'file_path', 'original_name',
'mime_type', 'uploaded_by',
];
public function consultation(): BelongsTo
{
return $this->belongsTo(Consultation::class, 'consultation_id');
}
public function url(): ?string
{
return $this->file_path && Storage::disk('public')->exists($this->file_path)
? Storage::disk('public')->url($this->file_path)
: null;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class Department extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_departments';
protected $fillable = [
'owner_ref', 'branch_id', 'name', 'type', 'is_active',
];
protected function casts(): array
{
return ['is_active' => 'boolean'];
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Diagnosis extends Model
{
use BelongsToOwner;
protected $table = 'care_diagnoses';
protected $fillable = [
'owner_ref', 'consultation_id', 'code', 'description', 'is_primary', 'notes',
];
protected function casts(): array
{
return ['is_primary' => 'boolean'];
}
public function consultation(): BelongsTo
{
return $this->belongsTo(Consultation::class, 'consultation_id');
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class DispensingRecord extends Model
{
use BelongsToOwner;
protected $table = 'care_dispensing_records';
protected $fillable = [
'owner_ref', 'prescription_id', 'prescription_item_id',
'drug_batch_id', 'quantity', 'dispensed_by', 'dispensed_at',
];
protected function casts(): array
{
return ['dispensed_at' => 'datetime'];
}
public function prescription(): BelongsTo
{
return $this->belongsTo(Prescription::class, 'prescription_id');
}
public function prescriptionItem(): BelongsTo
{
return $this->belongsTo(PrescriptionItem::class, 'prescription_item_id');
}
public function drugBatch(): BelongsTo
{
return $this->belongsTo(DrugBatch::class, 'drug_batch_id');
}
}
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Drug extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_drugs';
protected $fillable = [
'owner_ref', 'organization_id', 'branch_id', 'name', 'generic_name',
'sku', 'unit', 'unit_price_minor', 'reorder_level', 'is_active',
];
protected function casts(): array
{
return ['is_active' => 'boolean'];
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function batches(): HasMany
{
return $this->hasMany(DrugBatch::class, 'drug_id');
}
public function stockOnHand(): int
{
return (int) $this->batches()->sum('quantity_on_hand');
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class DrugBatch extends Model
{
use BelongsToOwner;
protected $table = 'care_drug_batches';
protected $fillable = [
'owner_ref', 'drug_id', 'batch_number', 'expiry_date',
'quantity_on_hand', 'cost_minor', 'received_at',
];
protected function casts(): array
{
return [
'expiry_date' => 'date',
'received_at' => 'datetime',
];
}
public function drug(): BelongsTo
{
return $this->belongsTo(Drug::class, 'drug_id');
}
public function dispensingRecords(): HasMany
{
return $this->hasMany(DispensingRecord::class, 'drug_batch_id');
}
public function isExpired(): bool
{
return $this->expiry_date !== null && $this->expiry_date->isPast();
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class EmergencyContact extends Model
{
use BelongsToOwner;
protected $table = 'care_emergency_contacts';
protected $fillable = ['owner_ref', 'patient_id', 'name', 'phone', 'relationship', 'is_primary'];
protected function casts(): array
{
return ['is_primary' => 'boolean'];
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InsurancePolicy extends Model
{
use BelongsToOwner;
protected $table = 'care_insurance_policies';
protected $fillable = [
'owner_ref', 'patient_id', 'provider_name', 'policy_number',
'coverage_type', 'expiry_date', 'notes',
];
protected function casts(): array
{
return ['expiry_date' => 'date'];
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InvestigationAttachment extends Model
{
use BelongsToOwner;
protected $table = 'care_investigation_attachments';
protected $fillable = [
'owner_ref', 'investigation_result_id', 'file_path', 'original_name',
'mime_type', 'uploaded_by',
];
public function result(): BelongsTo
{
return $this->belongsTo(InvestigationResult::class, 'investigation_result_id');
}
}
+115
View File
@@ -0,0 +1,115 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class InvestigationRequest extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_PENDING = 'pending';
public const STATUS_SAMPLE_COLLECTED = 'sample_collected';
public const STATUS_IN_PROGRESS = 'in_progress';
public const STATUS_AWAITING_REVIEW = 'awaiting_review';
public const STATUS_COMPLETED = 'completed';
public const STATUS_DELIVERED = 'delivered';
public const STATUS_CANCELLED = 'cancelled';
protected $table = 'care_investigation_requests';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'branch_id', 'visit_id', 'consultation_id',
'patient_id', 'investigation_type_id', 'practitioner_id', 'status', 'priority',
'clinical_notes', 'requested_by', 'assigned_member_id', 'sample_barcode',
'sample_collected_at', 'sample_collected_by', 'completed_at', 'delivered_at',
'approved_by', 'approved_at',
];
protected function casts(): array
{
return [
'sample_collected_at' => 'datetime',
'completed_at' => 'datetime',
'delivered_at' => 'datetime',
'approved_at' => 'datetime',
];
}
protected static function booted(): void
{
static::creating(function (InvestigationRequest $request) {
if (! $request->uuid) {
$request->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function investigationType(): BelongsTo
{
return $this->belongsTo(InvestigationType::class, 'investigation_type_id');
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function visit(): BelongsTo
{
return $this->belongsTo(Visit::class, 'visit_id');
}
public function consultation(): BelongsTo
{
return $this->belongsTo(Consultation::class, 'consultation_id');
}
public function practitioner(): BelongsTo
{
return $this->belongsTo(Practitioner::class, 'practitioner_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function assignedMember(): BelongsTo
{
return $this->belongsTo(Member::class, 'assigned_member_id');
}
public function result(): HasOne
{
return $this->hasOne(InvestigationResult::class, 'investigation_request_id');
}
/** @return list<string> */
public static function activeStatuses(): array
{
return [
self::STATUS_PENDING,
self::STATUS_SAMPLE_COLLECTED,
self::STATUS_IN_PROGRESS,
self::STATUS_AWAITING_REVIEW,
self::STATUS_COMPLETED,
];
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class InvestigationResult extends Model
{
use BelongsToOwner;
public const STATUS_DRAFT = 'draft';
public const STATUS_APPROVED = 'approved';
protected $table = 'care_investigation_results';
protected $fillable = [
'owner_ref', 'investigation_request_id', 'result_summary', 'interpretation',
'is_abnormal', 'status', 'entered_by', 'approved_by', 'approved_at',
];
protected function casts(): array
{
return [
'is_abnormal' => 'boolean',
'approved_at' => 'datetime',
];
}
public function request(): BelongsTo
{
return $this->belongsTo(InvestigationRequest::class, 'investigation_request_id');
}
public function values(): HasMany
{
return $this->hasMany(InvestigationResultValue::class, 'investigation_result_id');
}
public function attachments(): HasMany
{
return $this->hasMany(InvestigationAttachment::class, 'investigation_result_id');
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InvestigationResultValue extends Model
{
use BelongsToOwner;
protected $table = 'care_investigation_result_values';
protected $fillable = [
'owner_ref', 'investigation_result_id', 'parameter', 'value', 'unit',
'reference_low', 'reference_high', 'reference_text', 'is_abnormal',
];
protected function casts(): array
{
return [
'reference_low' => 'decimal:4',
'reference_high' => 'decimal:4',
'is_abnormal' => 'boolean',
];
}
public function result(): BelongsTo
{
return $this->belongsTo(InvestigationResult::class, 'investigation_result_id');
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class InvestigationType extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_investigation_types';
protected $fillable = [
'owner_ref', 'organization_id', 'name', 'code', 'category', 'description',
'unit', 'reference_low', 'reference_high', 'reference_text', 'price_minor', 'is_active',
];
protected function casts(): array
{
return [
'reference_low' => 'decimal:4',
'reference_high' => 'decimal:4',
'is_active' => 'boolean',
];
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function requests(): HasMany
{
return $this->hasMany(InvestigationRequest::class, 'investigation_type_id');
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Member extends Model
{
use BelongsToOwner;
protected $table = 'care_members';
protected $fillable = ['owner_ref', 'organization_id', 'user_ref', 'role', 'branch_id'];
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function hasRole(string ...$roles): bool
{
return in_array($this->role, $roles, true);
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Organization extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_organizations';
protected $fillable = [
'owner_ref', 'name', 'slug', 'logo_path', 'timezone', 'settings',
];
protected function casts(): array
{
return ['settings' => 'array'];
}
public function branches(): HasMany
{
return $this->hasMany(Branch::class, 'organization_id');
}
public function members(): HasMany
{
return $this->hasMany(Member::class, 'organization_id');
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Patient extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_patients';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'branch_id', 'patient_number',
'first_name', 'last_name', 'other_names', 'gender', 'date_of_birth',
'phone', 'email', 'national_id', 'address', 'city', 'region', 'notes',
];
protected function casts(): array
{
return ['date_of_birth' => 'date'];
}
protected static function booted(): void
{
static::creating(function (Patient $patient) {
if (! $patient->uuid) {
$patient->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function fullName(): string
{
return trim(implode(' ', array_filter([
$this->first_name,
$this->other_names,
$this->last_name,
])));
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function allergies(): HasMany
{
return $this->hasMany(PatientAllergy::class, 'patient_id');
}
public function conditions(): HasMany
{
return $this->hasMany(PatientCondition::class, 'patient_id');
}
public function familyHistory(): HasMany
{
return $this->hasMany(PatientFamilyHistory::class, 'patient_id');
}
public function emergencyContacts(): HasMany
{
return $this->hasMany(EmergencyContact::class, 'patient_id');
}
public function insurancePolicies(): HasMany
{
return $this->hasMany(InsurancePolicy::class, 'patient_id');
}
public function attachments(): HasMany
{
return $this->hasMany(PatientAttachment::class, 'patient_id');
}
public function appointments(): HasMany
{
return $this->hasMany(Appointment::class, 'patient_id');
}
public function visits(): HasMany
{
return $this->hasMany(Visit::class, 'patient_id');
}
public function investigationRequests(): HasMany
{
return $this->hasMany(InvestigationRequest::class, 'patient_id');
}
public function prescriptions(): HasMany
{
return $this->hasMany(Prescription::class, 'patient_id');
}
public function bills(): HasMany
{
return $this->hasMany(Bill::class, 'patient_id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PatientAllergy extends Model
{
use BelongsToOwner;
protected $table = 'care_patient_allergies';
protected $fillable = ['owner_ref', 'patient_id', 'allergen', 'severity', 'notes'];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Storage;
class PatientAttachment extends Model
{
use BelongsToOwner;
protected $table = 'care_patient_attachments';
protected $fillable = [
'owner_ref', 'patient_id', 'file_path', 'original_name',
'mime_type', 'document_type', 'uploaded_by',
];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function url(): ?string
{
return $this->file_path && Storage::disk('public')->exists($this->file_path)
? Storage::disk('public')->url($this->file_path)
: null;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PatientCondition extends Model
{
use BelongsToOwner;
protected $table = 'care_patient_conditions';
protected $fillable = ['owner_ref', 'patient_id', 'condition', 'onset_date', 'is_chronic', 'notes'];
protected function casts(): array
{
return [
'onset_date' => 'date',
'is_chronic' => 'boolean',
];
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PatientFamilyHistory extends Model
{
use BelongsToOwner;
protected $table = 'care_patient_family_history';
protected $fillable = ['owner_ref', 'patient_id', 'relation', 'condition', 'notes'];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Str;
class Payment extends Model
{
use BelongsToOwner;
protected $table = 'care_payments';
protected $fillable = [
'uuid', 'owner_ref', 'bill_id', 'amount_minor', 'method',
'reference', 'paid_at', 'recorded_by', 'notes',
];
protected function casts(): array
{
return ['paid_at' => 'datetime'];
}
protected static function booted(): void
{
static::creating(function (Payment $payment) {
if (! $payment->uuid) {
$payment->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function bill(): BelongsTo
{
return $this->belongsTo(Bill::class, 'bill_id');
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Practitioner extends Model
{
use BelongsToOwner, SoftDeletes;
protected $table = 'care_practitioners';
protected $fillable = [
'owner_ref', 'organization_id', 'branch_id', 'department_id', 'member_id',
'user_ref', 'name', 'specialty', 'is_active',
];
protected function casts(): array
{
return ['is_active' => 'boolean'];
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function department(): BelongsTo
{
return $this->belongsTo(Department::class, 'department_id');
}
public function member(): BelongsTo
{
return $this->belongsTo(Member::class, 'member_id');
}
public function schedules(): HasMany
{
return $this->hasMany(PractitionerSchedule::class, 'practitioner_id');
}
public function appointments(): HasMany
{
return $this->hasMany(Appointment::class, 'practitioner_id');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PractitionerSchedule extends Model
{
use BelongsToOwner;
protected $table = 'care_practitioner_schedules';
protected $fillable = ['owner_ref', 'practitioner_id', 'day_of_week', 'start_time', 'end_time'];
public function practitioner(): BelongsTo
{
return $this->belongsTo(Practitioner::class, 'practitioner_id');
}
}
+75
View File
@@ -0,0 +1,75 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Prescription extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_DRAFT = 'draft';
public const STATUS_ACTIVE = 'active';
public const STATUS_DISPENSED = 'dispensed';
public const STATUS_CANCELLED = 'cancelled';
protected $table = 'care_prescriptions';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'visit_id', 'consultation_id',
'patient_id', 'practitioner_id', 'status', 'notes',
'prescribed_by', 'dispensed_by', 'dispensed_at',
];
protected function casts(): array
{
return ['dispensed_at' => 'datetime'];
}
protected static function booted(): void
{
static::creating(function (Prescription $prescription) {
if (! $prescription->uuid) {
$prescription->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function visit(): BelongsTo
{
return $this->belongsTo(Visit::class, 'visit_id');
}
public function consultation(): BelongsTo
{
return $this->belongsTo(Consultation::class, 'consultation_id');
}
public function practitioner(): BelongsTo
{
return $this->belongsTo(Practitioner::class, 'practitioner_id');
}
public function items(): HasMany
{
return $this->hasMany(PrescriptionItem::class, 'prescription_id')->orderBy('sort_order');
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PrescriptionItem extends Model
{
use BelongsToOwner;
protected $table = 'care_prescription_items';
protected $fillable = [
'owner_ref', 'prescription_id', 'drug_id', 'is_procedure', 'name', 'dosage',
'frequency', 'duration', 'route', 'quantity', 'instructions', 'sort_order',
];
protected function casts(): array
{
return ['is_procedure' => 'boolean'];
}
public function prescription(): BelongsTo
{
return $this->belongsTo(Prescription::class, 'prescription_id');
}
public function drug(): BelongsTo
{
return $this->belongsTo(Drug::class, 'drug_id');
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
/**
* Thin local mirror of the platform identity (auth.ladill.com owns users).
* Keyed by the OIDC `sub` (public_id); every Care record is scoped to it.
*/
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
protected $fillable = ['public_id', 'name', 'email', 'avatar_url', 'password', 'last_app_active_at'];
protected $hidden = ['password', 'remember_token'];
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'last_app_active_at' => 'datetime',
'password' => 'hashed',
];
}
public function ownerRef(): string
{
return (string) $this->public_id;
}
public function avatarUrl(): ?string
{
$url = trim((string) $this->avatar_url);
return $url !== '' ? $url : null;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Visit extends Model
{
use BelongsToOwner, SoftDeletes;
public const STATUS_OPEN = 'open';
public const STATUS_IN_PROGRESS = 'in_progress';
public const STATUS_COMPLETED = 'completed';
protected $table = 'care_visits';
protected $fillable = [
'uuid', 'owner_ref', 'organization_id', 'branch_id', 'patient_id',
'status', 'checked_in_at', 'completed_at', 'checked_in_by',
];
protected function casts(): array
{
return [
'checked_in_at' => 'datetime',
'completed_at' => 'datetime',
];
}
protected static function booted(): void
{
static::creating(function (Visit $visit) {
if (! $visit->uuid) {
$visit->uuid = (string) Str::uuid();
}
});
}
public function getRouteKeyName(): string
{
return 'uuid';
}
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class, 'patient_id');
}
public function branch(): BelongsTo
{
return $this->belongsTo(Branch::class, 'branch_id');
}
public function organization(): BelongsTo
{
return $this->belongsTo(Organization::class, 'organization_id');
}
public function appointment(): HasOne
{
return $this->hasOne(Appointment::class, 'visit_id');
}
public function consultations(): HasMany
{
return $this->hasMany(Consultation::class, 'visit_id');
}
public function bill(): HasOne
{
return $this->hasOne(Bill::class, 'visit_id');
}
public function bills(): HasMany
{
return $this->hasMany(Bill::class, 'visit_id');
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
namespace App\Models;
use App\Models\Concerns\BelongsToOwner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class VitalSign extends Model
{
use BelongsToOwner;
protected $table = 'care_vital_signs';
protected $fillable = [
'owner_ref', 'consultation_id', 'bp_systolic', 'bp_diastolic', 'pulse',
'temperature', 'weight_kg', 'height_cm', 'spo2', 'respiratory_rate',
'recorded_by', 'recorded_at',
];
protected function casts(): array
{
return [
'temperature' => 'decimal:1',
'weight_kg' => 'decimal:2',
'height_cm' => 'decimal:1',
'recorded_at' => 'datetime',
];
}
public function consultation(): BelongsTo
{
return $this->belongsTo(Consultation::class, 'consultation_id');
}
}