Rapport-Laravel/app/Http/Controllers/AdherencepackratioreportCon...

498 lines
17 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Exports\InvoicesExport;
use App\Http\Requests\LambdaRequest;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
class AdherencepackratioreportController extends Controller
{
private const PARTITION = 'rapport-team2';
private const REPORT_START = 'April 2026';
private const RATIO_METRIC_ID = '1784522317-547f27be-1da3-4912-983e-120b49b0841a';
private const ALL_METRICS = [
'1781688574-0659e182-8771-4f7b-b3c8-eb6f45cf5e61' => 'DPI',
'1781688609-af666416-0aa2-4bc4-b7c1-c09a09de153b' => 'MDI',
'1781688715-c52dbfa2-1c07-4235-83b6-09e2d2783a7c' => 'DPI+MDI',
];
private const REPORT_TYPES = [
'dpi' => [
'label' => 'DPI',
'metrics' => [
'1781688574-0659e182-8771-4f7b-b3c8-eb6f45cf5e61',
'1781688715-c52dbfa2-1c07-4235-83b6-09e2d2783a7c',
],
'quarters_only' => false,
],
'mdi' => [
'label' => 'MDI',
'metrics' => [
'1781688609-af666416-0aa2-4bc4-b7c1-c09a09de153b',
'1781688715-c52dbfa2-1c07-4235-83b6-09e2d2783a7c',
],
'quarters_only' => false,
],
'quarter' => [
'label' => 'Quarter',
// Metrics used only to discover TMs; UI/export show quarter columns only.
'metrics' => [
'1781688574-0659e182-8771-4f7b-b3c8-eb6f45cf5e61',
'1781688609-af666416-0aa2-4bc4-b7c1-c09a09de153b',
'1781688715-c52dbfa2-1c07-4235-83b6-09e2d2783a7c',
],
'quarters_only' => true,
],
];
private ?string $accessToken = null;
private ?object $tokenUser = null;
public function __construct()
{
$this->_service = 'reports';
$this->_module = 'contact_metric_monthly';
$this->_viewname = 'adherencepackratioreport';
$this->_title = 'Adherence Pack Ratio Report';
$this->_base_url = '/adherencepackratioreport';
}
public function list(Request $request)
{
return redirect($this->buildUrl('/report', $request));
}
public function report(Request $request)
{
$token = $this->assertTeam2Access($request);
$type = $this->resolveReportType($request);
$perPage = $this->resolvePerPage($request);
$search = $this->resolveSearchTerm($request);
$payload = $this->buildReportPayload($request, false, $perPage, $search);
$response = LambdaRequest::getrequest(
$this->_service,
$this->_module . '/tm_adherence_pack_ratio',
$payload,
$token,
300
);
$report = $response->data ?? (object) [];
[$tms, $data, $pagination] = $this->resolveReportPage($report, $request, $perPage, $search);
$typeLabel = self::REPORT_TYPES[$type]['label'];
$quartersOnly = $this->isQuartersOnly($type);
return $this->view('report')->with([
'title' => $this->_title . ' (' . $typeLabel . ')',
'base_url' => $this->_base_url,
'token' => $token,
'type' => $type,
'typeLabel' => $typeLabel,
'quartersOnly' => $quartersOnly,
'months' => $quartersOnly ? [] : ($report->months ?? []),
'metrics' => $quartersOnly ? [] : ($report->metrics ?? []),
'quarters' => $report->quarters ?? [],
'quarter_data' => $report->quarter_data ?? (object) [],
'tms' => $tms,
'data' => $quartersOnly ? (object) [] : $data,
'pagination' => $pagination,
'perPage' => $perPage,
'search' => $search,
]);
}
public function export(Request $request)
{
$token = $this->assertTeam2Access($request);
$type = $this->resolveReportType($request);
$search = $this->resolveSearchTerm($request);
$payload = $this->buildReportPayload($request, true, 50, $search);
$response = LambdaRequest::getrequest(
$this->_service,
$this->_module . '/tm_adherence_pack_ratio',
$payload,
$token,
300
);
$report = $response->data ?? (object) [];
$quartersOnly = $this->isQuartersOnly($type);
$months = $quartersOnly ? [] : ($report->months ?? []);
$metrics = $quartersOnly ? [] : ($report->metrics ?? []);
$quarters = $report->quarters ?? [];
$quarterData = $report->quarter_data ?? (object) [];
$tms = $this->filterTmsBySearch($this->normalizeTmsList($report->tms ?? null), $search);
$data = $quartersOnly ? (object) [] : ($report->data ?? (object) []);
$headerRow1 = ['TM Employee Code', 'TM Name', 'Zone', 'Region', 'Area', 'Territory'];
$excelData = [];
if ($quartersOnly) {
foreach ($quarters as $quarter) {
$headerRow1[] = $this->quarterDisplayLabel($quarter);
}
$excelData[] = $headerRow1;
foreach ($tms as $tm) {
$row = [
$tm->employee_code ?? '',
$tm->name ?? $tm->id,
$tm->zone ?? '',
$tm->region ?? '',
$tm->area ?? '',
$tm->territory ?? '',
];
$tmQuarterData = is_object($quarterData) ? ($quarterData->{$tm->id} ?? null) : null;
foreach ($quarters as $quarter) {
$quarterKey = $quarter->key ?? '';
$value = is_object($tmQuarterData) ? ($tmQuarterData->{$quarterKey} ?? null) : null;
$row[] = $value !== null && $value !== '' ? $value : '-';
}
$excelData[] = $row;
}
} else {
$quartersAfterMonth = [];
foreach ($quarters as $quarter) {
$afterMonth = $quarter->after_month ?? null;
if ($afterMonth) {
$quartersAfterMonth[$afterMonth][] = $quarter;
}
}
$headerRow2 = array_fill(0, 6, '');
foreach ($months as $month) {
$monthKey = $month->key ?? '';
$headerRow1[] = $month->label ?? $monthKey;
$headerRow1 = array_merge($headerRow1, array_fill(0, max(count($metrics) - 1, 0), ''));
foreach ($metrics as $metric) {
$headerRow2[] = $metric->label ?? $metric->id;
}
foreach ($quartersAfterMonth[$monthKey] ?? [] as $quarter) {
$headerRow1[] = $quarter->label ?? $quarter->key;
$headerRow2[] = '';
}
}
$excelData = [$headerRow1, $headerRow2];
foreach ($tms as $tm) {
$row = [
$tm->employee_code ?? '',
$tm->name ?? $tm->id,
$tm->zone ?? '',
$tm->region ?? '',
$tm->area ?? '',
$tm->territory ?? '',
];
$tmQuarterData = is_object($quarterData) ? ($quarterData->{$tm->id} ?? null) : null;
foreach ($months as $month) {
$monthKey = $month->key ?? '';
$tmData = $data->{$tm->id} ?? null;
$monthData = is_object($tmData) ? ($tmData->{$monthKey} ?? null) : null;
foreach ($metrics as $metric) {
$metricId = $metric->id ?? '';
$value = is_object($monthData) ? ($monthData->{$metricId} ?? null) : null;
$row[] = $value !== null && $value !== '' ? $value : '-';
}
foreach ($quartersAfterMonth[$monthKey] ?? [] as $quarter) {
$quarterKey = $quarter->key ?? '';
$value = is_object($tmQuarterData) ? ($tmQuarterData->{$quarterKey} ?? null) : null;
$row[] = $value !== null && $value !== '' ? $value : '-';
}
}
$excelData[] = $row;
}
}
$typeLabel = strtolower(self::REPORT_TYPES[$type]['label']);
return Excel::download(new InvoicesExport($excelData), "adherence-pack-ratio-report-{$typeLabel}.xlsx");
}
protected function resolveReportType(Request $request): string
{
$type = strtolower(trim((string) $request->query('type', 'dpi')));
if (!isset(self::REPORT_TYPES[$type])) {
$type = 'dpi';
}
return $type;
}
protected function isQuartersOnly(string $type): bool
{
return !empty(self::REPORT_TYPES[$type]['quarters_only']);
}
protected function quarterDisplayLabel($quarter): string
{
$q = is_object($quarter) ? ($quarter->q ?? null) : ($quarter['q'] ?? null);
if ($q) {
return 'Q' . $q . ' %';
}
$label = is_object($quarter) ? ($quarter->label ?? $quarter->key ?? '') : ($quarter['label'] ?? $quarter['key'] ?? '');
$label = preg_replace('/\s*Ratio$/i', '', (string) $label) ?: (string) $label;
$label = trim((string) $label);
if ($label === '') {
return '';
}
if (preg_match('/%\s*$/', $label)) {
return $label;
}
return $label . ' %';
}
protected function metricsForType(string $type): array
{
$ids = self::REPORT_TYPES[$type]['metrics'] ?? self::REPORT_TYPES['dpi']['metrics'];
$metrics = [];
foreach ($ids as $id) {
$metrics[$id] = self::ALL_METRICS[$id] ?? $id;
}
return $metrics;
}
protected function resolveReportPage(object $report, Request $request, int $perPage, string $search = ''): array
{
$tms = $this->normalizeTmsList($report->tms ?? null);
if ($search !== '') {
$tms = $this->filterTmsBySearch($tms, $search);
}
$data = $report->data ?? (object) [];
$page = max(1, (int) $request->input('page', 1));
$apiPagination = $report->pagination ?? null;
$apiPaginated = is_object($apiPagination)
&& isset($apiPagination->total_pages)
&& (int) $apiPagination->total_pages >= 0
&& ($search !== '' || count($tms) <= $perPage);
if ($apiPaginated) {
$apiPagination->page = (int) ($apiPagination->page ?? $page);
$apiPagination->per_page = (int) ($apiPagination->per_page ?? $perPage);
return [$tms, $data, $apiPagination];
}
$total = count($tms);
$totalPages = max(1, (int) ceil($total / $perPage));
$page = min($page, $totalPages);
$offset = ($page - 1) * $perPage;
$pagedTms = array_slice($tms, $offset, $perPage);
$pagedData = (object) [];
foreach ($pagedTms as $tm) {
$tmId = is_object($tm) ? ($tm->id ?? null) : null;
if ($tmId && isset($data->{$tmId})) {
$pagedData->{$tmId} = $data->{$tmId};
}
}
$pagination = (object) [
'page' => $page,
'per_page' => $perPage,
'total' => $total,
'total_pages' => $totalPages,
];
return [$pagedTms, $pagedData, $pagination];
}
protected function resolveSearchTerm(Request $request): string
{
return trim((string) $request->query('search', ''));
}
protected function filterTmsBySearch(array $tms, string $search): array
{
if ($search === '') {
return $tms;
}
$needle = mb_strtolower($search);
$words = preg_split('/\s+/u', $needle, -1, PREG_SPLIT_NO_EMPTY);
if (empty($words)) {
return $tms;
}
return array_values(array_filter($tms, function ($tm) use ($words) {
if (!is_object($tm)) {
return false;
}
$haystack = $this->tmSearchHaystack($tm);
foreach ($words as $word) {
if (!str_contains($haystack, $word)) {
return false;
}
}
return true;
}));
}
protected function tmSearchHaystack(object $tm): string
{
$parts = [
$tm->name ?? $tm->id ?? '',
$tm->employee_code ?? '',
$tm->zone ?? '',
$tm->region ?? '',
$tm->area ?? '',
$tm->territory ?? '',
];
$normalized = [];
foreach ($parts as $part) {
$value = trim((string) $part);
if ($value === '' || $value === '-') {
continue;
}
$normalized[] = mb_strtolower($value);
}
return preg_replace('/\s+/u', ' ', implode(' ', $normalized));
}
protected function normalizeTmsList($raw): array
{
if (is_array($raw)) {
return array_values($raw);
}
if (is_object($raw)) {
return array_values((array) $raw);
}
return [];
}
protected function buildReportPayload(Request $request, bool $fetchAll = false, int $perPage = 50, string $search = ''): array
{
$type = $this->resolveReportType($request);
$metrics = $this->metricsForType($type);
$payload = [
'partition' => self::PARTITION,
'metric_ids' => json_encode(array_keys($metrics)),
'metric_labels' => json_encode($metrics),
'ratio_metric_id' => self::RATIO_METRIC_ID,
'start_month' => self::REPORT_START,
'type' => $type,
];
if ($search !== '') {
$payload['search'] = $search;
}
if ($fetchAll) {
$payload['all'] = '1';
} else {
$payload['page'] = max(1, (int) $request->input('page', 1));
$payload['per_page'] = $perPage;
}
return $payload;
}
protected function resolvePerPage(Request $request): int
{
$allowed = [25, 50, 100, 200];
$default = 50;
$raw = $request->query('per_page');
if ($raw !== null && $raw !== '') {
$perPage = (int) $raw;
if (!in_array($perPage, $allowed, true)) {
$perPage = $default;
}
$request->session()->put('adherence_pack_ratio_report_per_page', $perPage);
return $perPage;
}
if ($request->session()->has('adherence_pack_ratio_report_per_page')) {
$perPage = (int) $request->session()->get('adherence_pack_ratio_report_per_page');
if (in_array($perPage, $allowed, true)) {
return $perPage;
}
}
return $default;
}
protected function assertTeam2Access(Request $request): string
{
$token = $this->resolveAccessToken($request);
if (!$token) {
abort(401, 'Access token is required');
}
$user = $this->getUserFromToken($token);
if (!$user || ($user->domain ?? '') !== 'team2') {
abort(403);
}
$this->accessToken = $token;
$this->tokenUser = $user;
return $token;
}
protected function resolveAccessToken(Request $request): ?string
{
$token = trim((string) ($request->input('token') ?? $request->header('Authorization') ?? ''));
if ($token === '' && $request->session()->has('adherence_pack_ratio_report_token')) {
$token = (string) $request->session()->get('adherence_pack_ratio_report_token');
}
if ($token !== '') {
$request->session()->put('adherence_pack_ratio_report_token', $token);
}
return $token !== '' ? $token : null;
}
protected function getUserFromToken(string $token): ?object
{
$parts = explode('.', $token);
if (count($parts) < 2) {
return null;
}
$payload = json_decode(base64_decode(strtr($parts[1], '-_', '+/')));
if (!is_object($payload)) {
return null;
}
return $payload->user ?? null;
}
protected function buildUrl(string $path, Request $request): string
{
$perPage = $this->resolvePerPage($request);
$query = array_filter([
'token' => $request->input('token') ?: $request->session()->get('adherence_pack_ratio_report_token'),
'type' => $this->resolveReportType($request),
'page' => $request->input('page'),
'per_page' => $perPage,
'search' => $this->resolveSearchTerm($request),
], function ($value) {
return $value !== null && $value !== '';
});
$url = $this->_base_url . $path;
if (!empty($query)) {
$url .= '?' . http_build_query($query);
}
return $url;
}
}