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

316 lines
12 KiB
PHP

<?php
namespace Modules\ManagementTools\Http\Controllers;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Response;
use Modules\ManagementTools\Entities\MtExecutiveNote;
use Modules\ManagementTools\Entities\MtExecutiveTarget;
use Modules\ManagementTools\Services\ExecutiveCockpitService;
use Modules\ManagementTools\Utils\ManagementToolsUtil;
class ExecutiveCockpitController extends Controller
{
public function __construct(
protected ExecutiveCockpitService $cockpitService,
protected ManagementToolsUtil $mtUtil
) {
}
public function index(Request $request)
{
$this->authorizeCockpitView();
$context = $this->resolveCockpitContext($request);
$viewData = $this->buildCockpitViewData($context, true);
if (function_exists('activity')) {
activity('managementtools')
->withProperties([
'business_id' => $context['businessId'],
'start_date' => $context['startDate']->toDateString(),
'end_date' => $context['endDate']->toDateString(),
'active_tab' => $context['activeTab'],
])
->log('executive_cockpit_viewed');
}
return view('managementtools::executive_cockpit.index', $viewData);
}
public function content(Request $request)
{
$this->authorizeCockpitView();
$context = $this->resolveCockpitContext($request);
return view('managementtools::executive_cockpit.partials.content', $this->buildCockpitViewData($context, true));
}
public function drillTab(Request $request)
{
$this->authorizeCockpitView();
$context = $this->resolveCockpitContext($request);
$tabPayload = $this->cockpitService->buildDrilldownTabForViewer(
$context['businessId'],
$context['startDate'],
$context['endDate'],
$context['activeTab'],
auth()->user()
);
if (! $tabPayload) {
abort(404);
}
return view('managementtools::executive_cockpit.partials.drill_tab_body', [
'data' => [
'trend_month_labels' => $tabPayload['trend_month_labels'],
'drilldown_labels' => $tabPayload['drilldown_labels'],
],
'tab_key' => $tabPayload['tab_key'],
'section' => $tabPayload['section'],
]);
}
public function saveTargets(Request $request)
{
$this->authorizeCockpitManage();
$businessId = (int) $request->session()->get('user.business_id');
$request->validate([
'targets.sales_this_month' => 'nullable|numeric',
'targets.gross_margin_estimate' => 'nullable|numeric',
'targets.workforce_productivity' => 'nullable|numeric',
]);
$metrics = $request->input('targets', []);
foreach (['sales_this_month', 'gross_margin_estimate', 'workforce_productivity'] as $metric) {
if (! isset($metrics[$metric]) || $metrics[$metric] === '') {
continue;
}
MtExecutiveTarget::create([
'business_id' => $businessId,
'metric_key' => $metric,
'target_value' => (float) $metrics[$metric],
'effective_from' => Carbon::today()->toDateString(),
'created_by' => auth()->id(),
]);
if (function_exists('activity')) {
activity('managementtools')
->withProperties([
'business_id' => $businessId,
'metric' => $metric,
'target_value' => (float) $metrics[$metric],
])
->log('executive_target_saved');
}
}
return redirect()->back()->with('status', ['success' => true, 'msg' => __('managementtools::lang.targets_saved')]);
}
public function exportPdf(Request $request)
{
$this->authorizeCockpitView();
$businessId = (int) $request->session()->get('user.business_id');
$start = Carbon::parse($this->mtUtil->parseInputDate($request->input('start_date')) ?: Carbon::today()->subDays(29)->toDateString())->startOfDay();
$end = Carbon::parse($this->mtUtil->parseInputDate($request->input('end_date')) ?: Carbon::today()->toDateString())->endOfDay();
$data = $this->cockpitService->build($businessId, $start, $end);
$html = view('managementtools::executive_cockpit.pdf', compact('data'))->render();
$mpdf = new \Mpdf\Mpdf(['tempDir' => storage_path('app/mpdf-temp')]);
$mpdf->WriteHTML($html);
return Response::make($mpdf->Output('executive-cockpit.pdf', 'S'), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="executive-cockpit.pdf"',
]);
}
public function exportBoardPackPdf(Request $request)
{
$this->authorizeCockpitView();
$businessId = (int) $request->session()->get('user.business_id');
$start = Carbon::parse($this->mtUtil->parseInputDate($request->input('start_date')) ?: Carbon::today()->subDays(29)->toDateString())->startOfDay();
$end = Carbon::parse($this->mtUtil->parseInputDate($request->input('end_date')) ?: Carbon::today()->toDateString())->endOfDay();
$data = $this->cockpitService->build($businessId, $start, $end);
if (class_exists(\Modules\IntegrationHub\Events\ExecutiveCockpitExported::class)) {
event(new \Modules\IntegrationHub\Events\ExecutiveCockpitExported($businessId, $data));
}
$html = view('managementtools::executive_cockpit.board_pack_pdf', compact('data'))->render();
$mpdf = new \Mpdf\Mpdf(['tempDir' => storage_path('app/mpdf-temp')]);
$mpdf->WriteHTML($html);
return Response::make($mpdf->Output('board-pack.pdf', 'S'), 200, [
'Content-Type' => 'application/pdf',
'Content-Disposition' => 'inline; filename="board-pack.pdf"',
]);
}
public function saveNote(Request $request)
{
$this->authorizeCockpitManage();
$businessId = (int) $request->session()->get('user.business_id');
$request->validate([
'title' => 'required|string|max:255',
'note' => 'required|string|max:5000',
]);
MtExecutiveNote::create([
'business_id' => $businessId,
'user_id' => auth()->id(),
'title' => $request->input('title'),
'note' => $request->input('note'),
'note_date' => Carbon::today()->toDateString(),
'visibility' => 'leadership',
]);
if (function_exists('activity')) {
activity('managementtools')
->withProperties([
'business_id' => $businessId,
'title' => $request->input('title'),
])
->log('executive_note_saved');
}
return redirect()->back()->with('status', ['success' => true, 'msg' => __('managementtools::lang.note_saved')]);
}
protected function resolveCockpitContext(Request $request): array
{
$businessId = (int) $request->session()->get('user.business_id');
$start = $this->mtUtil->parseInputDate($request->input('start_date')) ?: Carbon::today()->subDays(29)->toDateString();
$end = $this->mtUtil->parseInputDate($request->input('end_date')) ?: Carbon::today()->toDateString();
$validTabs = ['finance', 'sales', 'maintenance', 'projects', 'workforce', 'crm', 'accounting', 'manufacturing', 'ie'];
$activeTab = in_array($request->input('tab'), $validTabs, true) ? $request->input('tab') : 'finance';
return [
'businessId' => $businessId,
'startDate' => Carbon::parse($start)->startOfDay(),
'endDate' => Carbon::parse($end)->endOfDay(),
'activeTab' => $activeTab,
];
}
protected function buildCockpitViewData(array $context, bool $lazyDrillTabs = false): array
{
$data = $this->cockpitService->buildForViewer(
$context['businessId'],
$context['startDate'],
$context['endDate'],
auth()->user(),
$lazyDrillTabs,
$context['activeTab']
);
$startDate = $this->mtUtil->formatForInput($context['startDate']->toDateString());
$endDate = $this->mtUtil->formatForInput($context['endDate']->toDateString());
return [
'data' => $data,
'start_date' => $startDate,
'end_date' => $endDate,
'active_tab' => $context['activeTab'],
'quick_links' => $this->quickLinks(),
'date_presets' => $this->datePresets(),
'export_urls' => $this->exportUrls($startDate, $endDate),
'content_url' => action([self::class, 'content']),
'drill_tab_url' => action([self::class, 'drillTab']),
'lazy_drill_tabs' => true,
];
}
protected function quickLinks(): array
{
return [
['title' => __('managementtools::lang.open_sales_report'), 'url' => url('/reports/product-sell-report')],
['title' => __('managementtools::lang.open_payment_report'), 'url' => url('/reports/sell-payment-report')],
['title' => __('managementtools::lang.open_maintenance_board'), 'url' => url('/maintenance/dashboard')],
['title' => __('managementtools::lang.open_ticket_center'), 'url' => url('/management-tools/tickets')],
['title' => __('managementtools::lang.open_project_center'), 'url' => url('/management-tools/project-categories')],
['title' => __('managementtools::lang.open_crm_board'), 'url' => url('/crm/dashboard')],
['title' => __('managementtools::lang.open_accounting_dashboard'), 'url' => url('/accounting/dashboard')],
['title' => __('managementtools::lang.open_manufacturing_board'), 'url' => url('/manufacturing/production')],
['title' => __('managementtools::lang.open_ie_dashboard'), 'url' => url('/industrial-engineering/dashboard')],
];
}
protected function datePresets(): array
{
$today = Carbon::today();
return [
[
'key' => '7d',
'label' => __('managementtools::lang.range_7_days'),
'start' => $this->mtUtil->formatForInput($today->copy()->subDays(6)->toDateString()),
'end' => $this->mtUtil->formatForInput($today->toDateString()),
],
[
'key' => '30d',
'label' => __('managementtools::lang.range_30_days'),
'start' => $this->mtUtil->formatForInput($today->copy()->subDays(29)->toDateString()),
'end' => $this->mtUtil->formatForInput($today->toDateString()),
],
[
'key' => 'month',
'label' => __('managementtools::lang.range_this_month'),
'start' => $this->mtUtil->formatForInput($today->copy()->startOfMonth()->toDateString()),
'end' => $this->mtUtil->formatForInput($today->toDateString()),
],
];
}
protected function exportUrls(string $startDate, string $endDate): array
{
$urls = [
'board_pack' => action([self::class, 'exportBoardPackPdf'], [
'start_date' => $startDate,
'end_date' => $endDate,
]),
'pdf' => action([self::class, 'exportPdf'], [
'start_date' => $startDate,
'end_date' => $endDate,
]),
];
if (\Module::has('IntegrationHub') && \Module::isEnabled('IntegrationHub') && auth()->user()->can('integrationhub.export')) {
$urls['bi_exports'] = url('/integrationhub/exports');
}
return $urls;
}
protected function authorizeCockpitView(): void
{
if (
! auth()->user()->can('managementtools.access')
|| ! auth()->user()->can('managementtools.daily_report_all')
|| ! auth()->user()->can('managementtools.board_view')
) {
abort(403, 'Unauthorized action.');
}
}
protected function authorizeCockpitManage(): void
{
if (
! auth()->user()->can('managementtools.access')
|| ! auth()->user()->can('managementtools.daily_report_all')
|| ! auth()->user()->can('managementtools.board_manage')
) {
abort(403, 'Unauthorized action.');
}
}
}