ultimatepos/Modules/ManagementTools/Http/Controllers/EmployeeTaskController.php

468 lines
18 KiB
PHP

<?php
namespace Modules\ManagementTools\Http\Controllers;
use App\User;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\ManagementTools\Entities\MtUserTask;
use Modules\ManagementTools\Utils\ManagementToolsUtil;
use Modules\ManagementTools\Utils\ProfessionalProjectBridge;
class EmployeeTaskController extends Controller
{
public function __construct(
protected ManagementToolsUtil $mtUtil,
protected ProfessionalProjectBridge $ppBridge
) {
}
protected function authorizeAccess(): void
{
if (! auth()->user()->can('managementtools.access') || ! auth()->user()->can('managementtools.manage_employee_tasks')) {
abort(403, 'Unauthorized action.');
}
}
protected function authorizeUserTasksPage(): void
{
if (! auth()->user()->can('managementtools.access')
|| (! auth()->user()->can('managementtools.manage_employee_tasks')
&& ! auth()->user()->can('managementtools.daily_report_all'))) {
abort(403, 'Unauthorized action.');
}
}
protected function authorizeOwnOrManage(): void
{
if (! auth()->user()->can('managementtools.daily_report_own')
&& ! auth()->user()->can('managementtools.manage_employee_tasks')) {
abort(403, 'Unauthorized action.');
}
}
protected const TABS = ['defined', 'pending', 'unreported', 'inability', 'incomplete', 'approved', 'all'];
public function index(Request $request)
{
$this->authorizeUserTasksPage();
$business_id = (int) $request->session()->get('user.business_id');
$active_tab = $request->query('tab', 'defined');
if (! in_array($active_tab, self::TABS, true)) {
$active_tab = 'defined';
}
return view('managementtools::employee_tasks.index', [
'tab_counts' => $this->buildTabCounts($business_id),
'active_tab' => $active_tab,
'can_manage' => auth()->user()->can('managementtools.manage_employee_tasks'),
]);
}
public function tabCounts(Request $request)
{
$this->authorizeUserTasksPage();
$business_id = (int) $request->session()->get('user.business_id');
return response()->json([
'success' => true,
'counts' => $this->buildTabCounts($business_id),
]);
}
public function tabContent(Request $request, string $tab)
{
$this->authorizeUserTasksPage();
if (! in_array($tab, self::TABS, true)) {
abort(404);
}
$business_id = (int) $request->session()->get('user.business_id');
$tasks = $this->tasksForTab($business_id, $tab, $request);
$count = $tasks instanceof \Illuminate\Pagination\AbstractPaginator ? $tasks->total() : $tasks->count();
$html = view('managementtools::employee_tasks.partials.tab_pane_content', [
'tab' => $tab,
'tasks' => $tasks,
'users' => $tab === 'approved' ? User::forDropdown($business_id, false) : [],
'filter_user_id' => $request->input('filter_user_id'),
'filter_category_id' => $request->input('filter_category_id'),
'filter_start' => $this->mtUtil->formatForInput($this->mtUtil->parseInputDate($request->filter_start)),
'filter_end' => $this->mtUtil->formatForInput($this->mtUtil->parseInputDate($request->filter_end)),
'can_manage' => auth()->user()->can('managementtools.manage_employee_tasks'),
'can_urgent_message' => auth()->user()->can('managementtools.urgent_messages'),
'can_task_report' => auth()->user()->can('managementtools.task_report'),
'has_pp_projects' => $this->ppBridge->isAvailable(),
'project_filter_options' => $this->ppBridge->isAvailable()
? $this->ppBridge->categoriesDropdown($business_id)
: [],
])->render();
return response()->json([
'success' => true,
'html' => $html,
'count' => $count,
'tab' => $tab,
]);
}
protected function buildTabCounts(int $business_id): array
{
$three_days_ago = Carbon::today()->subDays(3)->format('Y-m-d');
return [
'defined' => MtUserTask::forBusiness($business_id)->where('task_status', 0)->count(),
'pending' => MtUserTask::forBusiness($business_id)->pendingApproval()->count(),
'unreported' => MtUserTask::forBusiness($business_id)
->where('task_status', 0)
->whereDoesntHave('dailyReports', fn ($q) => $q->where('report_date', '>=', $three_days_ago))
->count(),
'inability' => MtUserTask::forBusiness($business_id)
->whereHas('dailyReports', fn ($q) => $q->where('task_status', 4))
->count(),
'incomplete' => MtUserTask::forBusiness($business_id)
->where('task_status', 0)
->whereNotNull('due_date')
->where('due_date', '<', Carbon::today())
->count(),
'approved' => MtUserTask::forBusiness($business_id)->where('task_status', 1)->count(),
'all' => MtUserTask::forBusiness($business_id)->count(),
];
}
protected function applyProjectFilter($query, Request $request): void
{
if (! $request->filled('filter_category_id') || ! $this->ppBridge->isAvailable()) {
return;
}
$filter = $request->input('filter_category_id');
if ($filter === 'uncategorized') {
$query->whereNotNull('project_id')->whereHas('ppProject', fn ($q) => $q->uncategorized());
} elseif (is_numeric($filter)) {
$query->whereHas('ppProject.categories', fn ($q) => $q->where('pp_project_categories.id', (int) $filter));
}
}
protected function tasksForTab(int $business_id, string $tab, Request $request)
{
$three_days_ago = Carbon::today()->subDays(3)->format('Y-m-d');
$per_page = 50;
$with = ['user', 'ppProject.categories'];
switch ($tab) {
case 'defined':
$query = MtUserTask::forBusiness($business_id)
->where('task_status', 0)
->with($with)
->orderByDesc('created_at');
$this->applyProjectFilter($query, $request);
return $query->paginate($per_page);
case 'pending':
$query = MtUserTask::forBusiness($business_id)
->pendingApproval()
->with($with)
->orderByDesc('created_at');
$this->applyProjectFilter($query, $request);
return $query->paginate($per_page);
case 'unreported':
$query = MtUserTask::forBusiness($business_id)
->where('task_status', 0)
->whereDoesntHave('dailyReports', fn ($q) => $q->where('report_date', '>=', $three_days_ago))
->with($with)
->orderByDesc('created_at');
$this->applyProjectFilter($query, $request);
return $query->paginate($per_page);
case 'inability':
$query = MtUserTask::forBusiness($business_id)
->whereHas('dailyReports', fn ($q) => $q->where('task_status', 4))
->with($with)
->orderByDesc('updated_at');
$this->applyProjectFilter($query, $request);
return $query->paginate($per_page);
case 'incomplete':
$query = MtUserTask::forBusiness($business_id)
->where('task_status', 0)
->whereNotNull('due_date')
->where('due_date', '<', Carbon::today())
->with($with)
->orderBy('due_date');
$this->applyProjectFilter($query, $request);
return $query->paginate(40);
case 'approved':
$query = MtUserTask::forBusiness($business_id)->where('task_status', 1)->with($with);
if ($request->filled('filter_user_id')) {
$query->where('user_id', (int) $request->filter_user_id);
}
if ($request->filled('filter_start')) {
$start = $this->mtUtil->parseInputDate($request->filter_start);
if ($start) {
$query->whereDate('updated_at', '>=', $start);
}
}
if ($request->filled('filter_end')) {
$end = $this->mtUtil->parseInputDate($request->filter_end);
if ($end) {
$query->whereDate('updated_at', '<=', $end);
}
}
$this->applyProjectFilter($query, $request);
return $query->orderByDesc('updated_at')->paginate($per_page);
case 'all':
$query = MtUserTask::forBusiness($business_id)
->with($with)
->orderByDesc('created_at');
$this->applyProjectFilter($query, $request);
return $query->paginate($per_page);
default:
abort(404);
}
}
public function create()
{
$this->authorizeAccess();
$business_id = (int) request()->session()->get('user.business_id');
$users = User::forDropdown($business_id, true);
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, (int) request()->input('user_id', auth()->id()));
return view('managementtools::employee_tasks.create', [
'users' => $users,
'statuses' => MtUserTask::statusLabels(),
'has_pp_projects' => $this->ppBridge->isAvailable(),
'projects_grouped' => $projects_grouped,
]);
}
/** Admin creates a system task (pre-approved). */
public function store(Request $request)
{
$this->authorizeAccess();
$business_id = (int) $request->session()->get('user.business_id');
$request->validate([
'user_id' => 'required|integer|exists:users,id',
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:2000',
'due_date' => 'nullable|string|max:20',
'task_status' => 'nullable|integer|in:0,1,2',
'project_id' => 'nullable|integer',
]);
if ($request->filled('project_id') && ! $this->ppBridge->validateProjectForUser($business_id, (int) $request->user_id, (int) $request->project_id)) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
}
MtUserTask::create([
'business_id' => $business_id,
'user_id' => $request->user_id,
'project_id' => $request->input('project_id'),
'title' => $request->title,
'description' => $request->description,
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
'task_status' => $request->input('task_status', 0),
'task_source' => MtUserTask::SOURCE_SYSTEM,
'approval_status' => MtUserTask::APPROVAL_APPROVED,
'approved_by' => auth()->id(),
'approved_at' => now(),
'created_by' => auth()->id(),
]);
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_added')]);
}
/** Modal form for employee task submission (CRM-style). */
public function createSubmitForm()
{
$this->authorizeOwnOrManage();
$business_id = (int) request()->session()->get('user.business_id');
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, auth()->id());
return view('managementtools::employee_tasks.submit_task_form', [
'projects_grouped' => $projects_grouped,
'has_pp_projects' => $this->ppBridge->isAvailable(),
]);
}
/** Employee submits own task — pending manager approval. */
public function submitOwn(Request $request)
{
$this->authorizeOwnOrManage();
$business_id = (int) $request->session()->get('user.business_id');
$user_id = auth()->id();
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, $user_id);
$has_projects = $this->ppBridge->isAvailable() && ! empty($projects_grouped);
$rules = [
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:2000',
'due_date' => 'nullable|string|max:20',
'estimated_hours' => 'required|integer|min:0|max:24',
'estimated_minutes' => 'required|integer|min:0|max:59',
];
if ($has_projects) {
$rules['project_id'] = 'required|integer';
}
$request->validate($rules);
if ($has_projects && ! $this->ppBridge->validateProjectForUser($business_id, $user_id, (int) $request->project_id)) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
}
$minutes = ((int) $request->estimated_hours * 60) + (int) $request->estimated_minutes;
MtUserTask::create([
'business_id' => $business_id,
'user_id' => $user_id,
'project_id' => $request->input('project_id'),
'title' => $request->title,
'description' => $request->description,
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
'estimated_minutes' => $minutes,
'task_status' => 0,
'task_source' => MtUserTask::SOURCE_USER,
'approval_status' => MtUserTask::APPROVAL_PENDING,
'created_by' => $user_id,
]);
return response()->json(['success' => true, 'msg' => __('managementtools::lang.user_task_submitted')]);
}
/** Employee deletes own pending submission. */
public function deleteOwn($id)
{
$this->authorizeOwnOrManage();
$business_id = (int) request()->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)
->where('user_id', auth()->id())
->userSubmitted()
->findOrFail($id);
if (! $task->isPendingApproval()) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
}
$task->delete();
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_deleted')]);
}
protected function getProjectsForUser(int $business_id, int $user_id): array
{
return $this->ppBridge->getProjectsForUser($business_id, $user_id);
}
public function approve($id)
{
$this->authorizeAccess();
$business_id = (int) request()->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
if (! $task->isPendingApproval()) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
}
$task->update([
'approval_status' => MtUserTask::APPROVAL_APPROVED,
'approved_by' => auth()->id(),
'approved_at' => now(),
]);
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_approved')]);
}
public function reject($id)
{
$this->authorizeAccess();
$business_id = (int) request()->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
if (! $task->isPendingApproval()) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
}
$task->update([
'approval_status' => MtUserTask::APPROVAL_REJECTED,
'approved_by' => auth()->id(),
'approved_at' => now(),
'task_status' => 2,
]);
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_rejected')]);
}
public function edit($id)
{
$this->authorizeAccess();
$business_id = (int) request()->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
$users = User::forDropdown($business_id, true);
$projects_grouped = $this->ppBridge->getProjectsGroupedByCategory($business_id, (int) $task->user_id);
return view('managementtools::employee_tasks.edit', [
'task' => $task,
'users' => $users,
'statuses' => MtUserTask::statusLabels(),
'has_pp_projects' => $this->ppBridge->isAvailable(),
'projects_grouped' => $projects_grouped,
]);
}
public function update(Request $request, $id)
{
$this->authorizeAccess();
$business_id = (int) $request->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
$request->validate([
'user_id' => 'required|integer|exists:users,id',
'title' => 'required|string|max:255',
'description' => 'nullable|string|max:2000',
'due_date' => 'nullable|string|max:20',
'task_status' => 'nullable|integer|in:0,1,2',
'project_id' => 'nullable|integer',
]);
if ($request->filled('project_id') && ! $this->ppBridge->validateProjectForUser($business_id, (int) $request->user_id, (int) $request->project_id)) {
return response()->json(['success' => false, 'msg' => __('managementtools::lang.invalid_project')]);
}
$task->update([
'user_id' => $request->user_id,
'project_id' => $request->input('project_id'),
'title' => $request->title,
'description' => $request->description,
'due_date' => $this->mtUtil->parseInputDate($request->due_date),
'task_status' => $request->input('task_status', 0),
]);
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_updated')]);
}
public function destroy($id)
{
$this->authorizeAccess();
$business_id = (int) request()->session()->get('user.business_id');
$task = MtUserTask::forBusiness($business_id)->findOrFail($id);
$task->delete();
return response()->json(['success' => true, 'msg' => __('managementtools::lang.task_deleted')]);
}
}