461 lines
17 KiB
PHP
461 lines
17 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\MtDailyReport;
|
|
use Modules\ManagementTools\Entities\MtDailyReportComment;
|
|
use Modules\ManagementTools\Entities\MtUserTask;
|
|
use Modules\ManagementTools\Entities\MtUserTimeLine;
|
|
use Modules\ManagementTools\Utils\DailyReportUtil;
|
|
use Modules\ManagementTools\Utils\ManagementToolsUtil;
|
|
use Modules\ManagementTools\Utils\ProfessionalProjectBridge;
|
|
|
|
class DailyReportController extends Controller
|
|
{
|
|
protected $reportUtil;
|
|
|
|
protected $mtUtil;
|
|
|
|
protected $ppBridge;
|
|
|
|
public function __construct(DailyReportUtil $reportUtil, ManagementToolsUtil $mtUtil, ProfessionalProjectBridge $ppBridge)
|
|
{
|
|
$this->reportUtil = $reportUtil;
|
|
$this->mtUtil = $mtUtil;
|
|
$this->ppBridge = $ppBridge;
|
|
}
|
|
|
|
protected function authorizeOwn()
|
|
{
|
|
if (! auth()->user()->can('managementtools.daily_report_own')) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
protected function authorizeAll()
|
|
{
|
|
if (! auth()->user()->can('managementtools.access') || ! auth()->user()->can('managementtools.daily_report_all')) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
protected function authorizeUserTasks()
|
|
{
|
|
if (! auth()->user()->can('managementtools.access')
|
|
|| (! auth()->user()->can('managementtools.manage_employee_tasks') && ! auth()->user()->can('managementtools.daily_report_all'))) {
|
|
abort(403, 'Unauthorized action.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Employee: task-based daily work report.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$this->authorizeOwn();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$user_id = auth()->id();
|
|
$selected_day = $request->query('day', $this->reportUtil->todayDate());
|
|
$is_editable = $this->reportUtil->isEditableDay($selected_day);
|
|
$is_past = $this->reportUtil->isPastDay($selected_day);
|
|
$day_selected = $request->has('day');
|
|
|
|
$slot_items = $this->reportUtil->buildSlotReportMap($business_id, $user_id, $selected_day);
|
|
|
|
[$workday_start, $workday_end] = $this->reportUtil->getWorkdayBounds($selected_day);
|
|
|
|
if ($is_past) {
|
|
$reported_task_ids = MtDailyReport::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->whereDate('report_date', $selected_day)
|
|
->whereNotNull('mt_user_task_id')
|
|
->pluck('mt_user_task_id')
|
|
->unique()
|
|
->filter();
|
|
|
|
$my_tasks = MtUserTask::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->whereIn('id', $reported_task_ids)
|
|
->orderBy('title')
|
|
->get();
|
|
} else {
|
|
$my_tasks = MtUserTask::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->active()
|
|
->assignable()
|
|
->orderBy('title')
|
|
->get();
|
|
}
|
|
|
|
$my_submitted_tasks = MtUserTask::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->userSubmitted()
|
|
->orderByDesc('created_at')
|
|
->get();
|
|
|
|
$has_projects = $this->ppBridge->isAvailable();
|
|
if ($has_projects && $my_submitted_tasks->isNotEmpty()) {
|
|
$project_names = $this->ppBridge->resolveProjectNames(
|
|
$my_submitted_tasks->pluck('project_id')->filter()->unique()->values()->all()
|
|
);
|
|
$my_submitted_tasks->each(function ($task) use ($project_names) {
|
|
$task->project_name = $task->project_id ? ($project_names[$task->project_id] ?? null) : null;
|
|
});
|
|
}
|
|
|
|
$day_timelines = MtUserTimeLine::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->whereBetween('starts_at', [$workday_start, $workday_end])
|
|
->with('userTask')
|
|
->orderBy('starts_at')
|
|
->get();
|
|
|
|
$slot_task_cache = [];
|
|
$report_settings = $this->reportUtil->getUserReportSettings($business_id, $user_id);
|
|
|
|
return view('managementtools::daily_reports.index', [
|
|
'slot_items' => $slot_items,
|
|
'report_settings' => $report_settings,
|
|
'my_tasks' => $my_tasks,
|
|
'my_submitted_tasks' => $my_submitted_tasks,
|
|
'has_projects' => $has_projects ?? false,
|
|
'day_timelines' => $day_timelines,
|
|
'days' => $this->reportUtil->generateRecentDays(15),
|
|
'selected_day' => $selected_day,
|
|
'is_editable' => $is_editable,
|
|
'is_past' => $is_past,
|
|
'day_selected' => $day_selected,
|
|
'submitted_days' => $this->reportUtil->getSubmittedDays($business_id, $user_id),
|
|
'task_status_options' => $this->reportUtil->taskStatusOptions(),
|
|
'user_status_options' => $this->reportUtil->userStatusOptions(),
|
|
'slot_task_cache' => &$slot_task_cache,
|
|
'business_id' => $business_id,
|
|
'user_id' => $user_id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Save task-based daily report (today only).
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorizeOwn();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$user_id = auth()->id();
|
|
$report_date = $request->input('report_date', $this->reportUtil->todayDate());
|
|
|
|
if (! $this->reportUtil->isEditableDay($report_date)) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'msg' => __('managementtools::lang.past_day_locked'),
|
|
], 422);
|
|
}
|
|
|
|
$finalize = $request->boolean('finalize');
|
|
$now = Carbon::now();
|
|
$saved = 0;
|
|
|
|
// Slot-based submission (CRM format)
|
|
$descriptions = $request->input('description', []);
|
|
if (is_array($descriptions) && count($descriptions) > 0) {
|
|
$tasks_input = $request->input('task', []);
|
|
$user_statuses = $request->input('user_status', []);
|
|
$task_statuses = $request->input('task_status', []);
|
|
|
|
foreach ($descriptions as $slot => $body) {
|
|
$body = trim((string) $body);
|
|
if ($body === '') {
|
|
continue;
|
|
}
|
|
|
|
if (Carbon::parse($slot)->gt($now)) {
|
|
continue;
|
|
}
|
|
|
|
$task_id = ! empty($tasks_input[$slot]) ? (int) $tasks_input[$slot] : null;
|
|
if ($task_id === 0) {
|
|
$task_id = null;
|
|
}
|
|
|
|
MtDailyReport::updateOrCreate(
|
|
[
|
|
'business_id' => $business_id,
|
|
'user_id' => $user_id,
|
|
'reported_at' => Carbon::parse($slot),
|
|
],
|
|
[
|
|
'body' => $body,
|
|
'report_date' => Carbon::parse($slot)->format('Y-m-d'),
|
|
'mt_user_task_id' => $task_id,
|
|
'user_status' => ! empty($user_statuses[$slot]) ? (int) $user_statuses[$slot] : null,
|
|
'task_status' => ! empty($task_statuses[$slot]) ? (int) $task_statuses[$slot] : null,
|
|
'created_by' => $user_id,
|
|
'submitted_at' => $finalize ? $now : null,
|
|
]
|
|
);
|
|
$saved++;
|
|
}
|
|
} else {
|
|
// Legacy task-card batch submission
|
|
$request->validate([
|
|
'report_date' => 'required|date',
|
|
'user_status' => 'nullable|integer|in:1,2,3,4',
|
|
'general_notes' => 'nullable|string|max:2000',
|
|
'tasks' => 'nullable|array',
|
|
'tasks.*.body' => 'nullable|string|max:5000',
|
|
'tasks.*.task_status' => 'nullable|integer|in:3,4,5',
|
|
]);
|
|
|
|
$reported_at = Carbon::parse($report_date)->startOfDay()->addHours($this->reportUtil->workdayStartHour());
|
|
$user_status = $request->input('user_status');
|
|
$general_notes = trim((string) $request->input('general_notes', ''));
|
|
|
|
if ($user_status || $general_notes !== '') {
|
|
MtDailyReport::updateOrCreate(
|
|
[
|
|
'business_id' => $business_id,
|
|
'user_id' => $user_id,
|
|
'report_date' => $report_date,
|
|
'mt_user_task_id' => null,
|
|
],
|
|
[
|
|
'body' => $general_notes,
|
|
'reported_at' => $reported_at,
|
|
'user_status' => $user_status ?: null,
|
|
'created_by' => $user_id,
|
|
'submitted_at' => $finalize ? $now : null,
|
|
]
|
|
);
|
|
$saved++;
|
|
}
|
|
|
|
$tasks_input = $request->input('tasks', []);
|
|
$valid_task_ids = MtUserTask::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->assignable()
|
|
->whereIn('id', array_keys($tasks_input))
|
|
->pluck('id')
|
|
->toArray();
|
|
|
|
foreach ($tasks_input as $task_id => $data) {
|
|
if (! in_array((int) $task_id, $valid_task_ids, true)) {
|
|
continue;
|
|
}
|
|
|
|
$body = trim((string) ($data['body'] ?? ''));
|
|
$task_status = ! empty($data['task_status']) ? (int) $data['task_status'] : null;
|
|
|
|
if ($body === '' && ! $task_status) {
|
|
continue;
|
|
}
|
|
|
|
MtDailyReport::updateOrCreate(
|
|
[
|
|
'business_id' => $business_id,
|
|
'user_id' => $user_id,
|
|
'report_date' => $report_date,
|
|
'mt_user_task_id' => (int) $task_id,
|
|
],
|
|
[
|
|
'body' => $body ?: '—',
|
|
'reported_at' => $reported_at,
|
|
'task_status' => $task_status,
|
|
'user_status' => $user_status,
|
|
'created_by' => $user_id,
|
|
'submitted_at' => $finalize ? $now : null,
|
|
]
|
|
);
|
|
$saved++;
|
|
}
|
|
}
|
|
|
|
if ($finalize && $saved === 0) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'msg' => __('managementtools::lang.report_nothing_to_submit'),
|
|
], 422);
|
|
}
|
|
|
|
$msg = $finalize
|
|
? __('managementtools::lang.report_day_submitted')
|
|
: __('managementtools::lang.report_draft_saved', ['count' => $saved]);
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'msg' => $msg,
|
|
'submitted' => $finalize,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Admin: all employees' reports for a day.
|
|
*/
|
|
public function employees(Request $request)
|
|
{
|
|
$this->authorizeAll();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$day = $request->query('day', Carbon::today()->format('Y-m-d'));
|
|
|
|
$reports = MtDailyReport::forBusiness($business_id)
|
|
->whereDate('report_date', $day)
|
|
->with(['user', 'comments.user', 'userTask'])
|
|
->orderBy('reported_at')
|
|
->get();
|
|
|
|
$grouped = $reports->groupBy('user_id')->map(function ($userReports) {
|
|
return $userReports->groupBy(function ($r) {
|
|
return ($r->mt_user_task_id ?: 'day').'|'.$r->body;
|
|
})->map(function ($bodyGroup) {
|
|
$first = $bodyGroup->first();
|
|
|
|
return (object) [
|
|
'body' => $first->body,
|
|
'body_count' => $bodyGroup->count(),
|
|
'first_slot' => $bodyGroup->min('reported_at'),
|
|
'last_slot' => $bodyGroup->max('reported_at'),
|
|
'id' => $first->id,
|
|
'user_id' => $first->user_id,
|
|
'score' => $first->score,
|
|
'user_status' => $first->user_status,
|
|
'task_status' => $first->task_status,
|
|
'mt_user_task_id' => $first->mt_user_task_id,
|
|
'userTask' => $first->userTask,
|
|
'user' => $first->user,
|
|
'comments' => $bodyGroup->flatMap->comments->unique('id'),
|
|
'created_at' => $first->created_at,
|
|
'updated_at' => $bodyGroup->max('updated_at'),
|
|
'submitted_at' => $bodyGroup->max('submitted_at'),
|
|
];
|
|
})->values();
|
|
});
|
|
|
|
$reported_user_ids = $grouped->keys()->toArray();
|
|
$users_without_report = User::where('business_id', $business_id)
|
|
->user()
|
|
->where('allow_login', 1)
|
|
->whereNotIn('id', $reported_user_ids)
|
|
->get(['id', 'first_name', 'last_name', 'surname']);
|
|
|
|
return view('managementtools::daily_reports.employees', [
|
|
'grouped' => $grouped,
|
|
'days' => $this->reportUtil->generateRecentDays(),
|
|
'selected_day' => $day,
|
|
'users_without_report' => $users_without_report,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Admin: analytics with date range + user filter.
|
|
*/
|
|
public function analytics(Request $request)
|
|
{
|
|
$this->authorizeAll();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$users = User::forDropdown($business_id, true);
|
|
$grouped = collect();
|
|
$start_date_input = $request->input('start_date');
|
|
$end_date_input = $request->input('end_date');
|
|
$user_id = $request->input('user_id');
|
|
$start_gregorian = $this->mtUtil->parseInputDate($start_date_input);
|
|
$end_gregorian = $this->mtUtil->parseInputDate($end_date_input);
|
|
|
|
if ($start_gregorian && $end_gregorian && $user_id) {
|
|
$reports = MtDailyReport::forBusiness($business_id)
|
|
->where('user_id', $user_id)
|
|
->whereBetween('report_date', [$start_gregorian, $end_gregorian])
|
|
->with(['user', 'comments.user', 'userTask'])
|
|
->orderBy('report_date')
|
|
->orderBy('reported_at')
|
|
->get();
|
|
|
|
$grouped = $reports->groupBy('body')->map(function ($bodyGroup) {
|
|
$first = $bodyGroup->first();
|
|
|
|
return (object) [
|
|
'body' => $first->body,
|
|
'body_count' => $bodyGroup->count(),
|
|
'first_slot' => $bodyGroup->min('reported_at'),
|
|
'last_slot' => $bodyGroup->max('reported_at'),
|
|
'id' => $first->id,
|
|
'score' => $first->score,
|
|
'user_status' => $first->user_status,
|
|
'userTask' => $first->userTask,
|
|
'user' => $first->user,
|
|
'comments' => $bodyGroup->flatMap->comments,
|
|
'created_at' => $first->created_at,
|
|
'updated_at' => $bodyGroup->max('updated_at'),
|
|
];
|
|
})->values();
|
|
}
|
|
|
|
return view('managementtools::daily_reports.analytics', [
|
|
'users' => $users,
|
|
'grouped' => $grouped,
|
|
'start_date' => $this->mtUtil->formatForInput($start_gregorian ?? $start_date_input),
|
|
'end_date' => $this->mtUtil->formatForInput($end_gregorian ?? $end_date_input),
|
|
'user_id' => $user_id,
|
|
]);
|
|
}
|
|
|
|
public function feedback(Request $request, $id)
|
|
{
|
|
$this->authorizeAll();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$score = (int) $request->input('score');
|
|
|
|
if (! in_array($score, [1, 2, 3, 4, 5], true)) {
|
|
return response()->json(['success' => false, 'msg' => __('managementtools::lang.something_went_wrong')]);
|
|
}
|
|
|
|
$report = MtDailyReport::forBusiness($business_id)->findOrFail($id);
|
|
|
|
MtDailyReport::forBusiness($business_id)
|
|
->where('user_id', $report->user_id)
|
|
->where('body', $report->body)
|
|
->whereDate('report_date', $report->report_date ?? $report->reported_at)
|
|
->update(['score' => $score]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.score_saved')]);
|
|
}
|
|
|
|
public function comments($id)
|
|
{
|
|
$this->authorizeAll();
|
|
|
|
$business_id = (int) request()->session()->get('user.business_id');
|
|
$report = MtDailyReport::forBusiness($business_id)
|
|
->with(['user', 'comments.user', 'userTask'])
|
|
->findOrFail($id);
|
|
|
|
return view('managementtools::daily_reports.partials.comments_modal', compact('report'));
|
|
}
|
|
|
|
public function storeComment(Request $request, $id)
|
|
{
|
|
$this->authorizeAll();
|
|
|
|
$business_id = (int) $request->session()->get('user.business_id');
|
|
$report = MtDailyReport::forBusiness($business_id)->findOrFail($id);
|
|
|
|
$request->validate(['body' => 'required|string|max:2000']);
|
|
|
|
MtDailyReportComment::create([
|
|
'business_id' => $business_id,
|
|
'daily_report_id' => $report->id,
|
|
'user_id' => auth()->id(),
|
|
'body' => $request->body,
|
|
]);
|
|
|
|
return response()->json(['success' => true, 'msg' => __('managementtools::lang.comment_added')]);
|
|
}
|
|
}
|