save('php://output'); }); $response->headers->set('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); $response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"'); return $response; } public function pitstop(){ $masterPitStop = MasterPitStopPraAkre::where('statusenabled', true)->get(); $data = [ 'title' => 'Pit Stop', 'masterPitStop' => $masterPitStop ]; return view('pitstop.index', $data); } public function progressUnit() { $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); return view('pitstop.progress_unit', [ 'title' => 'Monitoring Pra Akreditasi', 'totalSteps' => $totalSteps, ]); } public function progressUnitDetail(Request $request, $unit_id) { $unitId = (int) $unit_id; $unit = DB::connection('pgsql') ->table('public.unitkerjapegawai_m as ukp') ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select(['ukp.id', 'ukp.name']) ->first(); abort_if(!$unit, 404); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); return view('pitstop.progress_unit_detail', [ 'title' => 'Monitoring Pra Akreditasi - ' . ($unit->name ?? 'Unit'), 'unit' => $unit, 'totalSteps' => $totalSteps, ]); } public function progressExternalDetail(Request $request, $tipe) { $tipe = urldecode((string) $tipe); $tipe = trim($tipe); abort_if($tipe === '', 404); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $masterPitStop = MasterPitStopPraAkre::where('statusenabled', true)->get(); return view('pitstop.progress_external_detail', [ 'title' => 'Monitoring Karyawan Luar - ' . $tipe, 'tipe' => $tipe, 'totalSteps' => $totalSteps, 'masterPitStop' => $masterPitStop, ]); } public function dataProgress(Request $request) { $draw = $this->dtInt($request->input('draw'), 1); $start = $this->dtInt($request->input('start'), 0); $length = $this->dtInt($request->input('length'), 10); if ($length < 1) $length = 10; if ($length > 200) $length = 200; $search = trim((string) data_get($request->all(), 'search.value', '')); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $perPegawai = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('ukp.statusenabled', true) ->select([ DB::raw('ukp.id as unit_id'), DB::raw("coalesce(ukp.name, '-') as unit_name"), DB::raw('pg.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy('ukp.id', 'ukp.name', 'pg.id'); $unitAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.unit_id', 't.unit_name', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), DB::raw('sum(t.lulus_count) as total_step_lulus'), DB::raw('avg(t.lulus_count) as avg_step_lulus'), ]) ->groupBy('t.unit_id', 't.unit_name'); $recordsTotal = DB::connection('pgsql') ->query() ->fromSub($unitAgg, 'u') ->count(); $filteredQuery = DB::connection('pgsql')->query()->fromSub($unitAgg, 'u'); if ($search !== '') { $filteredQuery->where('u.unit_name', 'ILIKE', '%' . $search . '%'); } $recordsFiltered = (clone $filteredQuery)->count(); $rows = $filteredQuery ->orderByDesc('pegawai_selesai') ->orderByDesc('avg_step_lulus') ->orderBy('unit_name') ->offset($start) ->limit($length) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $totalPegawai = (int) ($r->total_pegawai ?? 0); $pegawaiSelesai = (int) ($r->pegawai_selesai ?? 0); $selesaiPct = $totalPegawai > 0 ? round(($pegawaiSelesai / $totalPegawai) * 100, 1) : 0; return [ 'unit_id' => (int) $r->unit_id, 'unit_name' => (string) ($r->unit_name ?? '-'), 'total_pegawai' => $totalPegawai, 'pegawai_selesai' => $pegawaiSelesai, 'selesai_pct' => $selesaiPct, 'total_step_lulus' => (int) ($r->total_step_lulus ?? 0), ]; })->values(); return response()->json([ 'draw' => $draw, 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, ]); } public function dataProgressExternal(Request $request){ $draw = $this->dtInt($request->input('draw'), 1); $start = $this->dtInt($request->input('start'), 0); $length = $this->dtInt($request->input('length'), 10); if ($length < 1) $length = 10; if ($length > 200) $length = 200; $search = trim((string) data_get($request->all(), 'search.value', '')); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $perPegawai = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->select([ DB::raw("coalesce(nullif(pl.tipe, ''), '-') as tipe"), DB::raw('pl.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), 'pl.id'); $tipeAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.tipe', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), DB::raw('sum(t.lulus_count) as total_step_lulus'), DB::raw('avg(t.lulus_count) as avg_step_lulus'), ]) ->groupBy('t.tipe'); $recordsTotal = DB::connection('pgsql') ->query() ->fromSub($tipeAgg, 'u') ->count(); $filteredQuery = DB::connection('pgsql')->query()->fromSub($tipeAgg, 'u'); if ($search !== '') { $filteredQuery->where('u.tipe', 'ILIKE', '%' . $search . '%'); } $recordsFiltered = (clone $filteredQuery)->count(); $rows = $filteredQuery ->orderByDesc('pegawai_selesai') ->orderByDesc('avg_step_lulus') ->orderBy('tipe') ->offset($start) ->limit($length) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $totalPegawai = (int) ($r->total_pegawai ?? 0); $pegawaiSelesai = (int) ($r->pegawai_selesai ?? 0); $selesaiPct = $totalPegawai > 0 ? round(($pegawaiSelesai / $totalPegawai) * 100, 1) : 0; return [ 'tipe' => (string) ($r->tipe ?? '-'), 'total_pegawai' => $totalPegawai, 'pegawai_selesai' => $pegawaiSelesai, 'selesai_pct' => $selesaiPct, 'total_step_lulus' => (int) ($r->total_step_lulus ?? 0), 'avg_step_lulus' => $totalSteps > 0 ? round((float) ($r->avg_step_lulus ?? 0), 2) : 0, ]; })->values(); return response()->json([ 'draw' => $draw, 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, ]); } public function dataProgressExternalDetail(Request $request) { $validator = Validator::make($request->all(), [ 'tipe' => 'required|string|max:100', 'limit' => 'nullable|integer|min:1|max:500', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $tipe = trim((string) $validator->validated()['tipe']); $limit = (int) ($validator->validated()['limit'] ?? 200); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $rows = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), '=', $tipe) ->select([ 'pl.id', 'pl.nama', 'pl.nik', DB::raw("coalesce(nullif(pl.tipe, ''), '-') as tipe"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy('pl.id', 'pl.nama', 'pl.nik', DB::raw("coalesce(nullif(pl.tipe, ''), '-')")) ->orderBy('pl.nama') ->limit($limit) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $lulus = (int) ($r->lulus_count ?? 0); $selesai = $totalSteps > 0 ? ($lulus >= $totalSteps ? 1 : 0) : 0; $pct = $totalSteps > 0 ? round(($lulus / $totalSteps) * 100, 1) : 0; return [ 'id' => (int) $r->id, 'nama' => (string) ($r->nama ?? '-'), 'nik' => (string) ($r->nik ?? '-'), 'tipe' => (string) ($r->tipe ?? '-'), 'lulus_count' => $lulus, 'pct' => $pct, 'selesai' => $selesai, ]; })->values(); return response()->json([ 'error' => 0, 'data' => $data, 'meta' => [ 'tipe' => $tipe, 'total_steps' => $totalSteps, 'count' => $data->count(), ], ]); } public function monitoringPdf(Request $request) { $unitId = $request->query('unit_id'); $unitId = is_null($unitId) || $unitId === '' ? null : (int) $unitId; $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $generatedAt = now(); // Default: ringkasan per unit (tanpa list pegawai) supaya tidak memakan memory besar. if (is_null($unitId)) { $perPegawai = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->select([ DB::raw('ukp.id as unit_id'), DB::raw("coalesce(ukp.name, '-') as unit_name"), DB::raw('pg.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy('ukp.id', 'ukp.name', 'pg.id'); $unitAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.unit_id', 't.unit_name', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), ]) ->groupBy('t.unit_id', 't.unit_name'); $rows = DB::connection('pgsql') ->query() ->fromSub($unitAgg, 'u') ->orderByDesc('pegawai_selesai') ->orderByDesc('total_pegawai') ->orderBy('unit_name') ->get(); $units = $rows->map(function ($r) { $totalPegawai = (int) ($r->total_pegawai ?? 0); $pegawaiSelesai = (int) ($r->pegawai_selesai ?? 0); $pct = $totalPegawai > 0 ? round(($pegawaiSelesai / $totalPegawai) * 100, 1) : 0; return (object) [ 'unit_id' => (int) $r->unit_id, 'unit_name' => (string) ($r->unit_name ?? '-'), 'total_pegawai' => $totalPegawai, 'pegawai_selesai' => $pegawaiSelesai, 'pct' => $pct, 'pegawai' => collect(), ]; }); $pdf = Pdf::loadView('pitstop.monitoring_pdf', [ 'title' => 'Monitoring Pra Akreditasi', 'generatedAt' => $generatedAt, 'totalSteps' => $totalSteps, 'units' => $units, 'isSummaryOnly' => true, ])->setPaper('a4', 'landscape'); $filename = 'monitoring-pra-akreditasi-ringkasan-' . $generatedAt->format('Ymd-Hi') . '.pdf'; return $pdf->download($filename); } // Mode detail: PDF per unit (lebih aman untuk memory). $unit = DB::connection('pgsql') ->table('public.unitkerjapegawai_m as ukp') ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select(['ukp.id', 'ukp.name']) ->first(); abort_if(!$unit, 404); $perPegawai = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select([ DB::raw('ukp.id as unit_id'), DB::raw("coalesce(ukp.name, '-') as unit_name"), DB::raw('pg.id as pegawai_id'), DB::raw("coalesce(pg.namalengkap, '-') as nama"), DB::raw("coalesce(pg.nip_pns, '-') as nip_pns"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_selesai_steps"), ]) ->groupBy('ukp.id', 'ukp.name', 'pg.id', 'pg.namalengkap', 'pg.nip_pns'); $rows = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->orderBy('t.nama') ->get(); $totalPegawai = $rows->count(); $pegawaiSelesai = $rows->filter(function ($r) use ($totalSteps) { return (int) ($r->lulus_count ?? 0) >= $totalSteps && $totalSteps > 0; })->count(); $pct = $totalPegawai > 0 ? round(($pegawaiSelesai / $totalPegawai) * 100, 1) : 0; $pegawai = $rows->map(function ($r) use ($totalSteps) { $lulus = (int) ($r->lulus_count ?? 0); $pctPegawai = $totalSteps > 0 ? round(($lulus / $totalSteps) * 100, 1) : 0; $belum = trim((string) ($r->belum_selesai_steps ?? '')); return (object) [ 'pegawai_id' => (int) $r->pegawai_id, 'nama' => (string) ($r->nama ?? '-'), 'nip_pns' => (string) ($r->nip_pns ?? '-'), 'total_steps' => $totalSteps, 'lulus_steps' => $lulus, 'pct' => $pctPegawai, 'belum_selesai_steps' => $belum, ]; })->values(); $units = collect([ (object) [ 'unit_id' => (int) $unit->id, 'unit_name' => (string) ($unit->name ?? '-'), 'total_pegawai' => $totalPegawai, 'pegawai_selesai' => $pegawaiSelesai, 'pct' => $pct, 'pegawai' => $pegawai, ], ]); $pdf = Pdf::loadView('pitstop.monitoring_pdf', [ 'title' => 'Monitoring Pra Akreditasi', 'generatedAt' => $generatedAt, 'totalSteps' => $totalSteps, 'units' => $units, 'isSummaryOnly' => false, ])->setPaper('a4', 'landscape'); $filename = 'monitoring-pra-akreditasi-unit-' . $unitId . '-' . $generatedAt->format('Ymd-Hi') . '.pdf'; return $pdf->download($filename); } public function monitoringPdfExternal(Request $request) { $tipe = $request->query('tipe'); $tipe = is_null($tipe) ? null : trim((string) $tipe); if ($tipe === '') $tipe = null; $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $generatedAt = now(); // Summary: per tipe (tanpa list pegawai) if (is_null($tipe)) { $perPegawai = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->select([ DB::raw("coalesce(nullif(pl.tipe, ''), '-') as tipe"), DB::raw('pl.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), 'pl.id'); $tipeAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.tipe', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), ]) ->groupBy('t.tipe') ->orderByDesc('pegawai_selesai') ->orderByDesc('total_pegawai') ->orderBy('tipe') ->get(); $types = $tipeAgg->map(function ($r) { return (object) [ 'tipe' => (string) ($r->tipe ?? '-'), 'total_pegawai' => (int) ($r->total_pegawai ?? 0), 'pegawai_selesai' => (int) ($r->pegawai_selesai ?? 0), 'pegawai' => collect(), ]; }); $pdf = Pdf::loadView('pitstop.monitoring_pdf_external', [ 'title' => 'Monitoring Karyawan Luar', 'generatedAt' => $generatedAt, 'totalSteps' => $totalSteps, 'types' => $types, 'isSummaryOnly' => true, ])->setPaper('a4', 'landscape'); $filename = 'monitoring-karyawan-luar-ringkasan-' . $generatedAt->format('Ymd-Hi') . '.pdf'; return $pdf->download($filename); } // Detail per tipe $perPegawai = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), '=', $tipe) ->select([ 'pl.id', DB::raw("coalesce(pl.nama, '-') as nama"), DB::raw("coalesce(pl.nik, '-') as nik"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pl.id', 'pl.nama', 'pl.nik') ->orderBy('pl.nama') ->get(); $pegawai = $perPegawai->map(function ($r) use ($totalSteps) { return (object) [ 'nama' => (string) ($r->nama ?? '-'), 'nik' => (string) ($r->nik ?? '-'), 'lulus_steps' => (int) ($r->lulus_count ?? 0), 'total_steps' => $totalSteps, 'belum_selesai_steps' => (string) ($r->belum_steps ?? ''), ]; }); $totalPegawai = (int) $pegawai->count(); $pegawaiSelesai = $pegawai->filter(function ($p) { return ((int) ($p->total_steps ?? 0)) > 0 && ((int) ($p->lulus_steps ?? 0)) >= ((int) ($p->total_steps ?? 0)); })->count(); $types = collect([ (object) [ 'tipe' => $tipe, 'total_pegawai' => $totalPegawai, 'pegawai_selesai' => $pegawaiSelesai, 'pegawai' => $pegawai, ], ]); $pdf = Pdf::loadView('pitstop.monitoring_pdf_external', [ 'title' => 'Monitoring Karyawan Luar - ' . $tipe, 'generatedAt' => $generatedAt, 'totalSteps' => $totalSteps, 'types' => $types, 'isSummaryOnly' => false, ])->setPaper('a4', 'landscape'); $filename = 'monitoring-karyawan-luar-' . preg_replace('/[^a-zA-Z0-9_-]+/', '-', $tipe) . '-' . $generatedAt->format('Ymd-Hi') . '.pdf'; return $pdf->download($filename); } public function monitoringExcel(Request $request) { $unitId = $request->query('unit_id'); $unitId = is_null($unitId) || $unitId === '' ? null : (int) $unitId; $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $generatedAt = now(); // Summary per unit if (is_null($unitId)) { $perPegawai = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->select([ DB::raw('ukp.id as unit_id'), DB::raw("coalesce(ukp.name, '-') as unit_name"), DB::raw('pg.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy('ukp.id', 'ukp.name', 'pg.id'); $unitAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.unit_id', 't.unit_name', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), ]) ->groupBy('t.unit_id', 't.unit_name') ->orderBy('t.unit_name') ->get(); $filename = 'monitoring-pra-akreditasi-ringkasan-' . $generatedAt->format('Ymd-Hi') . '.xlsx'; return $this->streamXlsx($filename, function (Spreadsheet $spreadsheet) use ($unitAgg, $totalSteps) { $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('Internal (Summary)'); $headers = ['Nama Unit', 'Total Pitstop', 'Total Karyawan', 'Karyawan Selesai']; foreach ($headers as $i => $h) { $sheet->setCellValueByColumnAndRow($i + 1, 1, $h); } $sheet->getStyle('A1:E1')->getFont()->setBold(true); $row = 2; foreach ($unitAgg as $r) { $sheet->setCellValueByColumnAndRow(1, $row, (string) ($r->unit_name ?? '-')); $sheet->setCellValueByColumnAndRow(2, $row, (int) $totalSteps); $sheet->setCellValueByColumnAndRow(3, $row, (int) ($r->total_pegawai ?? 0)); $sheet->setCellValueByColumnAndRow(4, $row, (int) ($r->pegawai_selesai ?? 0)); $row++; } foreach(range('A', 'E') as $col){ $sheet->getColumnDimension($col)->setAutoSize(true); } }); } // Detail per unit $unit = DB::connection('pgsql') ->table('public.unitkerjapegawai_m as ukp') ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select(['ukp.id', 'ukp.name']) ->first(); abort_if(!$unit, 404); $rows = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select([ 'pg.id', DB::raw("coalesce(pg.namalengkap, '-') as nama"), DB::raw("coalesce(pg.nip_pns, '-') as nip_pns"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pg.id', 'pg.namalengkap', 'pg.nip_pns') ->orderBy('pg.namalengkap') ->get(); $filename = 'monitoring-pra-akreditasi-unit-' . (int) $unitId . '-' . $generatedAt->format('Ymd-Hi') . '.xlsx'; return $this->streamXlsx($filename, function (Spreadsheet $spreadsheet) use ($rows, $unit, $totalSteps) { $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('Internal (Detail)'); $headers = ['Nama Unit', 'Nama Karyawan', 'NIP', 'Lulus Pitstop', 'Total Pitstop', 'Belum Dikerjakan']; foreach ($headers as $i => $h) { $sheet->setCellValueByColumnAndRow($i + 1, 1, $h); } $sheet->getStyle('A1:I1')->getFont()->setBold(true); $row = 2; foreach ($rows as $r) { $lulus = (int) ($r->lulus_count ?? 0); $selesai = $totalSteps > 0 && $lulus >= $totalSteps ? 1 : 0; $sheet->setCellValueByColumnAndRow(1, $row, (string) ($unit->name ?? '-')); $sheet->setCellValueByColumnAndRow(2, $row, (string) ($r->nama ?? '-')); $sheet->setCellValueExplicitByColumnAndRow(3, $row, (string) ($r->nip_pns ?? '-'), DataType::TYPE_STRING); $sheet->setCellValueByColumnAndRow(4, $row, $lulus); $sheet->setCellValueByColumnAndRow(5, $row, (int) $totalSteps); $sheet->setCellValueByColumnAndRow(6, $row, (string) ($r->belum_steps ?? '')); $row++; } foreach(range('A', 'I') as $col){ $sheet->getColumnDimension($col)->setAutoSize(true); } }); } public function monitoringExcelExternal(Request $request) { $tipe = $request->query('tipe'); $tipe = is_null($tipe) ? null : trim((string) $tipe); if ($tipe === '') $tipe = null; $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $generatedAt = now(); // Summary per tipe if (is_null($tipe)) { $perPegawai = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->select([ DB::raw("coalesce(nullif(pl.tipe, ''), '-') as tipe"), DB::raw('pl.id as pegawai_id'), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), ]) ->groupBy(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), 'pl.id'); $tipeAgg = DB::connection('pgsql') ->query() ->fromSub($perPegawai, 't') ->select([ 't.tipe', DB::raw('count(*) as total_pegawai'), DB::raw('sum(case when t.lulus_count >= ' . $totalSteps . ' then 1 else 0 end) as pegawai_selesai'), ]) ->groupBy('t.tipe') ->orderBy('t.tipe') ->get(); $filename = 'monitoring-karyawan-luar-ringkasan-' . $generatedAt->format('Ymd-Hi') . '.xlsx'; return $this->streamXlsx($filename, function (Spreadsheet $spreadsheet) use ($tipeAgg, $totalSteps) { $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('External (Summary)'); $headers = ['Unit', 'Total Pitstop', 'Total Karyawan External', 'Karyawan External Selesai']; foreach ($headers as $i => $h) { $sheet->setCellValueByColumnAndRow($i + 1, 1, $h); } $sheet->getStyle('A1:D1')->getFont()->setBold(true); $row = 2; foreach ($tipeAgg as $r) { $sheet->setCellValueByColumnAndRow(1, $row, (string) ($r->tipe ?? '-')); $sheet->setCellValueByColumnAndRow(2, $row, (int) $totalSteps); $sheet->setCellValueByColumnAndRow(3, $row, (int) ($r->total_pegawai ?? 0)); $sheet->setCellValueByColumnAndRow(4, $row, (int) ($r->pegawai_selesai ?? 0)); $row++; } foreach(range('A', 'D') as $col){ $sheet->getColumnDimension($col)->setAutoSize(true); } }); } // Detail per tipe $rows = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), '=', $tipe) ->select([ 'pl.id', DB::raw("coalesce(pl.nama, '-') as nama"), DB::raw("coalesce(pl.nik, '-') as nik"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pl.id', 'pl.nama', 'pl.nik') ->orderBy('pl.nama') ->get(); $filename = 'monitoring-karyawan-luar-' . preg_replace('/[^a-zA-Z0-9_-]+/', '-', (string) $tipe) . '-' . $generatedAt->format('Ymd-Hi') . '.xlsx'; return $this->streamXlsx($filename, function (Spreadsheet $spreadsheet) use ($rows, $tipe, $totalSteps) { $sheet = $spreadsheet->getActiveSheet(); $sheet->setTitle('External (Detail)'); $headers = ['Unit', 'Nama', 'NIK', 'Lulus Pitstop', 'Total Pitstop', 'Belum Dikerjakan']; foreach ($headers as $i => $h) { $sheet->setCellValueByColumnAndRow($i + 1, 1, $h); } $sheet->getStyle('A1:H1')->getFont()->setBold(true); $row = 2; foreach ($rows as $r) { $lulus = (int) ($r->lulus_count ?? 0); $selesai = $totalSteps > 0 && $lulus >= $totalSteps ? 1 : 0; $sheet->setCellValueByColumnAndRow(1, $row, (string) $tipe); $sheet->setCellValueByColumnAndRow(2, $row, (string) ($r->nama ?? '-')); $sheet->setCellValueExplicitByColumnAndRow(3, $row, (string) ($r->nik ?? '-'), DataType::TYPE_STRING); $sheet->setCellValueByColumnAndRow(4, $row, $lulus); $sheet->setCellValueByColumnAndRow(5, $row, (int) $totalSteps); $sheet->setCellValueByColumnAndRow(6, $row, (string) ($r->belum_steps ?? '')); $row++; } foreach(range('A', 'H') as $col){ $sheet->getColumnDimension($col)->setAutoSize(true); } }); } public function dataProgressUnit(Request $request, $unit_id) { $unitId = (int) $unit_id; $draw = $this->dtInt($request->input('draw'), 1); $start = $this->dtInt($request->input('start'), 0); $length = $this->dtInt($request->input('length'), 10); if ($length < 1) $length = 10; if ($length > 200) $length = 200; $search = trim((string) data_get($request->all(), 'search.value', '')); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $baseNoSearch = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->where('ukp.id', $unitId) ->select([ 'pg.id', DB::raw("coalesce(pg.namalengkap, '-') as nama"), DB::raw("coalesce(pg.nip_pns, '-') as nip_pns"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select count(*) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), 0) as belum_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pg.id', 'pg.namalengkap', 'pg.nip_pns'); $base = clone $baseNoSearch; if ($search !== '') { $base->where('pg.namalengkap', 'ILIKE', '%' . $search . '%'); } $recordsTotal = DB::connection('pgsql')->query()->fromSub($baseNoSearch, 't')->count(); $recordsFiltered = $search === '' ? $recordsTotal : DB::connection('pgsql')->query()->fromSub($base, 't')->count(); $rows = $base ->orderByDesc('lulus_count') ->orderBy('pg.namalengkap') ->offset($start) ->limit($length) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $lulus = (int) ($r->lulus_count ?? 0); $pct = $totalSteps > 0 ? round(($lulus / $totalSteps) * 100, 1) : 0; return [ 'id' => (int) $r->id, 'nama' => (string) ($r->nama ?? '-'), 'nip_pns' => (string) ($r->nip_pns ?? '-'), 'lulus_count' => $lulus, 'pct' => $pct, 'selesai' => $totalSteps > 0 ? ($lulus >= $totalSteps ? 1 : 0) : 0, 'belum_count' => (int) ($r->belum_count ?? 0), 'belum_steps' => (string) ($r->belum_steps ?? ''), ]; })->values(); return response()->json([ 'draw' => $draw, 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, ]); } public function dataProgressExternalByTipe(Request $request, $tipe) { $tipe = urldecode((string) $tipe); $tipe = trim($tipe); abort_if($tipe === '', 404); $draw = $this->dtInt($request->input('draw'), 1); $start = $this->dtInt($request->input('start'), 0); $length = $this->dtInt($request->input('length'), 10); if ($length < 1) $length = 10; if ($length > 200) $length = 200; $search = trim((string) data_get($request->all(), 'search.value', '')); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); $baseNoSearch = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where(DB::raw("coalesce(nullif(pl.tipe, ''), '-')"), '=', $tipe) ->select([ 'pl.id', DB::raw("coalesce(pl.nama, '-') as nama"), DB::raw("coalesce(pl.nik, '-') as nik"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select count(*) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), 0) as belum_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pl.id', 'pl.nama', 'pl.nik'); $base = clone $baseNoSearch; if ($search !== '') { $base->where(function ($q) use ($search) { $q->where('pl.nama', 'ILIKE', '%' . $search . '%') ->orWhere('pl.nik', 'ILIKE', '%' . $search . '%'); }); } $recordsTotal = DB::connection('pgsql')->query()->fromSub($baseNoSearch, 't')->count(); $recordsFiltered = $search === '' ? $recordsTotal : DB::connection('pgsql')->query()->fromSub($base, 't')->count(); $rows = $base ->orderByDesc('lulus_count') ->orderBy('pl.nama') ->offset($start) ->limit($length) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $lulus = (int) ($r->lulus_count ?? 0); $pct = $totalSteps > 0 ? round(($lulus / $totalSteps) * 100, 1) : 0; return [ 'id' => (int) $r->id, 'nama' => (string) ($r->nama ?? '-'), 'nik' => (string) ($r->nik ?? '-'), 'lulus_count' => $lulus, 'pct' => $pct, 'selesai' => $totalSteps > 0 ? ($lulus >= $totalSteps ? 1 : 0) : 0, 'belum_count' => (int) ($r->belum_count ?? 0), 'belum_steps' => (string) ($r->belum_steps ?? ''), ]; })->values(); return response()->json([ 'draw' => $draw, 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, ]); } public function dataProgressAllKaryawan(Request $request) { $draw = $this->dtInt($request->input('draw'), 1); $start = $this->dtInt($request->input('start'), 0); $length = $this->dtInt($request->input('length'), 10); if ($length < 1) $length = 10; if ($length > 200) $length = 200; $search = trim((string) data_get($request->all(), 'search.value', '')); $totalSteps = (int) MasterPitStopPraAkre::where('statusenabled', true)->count(); // Internal $internal = DB::connection('pgsql') ->table('public.mappegawaijabatantounitkerja_m as mp') ->join('public.pegawai_m as pg', 'pg.id', '=', 'mp.objectpegawaifk') ->join('public.unitkerjapegawai_m as ukp', 'ukp.id', '=', 'mp.objectunitkerjapegawaifk') ->leftJoin('public.praakre as p', 'p.pegawai_id', '=', 'pg.id') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('mp.statusenabled', true) ->where('mp.isprimary', true) ->where('pg.statusenabled', true) ->where('pg.kedudukanfk', 1) ->where('ukp.statusenabled', true) ->select([ DB::raw("'internal' as tipe_karyawan"), 'pg.id', DB::raw("coalesce(pg.namalengkap, '-') as nama"), DB::raw("coalesce(pg.nip_pns, '-') as identitas"), DB::raw("coalesce(ukp.name, '-') as unit_name"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select count(*) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), 0) as belum_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pg.id and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pg.id', 'pg.namalengkap', 'pg.nip_pns', 'ukp.name'); // External $external = DB::connection('pgsql') ->table('public.pegawai_luar_pl as pl') ->leftJoin('public.praakre as p', function ($join) { $join->on('p.pegawai_id', '=', 'pl.id') ->where('p.tipe_karyawan', 'luar'); }) ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->select([ DB::raw("'luar' as tipe_karyawan"), 'pl.id', DB::raw("coalesce(pl.nama, '-') as nama"), DB::raw("coalesce(pl.nik, '-') as identitas"), DB::raw("coalesce(nullif(pl.tipe, ''), '-') as unit_name"), DB::raw("count(distinct m.id) filter (where p.status='lulus') as lulus_count"), DB::raw("coalesce(( select count(*) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), 0) as belum_count"), DB::raw("coalesce(( select string_agg(ms.nama, ', ' order by ms.id) from public.masterpitstop ms left join public.praakre px on px.pegawai_id = pl.id and px.tipe_karyawan = 'luar' and px.status = 'lulus' and px.masterpitstop_id = ms.id::text where ms.statusenabled = true and px.id is null ), '') as belum_steps"), ]) ->groupBy('pl.id', 'pl.nama', 'pl.nik', DB::raw("coalesce(nullif(pl.tipe, ''), '-')")); $union = $internal->unionAll($external); $baseNoSearch = DB::connection('pgsql')->query()->fromSub($union, 'u'); $base = DB::connection('pgsql')->query()->fromSub($union, 'u'); if ($search !== '') { $base->where(function ($q) use ($search) { $q->where('u.nama', 'ILIKE', '%' . $search . '%') ->orWhere('u.identitas', 'ILIKE', '%' . $search . '%') ->orWhere('u.unit_name', 'ILIKE', '%' . $search . '%') ->orWhere('u.tipe_karyawan', 'ILIKE', '%' . $search . '%'); }); } $recordsTotal = DB::connection('pgsql')->query()->fromSub($baseNoSearch, 't')->count(); $recordsFiltered = $search === '' ? $recordsTotal : DB::connection('pgsql')->query()->fromSub($base, 't')->count(); $rows = $base ->orderByDesc('lulus_count') ->orderBy('nama') ->offset($start) ->limit($length) ->get(); $data = $rows->map(function ($r) use ($totalSteps) { $lulus = (int) ($r->lulus_count ?? 0); $pct = $totalSteps > 0 ? round(($lulus / $totalSteps) * 100, 1) : 0; return [ 'tipe_karyawan' => (string) ($r->tipe_karyawan ?? 'internal'), 'id' => (int) ($r->id ?? 0), 'nama' => (string) ($r->nama ?? '-'), 'identitas' => (string) ($r->identitas ?? '-'), 'unit_name' => (string) ($r->unit_name ?? '-'), 'lulus_count' => $lulus, 'pct' => $pct, 'selesai' => $totalSteps > 0 ? ($lulus >= $totalSteps ? 1 : 0) : 0, 'belum_count' => (int) ($r->belum_count ?? 0), 'belum_steps' => (string) ($r->belum_steps ?? ''), ]; })->values(); return response()->json([ 'draw' => $draw, 'recordsTotal' => $recordsTotal, 'recordsFiltered' => $recordsFiltered, 'data' => $data, ]); } public function progressDetail(Request $request) { $validator = Validator::make($request->all(), [ 'pegawai_id' => 'required|integer|exists:pgsql.public.pegawai_m,id', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $pegawaiId = (int) $validator->validated()['pegawai_id']; $rows = DB::connection('pgsql') ->table('public.praakre as p') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('p.pegawai_id', $pegawaiId) ->where('p.status', 'lulus') ->whereNotNull('m.id') ->select([ 'p.id', 'p.masterpitstop_id', DB::raw("coalesce(m.nama, '-') as step_nama"), 'p.status', DB::raw("to_char(p.created_at, 'DD-MM-YYYY HH24:MI') as created_at"), ]) ->orderByDesc('p.id') ->limit(500) ->get(); return response()->json([ 'error' => 0, 'data' => $rows, ]); } public function progressDetailExternal(Request $request) { $validator = Validator::make($request->all(), [ 'pegawai_id' => 'required|integer|exists:pgsql.public.pegawai_luar_pl,id', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $pegawaiId = (int) $validator->validated()['pegawai_id']; $rows = DB::connection('pgsql') ->table('public.praakre as p') ->leftJoin('public.masterpitstop as m', function ($join) { $join->on(DB::raw('m.id::text'), '=', 'p.masterpitstop_id') ->where('m.statusenabled', true); }) ->where('p.tipe_karyawan', 'luar') ->where('p.status', 'lulus') ->where('p.pegawai_id', $pegawaiId) ->whereNotNull('m.id') ->select([ 'p.id', 'p.masterpitstop_id', DB::raw("coalesce(m.nama, '-') as step_nama"), 'p.status', DB::raw("to_char(p.created_at, 'DD-MM-YYYY HH24:MI') as created_at"), ]) ->orderByDesc('p.id') ->limit(500) ->get(); return response()->json([ 'error' => 0, 'data' => $rows, ]); } public function pegawaiSteps(Request $request) { $validator = Validator::make($request->all(), [ 'pegawai_id' => 'required|integer|exists:pgsql.public.pegawai_m,id', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $pegawaiId = (int) $validator->validated()['pegawai_id']; $lockedSteps = PraAkre::where('pegawai_id', $pegawaiId) ->where('status', 'lulus') ->distinct() ->pluck('masterpitstop_id'); return response()->json([ 'error' => 0, 'data' => [ 'locked_steps' => $lockedSteps, ], ]); } public function pegawaiStepsExternal(Request $request) { $validator = Validator::make($request->all(), [ 'pegawai_id' => 'required|integer|exists:pgsql.public.pegawai_luar_pl,id', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $pegawaiId = (int) $validator->validated()['pegawai_id']; $lockedSteps = PraAkre::where('pegawai_id', $pegawaiId) ->where('tipe_karyawan', 'luar') ->where('status', 'lulus') ->distinct() ->pluck('masterpitstop_id'); return response()->json([ 'error' => 0, 'data' => [ 'locked_steps' => $lockedSteps, ], ]); } public function submit(Request $request) { $tipeKaryawan = (string) $request->input('tipe_karyawan', 'internal'); $tipeKaryawan = $tipeKaryawan === 'luar' ? 'luar' : 'internal'; $validator = Validator::make($request->all(), [ 'karyawan_id' => ['required', 'integer'], 'step' => 'required', 'status' => 'required|in:lulus,tidak_lulus', 'unit_id' => 'nullable|string|max:15', 'tipe_karyawan' => 'nullable|in:internal,luar', ]); if ($validator->fails()) { return response()->json([ 'error' => 1, 'message' => 'Validasi gagal.', 'errors' => $validator->errors(), ], 422); } $payload = $validator->validated(); $payload['tipe_karyawan'] = $tipeKaryawan; $pegawaiId = (int) $payload['karyawan_id']; $masterPitstopId = (string) $payload['step']; if ($tipeKaryawan === 'luar') { $exists = DB::connection('pgsql') ->table('public.pegawai_luar_pl') ->where('id', $pegawaiId) ->exists(); if (!$exists) { return response()->json([ 'error' => 1, 'message' => 'Karyawan luar tidak ditemukan.', ], 422); } } else { $exists = DB::connection('pgsql') ->table('public.pegawai_m') ->where('id', $pegawaiId) ->exists(); if (!$exists) { return response()->json([ 'error' => 1, 'message' => 'Karyawan tidak ditemukan.', ], 422); } } $alreadyPassed = PraAkre::where('pegawai_id', $pegawaiId) ->where('tipe_karyawan', $tipeKaryawan) ->where('masterpitstop_id', $masterPitstopId) ->where('status', 'lulus') ->exists(); if ($alreadyPassed) { return response()->json([ 'error' => 1, 'message' => 'Step ini sudah lulus dan terkunci.', ], 409); } $row = PraAkre::create([ 'pegawai_id' => $pegawaiId, 'masterpitstop_id' => $masterPitstopId, 'unit_id' => $tipeKaryawan === 'luar' ? null : ($payload['unit_id'] ?? null), 'status' => $payload['status'], 'tipe_karyawan' => $payload['tipe_karyawan'], 'action_at' => auth()->user()->objectpegawaifk , 'created_at' => now(), ]); return response()->json([ 'error' => 0, 'message' => 'Data berhasil disimpan.', 'data' => [ 'id' => $row->id, 'pegawai_id' => $row->pegawai_id, 'masterpitstop_id' => $row->masterpitstop_id, 'status' => $row->status, ], ]); } }