Initial commit - Physiotherapy Clinic Management System

This commit is contained in:
Deploy committed 2026-03-10 00:38:51 +03:00
commit dbb326f2cc
300 files changed
+144285

No files matched your search

+50
View File
@@ -0,0 +1,50 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Appointment extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'patient_id',
'user_id',
'appointment_date',
'duration_minutes',
'status',
'notes',
'cancellation_reason',
'cancelled_by',
'cancelled_at',
];
protected $casts = [
'appointment_date' => 'datetime',
'cancelled_at' => 'datetime',
];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function cancelledBy(): BelongsTo
{
return $this->belongsTo(User::class, 'cancelled_by');
}
public function treatments()
{
return $this->hasMany(Treatment::class);
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ChartOfAccount extends Model
{
use HasFactory;
protected $fillable = [
'code',
'name',
'type',
'category',
'description',
'is_active',
];
protected $casts = [
'is_active' => 'boolean',
];
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Currency extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'code',
'name',
'symbol',
'decimal_places',
'exchange_rate_to_base',
'is_base',
'is_active',
];
protected $casts = [
'decimal_places' => 'integer',
'exchange_rate_to_base' => 'decimal:6',
'is_base' => 'boolean',
'is_active' => 'boolean',
];
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Invoice extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'invoice_number',
'patient_id',
'treatment_id',
'invoice_date',
'due_date',
'subtotal',
'tax_amount',
'discount_amount',
'total_amount',
'paid_amount',
'status',
'currency_id',
'notes',
'created_by',
];
protected $casts = [
'invoice_date' => 'date',
'due_date' => 'date',
'subtotal' => 'decimal:2',
'tax_amount' => 'decimal:2',
'discount_amount' => 'decimal:2',
'total_amount' => 'decimal:2',
'paid_amount' => 'decimal:2',
];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class);
}
public function treatment(): BelongsTo
{
return $this->belongsTo(Treatment::class);
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function createdBy(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function payments(): HasMany
{
return $this->hasMany(Payment::class);
}
public function getBalanceAttribute(): float
{
return $this->total_amount - $this->paid_amount;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class LedgerEntry extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'entry_date',
'chart_of_account_id',
'currency_id',
'amount',
'description',
'source_type',
'source_id',
'created_by',
];
protected $casts = [
'entry_date' => 'date',
'amount' => 'decimal:2',
];
public function chartOfAccount(): BelongsTo
{
return $this->belongsTo(ChartOfAccount::class);
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
public function scopeIncome($query)
{
return $query->whereHas('chartOfAccount', function ($q) {
$q->where('type', 'income');
});
}
public function scopeExpense($query)
{
return $query->whereHas('chartOfAccount', function ($q) {
$q->where('type', 'expense');
});
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Package extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'number_of_sessions',
'price',
'currency_id',
'validity_days',
'description',
'is_active',
];
protected $casts = [
'number_of_sessions' => 'integer',
'price' => 'decimal:2',
'validity_days' => 'integer',
'is_active' => 'boolean',
];
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function patientPackages(): HasMany
{
return $this->hasMany(PatientPackage::class);
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Patient extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'patient_code',
'first_name',
'last_name',
'email',
'phone',
'date_of_birth',
'gender',
'address',
'emergency_contact_name',
'emergency_contact_phone',
'medical_history',
'allergies',
'notes',
'created_by',
];
protected $casts = [
'date_of_birth' => 'date',
];
public function appointments(): HasMany
{
return $this->hasMany(Appointment::class);
}
public function treatments(): HasMany
{
return $this->hasMany(Treatment::class);
}
public function invoices(): HasMany
{
return $this->hasMany(Invoice::class);
}
public function fullName(): string
{
return "{$this->first_name} {$this->last_name}";
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PatientPackage extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'patient_id',
'package_id',
'total_sessions',
'remaining_sessions',
'start_date',
'end_date',
'status',
];
protected $casts = [
'total_sessions' => 'integer',
'remaining_sessions' => 'integer',
'start_date' => 'date',
'end_date' => 'date',
];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class);
}
public function package(): BelongsTo
{
return $this->belongsTo(Package::class);
}
public function consumeSession(): bool
{
if ($this->remaining_sessions > 0 && $this->status === 'active') {
$this->remaining_sessions--;
if ($this->remaining_sessions === 0) {
$this->status = 'completed';
}
$this->save();
return true;
}
return false;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Payment extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'payment_number',
'invoice_id',
'payment_date',
'amount',
'payment_method',
'reference_number',
'notes',
'received_by',
];
protected $casts = [
'payment_date' => 'date',
'amount' => 'decimal:2',
];
public function invoice(): BelongsTo
{
return $this->belongsTo(Invoice::class);
}
public function receivedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'received_by');
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Reminder extends Model
{
use HasFactory;
protected $fillable = [
'appointment_id',
'template_id',
'channel',
'content',
'send_at',
'sent_at',
'status',
'error_message',
'response',
];
protected $casts = [
'send_at' => 'datetime',
'sent_at' => 'datetime',
];
public function appointment(): BelongsTo
{
return $this->belongsTo(Appointment::class);
}
public function template(): BelongsTo
{
return $this->belongsTo(ReminderTemplate::class, 'template_id');
}
public function markAsSent(string $response = null): void
{
$this->status = 'sent';
$this->sent_at = now();
$this->response = $response;
$this->save();
}
public function markAsFailed(string $errorMessage): void
{
$this->status = 'failed';
$this->error_message = $errorMessage;
$this->save();
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class ReminderTemplate extends Model
{
use HasFactory;
protected $fillable = [
'name',
'content',
'channel',
'hours_before',
'is_active',
];
protected $casts = [
'hours_before' => 'integer',
'is_active' => 'boolean',
];
public function reminders(): HasMany
{
return $this->hasMany(Reminder::class, 'template_id');
}
public function parseContent(array $placeholders): string
{
$content = $this->content;
foreach ($placeholders as $key => $value) {
$content = str_replace('{' . $key . '}', $value, $content);
}
return $content;
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Treatment extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'patient_id',
'appointment_id',
'user_id',
'treatment_code',
'name',
'description',
'sessions_count',
'sessions_completed',
'start_date',
'end_date',
'status',
'total_cost',
'currency_id',
'notes',
];
protected $casts = [
'start_date' => 'date',
'end_date' => 'date',
'sessions_count' => 'integer',
'sessions_completed' => 'integer',
'total_cost' => 'decimal:2',
];
public function patient(): BelongsTo
{
return $this->belongsTo(Patient::class);
}
public function appointment(): BelongsTo
{
return $this->belongsTo(Appointment::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function invoices(): HasMany
{
return $this->hasMany(Invoice::class);
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable;
protected $fillable = [
'name',
'email',
'password',
'role',
'specialization',
'hourly_rate',
'session_rate',
'target_sessions_per_day',
'target_sessions_per_month',
'working_schedule',
'two_factor_secret',
'two_factor_recovery_codes',
'two_factor_enabled',
];
protected $hidden = [
'password',
'remember_token',
'two_factor_secret',
'two_factor_recovery_codes',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'two_factor_enabled' => 'boolean',
'two_factor_recovery_codes' => 'array',
];
public function isTwoFactorEnabled(): bool
{
return $this->two_factor_enabled === true;
}
public function getTwoFactorSecret(): ?string
{
return $this->two_factor_secret ? decrypt($this->two_factor_secret) : null;
}
public function setTwoFactorSecret(string $secret): void
{
$this->two_factor_secret = encrypt($secret);
}
public function generateRecoveryCodes(): array
{
$codes = [];
for ($i = 0; $i < 8; $i++) {
$codes[] = bin2hex(random_bytes(4));
}
return $codes;
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Wage extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'user_id',
'period_start',
'period_end',
'sessions_count',
'hours_worked',
'base_amount',
'bonus',
'deductions',
'total_amount',
'currency_id',
'status',
'notes',
'created_by',
];
protected $casts = [
'period_start' => 'date',
'period_end' => 'date',
'sessions_count' => 'integer',
'hours_worked' => 'integer',
'base_amount' => 'decimal:2',
'bonus' => 'decimal:2',
'deductions' => 'decimal:2',
'total_amount' => 'decimal:2',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
}