Files
GSB-Construction/Modules/ProjectManagement/app/Models/Task.php

127 lines
3.3 KiB
PHP

<?php
namespace Modules\ProjectManagement\Models;
use Modules\MasterData\Models\Material;
use Modules\MasterData\Models\MaterialGroup;
use App\Models\User;
use App\Traits\HasPublicIdentifier;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Modules\ProjectManagement\Enums\DelayReason;
use Modules\ProjectManagement\Enums\TaskStatus;
class Task extends Model
{
use HasPublicIdentifier;
protected $fillable = [
'project_id',
'name',
'description',
'area',
'status',
'sort_order',
'labor_cost',
'estimated_hours',
'actual_hours',
'start_date',
'end_date',
'actual_start_date',
'actual_end_date',
'completion_percentage',
];
protected function casts(): array
{
return [
'status' => TaskStatus::class,
'labor_cost' => 'decimal:2',
'estimated_hours' => 'decimal:2',
'actual_hours' => 'decimal:2',
'completion_percentage' => 'decimal:2',
'start_date' => 'date',
'end_date' => 'date',
'actual_start_date' => 'date',
'actual_end_date' => 'date',
];
}
public function project(): BelongsTo
{
return $this->belongsTo(Project::class);
}
public function users(): BelongsToMany
{
return $this->belongsToMany(User::class);
}
public function taskMaterials(): HasMany
{
return $this->hasMany(TaskMaterial::class);
}
public function activities(): HasMany
{
return $this->hasMany(TaskActivity::class)->orderBy('created_at', 'desc');
}
public function delays(): HasMany
{
return $this->hasMany(TaskDelay::class)->orderByDesc('delay_date');
}
public function getTotalDelayDaysAttribute(): float
{
return round((float) $this->delays->sum('lost_hours') / 8, 1);
}
public function getWeatherDelayCountAttribute(): int
{
return $this->delays->where('reason_type', DelayReason::Weather)->count();
}
public function getMaterialCostAttribute(): float
{
return $this->taskMaterials->sum(fn (TaskMaterial $tm) => $tm->effective_cost);
}
public function getTotalCostAttribute(): float
{
return (float) $this->labor_cost + $this->material_cost;
}
public function transitionTo(TaskStatus $newStatus): void
{
$currentStatus = $this->status;
if (!in_array($newStatus, $currentStatus->allowedTransitions())) {
throw new \InvalidArgumentException(
"Cannot transition task from {$currentStatus->label()} to {$newStatus->label()}"
);
}
$updates = ['status' => $newStatus];
if ($newStatus === TaskStatus::InProgress && !$this->actual_start_date) {
$updates['actual_start_date'] = now();
}
if ($newStatus === TaskStatus::Completed) {
$updates['actual_end_date'] = now();
$updates['completion_percentage'] = 100;
}
$this->update($updates);
}
public function scopeStatus($query, TaskStatus $status)
{
return $query->where('status', $status);
}
}