diff --git a/public_html/assets/api/install.php b/public_html/assets/api/install.php new file mode 100644 index 0000000..a1f7697 --- /dev/null +++ b/public_html/assets/api/install.php @@ -0,0 +1,1741 @@ +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 <> 'DELETED' + ) + 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'] ?? ''; + $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, + '{$driver_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_request_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'] ?? []; + $action = $_POST['action']; + + if (!$iw_uid) { + echo $this->json(['ok' => 0, 'msg' => 'no iw_uid']); + return; + } + + // 기존 컨테이너 전부 삭제 + qry(" + DELETE FROM tbl_install_waitlist_container + WHERE iwc_wait_uid = '{$iw_uid}' AND iwc_action = '{$action}' + "); + + // 재 insert + if (is_array($items)) { + foreach ($items as $it) { + + $action = addslashes($it['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 = '{$action}', + 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}' + "; + qry($sql); + } + } + + 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 = 'REMOVE' + AND dic.dic_type = 'D' + THEN 1 ELSE 0 + END + ) AS remove_d_cnt, + SUM( + CASE + WHEN dic.dic_action = 'REMOVE' + AND dic.dic_type <> 'D' + THEN 1 ELSE 0 + END + ) AS remove_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'); + } + + qry("START TRANSACTION"); + + /* ======================================================= + A) 기존 daily_install 순서 업데이트 + ======================================================= */ + if (is_array($existing_rows) && count($existing_rows)) { + foreach ($existing_rows as $idx => $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]); + } + + // driver/date까지 같이 조건으로 걸면 더 안전함 (다른 날짜/driver 수정 방지) + $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) -> daily_install INSERT + status update + ======================================================= */ + if (is_array($trial_rows) && count($trial_rows)) { + foreach ($trial_rows as $idx => $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_uid = intval($_POST['di_uid'] ?? 0); + if (!$di_uid) { + echo $this->json(['ok'=>0,'msg'=>'invalid di_uid']); + return; + } + + $fail = function($stage, $msg, $extra = []) { + echo $this->json(array_merge([ + 'ok' => 0, + 'stage' => $stage, + 'msg' => $msg, + 'db' => mysqli_error($GLOBALS['conn']) + ], $extra)); + exit; + }; + + $qr = qry("SELECT di_wait_uid, di_driver_uid, di_install_date FROM tbl_daily_install WHERE di_uid = '{$di_uid}'"); + $row = fetch_array($qr); + + if (empty($row['di_wait_uid'])) { + $fail('select', 'daily_install not found', ['di_uid'=>$di_uid]); + } + + $iw_uid = intval($row['di_wait_uid']); + $di_driver_uid = intval($row['di_driver_uid']); + $di_install_date = $row['di_install_date']; // ✅ 문자열 유지 + + qry("START TRANSACTION"); + + // container 삭제는 하지말자. + // if (qry("DELETE FROM tbl_daily_install_container WHERE dic_di_uid = '{$di_uid}'") === false) { + // qry("ROLLBACK"); + // $fail('delete_container', 'failed', ['di_uid'=>$di_uid]); + // } + + // daily_install 삭제 + if (!qry("DELETE FROM tbl_daily_install WHERE di_uid = '{$di_uid}' LIMIT 1")) { + qry("ROLLBACK"); + $fail('delete_daily_install', 'failed', ['di_uid'=>$di_uid]); + } + + // waitlist WAITING 복귀 + if (!$this->update_waitlist_status($iw_uid, 'WAITING')) { + qry("ROLLBACK"); + $fail('update_waitlist', 'failed', ['iw_uid'=>$iw_uid]); + } + + // seq 재정렬 (같은 driver/date) + if (!$this->resequence_daily_install($di_driver_uid, $di_install_date)) { + qry("ROLLBACK"); + $fail('resequence', 'failed', ['di_driver_uid'=>$di_driver_uid,'di_install_date'=>$di_install_date]); + } + + qry("COMMIT"); + echo $this->json(['ok'=>1]); + } + + 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'] ?? ''); + $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; + $remove_date = $_POST['remove_date'] ?? null; + $install_note = addslashes($_POST['install_note'] ?? ''); + $remove_note = addslashes($_POST['remove_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_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_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"; + $remove_at_sql = $remove_date ? "'{$remove_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_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_remove_at={$remove_at_sql}, + cc_remove_note='{$remove_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==='REMOVE'){ + $sql=" + UPDATE tbl_customer_container SET + cc_status='I', + cc_remove_date='{$remove_date}', + cc_remove_note='{$remove_note}', + cc_remove_at=NOW(), + cc_updateddate=DATE_FORMAT(NOW(),'%Y%m%d%H%i%s') + WHERE cc_uid='{$cc_uid}' + "; + qry($sql); + } + + // + 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}' + "); + } + } + + if ($remove_date) { + + $remove_yyyymmdd = str_replace('-', '', $remove_date); + + $r = qry("SELECT c_removaldate FROM tbl_customer WHERE c_uid='{$c_uid}'"); + $row = mysqli_fetch_assoc($r); + $current = $row['c_removaldate']; + + if (!$current || $current === 'N/A' || $remove_yyyymmdd > $current) { + + qry(" + UPDATE tbl_customer + SET c_removaldate='{$remove_yyyymmdd}' + WHERE c_uid='{$c_uid}' + "); + } + } + + + 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'] ?? '')); + $note = trim($_POST['install_note'] ?? ''); + $oil_qty = intval($_POST['oil_qty'] ?? 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, $note, $oil_qty); + + // 3) 컨테이너 반영 + if ($di['di_status'] === 'A' && $newStatus === 'P') { + $this->apply_container_changes_for_di($di_uid, intval($di['di_customer_uid'])); + } + + // 4) oil pickup → daily + $this->saveDailyFromInstallIfNeeded($di_uid, $oil_qty); + + // 5) waitlist 처리 + $this->handleWaitlistAfterInstall($di, $newStatus, $member_uid, $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 $note, + int $oil_qty + ) + { + $conn = $GLOBALS['conn']; + + $newStatus = mysqli_real_escape_string($conn, $newStatus); + $noteEsc = mysqli_real_escape_string($conn, $note); + $oil_qty = intval($oil_qty); + + $sql = " + UPDATE tbl_daily_install + SET + di_status = '{$newStatus}', + di_install_note = '{$noteEsc}', + di_oil_pickup = '{$oil_qty}', + 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'); + } + } + + private function saveDailyFromInstallIfNeeded(int $di_uid, int $oil_qty): void + { + // 0이면 daily에 반영할 필요 없음 + if ($oil_qty <= 0) return; + + // payload 만들기 + $payload = $this->buildDailyPayloadFromInstall($di_uid, $oil_qty); + + // ************************************************* payload 에 아래 포함시켜라. + // $payload['d_jobtype'] = $payload['d_jobtype'] ?? 'INSTALL'; + + $this->saveDaily($payload, ['in_tx' => 1]); + } + + 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_requested_due_date, + iw_work_type, + iw_status, + iw_priority, + iw_created_by, + iw_scheduled, + iw_scheduled_cycle, + iw_request_by, + iw_request_note, + iw_cs_note, + 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'])."', + '{$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']}', + '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_request_note'])."', + '".mysqli_real_escape_string($GLOBALS['conn'], (string)$wait['iw_cs_note'])."', + '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'); + } + } + + private function buildDailyPayloadFromInstall(int $di_uid, int $oil_qty): array + { + $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'); + + $orderdate = date('Ymd', strtotime($di['di_install_date'])); + $now14 = date('YmdHis'); + + $qrCus = qry("SELECT * 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'); + + $days = 0; + if (!empty($customer['c_lastpickupdate'])) { + $last = strtotime(substr($customer['c_lastpickupdate'],0,8)); + $today = strtotime($orderdate); + $days = max(0, floor(($today-$last)/86400)); + } + + $estquantity = max( + 0, + ($customer['c_fullquantity'] - $customer['c_fullquantitydaily']) + + ($days * $customer['c_fullquantitydaily']) + ); + + return [ + 'd_orderdate' => $orderdate, + 'd_accountno' => $di['di_accountno'], + 'd_customeruid' => $di['di_customer_uid'], + // 'd_driveruid' => $di['di_driver_uid'], + 'd_driveruid' => 43, // 현재 E.X 로 고정, hard coding + 'd_name' => $di['di_customer_name'], + 'd_paymenttype' => $customer['c_paymenttype'], + 'd_cycle' => $customer['c_paymentcycle'], + 'd_rate' => $customer['c_rate'], + 'd_status' => 'F', + 'd_visit' => 'Y', + 'd_visitdate' => $now14, + 'd_quantity' => $oil_qty, + 'd_estquantity' => $estquantity, + 'd_inputdate' => $now14, + 'd_createddate' => $now14, + 'd_createruid' => $_SESSION['ss_UID'] ?? '' + ]; + } + + + + + + + public function upload_after_photo() { + + $di_uid = intval($_POST['di_uid'] ?? 0); + $member_uid = intval($_POST['member_uid'] ?? 0); + $note = trim($_POST['note'] ?? ''); + + $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['after_photo']) || $_FILES['after_photo']['error'] !== 0){ + $fail('file', 'no file'); + } + + $customer_uid = intval($di['di_customer_uid']); + $file = $_FILES['after_photo']; + + $ok = $this->save_customer_image_single( + $customer_uid, + $member_uid, + 'install', + $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_sourceuid = '{$i_sourceuid}' + 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.".$ext; + $savePath = $upload_folder . "/" . $newFileName; + + if(!move_uploaded_file($file['tmp_name'], $savePath)){ + 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, $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'); + + // REMOVE + if($action === 'REMOVE'){ + + $removeNote = mysqli_real_escape_string( + $GLOBALS['conn'], + "Removed via DI#{$di_uid}" + ); + + qry(" + UPDATE tbl_customer_container + SET cc_status='I', + cc_remove_note='{$removeNote}', + cc_remove_at=NOW(), + cc_remove_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, + cc_modified_at + ) VALUES ( + '{$customer_uid}', + '{$type}', {$capacityVal}, + '{$owner_type}', + '{$is_used}', '{$has_lock}', '{$has_wheel}', '{$has_wood}', + 'A', + 'Installed via DI#{$di_uid}', + NOW(), NOW(), + '{$nowYmdHis}', '{$nowYmdHis}', + NOW() + ) + "); + } + } + } + + 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; + } +}