load->database(); date_default_timezone_set('Asia/Jakarta'); } // ===================================================== // INDEX // ===================================================== public function index() { $data = [ "active_menu" => "generate_schedule" ]; // ============================================ // POSITIONS // ============================================ $data['positions'] = $this->db ->where('is_active',1) ->order_by('position_name','ASC') ->get('k_positions') ->result(); // ============================================ // SHIFTS // ============================================ $data['shifts'] = $this->db ->order_by('shift_code','ASC') ->get('k_shifts') ->result(); $this->load->view('partials/header', $data); $this->load->view('employees/generate_schedule', $data); $this->load->view('partials/footer'); } // ===================================================== // CALENDAR DATA // ===================================================== public function calendar_data() { $month = (int)$this->input->get('month'); $year = (int)$this->input->get('year'); if(empty($month)){ $month = date('n'); } if(empty($year)){ $year = date('Y'); } $firstDate = $year . '-' . str_pad($month,2,'0',STR_PAD_LEFT) . '-01'; // ============================================ // START MONDAY // ============================================ $startDate = date( 'Y-m-d', strtotime('monday this week', strtotime($firstDate)) ); if(date('N', strtotime($firstDate)) == 1){ $startDate = $firstDate; } // ============================================ // END SUNDAY // ============================================ $lastDate = date( 'Y-m-t', strtotime($firstDate) ); $endDate = date( 'Y-m-d', strtotime('sunday this week', strtotime($lastDate)) ); $days = []; $current = strtotime($startDate); $end = strtotime($endDate); while($current <= $end){ $date = date('Y-m-d', $current); // ======================================== // GET SCHEDULE GROUP BY SHIFT // ======================================== $this->db->select(' sh.id as shift_id, sh.shift_name, p.position_name, COUNT(s.id) as total_employee '); $this->db->from('k_employee_shift_schedules s'); $this->db->join( 'k_employees e', 'e.id = s.employee_id', 'left' ); $this->db->join( 'k_positions p', 'p.id = e.position_id', 'left' ); $this->db->join( 'k_shifts sh', 'sh.id = s.shift_id', 'left' ); $this->db->where('DATE(s.start_datetime)', $date); // ======================================== // GROUPING // ======================================== $this->db->group_by('sh.id'); $this->db->group_by('p.id'); $this->db->order_by('sh.checkin_time','ASC'); $this->db->order_by('p.position_name','ASC'); $rows = $this->db->get()->result(); // ======================================== // FORMAT GROUPED // ======================================== $grouped = []; if($rows){ foreach($rows as $r){ $shiftId = $r->shift_id ?: 0; if(!isset($grouped[$shiftId])){ $grouped[$shiftId] = [ 'shift_id' => $r->shift_id, 'shift_name' => $r->shift_name ?: '-', 'positions' => [] ]; } $grouped[$shiftId]['positions'][] = [ 'position_name' => $r->position_name ?: '-', 'total' => (int)$r->total_employee ]; } } $days[] = [ 'date' => date('d', strtotime($date)), 'month' => date('M', strtotime($date)), 'day_name' => $this->translateDay( date('l', strtotime($date)) ), 'full_date' => tanggal_indo($date), 'full_date_raw' => $date, 'is_current_month' => ( date('m', strtotime($date)) == str_pad($month,2,'0',STR_PAD_LEFT) ), 'shifts' => array_values($grouped) ]; $current = strtotime('+1 day', $current); } echo json_encode([ 'days' => $days ]); } // ===================================================== // GET EMPLOYEE // ===================================================== public function get_employees() { $position_id = $this->input->get('position_id'); $this->db->select(' e.id, e.full_name, e.fingerprint_user_id, p.position_name '); $this->db->from('k_employees e'); $this->db->join( 'k_positions p', 'p.id=e.position_id', 'left' ); $this->db->where('e.is_active',1); if(!empty($position_id)){ $this->db->where('e.position_id',$position_id); } $this->db->order_by('e.full_name','ASC'); $employees = $this->db->get()->result(); echo json_encode([ 'data' => $employees ]); } // ===================================================== // SAVE MANUAL SCHEDULE // ===================================================== public function save_manual_schedule() { try{ $employee_ids = $this->input->post('employee_ids'); $schedule_date = $this->input->post('schedule_date'); $shift_id = $this->input->post('shift_id'); // ============================================ // VALIDASI // ============================================ if(empty($employee_ids)){ throw new Exception('Employee belum dipilih'); } if(empty($schedule_date)){ throw new Exception('Tanggal wajib diisi'); } if(empty($shift_id)){ throw new Exception('Shift wajib dipilih'); } // ============================================ // GET SHIFT // ============================================ $shift = $this->db ->where('id', $shift_id) ->get('k_shifts') ->row(); if(!$shift){ throw new Exception('Shift tidak ditemukan'); } // ============================================ // BUILD START DATETIME // ============================================ $start_datetime = date( 'Y-m-d H:i:s', strtotime( $schedule_date . ' ' . $shift->checkin_time ) ); // ============================================ // BUILD END DATETIME // ============================================ $end_datetime = date( 'Y-m-d H:i:s', strtotime( $schedule_date . ' ' . $shift->checkout_time ) ); // ============================================ // JIKA SHIFT NYEBRANG HARI // ============================================ $is_cross_day = 0; if( strtotime($shift->checkout_time) <= strtotime($shift->checkin_time) ){ $end_datetime = date( 'Y-m-d H:i:s', strtotime('+1 day', strtotime($end_datetime)) ); $is_cross_day = 1; } // ============================================ // HITUNG TOTAL JAM KERJA // ============================================ $work_hours = ( strtotime($end_datetime) - strtotime($start_datetime) ) / 3600; // ============================================ // LOOP EMPLOYEE // ============================================ foreach($employee_ids as $employee_id){ // ======================================== // CHECK EXIST // ======================================== $exists = $this->db ->where('employee_id', $employee_id) ->where('schedule_date', $schedule_date) ->get('k_employee_shift_schedules') ->row(); // ======================================== // DATA SAVE // ======================================== $saveData = [ 'employee_id' => $employee_id, 'shift_id' => $shift_id, 'start_datetime' => $start_datetime, 'end_datetime' => $end_datetime, 'work_hours' => $work_hours, 'is_cross_day' => $is_cross_day, 'schedule_source' => 'manual', 'notes' => 'manual planner', 'updated_at' => date('Y-m-d H:i:s') ]; // ======================================== // UPDATE // ======================================== if($exists){ $this->db ->where('id', $exists->id) ->update( 'k_employee_shift_schedules', $saveData ); // ======================================== // INSERT // ======================================== } else { $saveData['created_at'] = date('Y-m-d H:i:s'); $this->db->insert( 'k_employee_shift_schedules', $saveData ); } } echo json_encode([ 'status' => true, 'message' => 'Jadwal berhasil disimpan' ]); } catch(Exception $e){ echo json_encode([ 'status' => false, 'message' => $e->getMessage() ]); } } // ===================================================== // TRANSLATE DAY // ===================================================== private function translateDay($day) { $days = [ 'Sunday' => 'Minggu', 'Monday' => 'Senin', 'Tuesday' => 'Selasa', 'Wednesday' => 'Rabu', 'Thursday' => 'Kamis', 'Friday' => 'Jumat', 'Saturday' => 'Sabtu' ]; return isset($days[$day]) ? $days[$day] : $day; } // ===================================================== // DETAIL SCHEDULE // ===================================================== public function detail_schedule() { $schedule_date = $this->input->get('schedule_date'); if(empty($schedule_date)){ echo json_encode([ 'status' => false, 'message'=> 'Tanggal wajib diisi', 'data' => [] ]); return; } // ============================================ // GET DATA // ============================================ $this->db->select(' s.id as schedule_id, s.employee_id, s.shift_id, s.start_datetime, s.end_datetime, e.employee_code, e.full_name, e.fingerprint_user_id, p.id as position_id, p.position_name, sh.shift_name, sh.checkin_time, sh.checkout_time, sh.color '); $this->db->from('k_employee_shift_schedules s'); $this->db->join( 'k_employees e', 'e.id=s.employee_id', 'left' ); $this->db->join( 'k_positions p', 'p.id=e.position_id', 'left' ); $this->db->join( 'k_shifts sh', 'sh.id=s.shift_id', 'left' ); // ============================================ // FILTER DATE // ============================================ $this->db->where( 'DATE(s.start_datetime)', $schedule_date ); $this->db->order_by('p.position_name','ASC'); $this->db->order_by('e.full_name','ASC'); $rows = $this->db->get()->result(); // ============================================ // GROUP POSITION // ============================================ $grouped = []; if($rows){ foreach($rows as $r){ $position_id = $r->position_id ?: 0; // ==================================== // CREATE GROUP // ==================================== if(!isset($grouped[$position_id])){ $grouped[$position_id] = [ 'position_id' => $r->position_id, 'position_name' => $r->position_name ?: '-', 'employees' => [] ]; } // ==================================== // EMPLOYEE // ==================================== $grouped[$position_id]['employees'][] = [ 'schedule_id' => $r->schedule_id, 'employee_id' => $r->employee_id, 'full_name' => $r->full_name, 'employee_code' => $r->employee_code, 'fingerprint_user_id' => $r->fingerprint_user_id, 'shift_id' => $r->shift_id, 'shift_name' => $r->shift_name, 'checkin' => $r->checkin_time ? date( 'H:i', strtotime($r->checkin_time) ) : '-', 'checkout' => $r->checkout_time ? date( 'H:i', strtotime($r->checkout_time) ) : '-', 'start_datetime' => $r->start_datetime, 'end_datetime' => $r->end_datetime, 'color' => $r->color ]; } } echo json_encode([ 'status' => true, 'data' => array_values($grouped) ]); } // ===================================================== // UPDATE SCHEDULE SHIFT // ===================================================== public function update_schedule_shift() { try{ $schedule_id = $this->input->post('schedule_id'); $shift_id = $this->input->post('shift_id'); // ============================================ // VALIDASI // ============================================ if(empty($schedule_id)){ throw new Exception('Schedule ID kosong'); } if(empty($shift_id)){ throw new Exception('Shift wajib dipilih'); } // ============================================ // CHECK SCHEDULE // ============================================ $schedule = $this->db ->where('id', $schedule_id) ->get('k_employee_shift_schedules') ->row(); if(!$schedule){ throw new Exception('Schedule tidak ditemukan'); } // ============================================ // CHECK SHIFT // ============================================ $shift = $this->db ->where('id', $shift_id) ->get('k_shifts') ->row(); if(!$shift){ throw new Exception('Shift tidak ditemukan'); } // ============================================ // AMBIL TANGGAL // ============================================ $schedule_date = date( 'Y-m-d', strtotime($schedule->start_datetime) ); // ============================================ // BUILD START DATETIME // ============================================ $start_datetime = date( 'Y-m-d H:i:s', strtotime( $schedule_date . ' ' . $shift->checkin_time ) ); // ============================================ // BUILD END DATETIME // ============================================ $end_datetime = date( 'Y-m-d H:i:s', strtotime( $schedule_date . ' ' . $shift->checkout_time ) ); // ============================================ // CROSS DAY // ============================================ $is_cross_day = 0; if( strtotime($shift->checkout_time) <= strtotime($shift->checkin_time) ){ $end_datetime = date( 'Y-m-d H:i:s', strtotime('+1 day', strtotime($end_datetime)) ); $is_cross_day = 1; } // ============================================ // WORK HOURS // ============================================ $work_hours = $shift->work_hours; // ============================================ // UPDATE // ============================================ $this->db ->where('id', $schedule_id) ->update( 'k_employee_shift_schedules', [ 'shift_id' => $shift_id, 'start_datetime' => $start_datetime, 'end_datetime' => $end_datetime, 'work_hours' => $work_hours, 'is_cross_day' => $is_cross_day, 'updated_at' => date('Y-m-d H:i:s') ] ); echo json_encode([ 'status' => true, 'message' => 'Shift berhasil diupdate' ]); } catch(Exception $e){ echo json_encode([ 'status' => false, 'message' => $e->getMessage() ]); } } // ===================================================== // DELETE SCHEDULE // ===================================================== public function delete_schedule() { try{ $schedule_id = $this->input->post('schedule_id'); // ============================================ // VALIDASI // ============================================ if(empty($schedule_id)){ throw new Exception('Schedule ID kosong'); } // ============================================ // CHECK DATA // ============================================ $schedule = $this->db ->where('id', $schedule_id) ->get('k_employee_shift_schedules') ->row(); if(!$schedule){ throw new Exception('Schedule tidak ditemukan'); } // ============================================ // DELETE // ============================================ $this->db ->where('id', $schedule_id) ->delete('k_employee_shift_schedules'); echo json_encode([ 'status' => true, 'message' => 'Schedule berhasil dihapus' ]); } catch(Exception $e){ echo json_encode([ 'status' => false, 'message' => $e->getMessage() ]); } } }