first&last

This commit is contained in:
sbb45
2026-03-20 15:47:17 +05:00
commit d6441abdd2
230 changed files with 20367 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ActivityLog extends Model
{
use HasFactory;
public const UPDATED_AT = null;
protected $fillable = [
'user_id',
'model_type',
'model_id',
'action',
'description',
'ip_address',
'created_at',
];
protected $casts = [
'created_at' => 'datetime',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Department extends Model
{
use HasFactory;
protected $fillable = [
'name',
'description',
];
public function users(): HasMany
{
return $this->hasMany(User::class);
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace App\Models;
use App\Enums\EquipmentCondition;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Equipment extends Model
{
use HasFactory;
protected $table = 'equipment';
protected $fillable = [
'name',
'serial_number',
'inventory_number',
'category_id',
'client_id',
'purchase_date',
'warranty_until',
'condition',
'notes',
];
protected $casts = [
'purchase_date' => 'date',
'warranty_until' => 'date',
'condition' => EquipmentCondition::class,
];
public function category(): BelongsTo
{
return $this->belongsTo(EquipmentCategory::class, 'category_id');
}
public function client(): BelongsTo
{
return $this->belongsTo(User::class, 'client_id');
}
public function requests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'equipment_id');
}
public function scopeWarrantyExpired(Builder $query): Builder
{
return $query->whereNotNull('warranty_until')->whereDate('warranty_until', '<', now());
}
public function scopeWarrantyExpiringSoon(Builder $query, int $days = 30): Builder
{
return $query
->whereNotNull('warranty_until')
->whereDate('warranty_until', '>=', now())
->whereDate('warranty_until', '<=', now()->addDays($days));
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class EquipmentCategory extends Model
{
use HasFactory;
protected $fillable = [
'name',
'slug',
'description',
];
public function equipment(): HasMany
{
return $this->hasMany(Equipment::class, 'category_id');
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Priority extends Model
{
use HasFactory;
protected $fillable = [
'name',
'color_hex',
'level',
];
protected $casts = [
'level' => 'integer',
];
public function requests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'priority_id');
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class RequestAttachment extends Model
{
use HasFactory;
protected $fillable = [
'request_id',
'user_id',
'filename',
'path',
'size',
];
protected $casts = [
'size' => 'integer',
];
public function request(): BelongsTo
{
return $this->belongsTo(ServiceRequest::class, 'request_id');
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class RequestComment extends Model
{
use HasFactory;
protected $fillable = [
'request_id',
'user_id',
'body',
'is_internal',
];
protected $casts = [
'is_internal' => 'boolean',
];
public function request(): BelongsTo
{
return $this->belongsTo(ServiceRequest::class, 'request_id');
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class RequestType extends Model
{
use HasFactory;
protected $fillable = [
'name',
'description',
'sla_hours',
];
protected $casts = [
'sla_hours' => 'integer',
];
public function requests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'type_id');
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace App\Models;
use App\Enums\StatusSlug;
use App\Enums\UserRole;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class ServiceRequest extends Model
{
use HasFactory;
use SoftDeletes;
protected $table = 'requests';
protected $fillable = [
'ticket_number',
'title',
'description',
'type_id',
'priority_id',
'status_id',
'client_id',
'assigned_to',
'equipment_id',
'location',
'planned_at',
'deadline_at',
'started_at',
'completed_at',
'resolution_notes',
'client_rating',
'client_comment',
];
protected $casts = [
'planned_at' => 'datetime',
'deadline_at' => 'datetime',
'started_at' => 'datetime',
'completed_at' => 'datetime',
'client_rating' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
public function type(): BelongsTo
{
return $this->belongsTo(RequestType::class, 'type_id');
}
public function priority(): BelongsTo
{
return $this->belongsTo(Priority::class, 'priority_id');
}
public function status(): BelongsTo
{
return $this->belongsTo(Status::class, 'status_id');
}
public function client(): BelongsTo
{
return $this->belongsTo(User::class, 'client_id');
}
public function assignee(): BelongsTo
{
return $this->belongsTo(User::class, 'assigned_to');
}
public function equipment(): BelongsTo
{
return $this->belongsTo(Equipment::class, 'equipment_id');
}
public function comments(): HasMany
{
return $this->hasMany(RequestComment::class, 'request_id')->latest();
}
public function attachments(): HasMany
{
return $this->hasMany(RequestAttachment::class, 'request_id')->latest();
}
public function scopeOpen(Builder $query): Builder
{
return $query->whereHas('status', fn (Builder $status) => $status->where('is_final', false));
}
public function scopeOverdue(Builder $query): Builder
{
return $query
->whereNotNull('deadline_at')
->where('deadline_at', '<', now())
->whereHas('status', fn (Builder $status) => $status->where('is_final', false));
}
public function scopeMine(Builder $query, User $user): Builder
{
if ($user->isClient()) {
return $query->where('client_id', $user->id);
}
if ($user->isTechnician()) {
return $query->where('assigned_to', $user->id);
}
return $query;
}
public function scopeForRole(Builder $query, User $user): Builder
{
return match ($user->role) {
UserRole::CLIENT => $query->where('client_id', $user->id),
UserRole::TECHNICIAN => $query->where('assigned_to', $user->id),
default => $query,
};
}
public function isOverdue(): bool
{
if ($this->deadline_at === null) {
return false;
}
return $this->deadline_at->isPast() && !$this->status?->is_final;
}
public function hasStatus(StatusSlug $slug): bool
{
return $this->status?->slug === $slug->value;
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Setting extends Model
{
use HasFactory;
protected $fillable = [
'key',
'value',
'type',
];
public static function getValue(string $key, mixed $default = null): mixed
{
$setting = static::query()->where('key', $key)->first();
if (!$setting) {
return $default;
}
return $setting->castValue();
}
public static function setValue(string $key, mixed $value, string $type = 'string'): self
{
return static::query()->updateOrCreate(
['key' => $key],
['value' => static::serializeValue($value, $type), 'type' => $type]
);
}
public function castValue(): mixed
{
return match ($this->type) {
'integer' => (int) $this->value,
'boolean' => filter_var($this->value, FILTER_VALIDATE_BOOLEAN),
'json' => $this->value ? json_decode($this->value, true, 512, JSON_THROW_ON_ERROR) : [],
default => $this->value,
};
}
private static function serializeValue(mixed $value, string $type): ?string
{
if ($value === null) {
return null;
}
return match ($type) {
'json' => json_encode($value, JSON_THROW_ON_ERROR),
'boolean' => $value ? '1' : '0',
default => (string) $value,
};
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Status extends Model
{
use HasFactory;
protected $fillable = [
'name',
'color_hex',
'slug',
'is_final',
'sort_order',
];
protected $casts = [
'is_final' => 'boolean',
'sort_order' => 'integer',
];
public function requests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'status_id');
}
public function scopeFinal(Builder $query): Builder
{
return $query->where('is_final', true);
}
public function scopeActiveFlow(Builder $query): Builder
{
return $query->where('is_final', false);
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
namespace App\Models;
use App\Enums\UserRole;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use Notifiable;
use SoftDeletes;
protected $fillable = [
'name',
'email',
'password',
'role',
'phone',
'avatar',
'department_id',
'is_active',
'last_login_at',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'last_login_at' => 'datetime',
'is_active' => 'boolean',
'role' => UserRole::class,
];
public function department(): BelongsTo
{
return $this->belongsTo(Department::class);
}
public function clientRequests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'client_id');
}
public function assignedRequests(): HasMany
{
return $this->hasMany(ServiceRequest::class, 'assigned_to');
}
public function requestComments(): HasMany
{
return $this->hasMany(RequestComment::class);
}
public function requestAttachments(): HasMany
{
return $this->hasMany(RequestAttachment::class);
}
public function activityLogs(): HasMany
{
return $this->hasMany(ActivityLog::class);
}
public function equipment(): HasMany
{
return $this->hasMany(Equipment::class, 'client_id');
}
public function isAdmin(): bool
{
return $this->role === UserRole::ADMIN;
}
public function isManager(): bool
{
return $this->role === UserRole::MANAGER;
}
public function isTechnician(): bool
{
return $this->role === UserRole::TECHNICIAN;
}
public function isClient(): bool
{
return $this->role === UserRole::CLIENT;
}
public function isStaff(): bool
{
return $this->isAdmin() || $this->isManager() || $this->isTechnician();
}
}