243 lines
9.4 KiB
PHP
243 lines
9.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Penugasan;
|
|
use App\Models\Penggajian;
|
|
use App\Models\Kasbon;
|
|
use App\Models\Teknisi;
|
|
use App\Models\Absensi;
|
|
use App\Models\TarifPekerjaan;
|
|
use Illuminate\Http\Request;
|
|
use Exception;
|
|
use Carbon\Carbon;
|
|
|
|
class DashboardApiController extends Controller
|
|
{
|
|
/**
|
|
* GET - Data dashboard mobile teknisi
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
try {
|
|
$idTeknisi = $request->input('id_teknisi');
|
|
|
|
if (!$idTeknisi) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'ID Teknisi tidak ditemukan'
|
|
], 401);
|
|
}
|
|
|
|
$teknisi = Teknisi::findOrFail($idTeknisi);
|
|
|
|
// 1. Tugas Hari Ini / Aktif
|
|
$tugasAktif = Penugasan::where(function ($q) use ($idTeknisi) {
|
|
$q->where('id_teknisi', $idTeknisi)
|
|
->orWhereHas('timTeknisi', function ($sq) use ($idTeknisi) {
|
|
$sq->where('id_teknisi', $idTeknisi);
|
|
});
|
|
})
|
|
->whereIn('status_pekerjaan', ['belum_mulai', 'dalam_proses'])
|
|
->count();
|
|
|
|
// 2. Gaji Bulan Berjalan (Estimasi Ongkos Kerja)
|
|
$now = Carbon::now();
|
|
|
|
// Cari semua penugasan yang melibatkan teknisi ini di bulan berjalan (eager loaded untuk performa)
|
|
$penugasans = Penugasan::where(function ($q) use ($idTeknisi) {
|
|
// (a) Teknisi utama penugasan
|
|
$q->where('id_teknisi', $idTeknisi)
|
|
// (b) Anggota tim yang hadir
|
|
->orWhereHas('timTeknisi', function ($sq) use ($idTeknisi) {
|
|
$sq->where('id_teknisi', $idTeknisi)
|
|
->where('status_kehadiran', 'hadir');
|
|
});
|
|
})
|
|
->where('status_pekerjaan', 'selesai')
|
|
->where(function ($q) use ($now) {
|
|
// Filter berdasarkan bulan selesai (Prioritas: tanggal_diselesaikan, fallback ke updated_at)
|
|
$q->where(function ($q2) use ($now) {
|
|
$q2->whereNotNull('tanggal_diselesaikan')
|
|
->whereMonth('tanggal_diselesaikan', $now->month)
|
|
->whereYear('tanggal_diselesaikan', $now->year);
|
|
})->orWhere(function ($q2) use ($now) {
|
|
$q2->whereNull('tanggal_diselesaikan')
|
|
->whereMonth('updated_at', $now->month)
|
|
->whereYear('updated_at', $now->year);
|
|
});
|
|
})
|
|
->with(['items.tarif', 'timTeknisi'])
|
|
->get();
|
|
|
|
$estimasiGaji = 0;
|
|
$tugasSelesaiBulanIni = 0;
|
|
|
|
foreach ($penugasans as $penugasan) {
|
|
$tugasSelesaiBulanIni++;
|
|
|
|
// Hitung jumlah anggota tim yang hadir
|
|
$jumlahHadir = $penugasan->countTimHadir();
|
|
if ($jumlahHadir === 0) {
|
|
$jumlahHadir = 1;
|
|
}
|
|
|
|
// Hitung total ongkos dari penugasan_items
|
|
$totalOngkosTugas = 0;
|
|
if ($penugasan->items->count() > 0) {
|
|
foreach ($penugasan->items as $item) {
|
|
$itemTotal = (float) $item->total_nilai_pekerjaan;
|
|
if ($itemTotal <= 0) {
|
|
$itemTotal = $this->calculatePenugasanItemValue($item);
|
|
}
|
|
$totalOngkosTugas += $itemTotal;
|
|
}
|
|
}
|
|
|
|
// Fallback: ambil dari total_nilai_pekerjaan penugasan induk atau tarif
|
|
if ($totalOngkosTugas <= 0) {
|
|
$totalOngkosTugas = $this->calculatePenugasanValue($penugasan);
|
|
}
|
|
|
|
if ($totalOngkosTugas <= 0) {
|
|
continue;
|
|
}
|
|
|
|
// Bagian ongkos = total ongkos dibagi jumlah anggota tim yang hadir
|
|
$bagianOngkos = $totalOngkosTugas / $jumlahHadir;
|
|
$estimasiGaji += $bagianOngkos;
|
|
}
|
|
|
|
// 3. Total Kasbon Aktif
|
|
$totalKasbon = Kasbon::where('id_teknisi', $idTeknisi)
|
|
->where('status', 'belum_lunas')
|
|
->sum('jumlah_kasbon');
|
|
|
|
// 4. Gaji Terakhir Diterima
|
|
$gajiTerakhir = Penggajian::where('id_teknisi', $idTeknisi)
|
|
->where('status_pembayaran', 'sudah_bayar')
|
|
->orderBy('periode_tahun', 'desc')
|
|
->orderBy('periode_bulan', 'desc')
|
|
->first();
|
|
|
|
// 5. Statistik Absensi Bulan Ini
|
|
$absensiBulanIni = Absensi::where('id_teknisi', $idTeknisi)
|
|
->whereMonth('tanggal', $now->month)
|
|
->whereYear('tanggal', $now->year)
|
|
->get();
|
|
|
|
$hadir = $absensiBulanIni->where('status', 'hadir')->count();
|
|
$totalAbsen = $absensiBulanIni->count();
|
|
$kehadiran = $totalAbsen > 0 ? round(($hadir / $totalAbsen) * 100) : 0;
|
|
|
|
// Rata-rata jam kerja (dalam jam)
|
|
$totalDurasiMenit = $absensiBulanIni->where('status', 'hadir')->sum(function($a) {
|
|
return $a->durasi_kerja;
|
|
});
|
|
$jamKerja = round($totalDurasiMenit / 60, 1);
|
|
|
|
// Efisiensi
|
|
$poolTugas = $tugasAktif + $tugasSelesaiBulanIni;
|
|
$efisiensi = $poolTugas > 0 ? round(($tugasSelesaiBulanIni / $poolTugas) * 100) : 0;
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'message' => 'Data dashboard berhasil diambil',
|
|
'data' => [
|
|
'teknisi' => [
|
|
'nama' => $teknisi->nama,
|
|
'spesialisasi' => $teknisi->spesialisasi,
|
|
'foto' => $teknisi->foto_url
|
|
],
|
|
'statistik' => [
|
|
'tugas_aktif' => $tugasAktif,
|
|
'tugas_selesai' => $tugasSelesaiBulanIni,
|
|
'estimasi_gaji' => (float) $estimasiGaji,
|
|
'total_kasbon' => (float) $totalKasbon,
|
|
'gaji_terakhir' => $gajiTerakhir ? (float) $gajiTerakhir->gaji_bersih : 0,
|
|
'periode_terakhir' => $gajiTerakhir ? Penggajian::getNamaBulan($gajiTerakhir->periode_bulan) . ' ' . $gajiTerakhir->periode_tahun : '-',
|
|
'kehadiran' => $kehadiran,
|
|
'jam_kerja' => $jamKerja,
|
|
'efisiensi' => $efisiensi,
|
|
]
|
|
]
|
|
]);
|
|
|
|
} catch (Exception $e) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => 'Gagal mengambil data dashboard: ' . $e->getMessage()
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Calculate nilai penugasan dengan fallback ke tarif
|
|
*/
|
|
private function calculatePenugasanValue($penugasan): float
|
|
{
|
|
if ($penugasan->total_nilai_pekerjaan > 0) {
|
|
return (float) $penugasan->total_nilai_pekerjaan;
|
|
}
|
|
|
|
$tarif = $penugasan->tarif;
|
|
if (!$tarif) {
|
|
$query = TarifPekerjaan::where('jenis_pekerjaan', $penugasan->jenis_pekerjaan)
|
|
->where('is_active', true);
|
|
if ($penugasan->dimensi_pipa) {
|
|
$query->where('dimensi_pipa', $penugasan->dimensi_pipa);
|
|
}
|
|
$tarif = $query->first();
|
|
}
|
|
|
|
if (!$tarif) return 0;
|
|
|
|
if ($penugasan->jarak_meter > 0 && $tarif->tarif_per_meter) {
|
|
return (float) $tarif->tarif_per_meter * (float) $penugasan->jarak_meter;
|
|
}
|
|
if ($penugasan->jumlah_unit > 0 && $tarif->tarif_per_unit) {
|
|
return (float) $tarif->tarif_per_unit * (int) $penugasan->jumlah_unit;
|
|
}
|
|
if ($penugasan->jumlah_titik > 0 && $tarif->tarif_per_unit) {
|
|
return (float) $tarif->tarif_per_unit * (int) $penugasan->jumlah_titik;
|
|
}
|
|
|
|
return (float) ($tarif->tarif_per_unit ?? $tarif->tarif_per_meter ?? 0);
|
|
}
|
|
|
|
/**
|
|
* Calculate nilai untuk setiap PenugasanItem
|
|
*/
|
|
private function calculatePenugasanItemValue($item): float
|
|
{
|
|
if ($item->total_nilai_pekerjaan > 0) {
|
|
return (float) $item->total_nilai_pekerjaan;
|
|
}
|
|
|
|
$tarif = $item->tarif;
|
|
if (!$tarif) {
|
|
$query = TarifPekerjaan::where('jenis_pekerjaan', $item->jenis_pekerjaan)
|
|
->where('is_active', true);
|
|
if ($item->dimensi_pipa) {
|
|
$query->where('dimensi_pipa', $item->dimensi_pipa);
|
|
}
|
|
$tarif = $query->first();
|
|
}
|
|
|
|
if (!$tarif) return 0;
|
|
|
|
if ($item->jarak_meter > 0 && $tarif->tarif_per_meter) {
|
|
return (float) $tarif->tarif_per_meter * (float) $item->jarak_meter;
|
|
}
|
|
if ($item->jumlah_unit > 0 && $tarif->tarif_per_unit) {
|
|
return (float) $tarif->tarif_per_unit * (int) $item->jumlah_unit;
|
|
}
|
|
if ($item->jumlah_titik > 0 && $tarif->tarif_per_unit) {
|
|
return (float) $tarif->tarif_per_unit * (int) $item->jumlah_titik;
|
|
}
|
|
|
|
return (float) ($tarif->tarif_per_unit ?? $tarif->tarif_per_meter ?? 0);
|
|
}
|
|
}
|