178 lines
5.6 KiB
PHP
178 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\KehadiranTaklimat;
|
|
use App\Models\SesiTaklimat;
|
|
use App\Models\Sppm\PetugasPermohonan;
|
|
use App\Support\SimpleXlsx;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class EksportKehadiranController extends Controller
|
|
{
|
|
/**
|
|
* Susunan jawatan dalam laporan: PPM dahulu, kemudian mengikut Saluran
|
|
* (KTM sebelum KP), dan akhir sekali PPAPM.
|
|
*/
|
|
private const JAWATAN_KUMPULAN = [
|
|
'PPM' => 0,
|
|
'KTM' => 1,
|
|
'KP_KMU' => 1,
|
|
'PPAPM_MANUAL' => 2,
|
|
'PPAPM_KOMPUTER' => 2,
|
|
'PPAPM_BERGERAK' => 2,
|
|
];
|
|
|
|
private const PENGEPALA = [
|
|
'Bil', 'DUN', 'Pusat Mengundi', 'Saluran', 'Jawatan', 'Nama', 'No. KP', 'No. Telefon', 'Emel', 'Tarikh Hadir',
|
|
];
|
|
|
|
/**
|
|
* Eksport kehadiran semua petugas merentas semua sesi taklimat yang
|
|
* dilihat pengguna, dikumpul kepada satu fail Excel: worksheet "Hadir"
|
|
* dan "Tidak Hadir".
|
|
*/
|
|
public function __invoke(Request $request): Response
|
|
{
|
|
$user = $request->user();
|
|
|
|
$sesiSemua = SesiTaklimat::with('dun')
|
|
->when($user->isPengurus(), fn ($q) => $q->where('dun_id', $user->dun_id))
|
|
->get();
|
|
|
|
$hadir = [];
|
|
$tidakHadir = [];
|
|
|
|
foreach ($sesiSemua->groupBy('dun_id') as $sesiDun) {
|
|
$dun = $sesiDun->first()->dun;
|
|
$dunLabel = trim(($dun->code ?? '').' '.($dun->nama ?? ''));
|
|
$sesiIds = $sesiDun->pluck('id');
|
|
|
|
// Tarikh hadir paling awal bagi setiap petugas merentas sesi DUN ini.
|
|
$tarikhHadir = [];
|
|
$rekodKehadiran = KehadiranTaklimat::whereIn('sesi_taklimat_id', $sesiIds)
|
|
->orderBy('hadir_at')
|
|
->get(['permohonan_id', 'sesi_taklimat_id']);
|
|
|
|
foreach ($rekodKehadiran as $rekod) {
|
|
if (! array_key_exists($rekod->permohonan_id, $tarikhHadir)) {
|
|
$tarikhHadir[$rekod->permohonan_id] = optional(
|
|
$sesiDun->firstWhere('id', $rekod->sesi_taklimat_id)?->tarikh
|
|
)->format('d/m/Y') ?? '';
|
|
}
|
|
}
|
|
|
|
$petugas = PetugasPermohonan::assignedUntukDun((int) $sesiDun->first()->dun_id)
|
|
->with(['assignments' => fn ($q) => $q->orderBy('urutan')->with(['pusatMengundi', 'saluran', 'jawatan'])])
|
|
->get();
|
|
|
|
foreach ($petugas as $p) {
|
|
$baris = $this->barisPetugas($p, $dunLabel);
|
|
|
|
if (array_key_exists($p->id, $tarikhHadir)) {
|
|
$baris['tarikh_hadir'] = $tarikhHadir[$p->id];
|
|
$hadir[] = $baris;
|
|
} else {
|
|
$tidakHadir[] = $baris;
|
|
}
|
|
}
|
|
}
|
|
|
|
$hadir = $this->susun($hadir);
|
|
$tidakHadir = $this->susun($tidakHadir);
|
|
|
|
$xlsx = (new SimpleXlsx)
|
|
->addSheet('Hadir', $this->keBaris($hadir))
|
|
->addSheet('Tidak Hadir', $this->keBaris($tidakHadir));
|
|
|
|
$namaFail = 'Kehadiran-Taklimat-'.now()->format('Ymd-His').'.xlsx';
|
|
|
|
return response($xlsx->toString(), 200, [
|
|
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
'Content-Disposition' => 'attachment; filename="'.$namaFail.'"',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Hasilkan satu baris data petugas berserta kunci susunan.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function barisPetugas(PetugasPermohonan $p, string $dunLabel): array
|
|
{
|
|
$assignment = $p->assignmentUtama();
|
|
|
|
$pusatNama = $assignment?->pusatMengundi?->nama ?? '';
|
|
$saluranNo = $assignment?->saluran?->nombor_saluran;
|
|
$kod = $assignment?->jawatan?->kod ?? '';
|
|
|
|
$kumpulan = self::JAWATAN_KUMPULAN[$kod] ?? 9;
|
|
// Dalam kumpulan Saluran, KTM (0) didahulukan sebelum KP (1).
|
|
$subJawatan = $kod === 'KTM' ? 0 : 1;
|
|
|
|
return [
|
|
'dun' => $dunLabel,
|
|
'pusat' => $pusatNama,
|
|
'saluran' => $saluranNo !== null ? 'Saluran '.$saluranNo : '',
|
|
'jawatan' => $assignment?->jawatan?->nama ?? '-',
|
|
'nama' => $p->nama_ic ?? '',
|
|
'no_kp' => $p->no_kp ?? '',
|
|
'no_telefon' => $p->no_telefon ?? '',
|
|
'emel' => $p->emel ?? '',
|
|
'tarikh_hadir' => '',
|
|
'_sort' => [
|
|
$dunLabel,
|
|
$pusatNama,
|
|
$kumpulan,
|
|
$kumpulan === 1 ? (int) ($saluranNo ?? 0) : 0,
|
|
$subJawatan,
|
|
$p->nama_ic ?? '',
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Susun mengikut pusat mengundi > PPM > Saluran (KTM > KP) > PPAPM.
|
|
*
|
|
* @param array<int,array<string,mixed>> $baris
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function susun(array $baris): array
|
|
{
|
|
usort($baris, function ($a, $b) {
|
|
return $a['_sort'] <=> $b['_sort'];
|
|
});
|
|
|
|
return $baris;
|
|
}
|
|
|
|
/**
|
|
* Tukar baris data kepada matriks (pengepala + sel) untuk worksheet.
|
|
*
|
|
* @param array<int,array<string,mixed>> $baris
|
|
* @return array<int,array<int,string>>
|
|
*/
|
|
private function keBaris(array $baris): array
|
|
{
|
|
$rows = [self::PENGEPALA];
|
|
|
|
foreach ($baris as $i => $b) {
|
|
$rows[] = [
|
|
$i + 1,
|
|
$b['dun'],
|
|
$b['pusat'],
|
|
$b['saluran'],
|
|
$b['jawatan'],
|
|
$b['nama'],
|
|
$b['no_kp'],
|
|
$b['no_telefon'],
|
|
$b['emel'],
|
|
$b['tarikh_hadir'],
|
|
];
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
}
|