goiintra/public_html/assets/api/install.php

2624 lines
81 KiB
PHP

<?php
trait InstallAPI {
protected $dailyService;
public function __construct() {
$this->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}'
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')
)
";
qry($sql);
if (mysqli_affected_rows($GLOBALS['conn']) == 0) {
echo $this->json([
'ok' => 0,
'msg' => '이미 waitlist에 존재합니다.'
]);
return;
}
$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)) {
$photos[] = [
'i_uid' => (int)$img['i_uid'],
'i_filepath' => $img['i_filepath'],
'i_filename' => $img['i_filename']
];
}
$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');
}
}
}
// assigned check
$rs = qry("
SELECT di_uid
FROM tbl_daily_install
WHERE di_wait_uid = '{$iw_uid}'
LIMIT 1
");
$di = mysqli_fetch_assoc($rs);
// assigned sync
if ($di && $di['di_uid']) {
$this->sync_daily_install_containers(
$iw_uid,
$di['di_uid'],
$requestAction
);
}
qry("COMMIT");
} catch (Exception $e) {
qry("ROLLBACK");
echo $this->json([
'ok' => 0,
'msg' => $e->getMessage()
]);
return;
}
echo $this->json(['ok' => 1]);
}
private function sync_daily_install_containers($iw_uid, $di_uid, $action)
{
$iw_uid = intval($iw_uid);
$di_uid = intval($di_uid);
$action = addslashes(trim($action));
if (!$iw_uid) {
throw new Exception('invalid iw_uid');
}
if (!$di_uid) {
throw new Exception('invalid di_uid');
}
if (!$action) {
throw new Exception('invalid action');
}
// 기존 daily container 삭제
$sqlDelete = "
DELETE FROM tbl_daily_install_container
WHERE
dic_di_uid = '{$di_uid}'
AND dic_action = '{$action}'
";
if (!qry($sqlDelete)) {
throw new Exception('daily container delete failed');
}
// waitlist container 조회
$rs = qry("
SELECT *
FROM tbl_install_waitlist_container
WHERE
iwc_wait_uid = '{$iw_uid}'
AND iwc_action = '{$action}'
ORDER BY iwc_uid ASC
");
if (!$rs) {
throw new Exception('wait container select failed');
}
// 재 insert
while ($r = mysqli_fetch_assoc($rs)) {
$type = addslashes($r['iwc_type'] ?? '');
$capacity = isset($r['iwc_capacity'])
&& $r['iwc_capacity'] !== null
? intval($r['iwc_capacity'])
: 'NULL';
$is_used = addslashes($r['iwc_is_used'] ?? 'N');
$owner_type = addslashes($r['iwc_owner_type'] ?? 'C');
$has_lock = addslashes($r['iwc_has_lock'] ?? 'N');
$has_wheel = addslashes($r['iwc_has_wheel'] ?? 'N');
$has_frame = addslashes($r['iwc_has_woodframe'] ?? 'N');
$sqlInsert = "
INSERT INTO tbl_daily_install_container
SET
dic_di_uid = '{$di_uid}',
dic_action = '{$action}',
dic_type = '{$type}',
dic_capacity = {$capacity},
dic_is_used = '{$is_used}',
dic_owner_type = '{$owner_type}',
dic_has_lock = '{$has_lock}',
dic_has_wheel = '{$has_wheel}',
dic_has_woodframe = '{$has_frame}'
";
if (!qry($sqlInsert)) {
throw new Exception('daily container insert failed');
}
}
return true;
}
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;
$person_reason = $_POST['person_reason'] ?? null;
$category = intval($_POST['category'] ?? 0);
$scheduled = intval($_POST['scheduled'] ?? 0);
$cycle = intval($_POST['scheduled_cycle'] ?? 0);
$lock_date = intval($_POST['lock_date'] ?? 0);
$need_oil = intval($_POST['need_oil'] ?? 0);
// Two-person OFF = NULL
if (!$person_cnt) {
$person_cnt = null;
$person_reason = null;
}
// Recurring OFF
if (!$scheduled) {
$cycle = 0;
}
qry("START TRANSACTION");
try {
$sql = "
UPDATE tbl_install_waitlist
SET
iw_request_at = ".($request_at ? "'{$request_at}'" : "NOW()").",
iw_requested_due_date = ".($due_date ? "'{$due_date}'" : "NULL").",
iw_lock_date = '{$lock_date}',
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_need_oil_pickup = '{$need_oil}',
iw_category = '{$category}',
iw_status = CASE
WHEN iw_status IN ('DRAFT')
THEN 'WAITING'
ELSE iw_status
END
WHERE iw_uid = '{$iw_uid}'
LIMIT 1
";
if (!qry($sql)) {
throw new Exception('waitlist update failed');
}
// assigned check
$rs = qry("
SELECT di_uid
FROM tbl_daily_install
WHERE di_wait_uid = '{$iw_uid}'
LIMIT 1
");
if (!$rs) {
throw new Exception('daily install select failed');
}
$di = mysqli_fetch_assoc($rs);
// assigned sync
if ($di && $di['di_uid']) {
$di_uid = intval($di['di_uid']);
$sql2 = "
UPDATE tbl_daily_install
SET
di_request_note = ".($request_note ? "'".addslashes($request_note)."'" : "NULL").",
di_cs_note = ".($cs_note ? "'".addslashes($cs_note)."'" : "NULL").",
di_work_note = ".($work_note ? "'".addslashes($work_note)."'" : "NULL").",
di_lock_date = '{$lock_date}',
di_oil_pickup = '{$need_oil}'
WHERE di_uid = '{$di_uid}'
LIMIT 1
";
if (!qry($sql2)) {
throw new Exception('daily install update 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_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_lock_date,
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,
di.di_request_note,
di.di_cs_note,
di.di_work_note,
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_lock_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_request_note,
di_work_note,
di_cs_note,
di_created_by
)
SELECT
'{$di_order_seq}',
'{$di_driver_uid}',
'{$di_install_date}',
iw.iw_lock_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_request_note,
iw.iw_work_note,
iw.iw_cs_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);
});
$has_existing = is_array($existing_rows) && count($existing_rows) > 0;
$has_trial = is_array($trial_rows) && count($trial_rows) > 0;
$base_seq = 0;
if ($has_existing) {
$seq_values = array_map(function($r){
return intval($r['di_order_seq'] ?? 0);
}, $existing_rows);
$min_seq = min(array_filter($seq_values));
$base_seq = $min_seq - 1;
} else if ($has_trial) {
$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);
}
$seq = 1;
foreach($all as &$r){
$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);
}
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'] !== 'P' && $newStatus === 'P') {
if ($di['di_container_applied'] == 0) {
$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']), $di['di_install_date']);
// 실행 완료 기록
$this->markContainerApplied($di_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) {
// 기존 note 삭제
$di_uid = intval($di['di_uid'] ?? 0);
if ($di_uid > 0) {
$noteDailyUid = 'i_' . $di_uid;
$n_dailyuid = mysqli_real_escape_string($connect, $noteDailyUid);
mysqli_query($connect, "
DELETE FROM tbl_note
WHERE n_dailyuid = '{$n_dailyuid}'
");
}
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 markContainerApplied(
int $di_uid
)
{
$conn = $GLOBALS['conn'];
$sql = "
UPDATE tbl_daily_install
SET
di_container_applied = 1
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, 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_category,
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_category']}',
'{$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['photos'])){
$fail('file', 'no files');
}
$customer_uid = intval($di['di_customer_uid']);
$ok = $this->save_customer_image_multi(
$customer_uid,
$member_uid,
$i_type,
$di_uid,
$note,
$_FILES['photos']
);
if(!$ok){
$fail('save_image', 'upload failed');
}
echo $this->json(['ok'=>1]);
exit;
}
private function save_customer_image_multi($i_customeruid, $i_memberuid, $i_type, $i_sourceuid, $i_note, $files){
$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'];
// 기존 row 비활성화
$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);
}
$savedFiles = [];
foreach ($files['name'] as $i => $name) {
if ($files['error'][$i] !== 0) continue;
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if(!$ext || !in_array($ext, $allowed)) continue;
$tmp = $files['tmp_name'][$i];
$newFileName = "IMG_".$i_customeruid."_".time().rand(1000,9999)."_".$i.".jpg";
$savePath = $upload_folder . "/" . $newFileName;
if (!$this->compressAndResizeImage($tmp, $savePath, $ext, 1200, 75)) {
continue;
}
$savedFiles[] = $newFileName;
}
if(empty($savedFiles)) return false;
$fileNameString = implode(',', $savedFiles);
$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}',
'{$fileNameString}',
'{$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, $install_date)
{
$customer_uid = intval($customer_uid);
$install_yyyymmdd = null;
if (!empty($install_date)) {
$install_yyyymmdd = str_replace('-', '', $install_date);
}
$qr = qry("
SELECT
c_manualvolume,
c_fullcycleflag,
c_fullcycleforced,
c_mainvolume,
c_installdate,
c_exchangedate,
c_removaldate
FROM tbl_customer
WHERE c_uid = '{$customer_uid}'
LIMIT 1
");
$cus = mysqli_fetch_assoc($qr);
if (!$cus) {
throw new Exception('customer not found');
}
if ($install_yyyymmdd) {
// first install
$current_installdate = $cus['c_installdate'];
if (!$current_installdate || $current_installdate === 'N/A' || $install_yyyymmdd < $current_installdate) {
qry("
UPDATE tbl_customer
SET c_installdate='{$install_yyyymmdd}'
WHERE c_uid='{$customer_uid}'
");
}
// exchange date (최근 install 작업)
$current_exchangedate = $cus['c_exchangedate'];
if ($current_exchangedate < $install_yyyymmdd) {
qry("
UPDATE tbl_customer
SET c_exchangedate='{$install_yyyymmdd}'
WHERE c_uid='{$customer_uid}'
");
}
// removal date
$q_activeCount = qry("
SELECT COUNT(*) AS active_cnt
FROM tbl_customer_container
WHERE cc_customer_uid = '{$customer_uid}'
AND cc_status = 'A'
");
$row_activeCount = mysqli_fetch_assoc($q_activeCount);
$activeCnt = (int)($row_activeCount['active_cnt'] ?? 0);
if ($activeCnt === 0) {
qry("
UPDATE tbl_customer
SET c_removaldate = '{$install_yyyymmdd}'
WHERE c_uid = '{$customer_uid}'
");
} else {
// 치웠다가 다시 설치
qry("
UPDATE tbl_customer
SET c_removaldate = ''
WHERE c_uid = '{$customer_uid}'
AND c_removaldate <> ''
");
}
}
// =========================================
// 1) 수동 관리
// =========================================
if ($cus['c_manualvolume'] === 'Y') {
// 아무것도 하지 않음
return;
}
// 기존 mainvolume 저장
$oldVolume = intval($cus['c_mainvolume'] ?? 0);
// =========================================
// 2) 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;
}
// =========================================
// 3) 컨테이너 없으면 초기화
// =========================================
if (!count($containers)) {
qry("
UPDATE tbl_customer
SET
c_mainvolume = 0,
c_maincontainer = ''
WHERE c_uid = '{$customer_uid}'
LIMIT 1
");
return;
}
// =========================================
// 4) 타입 분류 (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 등은 무시
}
// =========================================
// 5) 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;
}
// =========================================
// 6) 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;
}
// =========================================
// 7) 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);
$install_date = trim($_POST['install_date'] ?? '');
$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');
}
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;
}
}