dailyService = new DailyService(); } public function search_customer() { $keyword = trim($_POST['keyword'] ?? ''); if ($keyword === '') { echo $this->json([]); return; } $keyword = addslashes($keyword); $sql = " SELECT c.c_uid, c.c_accountno, c.c_name, c.c_address, c.c_city, c.c_postal, c.c_phone FROM tbl_customer c WHERE ( c.c_accountno LIKE '%{$keyword}%' OR c.c_name LIKE '%{$keyword}%' ) AND NOT EXISTS ( SELECT 1 FROM tbl_install_waitlist iw WHERE iw.iw_customer_uid = c.c_uid AND iw.iw_status not in ('DELETED', 'COMPLETED') ) ORDER BY c.c_name LIMIT 20 "; $res = qry($sql); $rows = []; while ($row = mysqli_fetch_assoc($res)) { $rows[] = $row; } echo $this->json($rows); } public function install_wait_create_draft() { $c_uid = $_POST['c_uid'] ?? 0; $job_type = $_POST['job_type'] ?? ''; $driver_uid = $_POST['driver_uid'] ?? ''; $member_uid = $_POST['member_uid'] ?? ''; $request_note = $_POST['request_note'] ?? ''; if (!$c_uid || !$job_type) { echo $this->json(['ok' => 0, 'msg' => 'invalid params']); return; } $sql = " INSERT INTO tbl_install_waitlist ( iw_customer_uid, iw_work_type, iw_accountno, iw_customer_name, iw_address, iw_city, iw_postal, iw_phone, iw_lat, iw_lng, iw_status, iw_request_source, iw_request_by, iw_request_note, iw_request_at, iw_created_by, iw_created_at ) SELECT '{$c_uid}' AS iw_customer_uid, '".addslashes($job_type)."' AS iw_work_type, c.c_accountno AS iw_accountno, c.c_name AS iw_customer_name, c.c_address AS iw_address, c.c_city AS iw_city, c.c_postal AS iw_postal, c.c_phone AS iw_phone, c.c_geolat AS iw_lat, c.c_geolon AS iw_lng, 'DRAFT' AS iw_status, 'MAP' AS iw_request_source, '{$driver_uid}' AS iw_request_by, '".addslashes($request_note)."' AS iw_request_note, NOW() AS iw_request_at, '{$member_uid}' AS iw_created_by, NOW() AS iw_created_at FROM tbl_customer c WHERE c.c_uid = '{$c_uid}' "; qry($sql); $iw_uid = mysqli_insert_id($GLOBALS['conn']); // or global db handle echo $this->json([ 'ok' => 1, 'iw_uid' => $iw_uid ]); } public function install_wait_update_draft() { $iw_uid = intval($_POST['iw_uid'] ?? 0); $job_type = addslashes($_POST['job_type'] ?? ''); $request_note = addslashes($_POST['request_note'] ?? ''); if (!$iw_uid) { echo $this->json(['ok'=>0,'msg'=>'invalid iw_uid']); return; } // DRAFT 상태만 수정 가능 $sqlCheck = " SELECT iw_status FROM tbl_install_waitlist WHERE iw_uid = '{$iw_uid}' LIMIT 1 "; $r = qry($sqlCheck); $row = mysqli_fetch_assoc($r); if (!$row || $row['iw_status'] !== 'DRAFT') { echo $this->json(['ok'=>0,'msg'=>'cannot modify this request']); return; } $sql = " UPDATE tbl_install_waitlist SET iw_work_type = '{$job_type}', iw_request_note = '{$request_note}', iw_updated_at = NOW() WHERE iw_uid = '{$iw_uid}' LIMIT 1 "; qry($sql); echo $this->json(['ok'=>1]); } public function install_wait_delete_draft() { $iw_uid = intval($_POST['iw_uid'] ?? 0); if (!$iw_uid) { echo $this->json(['ok'=>0]); return; } // 상태 확인 $sqlCheck = " SELECT iw_status FROM tbl_install_waitlist WHERE iw_uid = '{$iw_uid}' LIMIT 1 "; $r = qry($sqlCheck); $row = mysqli_fetch_assoc($r); if (!$row || $row['iw_status'] !== 'DRAFT') { echo $this->json(['ok'=>0, 'msg'=>'cannot delete']); return; } // Soft delete $sql = " UPDATE tbl_install_waitlist SET iw_status = 'DELETED', iw_deleted_at = NOW() WHERE iw_uid = '{$iw_uid}' LIMIT 1 "; qry($sql); echo $this->json(['ok'=>1]); } public function install_wait_get_active() { $c_uid = intval($_POST['c_uid'] ?? 0); if (!$c_uid) { echo $this->json(['ok'=>0,'msg'=>'invalid params']); return; } // DRAFT/WAITING/ASSIGNED 중 최신 1개 (원하는 정책이면 ORDER를 상태 우선으로 바꿔도 됨) $sql = " SELECT iw.iw_uid, iw.iw_status, iw.iw_work_type, iw.iw_request_note, CASE WHEN iw.iw_request_by = 'Customer' THEN 'Customer' ELSE CONCAT(IFNULL(m.m_firstname,''), ' ', IFNULL(m.m_lastname,'')) END AS iw_request_by_name, iw.iw_request_by, iw.iw_request_at, iw.iw_created_at, di.di_install_date, di.di_install_time FROM tbl_install_waitlist iw LEFT JOIN tbl_daily_install di ON di.di_wait_uid = iw.iw_uid LEFT JOIN tbl_member m ON iw.iw_created_by = m.m_uid WHERE iw.iw_customer_uid = '{$c_uid}' AND iw.iw_status IN ('DRAFT','WAITING','ASSIGNED') ORDER BY iw.iw_uid DESC, di.di_uid DESC LIMIT 1 "; $r = qry($sql); if ($row = mysqli_fetch_assoc($r)) { // (A) iw_uid에 연결된 사진 조회해서 붙이기 $iw_uid = (int)$row['iw_uid']; $photos = []; $sql_img = " SELECT i_uid, i_filename, i_filepath FROM tbl_customer_image WHERE i_status = 'A' AND i_type = 'install_order' AND i_sourceuid = '{$iw_uid}' ORDER BY i_uid ASC "; $ri = qry($sql_img); while ($img = mysqli_fetch_assoc($ri)) { $imgPath = $img['i_filepath'] . $img['i_filename']; $photos[] = [ 'i_uid' => (int)$img['i_uid'], 'imgPath' => $imgPath ]; } $row['photos'] = $photos; echo $this->json([ 'ok' => 1, 'exists' => 1, 'data' => $row ]); return; } echo $this->json(['ok'=>1,'exists'=>0]); } public function install_wait_upsert_containers() { $iw_uid = intval($_POST['iw_uid'] ?? 0); $items = $_POST['items'] ?? []; $requestAction = $_POST['action'] ?? null; if (!$iw_uid) { echo $this->json(['ok' => 0, 'msg' => 'no iw_uid']); return; } qry("START TRANSACTION"); try { // 기존 컨테이너 전부 삭제 qry(" DELETE FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$iw_uid}' AND iwc_action = '{$requestAction}' "); // 재 insert if (is_array($items)) { foreach ($items as $it) { // item action 따로 사용 $itemAction = addslashes($it['action'] ?? ''); if (!$itemAction) { throw new Exception('missing item action'); } $type = addslashes($it['type'] ?? ''); $capacity = isset($it['capacity']) && $it['capacity'] !== '' ? intval($it['capacity']) : 'NULL'; $is_used = addslashes($it['is_used'] ?? 'N'); $owner_type = addslashes($it['owner_type'] ?? 'C'); $has_lock = addslashes($it['has_lock'] ?? 'N'); $has_wheel = addslashes($it['has_wheel'] ?? 'N'); $has_frame = addslashes($it['has_frame'] ?? 'N'); $sql = " INSERT INTO tbl_install_waitlist_container SET iwc_wait_uid = '{$iw_uid}', iwc_action = '{$itemAction}', iwc_type = '{$type}', iwc_capacity = {$capacity}, iwc_is_used = '{$is_used}', iwc_owner_type = '{$owner_type}', iwc_has_lock = '{$has_lock}', iwc_has_wheel = '{$has_wheel}', iwc_has_woodframe = '{$has_frame}' "; if (!qry($sql)) { throw new Exception('insert failed'); } } } qry("COMMIT"); } catch (Exception $e) { qry("ROLLBACK"); echo $this->json([ 'ok' => 0, 'msg' => $e->getMessage() ]); return; } echo $this->json(['ok' => 1]); } public function get_customer_containers_active() { $c_uid = intval($_POST['c_uid'] ?? 0); if (!$c_uid) { echo $this->json([]); return; } $sql = " SELECT cc_uid, cc_type, cc_capacity, cc_owner_type, cc_is_used, cc_has_lock, cc_has_wheel, cc_has_woodframe FROM tbl_customer_container WHERE cc_customer_uid = '{$c_uid}' AND cc_status = 'A' ORDER BY cc_type, cc_capacity "; $res = qry($sql); $rows = []; while ($row = mysqli_fetch_assoc($res)) { $rows[] = $row; } echo $this->json($rows); } public function install_wait_delete() { $iw_uid = intval($_POST['iw_uid'] ?? 0); if (!$iw_uid) { echo $this->json(['ok' => 0, 'msg' => 'ID is missing']); return; } // qry() + fetch $rs = qry("SELECT iw_status FROM tbl_install_waitlist WHERE iw_uid = '{$iw_uid}'"); $row = mysqli_fetch_assoc($rs); if (!$row) { echo $this->json(['ok' => 0, 'msg' => 'Item not found']); return; } qry("START TRANSACTION"); // 지우지는 말자. // $ok1 = qry("DELETE FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$iw_uid}'"); if (!$this->update_waitlist_status($iw_uid, 'DELETED')) { qry("ROLLBACK"); $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]); } qry("COMMIT"); echo $this->json(['ok' => 1]); } public function install_wait_complete() { $iw_uid = intval($_POST['iw_uid'] ?? 0); if (!$iw_uid) { echo $this->json(['ok' => 0, 'msg' => 'no iw_uid']); return; } // set parameters $request_at = $_POST['request_at'] ?? null; $due_date = $_POST['due_date'] ?? null; $request_by = $_POST['request_by'] ?? null; $request_note = $_POST['request_note'] ?? null; $cs_note = $_POST['cs_note'] ?? null; $work_note = $_POST['work_note'] ?? null; $person_cnt = $_POST['person_cnt'] ?? null; // null or 2 $person_reason= $_POST['person_reason'] ?? null; $scheduled = intval($_POST['scheduled'] ?? 0); $cycle = intval($_POST['scheduled_cycle'] ?? 0); // Two-person OFF = NULL if (!$person_cnt) { $person_cnt = null; $person_reason = null; } // Recurring OFF = 0 if (!$scheduled) { $cycle = 0; } qry(" UPDATE tbl_install_waitlist SET iw_request_at = ".($request_at ? "'{$request_at}'" : "NOW()").", iw_requested_due_date = ".($due_date ? "'{$due_date}'" : "NULL").", iw_request_by = ".($request_by ? "'{$request_by}'" : "NULL").", iw_request_note = ".($request_note ? "'".addslashes($request_note)."'" : "NULL").", iw_cs_note = ".($cs_note ? "'".addslashes($cs_note)."'" : "NULL").", iw_work_note = ".($work_note ? "'".addslashes($work_note)."'" : "NULL").", iw_required_person_cnt = ".($person_cnt !== null ? intval($person_cnt) : "NULL").", iw_multi_person_reason = ".($person_reason ? "'".addslashes($person_reason)."'" : "NULL").", iw_scheduled = '{$scheduled}', iw_scheduled_cycle = '{$cycle}', iw_status = 'WAITING' WHERE iw_uid = '{$iw_uid}' LIMIT 1 "); echo $this->json(['ok' => 1]); } // 수정을 위한 public function get_install_wait_detail() { $iw_uid = intval($_POST['iw_uid'] ?? 0); $rs = qry(" SELECT * FROM tbl_install_waitlist WHERE iw_uid = '{$iw_uid}' LIMIT 1 "); $row = mysqli_fetch_assoc($rs); echo json_encode($row ?: null); } public function get_install_wait_containers() { $iw_uid = intval($_POST['iw_uid'] ?? 0); $rows = []; $rs = qry(" SELECT * FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$iw_uid}' ORDER BY iwc_action "); while ($r = mysqli_fetch_assoc($rs)) { $rows[] = $r; } echo json_encode($rows); } // ========================================= // install wait list - right // ========================================= public function inq_daily_install_points() { $di_install_date = trim($_POST['di_install_date'] ?? ''); $di_driver_uid = intval($_POST['di_driver_uid'] ?? 0); if (!$di_install_date || !$di_driver_uid) { echo $this->json([]); return; } // install.php 스타일: addslashes / qry $date = addslashes($di_install_date); $sql = " SELECT di.di_uid, di.di_order_seq, di.di_driver_uid, ifnull(mem.m_initial,'') as di_customer_driver_initial, di.di_install_date, di.di_install_time, di.di_wait_uid, di.di_customer_uid, di.di_accountno, di.di_phone, di.di_address, di.di_city, di.di_postal, di.di_lat, di.di_lng, di.di_work_type, di.di_status, c.c_name AS di_customer_name, SUM( CASE WHEN dic.dic_action = 'PICKUP' AND dic.dic_type = 'D' THEN 1 ELSE 0 END ) AS pickup_d_cnt, SUM( CASE WHEN dic.dic_action = 'PICKUP' AND dic.dic_type <> 'D' THEN 1 ELSE 0 END ) AS pickup_b_cnt, SUM( CASE WHEN dic.dic_action = 'INSTALL' AND dic.dic_type = 'D' THEN 1 ELSE 0 END ) AS install_d_cnt, SUM( CASE WHEN dic.dic_action = 'INSTALL' AND dic.dic_type <> 'D' THEN 1 ELSE 0 END ) AS install_b_cnt FROM tbl_daily_install di LEFT JOIN tbl_member mem ON di.di_customer_driver_uid = mem.m_uid LEFT JOIN tbl_customer c ON c.c_uid = di.di_customer_uid LEFT JOIN tbl_daily_install_container dic ON dic.dic_di_uid = di.di_uid WHERE di.di_status = 'A' AND di.di_install_date = '{$date}' AND di.di_driver_uid = '{$di_driver_uid}' GROUP BY di.di_uid ORDER BY di.di_order_seq ASC "; $res = qry($sql); $rows = []; while ($row = mysqli_fetch_assoc($res)) { $rows[] = $row; } echo $this->json($rows); } private function insert_daily_install_from_waitlist($iw_uid, $di_driver_uid, $di_install_date, $di_order_seq) { $created_by = mysqli_real_escape_string($GLOBALS['conn'], $_SESSION['ss_UID'] ?? ''); $sql = " INSERT INTO tbl_daily_install ( di_order_seq, di_driver_uid, di_install_date, di_wait_uid, di_customer_uid, di_customer_name, di_customer_driver_uid, di_accountno, di_phone, di_address, di_city, di_postal, di_lat, di_lng, di_work_type, di_oil_pickup, di_request_note, di_work_note, di_created_by ) SELECT '{$di_order_seq}', '{$di_driver_uid}', '{$di_install_date}', iw.iw_uid, iw.iw_customer_uid, iw.iw_customer_name, c.c_driveruid, iw.iw_accountno, iw.iw_phone, iw.iw_address, iw.iw_city, iw.iw_postal, iw.iw_lat, iw.iw_lng, iw.iw_work_type, iw.iw_need_oil_pickup, iw.iw_request_note, iw.iw_work_note, '{$created_by}' FROM tbl_install_waitlist iw LEFT JOIN tbl_customer c ON c.c_uid = iw.iw_customer_uid WHERE iw_uid = '{$iw_uid}' "; if (!qry($sql)) { return false; } return mysqli_insert_id($GLOBALS['conn']); } private function insert_daily_install_container_from_waitlist($di_uid, $iw_uid) { $sql = " INSERT INTO tbl_daily_install_container ( dic_di_uid, dic_action, dic_type, dic_capacity, dic_is_used, dic_has_lock, dic_has_wheel, dic_has_woodframe, dic_owner_type ) SELECT '{$di_uid}', iwc_action, iwc_type, iwc_capacity, iwc_is_used, iwc_has_lock, iwc_has_wheel, iwc_has_woodframe, iwc_owner_type FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$iw_uid}' "; // 0 row여도 정상 return qry($sql) !== false; } private function update_waitlist_status($iw_uid, $status) { $status = mysqli_real_escape_string($GLOBALS['conn'], $status); $sql = " UPDATE tbl_install_waitlist SET iw_status = '{$status}' WHERE iw_uid = '{$iw_uid}' "; return qry($sql); } public function confirm_daily_install() { $trial_rows = $_POST['trial_rows'] ?? []; $existing_rows = $_POST['existing_rows'] ?? []; $di_driver_uid = intval($_POST['di_driver_uid'] ?? 0); $di_install_date = $_POST['di_install_date'] ?? null; $fail = function($stage, $msg, $extra = []) { echo $this->json(array_merge([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($GLOBALS['conn']) ], $extra)); exit; }; if (!$di_driver_uid || !$di_install_date) { $fail('validate', 'invalid params'); } if ((!is_array($trial_rows) || !count($trial_rows)) && (!is_array($existing_rows) || !count($existing_rows))) { $fail('validate', 'nothing to confirm'); } /* ======================================================= 0. 기존 DB max seq 가져오기 ======================================================= */ $qr_max = qry(" SELECT MAX(di_order_seq) AS max_seq FROM tbl_daily_install WHERE di_driver_uid = '{$di_driver_uid}' AND di_install_date = '{$di_install_date}' "); $row_max = fetch_array($qr_max); $base_seq = intval($row_max['max_seq'] ?? 0); /* ======================================================= 1. 전체 rows 합치고 seq 재정렬 ======================================================= */ $all = array_merge( is_array($existing_rows) ? $existing_rows : [], is_array($trial_rows) ? $trial_rows : [] ); usort($all, function($a, $b){ return intval($a['di_order_seq'] ?? 0) <=> intval($b['di_order_seq'] ?? 0); }); $seq = 1; foreach($all as &$r){ // base_seq 더해줌 $r['di_order_seq'] = $base_seq + $seq++; } unset($r); /* ======================================================= 2. 다시 existing / trial 분리 ======================================================= */ $existing_map = []; foreach($existing_rows as $row){ $existing_map[intval($row['di_uid'])] = true; } $new_existing_rows = []; $new_trial_rows = []; foreach($all as $r){ if(isset($existing_map[intval($r['di_uid'] ?? 0)])){ $new_existing_rows[] = $r; }else{ $new_trial_rows[] = $r; } } qry("START TRANSACTION"); /* ======================================================= A) 기존 daily_install 순서 업데이트 ======================================================= */ foreach ($new_existing_rows as $row) { $di_uid = intval($row['di_uid'] ?? 0); $di_order_seq = intval($row['di_order_seq'] ?? 0); if (!$di_uid || !$di_order_seq) { qry("ROLLBACK"); $fail('validate_existing_row', 'invalid existing row', ['row'=>$row]); } $sql = " UPDATE tbl_daily_install SET di_order_seq = '{$di_order_seq}' WHERE di_uid = '{$di_uid}' AND di_driver_uid = '{$di_driver_uid}' AND di_install_date = '{$di_install_date}' LIMIT 1 "; if (!qry($sql)) { qry("ROLLBACK"); $fail('update_existing_order', 'failed', ['di_uid'=>$di_uid]); } } /* ======================================================= B) trial(waitlist) → INSERT ======================================================= */ foreach ($new_trial_rows as $row) { $iw_uid = intval($row['iw_uid'] ?? 0); $di_order_seq = intval($row['di_order_seq'] ?? 0); if (!$iw_uid || !$di_order_seq) { qry("ROLLBACK"); $fail('validate_trial_row', 'invalid trial row', ['row'=>$row]); } $di_uid = $this->insert_daily_install_from_waitlist( $iw_uid, $di_driver_uid, $di_install_date, $di_order_seq ); if (!$di_uid) { qry("ROLLBACK"); $fail('insert_daily_install', 'failed', ['iw_uid'=>$iw_uid]); } if (!$this->insert_daily_install_container_from_waitlist($di_uid, $iw_uid)) { qry("ROLLBACK"); $fail('insert_container', 'failed', ['iw_uid'=>$iw_uid]); } if (!$this->update_waitlist_status($iw_uid, 'ASSIGNED')) { qry("ROLLBACK"); $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]); } } qry("COMMIT"); echo $this->json(['ok'=>1]); exit; } public function unassign_daily_install() { $di_uids = $_POST['di_uids'] ?? []; if (!is_array($di_uids) || !count($di_uids)) { echo $this->json(['ok'=>0,'msg'=>'invalid di_uids']); return; } // 숫자만 필터 $di_uids = array_map('intval', $di_uids); $di_uids = array_filter($di_uids); if (!count($di_uids)) { echo $this->json(['ok'=>0,'msg'=>'empty di_uids']); return; } $in = implode(',', $di_uids); $fail = function($stage, $msg, $extra = []) { echo $this->json(array_merge([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($GLOBALS['conn']) ], $extra)); exit; }; // 1. 대상 조회 $qr = qry(" SELECT di_uid, di_wait_uid, di_driver_uid, di_install_date FROM tbl_daily_install WHERE di_uid IN ($in) "); $rows = []; while ($r = fetch_array($qr)) { $rows[] = $r; } if (!count($rows)) { $fail('select', 'no rows found'); } qry("START TRANSACTION"); // 2. 삭제 if (!qry("DELETE FROM tbl_daily_install WHERE di_uid IN ($in)")) { qry("ROLLBACK"); $fail('delete_daily_install', 'failed'); } // 3. waitlist 복귀 (중복 제거) $iw_uids = []; foreach ($rows as $r) { if (!empty($r['di_wait_uid'])) { $iw_uids[] = intval($r['di_wait_uid']); } } $iw_uids = array_unique($iw_uids); foreach ($iw_uids as $iw_uid) { if (!$this->update_waitlist_status($iw_uid, 'WAITING')) { qry("ROLLBACK"); $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]); } } // 4. resequence (driver + date별 그룹) $group = []; foreach ($rows as $r) { $key = $r['di_driver_uid'].'|'.$r['di_install_date']; $group[$key] = [ 'driver_uid' => $r['di_driver_uid'], 'date' => $r['di_install_date'] ]; } foreach ($group as $g) { if (!$this->resequence_daily_install($g['driver_uid'], $g['date'])) { qry("ROLLBACK"); $fail('resequence', 'failed', $g); } } qry("COMMIT"); echo $this->json([ 'ok' => 1, 'cnt' => count($rows) ]); } public function resequence_daily_install($di_driver_uid, $di_install_date) { $di_driver_uid = intval($di_driver_uid); $di_install_date = mysqli_real_escape_string($GLOBALS['conn'], $di_install_date); $qr = qry(" SELECT di_uid FROM tbl_daily_install WHERE di_driver_uid = '{$di_driver_uid}' AND di_install_date = '{$di_install_date}' AND di_status = 'A' ORDER BY di_order_seq ASC, di_uid ASC "); if (!$qr) return false; $seq = 1; while ($row = fetch_array($qr)) { $di_uid = intval($row['di_uid']); if (!qry("UPDATE tbl_daily_install SET di_order_seq = '{$seq}' WHERE di_uid = '{$di_uid}'")) { return false; } $seq++; } return true; } public function update_waitlist_latlng() { $iw_uid = intval($_POST['iw_uid'] ?? 0); $lat = $_POST['lat'] ?? null; $lng = $_POST['lng'] ?? null; if (!$iw_uid || !$lat || !$lng) { echo $this->json(['ok'=>0,'msg'=>'invalid params']); return; } $lat = floatval($lat); $lng = floatval($lng); $sql = " UPDATE tbl_install_waitlist SET iw_lat = '{$lat}', iw_lng = '{$lng}' WHERE iw_uid = '{$iw_uid}' LIMIT 1 "; if (!qry($sql)) { echo $this->json([ 'ok' => 0, 'msg'=> 'update failed', 'db' => mysqli_error($GLOBALS['conn']) ]); return; } echo $this->json(['ok'=>1]); } public function get_customer_container_one(){ $cc_uid = intval($_POST['cc_uid']); $r = mysqli_query($GLOBALS['conn'],"SELECT * FROM tbl_customer_container WHERE cc_uid='{$cc_uid}' LIMIT 1"); $row = mysqli_fetch_assoc($r); if(!$row){ echo $this->json(['ok'=>0]); return; } if(!$row){ echo $this->json(['ok'=>0]); return; } echo $this->json(['ok'=>1,'row'=>$row]); } public function customer_container_save(){ $mode = $_POST['mode'] ?? 'ADD'; $cc_uid = intval($_POST['cc_uid'] ?? 0); $c_uid = intval($_POST['c_uid'] ?? 0); $type = addslashes($_POST['type'] ?? ''); if( $mode != 'PICKUP' ) $capacity = $_POST['capacity'] !== '' ? intval($_POST['capacity']) : "NULL"; $is_used = $_POST['is_used'] ?? 'N'; $has_lock = $_POST['has_lock'] ?? 'N'; $has_wheel = $_POST['has_wheel'] ?? 'N'; $has_frame = $_POST['has_frame'] ?? 'N'; $owner_type = $_POST['owner_type'] ?? 'C'; $install_date = $_POST['install_date'] ?? null; $pickup_date = $_POST['pickup_date'] ?? null; $install_note = addslashes($_POST['install_note'] ?? ''); $pickup_note = addslashes($_POST['pickup_note'] ?? ''); if($mode==='ADD'){ $sql = " INSERT INTO tbl_customer_container (cc_customer_uid,cc_type,cc_capacity,cc_is_used, cc_has_lock,cc_has_wheel,cc_has_woodframe,cc_owner_type, cc_status, cc_install_date, cc_install_at,cc_install_note, cc_createddate,cc_updateddate) VALUES( '{$c_uid}','{$type}',{$capacity}, '{$is_used}','{$has_lock}','{$has_wheel}','{$has_frame}','{$owner_type}', 'A','{$install_date}','{$install_date}','{$install_note}', DATE_FORMAT(NOW(),'%Y%m%d%H%i%s'), DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') ) "; qry($sql); } if ($mode === 'EDIT') { $result = qry("SELECT cc_status FROM tbl_customer_container WHERE cc_uid='{$cc_uid}'"); $row = mysqli_fetch_assoc($result); if (!$row) { echo $this->json(['ok'=>0,'msg'=>'invalid container']); return; } $status = $row['cc_status']; // 날짜 처리 (빈 값이면 NULL) $install_at_sql = $install_date ? "'{$install_date} 00:00:00'" : "NULL"; $pickup_at_sql = $pickup_date ? "'{$pickup_date} 00:00:00'" : "NULL"; // ===== ACTIVE 수정 ===== if ($status === 'A') { $sql = " UPDATE tbl_customer_container SET cc_capacity={$capacity}, cc_is_used='{$is_used}', cc_has_lock='{$has_lock}', cc_has_wheel='{$has_wheel}', cc_has_woodframe='{$has_frame}', cc_owner_type='{$owner_type}', cc_install_date={$install_date}, cc_install_at={$install_at_sql}, cc_install_note='{$install_note}', cc_modified_at=NOW(), cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') WHERE cc_uid='{$cc_uid}' "; qry($sql); } // ===== INACTIVE 수정 ===== else { $sql = " UPDATE tbl_customer_container SET cc_pickup_at={$pickup_at_sql}, cc_pickup_note='{$pickup_note}', cc_modified_at=NOW(), cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') WHERE cc_uid='{$cc_uid}' "; qry($sql); } } if($mode==='PICKUP'){ $sql=" UPDATE tbl_customer_container SET cc_status='I', cc_pickup_date='{$pickup_date}', cc_pickup_note='{$pickup_note}', cc_pickup_at=NOW(), cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') WHERE cc_uid='{$cc_uid}' "; qry($sql); } // 최초 install if ($install_date) { $install_yyyymmdd = str_replace('-', '', $install_date); // 현재 c_installdate 가져오기 $r = qry("SELECT c_installdate FROM tbl_customer WHERE c_uid='{$c_uid}'"); $row = mysqli_fetch_assoc($r); $current = $row['c_installdate']; if (!$current || $current === 'N/A' || $install_yyyymmdd < $current) { qry(" UPDATE tbl_customer SET c_installdate='{$install_yyyymmdd}' WHERE c_uid='{$c_uid}' "); } } // c_exchangedate: 최신 install_date 또는 pickup_date $exchangeDates = []; if ($install_date) { $exchangeDates[] = str_replace('-', '', $install_date); } if ($pickup_date) { $exchangeDates[] = str_replace('-', '', $pickup_date); } if (!empty($exchangeDates)) { rsort($exchangeDates); $latestExchangeDate = $exchangeDates[0]; qry(" UPDATE tbl_customer SET c_exchangedate = '{$latestExchangeDate}' WHERE c_uid = '{$c_uid}' "); } // c_removaldate: 작업 후 active container가 하나도 없으면 최종 수거일 업데이트 // 반대로 active container가 있으면 c_removaldate 초기화 $r = qry(" SELECT COUNT(*) AS active_cnt FROM tbl_customer_container WHERE cc_customer_uid = '{$c_uid}' AND cc_status = 'A' "); $row = mysqli_fetch_assoc($r); $activeCnt = (int)($row['active_cnt'] ?? 0); if ($activeCnt === 0) { if ($pickup_date) { $pickup_yyyymmdd = str_replace('-', '', $pickup_date); qry(" UPDATE tbl_customer SET c_removaldate = '{$pickup_yyyymmdd}' WHERE c_uid = '{$c_uid}' "); } } else { qry(" UPDATE tbl_customer SET c_removaldate = '' WHERE c_uid = '{$c_uid}' and c_removaldate <> '' "); } echo $this->json(['ok'=>1]); } // DAILY INSTALL public function daily_install_update_status() { $di_uid = intval($_POST['di_uid'] ?? 0); $member_uid = intval($_POST['member_uid'] ?? 0); $newStatus = strtoupper(trim($_POST['status'] ?? '')); $install_note = trim($_POST['install_note'] ?? ''); $additional_note = trim($_POST['additional_note'] ?? ''); $oil_qty = intval($_POST['oil_qty'] ?? 0); $sludge = intval($_POST['sludge'] ?? 0); $allowed = ['P','M','X']; $fail = function($stage, $msg) { echo $this->json([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($GLOBALS['conn']) ]); exit; }; if(!$di_uid || !$member_uid || !in_array($newStatus, $allowed)){ $fail('validate', 'invalid params'); } // 1) install 조회 $qr = qry(" SELECT * FROM tbl_daily_install WHERE di_uid = '{$di_uid}' LIMIT 1 "); $di = mysqli_fetch_assoc($qr); if(!$di){ $fail('select_di', 'not found'); } qry("START TRANSACTION"); try { // 2) 상태 업데이트 $this->updateDailyInstallStatus($di_uid, $newStatus, $install_note, $oil_qty, $sludge); // 2-1) additional note 저장 $this->upsertAdditionalNote($di, $additional_note); // 3) 컨테이너 반영 if ($di['di_status'] === 'A' && $newStatus === 'P') { $this->apply_container_changes_for_di($di_uid, $install_note, intval($di['di_customer_uid'])); // 3-1) 컨테이너 반영 후 customer 의 main 볼륨이랑 main 컨테이너 변경 $this->updateCustomerMainContainer(intval($di['di_customer_uid'])); } // 4) oil pickup → daily $this->saveDailyFromInstallIfNeeded($di_uid, $oil_qty, $sludge); // 5) waitlist 처리 $this->handleWaitlistAfterInstall($di, $newStatus, $member_uid, $install_note); // 6) 순서 재정렬 $this->reorder_daily_install_sequence( $di['di_driver_uid'], $di['di_install_date'] ); qry("COMMIT"); echo $this->json(['ok'=>1]); exit; } catch (Exception $e) { qry("ROLLBACK"); $fail('exception', $e->getMessage()); } } private function updateDailyInstallStatus( int $di_uid, string $newStatus, string $install_note, int $oil_qty, int $sludge ) { $conn = $GLOBALS['conn']; $newStatus = mysqli_real_escape_string($conn, $newStatus); $noteEsc = mysqli_real_escape_string($conn, $install_note); $oil_qty = intval($oil_qty); $sludge = intval($sludge); $sql = " UPDATE tbl_daily_install SET di_status = '{$newStatus}', di_install_note = '{$noteEsc}', di_oil_pickup = '{$oil_qty}', di_sludge = '{$sludge}', di_install_time = IF('{$newStatus}' <> 'A', CURTIME(), di_install_time) WHERE di_uid = '{$di_uid}' LIMIT 1 "; if (!qry($sql)) { throw new Exception('daily install status update failed'); } } /** * install 추가 메모를 tbl_note에 upsert * * install 하면서 컨테이너 위치관련 note를 n_dailyuid 를 'i_{di_uid}' 형태로 저장 * 예: di_uid = 123 이면 n_dailyuid = 'i_123' */ private function upsertAdditionalNote(array $di, string $additional_note): void { $connect = $GLOBALS['conn']; // 디버그 // file_put_contents(__DIR__.'/debug.log', "NOTE: ".$additional_note."\n", FILE_APPEND); $noteText = trim($additional_note); if (strlen($noteText) <= 1) { return; } $di_uid = intval($di['di_uid'] ?? 0); if ($di_uid <= 0) { return; } // install 전용 note key $noteDailyUid = 'i_' . $di_uid; $n_dailyuid = mysqli_real_escape_string($connect, $noteDailyUid); $customeruid = mysqli_real_escape_string($connect, $di['di_customer_uid'] ?? ''); $memberuid = mysqli_real_escape_string($connect, $_SESSION['ss_UID'] ?? ''); $noteEsc = mysqli_real_escape_string($connect, $noteText); $now14 = date('YmdHis'); $level = intval($_SESSION['ss_LEVEL'] ?? 9); // 기존 note 존재 여부 확인 $qr = mysqli_query($connect, " SELECT n_uid FROM tbl_note WHERE n_dailyuid = '{$n_dailyuid}' LIMIT 1 "); if (!$qr) { throw new Exception('install note select failed'); } $existing = mysqli_fetch_assoc($qr); if ($existing) { // UPDATE $ok = mysqli_query($connect, " UPDATE tbl_note SET n_memberuid = '{$memberuid}', n_customeruid = '{$customeruid}', n_type = 'D', n_level = {$level}, n_view = 1, n_note = '{$noteEsc}', n_createddate = '{$now14}' WHERE n_dailyuid = '{$n_dailyuid}' LIMIT 1 "); if (!$ok) { throw new Exception('install note update failed'); } } else { // INSERT $ok = mysqli_query($connect, " INSERT INTO tbl_note ( n_memberuid, n_customeruid, n_dailyuid, n_type, n_level, n_view, n_note, n_createddate ) VALUES ( '{$memberuid}', '{$customeruid}', '{$n_dailyuid}', 'D', {$level}, 1, '{$noteEsc}', '{$now14}' ) "); if (!$ok) { throw new Exception('install note insert failed'); } } } private function saveDailyFromInstallIfNeeded(int $di_uid, int $oil_qty, int $sludge): void { if ($oil_qty <= 0) return; $qr = qry("SELECT * FROM tbl_daily_install WHERE di_uid='{$di_uid}' LIMIT 1"); $di = mysqli_fetch_assoc($qr); if (!$di) throw new Exception('install not found'); $visitdate = date('Ymd', strtotime($di['di_install_date'])); $now14 = date('YmdHis'); // customer account $qrCus = qry(" SELECT c_accountno, c_regionuid, c_region FROM tbl_customer WHERE c_uid='{$di['di_customer_uid']}' LIMIT 1 "); $customer = mysqli_fetch_assoc($qrCus); if (!$customer) { throw new Exception('customer not found'); } // ========================= // payload (saveInput 스타일) // ========================= $payload = [ 'd_customeruid' => $di['di_customer_uid'], 'd_accountno' => $customer['c_accountno'], 'd_orderdate' => $visitdate, 'd_driveruid' => $_SESSION['ss_UID'] ?? 0, // member uid 'd_druid' => 11, // driver uid 고정 hard coding 'd_drinitial' => 'I.N', // driver initial 고정 hard coding 'd_regionuid' => $customer['c_regionuid'] ?? 0, 'd_region' => $customer['c_region'] ?? '', 'd_visit' => 'Y', 'd_visitdate' => $visitdate . '000000', 'd_status' => 'F', 'd_jobtype' => 'INSTALL', 'd_quantity' => $oil_qty, 'd_sludge' => $sludge, 'd_inputdate' => $now14, 'd_is_transfer' => 'N', 'd_level' => $_SESSION['ss_LEVEL'] ?? '', 'd_createruid' => $_SESSION['ss_UID'] ?? '', 'd_createddate' => $now14, ]; // ========================= // 기존 daily 찾기 (중복 방지) // ========================= $opt = [ 'allow_update' => true, 'old_key' => [ 'd_orderdate' => $visitdate, 'd_accountno' => $customer['c_accountno'], 'd_jobtype' => 'INSTALL' ] ]; $this->dailyService->saveDaily($payload, $opt); } private function handleWaitlistAfterInstall(array $di, string $newStatus, int $member_uid, string $note) { $wait_uid = intval($di['di_wait_uid']); if ($wait_uid <= 0) return; if (in_array($newStatus, ['P','M'])) { $this->updateWaitlistStatus($wait_uid, 'COMPLETED'); $this->createNextScheduleIfNeeded( $wait_uid, $di['di_install_date'], $member_uid ); } elseif ($newStatus === 'X') { $this->updateWaitlistStatus($wait_uid, 'WAITING', $member_uid, $note); } } private function updateWaitlistStatus( int $wait_uid, string $status, ?int $member_uid = null, ?string $note = null ) { $status = strtoupper($status); $allowed = ['COMPLETED', 'WAITING']; if (!in_array($status, $allowed, true)) { throw new Exception('invalid waitlist status'); } $setParts = []; $setParts[] = "iw_status = '{$status}'"; if ($status === 'COMPLETED') { // 완료 시 cancel 정보 초기화 $setParts[] = "iw_cancel_note = NULL"; $setParts[] = "iw_cancel_at = NULL"; $setParts[] = "iw_cancel_by = NULL"; } if ($status === 'WAITING') { // 취소로 되돌릴 때 $noteEsc = mysqli_real_escape_string($GLOBALS['conn'], $note ?? ''); $member = intval($member_uid ?? 0); $setParts[] = "iw_cancel_note = '{$noteEsc}'"; $setParts[] = "iw_cancel_at = NOW()"; $setParts[] = "iw_cancel_by = '{$member}'"; } $sql = " UPDATE tbl_install_waitlist SET " . implode(',', $setParts) . " WHERE iw_uid = '{$wait_uid}' LIMIT 1 "; if (!qry($sql)) { throw new Exception('waitlist status update failed'); } } private function createNextScheduleIfNeeded(int $wait_uid, string $installDate, int $member_uid) { $qr = qry("SELECT * FROM tbl_install_waitlist WHERE iw_uid = '{$wait_uid}' LIMIT 1"); $wait = mysqli_fetch_assoc($qr); if (!$wait) { throw new Exception('waitlist not found'); } if (intval($wait['iw_scheduled']) !== 1) return; $cycleDays = intval($wait['iw_scheduled_cycle']); if ($cycleDays <= 0) return; if (empty($installDate)) { throw new Exception('install date missing'); } $nextDate = date('Y-m-d', strtotime($installDate . " +{$cycleDays} days")); // $sql = " INSERT INTO tbl_install_waitlist ( iw_customer_uid, iw_accountno, iw_customer_name, iw_phone, iw_address, iw_city, iw_postal, iw_lat, iw_lng, iw_requested_due_date, iw_work_type, iw_status, iw_priority, iw_created_by, iw_scheduled, iw_scheduled_cycle, iw_request_by, iw_request_at, iw_request_note, iw_cs_note, iw_work_note, iw_required_person_cnt, iw_multi_person_reason, iw_request_source ) VALUES ( '{$wait['iw_customer_uid']}', '{$wait['iw_accountno']}', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_customer_name'])."', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_phone'])."', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_address'])."', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_city'])."', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_postal'])."', '{$wait['iw_lat']}', '{$wait['iw_lng']}', '{$nextDate}', '".mysqli_real_escape_string($GLOBALS['conn'], $wait['iw_work_type'])."', 'WAITING', '{$wait['iw_priority']}', '{$wait['iw_created_by']}', 1, '{$cycleDays}', '{$wait['iw_request_by']}', '{$installDate}', '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_request_note'])."', '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_cs_note'])."', '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_work_note'])."', '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_required_person_cnt'])."', '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_multi_person_reason'])."', 'AUTO' ) "; if (!qry($sql)) { throw new Exception('auto reschedule insert failed'); } // 새 wait uid $new_wait_uid = mysqli_insert_id($GLOBALS['conn']); if (!$new_wait_uid) { throw new Exception('auto reschedule insert id missing'); } // 컨테이너 복사 $this->copyWaitContainers($wait_uid, (int)$new_wait_uid); } private function copyWaitContainers(int $from_wait_uid, int $to_wait_uid): void { $sql = " INSERT INTO tbl_install_waitlist_container ( iwc_wait_uid, iwc_action, iwc_type, iwc_capacity, iwc_is_used, iwc_has_lock, iwc_has_wheel, iwc_has_woodframe, iwc_target_cc_uid, iwc_owner_type ) SELECT '{$to_wait_uid}' AS iwc_wait_uid, iwc_action, iwc_type, iwc_capacity, iwc_is_used, iwc_has_lock, iwc_has_wheel, iwc_has_woodframe, iwc_target_cc_uid, iwc_owner_type FROM tbl_install_waitlist_container WHERE iwc_wait_uid = '{$from_wait_uid}' "; if (!qry($sql)) { throw new Exception('container copy insert failed'); } } public function upload_after_photo() { $di_uid = intval($_POST['di_uid'] ?? 0); $member_uid = intval($_POST['member_uid'] ?? 0); $note = trim($_POST['note'] ?? ''); $i_type = $_POST['i_type'] ?? 'install'; $fail = function($stage, $msg, $extra = []) { echo $this->json(array_merge([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($GLOBALS['conn']) ], $extra)); exit; }; if(!$di_uid || !$member_uid){ $fail('validate', 'invalid params'); } // daily_install 확인 (customer_uid 가져오기) $qr = qry(" SELECT di_customer_uid FROM tbl_daily_install WHERE di_uid = '{$di_uid}' LIMIT 1 "); $di = mysqli_fetch_assoc($qr); if(!$di){ $fail('select_di', 'daily_install not found'); } if(!isset($_FILES['photo']) || $_FILES['photo']['error'] !== 0){ $fail('file', 'no file'); } $file = $_FILES['photo']; $customer_uid = intval($di['di_customer_uid']); $ok = $this->save_customer_image_single( $customer_uid, $member_uid, $i_type, $di_uid, // i_sourceuid = di_uid $note, $file ); if(!$ok){ $fail('save_image', 'upload failed'); } echo $this->json(['ok'=>1]); exit; } private function save_customer_image_single($i_customeruid, $i_memberuid, $i_type, $i_sourceuid, $i_note, $file){ $conn = $GLOBALS['conn']; $i_customeruid = intval($i_customeruid); $i_memberuid = intval($i_memberuid); $i_sourceuid = intval($i_sourceuid); $i_type = mysqli_real_escape_string($conn, $i_type); $i_noteEsc = mysqli_real_escape_string($conn, trim($i_note)); $i_createddate = date("YmdHis"); // 확장자 체크(최소 보안) $allowed = ['jpg','jpeg','png','webp']; $ext = strtolower(pathinfo($file['name'] ?? '', PATHINFO_EXTENSION)); if(!$ext || !in_array($ext, $allowed)){ return false; } // 작업 단위 1장 유지: (customeruid + type, sourceuid 는 제외) $qry_update = " UPDATE tbl_customer_image SET i_status = 'I' WHERE i_customeruid = '{$i_customeruid}' AND i_type = '{$i_type}' AND i_status = 'A' "; if(qry($qry_update) === false) return false; // 저장 폴더 $upload_folder = getenv("DOCUMENT_ROOT")."/upload/customer_image/".$i_customeruid; if(!is_dir($upload_folder)){ mkdir($upload_folder, 0777, true); } $newFileName = "IMG_".$i_customeruid."_".time().rand(1000,9999)."_0.jpg"; $savePath = $upload_folder . "/" . $newFileName; if (!$this->compressAndResizeImage($file['tmp_name'], $savePath, $ext, 1200, 75)) { return false; } $dbFilePath = "/upload/customer_image/".$i_customeruid."/"; $sqlIns = " INSERT INTO tbl_customer_image ( i_customeruid, i_memberuid, i_createdby, i_createddate, i_type, i_filename, i_filepath, i_status, i_note, i_sourceuid ) VALUES ( '{$i_customeruid}', '{$i_memberuid}', '{$i_memberuid}', '{$i_createddate}', '{$i_type}', '{$newFileName}', '{$dbFilePath}', 'A', '{$i_noteEsc}', '{$i_sourceuid}' ) "; return qry($sqlIns) !== false; } private function apply_container_changes_for_di($di_uid, $note, $customer_uid){ $containers = []; $rc = qry("SELECT * FROM tbl_daily_install_container WHERE dic_di_uid = '{$di_uid}'"); while($row = mysqli_fetch_assoc($rc)){ $containers[] = $row; } foreach($containers as $c){ $action = strtoupper($c['dic_action'] ?? ''); $type = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_type'] ?? ''); if(is_null($c['dic_capacity'])){ $capacityCond = "IS NULL"; $capacityVal = "NULL"; }else{ $cap = intval($c['dic_capacity']); $capacityCond = "= '{$cap}'"; $capacityVal = "'{$cap}'"; } $owner_type = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_owner_type'] ?? 'C'); $is_used = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_is_used'] ?? 'N'); $has_lock = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_lock'] ?? 'N'); $has_wheel = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_wheel'] ?? 'N'); $has_wood = mysqli_real_escape_string($GLOBALS['conn'], $c['dic_has_woodframe'] ?? 'N'); // PICKUP if($action === 'PICKUP'){ qry(" UPDATE tbl_customer_container SET cc_status='I', cc_pickup_note='{$note}', cc_pickup_at=NOW(), cc_pickup_date=NOW(), cc_modified_at=NOW() WHERE cc_uid = ( SELECT cc_uid FROM ( SELECT cc_uid FROM tbl_customer_container WHERE cc_customer_uid='{$customer_uid}' AND cc_status='A' AND cc_type='{$type}' AND (cc_capacity {$capacityCond}) ORDER BY IFNULL(cc_install_at,'1970-01-01'), cc_uid LIMIT 1 ) x ) "); } // INSTALL else if($action === 'INSTALL'){ $nowYmdHis = date('YmdHis'); qry(" INSERT INTO tbl_customer_container ( cc_customer_uid, cc_type, cc_capacity, cc_owner_type, cc_is_used, cc_has_lock, cc_has_wheel, cc_has_woodframe, cc_status, cc_install_note, cc_install_at, cc_install_date, cc_createddate, cc_updateddate ) VALUES ( '{$customer_uid}', '{$type}', {$capacityVal}, '{$owner_type}', '{$is_used}', '{$has_lock}', '{$has_wheel}', '{$has_wood}', 'A', '{$note}', NOW(), NOW(), NOW(), NOW() ) "); } } } private function updateCustomerMainContainer($customer_uid) { $customer_uid = intval($customer_uid); // ========================================= // 0) 수동 관리 // ========================================= $qr = qry(" SELECT c_manualvolume, c_fullcycleflag, c_fullcycleforced, c_mainvolume FROM tbl_customer WHERE c_uid = '{$customer_uid}' LIMIT 1 "); $cus = mysqli_fetch_assoc($qr); if (!$cus) { throw new Exception('customer not found'); } if ($cus['c_manualvolume'] === 'Y') { // 아무것도 하지 않음 return; } // 기존 mainvolume 저장 $oldVolume = intval($cus['c_mainvolume'] ?? 0); // ========================================= // 1) active container 조회 // ========================================= $rc = qry(" SELECT cc_type, cc_capacity, cc_owner_type FROM tbl_customer_container WHERE cc_customer_uid = '{$customer_uid}' AND cc_status = 'A' "); $containers = []; while ($row = mysqli_fetch_assoc($rc)) { $containers[] = $row; } // ========================================= // 2) 컨테이너 없으면 초기화 // ========================================= if (!count($containers)) { qry(" UPDATE tbl_customer SET c_mainvolume = 0, c_maincontainer = '' WHERE c_uid = '{$customer_uid}' LIMIT 1 "); return; } // ========================================= // 3) 타입 분류 (D / B) // ========================================= $d_list = []; // D 계열 $b_list = []; // B 계열 foreach ($containers as $c) { $type = strtoupper(trim($c['cc_type'] ?? '')); $capacity = intval($c['cc_capacity'] ?? 0); // null / '' → 0 처리 // B 타입 판별 400B, 600B, CB 등 if (preg_match('/^\d*B$/', $type) || $type === 'CB') { $b_list[] = [ 'type' => $type, 'capacity' => $capacity ]; } // D 타입 판별 else if ($type === 'D') { $d_list[] = [ 'type' => $type, 'capacity' => 200 // D는 고정 200 ]; } // BUCKET / TANK 등은 무시 } // ========================================= // 4) B가 하나라도 있으면 → B 기준 처리 // ========================================= if (count($b_list) > 0) { $maxCapacity = 0; $maxType = ''; foreach ($b_list as $b) { if ($b['capacity'] > $maxCapacity) { $maxCapacity = $b['capacity']; $maxType = $b['type']; } } // capacity가 전부 0일 수도 있음 (예외 처리) if ($maxCapacity <= 0) { $maxType = $b_list[0]['type']; // fallback } // forced cycle $newVolume = $maxCapacity; $this->adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume); // main volume qry(" UPDATE tbl_customer SET c_mainvolume = '{$maxCapacity}', c_maincontainer = '{$maxType}' WHERE c_uid = '{$customer_uid}' LIMIT 1 "); return; } // ========================================= // 5) B 없고 D만 있는 경우 // ========================================= if (count($d_list) > 0) { $count = count($d_list); if ($count === 1) { $volume = 200; } else { $volume = $count * 200; } // forced cycle $newVolume = $volume; $this->adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume); // main volume qry(" UPDATE tbl_customer SET c_mainvolume = '{$volume}', c_maincontainer = 'D' WHERE c_uid = '{$customer_uid}' LIMIT 1 "); return; } // ========================================= // 6) fallback (이상 케이스) // ========================================= qry(" UPDATE tbl_customer SET c_mainvolume = 0, c_maincontainer = '' WHERE c_uid = '{$customer_uid}' LIMIT 1 "); } private function adjustFullCycle($customer_uid, $cus, $oldVolume, $newVolume) { // 조건 체크 if ($cus['c_manualvolume'] !== 'N') return; if (intval($cus['c_fullcycleflag']) !== 1) return; $forced = floatval($cus['c_fullcycleforced'] ?? 0); // 기존 값이 없으면 의미 없음 if ($forced <= 0) return; // oldVolume 0이면 비율 계산 불가 → skip if ($oldVolume <= 0) return; // newVolume 0이면 forced도 0으로 if ($newVolume <= 0) { $newForced = 0; } else { $ratio = $newVolume / $oldVolume; $newForced = $forced * $ratio; } qry(" UPDATE tbl_customer SET c_fullcycleforced = '{$newForced}' WHERE c_uid = '{$customer_uid}' LIMIT 1 "); } private function reorder_daily_install_sequence($driver_uid, $install_date) { $driver_uid = intval($driver_uid); $install_date = mysqli_real_escape_string($GLOBALS['conn'], $install_date); $seq = 1; // ================================ // 완료된 것들 먼저 (P/M/X) // ================================ $rsDone = qry(" SELECT di_uid FROM tbl_daily_install WHERE di_driver_uid = '{$driver_uid}' AND di_install_date = '{$install_date}' AND di_status <> 'A' ORDER BY di_order_seq ASC "); while($row = mysqli_fetch_assoc($rsDone)){ $uid = intval($row['di_uid']); qry(" UPDATE tbl_daily_install SET di_order_seq = '{$seq}' WHERE di_uid = '{$uid}' "); $seq++; } // ================================ // 그 다음 대기 상태 업데이트 // ================================ $rsActive = qry(" SELECT di_uid FROM tbl_daily_install WHERE di_driver_uid = '{$driver_uid}' AND di_install_date = '{$install_date}' AND di_status = 'A' ORDER BY di_order_seq ASC "); while($row = mysqli_fetch_assoc($rsActive)){ $uid = intval($row['di_uid']); qry(" UPDATE tbl_daily_install SET di_order_seq = '{$seq}' WHERE di_uid = '{$uid}' "); $seq++; } } public function daily_install_reorder() { $driver_uid = intval($_POST['driver_uid'] ?? 0); $orderJson = $_POST['order_list'] ?? ''; $fail = function($stage, $msg, $extra = []) { echo $this->json(array_merge([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($GLOBALS['conn']) ], $extra)); exit; }; if(!$driver_uid || !$orderJson){ $fail('validate', 'invalid params'); } $orderList = json_decode($orderJson, true); if(!is_array($orderList) || !count($orderList)){ $fail('decode', 'invalid order list'); } // driver의 install_date는 하나라고 가정 (화면 기준) $qr = qry(" SELECT di_install_date FROM tbl_daily_install WHERE di_driver_uid = '{$driver_uid}' LIMIT 1 "); $row = mysqli_fetch_assoc($qr); if(!$row){ $fail('select_date', 'no install data'); } $install_date = $row['di_install_date']; qry("START TRANSACTION"); foreach($orderList as $item){ $di_uid = intval($item['di_uid'] ?? 0); $seq = intval($item['di_order_seq'] ?? 0); if(!$di_uid || !$seq){ qry("ROLLBACK"); $fail('validate_row', 'invalid row'); } $sql = " UPDATE tbl_daily_install SET di_order_seq = '{$seq}' WHERE di_uid = '{$di_uid}' AND di_driver_uid = '{$driver_uid}' AND di_status = 'A' LIMIT 1 "; if(!qry($sql)){ qry("ROLLBACK"); $fail('update', 'update failed', ['di_uid'=>$di_uid]); } } // 마지막에 안전하게 정렬 보정 $this->reorder_daily_install_sequence($driver_uid, $install_date); qry("COMMIT"); echo $this->json(['ok'=>1]); exit; } public function bulk_update_daily_install() { $di_uids = $_POST['di_uids'] ?? []; $new_driver_uid = intval($_POST['di_driver_uid'] ?? 0); $new_install_date = trim($_POST['di_install_date'] ?? ''); if (!is_array($di_uids) || !count($di_uids)) { echo $this->json(['ok'=>0, 'msg'=>'invalid di_uids']); return; } $di_uids = array_map('intval', $di_uids); $di_uids = array_filter($di_uids); if (!count($di_uids)) { echo $this->json(['ok'=>0, 'msg'=>'empty di_uids']); return; } if (!$new_driver_uid || !$new_install_date) { echo $this->json(['ok'=>0, 'msg'=>'invalid target params']); return; } $in = implode(',', $di_uids); $conn = $GLOBALS['conn']; $fail = function($stage, $msg, $extra = []) use ($conn) { echo $this->json(array_merge([ 'ok' => 0, 'stage' => $stage, 'msg' => $msg, 'db' => mysqli_error($conn) ], $extra)); exit; }; // 1) 대상 조회 $qr = qry(" SELECT di_uid, di_driver_uid, di_install_date FROM tbl_daily_install WHERE di_uid IN ($in) "); $rows = []; while ($r = fetch_array($qr)) { $rows[] = $r; } if (!count($rows)) { $fail('select', 'no rows found'); } // 2) 기존 그룹 수집 $oldGroups = []; foreach ($rows as $r) { $key = intval($r['di_driver_uid']) . '|' . $r['di_install_date']; $oldGroups[$key] = [ 'driver_uid' => intval($r['di_driver_uid']), 'date' => $r['di_install_date'] ]; } qry("START TRANSACTION"); // 3) 대상 업데이트 $new_driver_uid_esc = intval($new_driver_uid); $new_install_date_esc = mysqli_real_escape_string($conn, $new_install_date); $sql = " UPDATE tbl_daily_install SET di_driver_uid = '{$new_driver_uid_esc}', di_install_date = '{$new_install_date_esc}' WHERE di_uid IN ($in) "; if (!qry($sql)) { qry("ROLLBACK"); $fail('update', 'bulk update failed'); } // 4) 새 그룹 resequence if (!$this->resequence_daily_install($new_driver_uid_esc, $new_install_date_esc)) { qry("ROLLBACK"); $fail('resequence_new', 'failed', [ 'di_driver_uid' => $new_driver_uid_esc, 'di_install_date' => $new_install_date_esc ]); } // 5) 이전 그룹 resequence foreach ($oldGroups as $g) { $sameGroup = intval($g['driver_uid']) === $new_driver_uid_esc && $g['date'] === $new_install_date_esc; if ($sameGroup) continue; if (!$this->resequence_daily_install($g['driver_uid'], $g['date'])) { qry("ROLLBACK"); $fail('resequence_old', 'failed', $g); } } qry("COMMIT"); echo $this->json([ 'ok' => 1, 'cnt' => count($rows) ]); } function compressAndResizeImage($srcPath, $destPath, $ext, $maxWidth = 1200, $quality = 75) { list($width, $height, $imageType) = getimagesize($srcPath); if (!$width || !$height) return false; switch ($imageType) { case IMAGETYPE_JPEG: $srcImg = imagecreatefromjpeg($srcPath); if (function_exists('exif_read_data')) { $exif = @exif_read_data($srcPath); if (!empty($exif['Orientation'])) { switch ($exif['Orientation']) { case 3: $srcImg = imagerotate($srcImg, 180, 0); break; case 6: $srcImg = imagerotate($srcImg, -90, 0); break; case 8: $srcImg = imagerotate($srcImg, 90, 0); break; } } $width = imagesx($srcImg); $height = imagesy($srcImg); } break; case IMAGETYPE_PNG: $srcImg = imagecreatefrompng($srcPath); break; case IMAGETYPE_WEBP: $srcImg = imagecreatefromwebp($srcPath); break; default: return false; } if (!$srcImg) return false; if ($width > $maxWidth) { $newWidth = $maxWidth; $newHeight = intval(($height / $width) * $newWidth); } else { $newWidth = $width; $newHeight = $height; } $dstImg = imagecreatetruecolor($newWidth, $newHeight); imagecopyresampled( $dstImg, $srcImg, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height ); $result = imagejpeg($dstImg, $destPath, $quality); imagedestroy($srcImg); imagedestroy($dstImg); return $result; } }