PM_Console/backend/app/Services/ReportService.php

252 خطوط
8.6 KiB
PHP

<?php
namespace App\Services;
use App\Models\ActivityLog;
use App\Models\Project;
use App\Models\Sprint;
use App\Models\Task;
use App\Models\User;
use Carbon\Carbon;
class ReportService
{
protected function applyDateFilters($query, $filters, $dateField = 'created_at')
{
if (! empty($filters['date_from'])) {
$query->where($dateField, '>=', $filters['date_from']);
}
if (! empty($filters['date_to'])) {
$query->where($dateField, '<=', $filters['date_to']);
}
return $query;
}
public function projectStatus($filters = [])
{
$query = Project::query();
if (! empty($filters['status'])) {
$query->where('status', $filters['status']);
}
if (! empty($filters['project_id'])) {
$query->where('id', $filters['project_id']);
}
$projects = $query->get();
$grouped = $projects->groupBy('status');
return $grouped->map(fn ($items, $status) => [
'status' => $status === 'planning' ? 'برنامه‌ریزی' : ($status === 'in_progress' ? 'در حال انجام' : ($status === 'done' ? 'تکمیل شده' : ($status === 'on_hold' ? 'متوقف' : $status))),
'count' => $items->count(),
'label' => $status,
'value' => $items->count(),
])->values();
}
public function delayedTasks($filters = [])
{
$query = Task::with(['project:id,title', 'assignee:id,name'])
->whereDate('due_date', '<', Carbon::today())
->where('status', '!=', 'done');
$query = $this->applyDateFilters($query, $filters, 'due_date');
if (! empty($filters['project_id'])) {
$query->where('project_id', $filters['project_id']);
}
if (! empty($filters['user_id'])) {
$query->where('assignee_id', $filters['user_id']);
}
if (! empty($filters['status'])) {
$query->where('status', $filters['status']);
}
return $query->orderBy('due_date')->get()->map(fn ($t) => [
'id' => $t->id,
'title' => $t->title,
'project' => ['title' => $t->project?->title],
'project_name' => $t->project?->title,
'assignee' => ['name' => $t->assignee?->name],
'assignee_name' => $t->assignee?->name,
'due_date' => $t->due_date?->format('Y-m-d'),
'delay_days' => abs(Carbon::today()->diffInDays($t->due_date, false)),
'days_delayed' => abs(Carbon::today()->diffInDays($t->due_date, false)),
'priority' => $t->priority,
]);
}
public function teamPerformance($filters = [])
{
$query = User::with(['tasks' => function ($q) use ($filters) {
if (! empty($filters['date_from'])) {
$q->where('created_at', '>=', $filters['date_from']);
}
if (! empty($filters['date_to'])) {
$q->where('created_at', '<=', $filters['date_to']);
}
}]);
if (! empty($filters['user_id'])) {
$query->where('id', $filters['user_id']);
}
$users = $query->get();
return $users->map(function ($user) {
$totalTasks = $user->tasks->count();
$completedTasks = $user->tasks->where('status', 'done')->count();
$delayedTasks = $user->tasks->where('due_date', '<', Carbon::now())->where('status', '!=', 'done')->count();
$inProgress = $user->tasks->whereNotIn('status', ['done', 'canceled'])->count();
return [
'name' => $user->name,
'completed' => $completedTasks,
'in_progress' => $inProgress,
'delayed' => $delayedTasks,
'total' => $totalTasks,
];
});
}
public function workload($filters = [])
{
$query = User::query();
if (! empty($filters['user_id'])) {
$query->where('id', $filters['user_id']);
}
$users = $query->get();
$allActiveTasks = Task::where('status', '!=', 'done')->count();
$maxTasks = max($allActiveTasks, 1);
return $users->map(function ($user) use ($maxTasks) {
$activeTasks = Task::where('assignee_id', $user->id)->where('status', '!=', 'done')->count();
$loadPercent = min(round(($activeTasks / $maxTasks) * 100), 100);
return [
'user_id' => $user->id,
'name' => $user->name,
'tasks_count' => $activeTasks,
'total' => $activeTasks,
'load_percent' => $loadPercent,
'load' => $loadPercent,
];
});
}
public function sprintProgress($filters = [])
{
$query = Sprint::with(['project:id,title', 'tasks']);
if (! empty($filters['project_id'])) {
$query->where('project_id', $filters['project_id']);
}
if (! empty($filters['status'])) {
$query->where('status', $filters['status']);
}
$sprints = $query->get();
return $sprints->map(function ($sprint) {
$totalTasks = $sprint->tasks->count();
$completedTasks = $sprint->tasks->where('status', 'done')->count();
return [
'id' => $sprint->id,
'title' => $sprint->title,
'project' => $sprint->project?->title,
'start_date' => $sprint->start_date,
'end_date' => $sprint->end_date,
'status' => $sprint->status,
'total_tasks' => $totalTasks,
'completed_tasks' => $completedTasks,
'progress' => $totalTasks > 0 ? round(($completedTasks / $totalTasks) * 100, 2) : 0,
];
});
}
public function timeEstimate($filters = [])
{
$query = Task::with('project');
$query = $this->applyDateFilters($query, $filters);
if (! empty($filters['project_id'])) {
$query->where('project_id', $filters['project_id']);
}
if (! empty($filters['user_id'])) {
$query->where('assignee_id', $filters['user_id']);
}
if (! empty($filters['status'])) {
$query->where('status', $filters['status']);
}
$tasks = $query->get();
return $tasks->map(function ($task) {
$diff = ($task->estimated_time ?? 0) - ($task->actual_time ?? 0);
return [
'id' => $task->id,
'title' => $task->title,
'project' => ['title' => $task->project?->title ?? '—'],
'estimated' => $task->estimated_time,
'estimated_time' => $task->estimated_time,
'actual' => $task->actual_time,
'actual_time' => $task->actual_time,
'diff' => $diff,
'difference' => $diff,
'status' => $task->status,
];
});
}
public function recentActivities($filters = [])
{
$query = ActivityLog::with('user');
$query = $this->applyDateFilters($query, $filters);
if (! empty($filters['project_id'])) {
$query->where('project_id', $filters['project_id']);
}
if (! empty($filters['user_id'])) {
$query->where('user_id', $filters['user_id']);
}
if (! empty($filters['action'])) {
$query->where('action', $filters['action']);
}
return $query->orderBy('created_at', 'desc')->limit(50)->get()->map(fn ($log) => [
'id' => $log->id,
'description' => $log->description,
'user' => $log->user?->name ?? '?',
'user_name' => $log->user?->name ?? '?',
'created_at' => $log->created_at?->toDateTimeString(),
'time_ago' => $log->created_at?->diffForHumans(),
]);
}
public function riskyProjects($filters = [])
{
$query = Project::whereIn('risk_level', ['high', 'critical'])->where('is_archived', false);
if (! empty($filters['status'])) {
$query->where('status', $filters['status']);
}
if (! empty($filters['project_id'])) {
$query->where('id', $filters['project_id']);
}
return $query->with('projectManager:id,name')->get()->map(fn ($p) => [
'id' => $p->id,
'title' => $p->title,
'risk_level' => $p->risk_level,
'progress' => $p->progress,
'delayed_tasks' => Task::where('project_id', $p->id)->where('due_date', '<', Carbon::now())->where('status', '!=', 'done')->count(),
]);
}
}