Files

284 lines
9.4 KiB
PHP
Raw Permalink Normal View History

2026-06-01 15:01:03 +07:00
<?php
namespace App\Services;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
class DashboardService
{
protected $startDate;
protected $endDate;
protected $allowedCabangIds = [];
protected $user;
public function __construct($user)
{
$this->user = $user;
$this->setPeriode();
$this->setUserVisibility();
}
/**
* Set Periode Cut-off (11 - 10)
*/
protected function setPeriode(): void
{
$startDay = (int) env('STARTING_DATE', 11);
$closingDay = (int) env('CLOSING_DATE', 10);
$now = Carbon::now();
if ($now->day >= $startDay) {
$this->startDate = $now->copy()->day($startDay)->startOfDay();
$this->endDate = $now->copy()->addMonth()->day($closingDay)->endOfDay();
} else {
$this->startDate = $now->copy()->subMonth()->day($startDay)->startOfDay();
$this->endDate = $now->copy()->day($closingDay)->endOfDay();
}
}
/**
* Atur Visibilitas Berdasarkan Hierarki Role
*/
protected function setUserVisibility(): void
{
// 1. MIS / HEAD / SUPERADMIN: Akses Nasional
if ($this->user->hasRole(['Marketing Information System', 'Head of Sales Management', 'Super Admin'])) {
$this->allowedCabangIds = [];
return;
}
$myCabangs = DB::table('user_has_cabang')
->where('user_id', $this->user->id)
->whereNull('deleted_at')
->pluck('cabang_id')
->toArray();
// 2. MOM / ADMIN REGION: Akses Regional (Semua cabang di dalam regionnya)
if ($this->user->hasRole(['Marketing Operational Manager Region', 'Admin Region'])) {
$regionIds = DB::table('cabang')
->whereIn('id', $myCabangs)
->pluck('region_id')
->toArray();
$this->allowedCabangIds = DB::table('cabang')
->whereIn('region_id', $regionIds)
->pluck('id')
->toArray();
}
// 3. AM / MR: Akses Cabang Spesifik
else {
$this->allowedCabangIds = $myCabangs;
}
}
/**
* Helper Filter: Digunakan di semua query analitik
*/
protected function applyFilters($query)
{
if (!empty($this->allowedCabangIds)) {
$query->whereIn('combined_forms.cabang_id', $this->allowedCabangIds);
}
return $query;
}
/**
* Query Utama: Menggabungkan 5 tabel kategori expense
*/
protected function getCombinedFormsQuery()
{
$tables = [
'form_up_country',
'form_vehicle_running_cost',
'form_entertainment_presentation',
'form_meeting_seminar',
'form_others'
];
$query = null;
foreach ($tables as $table) {
$sub = DB::table($table)
->select(
'id',
'cabang_id',
'total',
'status',
'created_at',
'final_approved_at',
DB::raw("'$table' as source_table")
)
->whereNull('deleted_at');
$query = ($query === null) ? $sub : $query->unionAll($sub);
}
return DB::table(DB::raw("({$query->toSql()}) as combined_forms"))
->mergeBindings($query)
->join('cabang', 'combined_forms.cabang_id', '=', 'cabang.id')
->join('region', 'cabang.region_id', '=', 'region.id');
}
/**
* Data untuk 4 Kartu Statistik Utama
*/
public function getApprovalStats(): array
{
// Ambil semua data tanpa filter tanggal dulu untuk kartu "Waiting"
$allData = $this->getCombinedFormsQuery()
->select('combined_forms.status', 'combined_forms.total', 'combined_forms.created_at')
->get();
$map = [
// 1. TOTAL PENDING: Khusus periode aktif (11 Maret - 10 April) & Status On Progress
'total_pending' => [
'label' => 'Total Pending',
'keys' => ['on progress'],
'color' => 'warning',
'icon' => 'fas fa-hourglass-half',
'use_period' => true
],
// 2. WAITING APPR 1: Semua yang statusnya On Progress
'appr1' => [
'label' => 'Waiting Appr 1',
'keys' => ['on progress'],
'color' => 'info',
'icon' => 'fas fa-user-check',
'use_period' => false
],
// 3. WAITING APPR 2: Semua yang statusnya Approved 1
'appr2' => [
'label' => 'Waiting Appr 2',
'keys' => ['approved 1'],
'color' => 'primary',
'icon' => 'fas fa-file-invoice-dollar',
'use_period' => false
],
// 4. WAITING FINAL APPROVED: Semua yang statusnya Approved 2
'waiting_final' => [
'label' => 'Waiting Final Approved',
'keys' => ['approved 2'],
'color' => 'secondary',
'icon' => 'fas fa-tasks',
'use_period' => false
],
// 5. FINAL APPROVED (CLOSED): Semua yang sudah Closed
'closed' => [
'label' => 'Final Approved',
'keys' => ['closed', 'approved'],
'color' => 'success',
'icon' => 'fas fa-check-double',
'use_period' => false
],
];
$results = [];
foreach ($map as $config) {
$filtered = $allData->filter(function ($item) use ($config) {
$statusMatch = in_array(strtolower(trim((string)$item->status)), $config['keys']);
// Jika kartu membutuhkan filter periode (Maret 11 - April 10)
if ($config['use_period']) {
$dateMatch = Carbon::parse($item->created_at)->between($this->startDate, $this->endDate);
return $statusMatch && $dateMatch;
}
return $statusMatch;
});
$results[] = [
'label' => $config['label'],
'count' => $filtered->count(),
'amount' => (float) $filtered->sum('total'),
'color' => $config['color'],
'icon' => $config['icon']
];
}
return $results;
}
/**
* Mengambil daftar pengajuan dengan status 'Approved 2'
* Untuk antrian persetujuan akhir (HOSM/MIS)
*/
public function getPendingFinalApproval()
{
$query = $this->getCombinedFormsQuery()
->select(
'combined_forms.*',
'cabang.name as cabang_name',
'region.name as region_name'
)
->where('combined_forms.status', 'Approved 2');
$this->applyFilters($query);
return $query->orderBy('combined_forms.created_at', 'desc')->paginate(10);
}
/**
* Summary per Kategori untuk Chart
*/
public function getTotals(): array
{
$query = $this->getCombinedFormsQuery()
->whereBetween('combined_forms.created_at', [$this->startDate, $this->endDate])
->whereIn('combined_forms.status', ['Approved', 'Closed']);
$this->applyFilters($query);
$results = $query->select('combined_forms.source_table', DB::raw('SUM(combined_forms.total) as total_per_table'))
->groupBy('combined_forms.source_table')
->get();
return [
'forms' => [
'upcountry' => $results->where('source_table', 'form_up_country')->first()->total_per_table ?? 0,
'vehicle' => $results->where('source_table', 'form_vehicle_running_cost')->first()->total_per_table ?? 0,
'entertainment' => $results->where('source_table', 'form_entertainment_presentation')->first()->total_per_table ?? 0,
'meeting' => $results->where('source_table', 'form_meeting_seminar')->first()->total_per_table ?? 0,
'others' => $results->where('source_table', 'form_others')->first()->total_per_table ?? 0,
],
'grand_total' => $results->sum('total_per_table')
];
}
/**
* Top 5 Pengeluaran Terbanyak per Cabang
*/
/**
* Top 10 Pengeluaran Terbanyak (Volume Giants)
*/
public function getTopExpenses()
{
$query = $this->getCombinedFormsQuery()
->whereBetween('combined_forms.created_at', [$this->startDate, $this->endDate]);
$this->applyFilters($query);
return $query->select('cabang.name', 'region.name as region_name', DB::raw('SUM(combined_forms.total) as total_amount'))
->groupBy('cabang.id', 'cabang.name', 'region.name')
->orderBy('total_amount', 'desc')
->take(10) // Update ke 10
->get();
}
/**
* Top 10 Efisiensi Approval (Speed Kings)
*/
public function getFastestClosing()
{
$query = $this->getCombinedFormsQuery()
->select('cabang.name', DB::raw('AVG(TIMESTAMPDIFF(HOUR, combined_forms.created_at, combined_forms.final_approved_at)) as avg_hours'))
->whereIn('combined_forms.status', ['Approved', 'Closed'])
->whereNotNull('combined_forms.final_approved_at')
->whereBetween('combined_forms.final_approved_at', [$this->startDate, $this->endDate]);
$this->applyFilters($query);
return $query->groupBy('cabang.id', 'cabang.name')
->orderBy('avg_hours', 'asc')
->take(10) // Update ke 10
->get();
}
}