1229 lines
39 KiB
PHP
1229 lines
39 KiB
PHP
<?php
|
|
|
|
require_once getenv("DOCUMENT_ROOT")."/assets/conf.inc.php";
|
|
require_once getenv("DOCUMENT_ROOT")."/assets/dbCon.php";
|
|
|
|
class DailyService extends CONF {
|
|
|
|
public function saveDaily(array $data = [], array $opt = [])
|
|
{
|
|
$connect = $GLOBALS['conn'] ?? null;
|
|
if (!$connect) {
|
|
throw new Exception("DB connection not found");
|
|
}
|
|
|
|
$inTx = !empty($opt['in_tx']);
|
|
|
|
if (empty($data['d_orderdate']) || empty($data['d_accountno'])) {
|
|
throw new Exception("d_orderdate, d_accountno required");
|
|
}
|
|
|
|
// =========================
|
|
// KEY NORMALIZE
|
|
// =========================
|
|
$d_orderdate = preg_replace('/[^0-9]/', '', $data['d_orderdate']);
|
|
if (strlen($d_orderdate) !== 8) {
|
|
throw new Exception("invalid d_orderdate");
|
|
}
|
|
|
|
$d_accountno = mysqli_real_escape_string($connect, trim($data['d_accountno']));
|
|
if ($d_accountno === '') {
|
|
throw new Exception("invalid d_accountno");
|
|
}
|
|
|
|
$d_jobtype = mysqli_real_escape_string($connect, trim($data['d_jobtype']));
|
|
if ($d_jobtype === '') {
|
|
$d_jobtype = 'UCO';
|
|
}
|
|
|
|
$now14 = date('YmdHis');
|
|
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "START TRANSACTION");
|
|
}
|
|
|
|
try {
|
|
|
|
// =========================
|
|
// EXISTING LOAD (개선됨)
|
|
// =========================
|
|
|
|
$existing = null;
|
|
|
|
// 1. d_uid 우선 (가장 안전)
|
|
if (!empty($opt['match_uid'])) {
|
|
$uid = (int)$opt['match_uid'];
|
|
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_uid = '{$uid}'
|
|
LIMIT 1
|
|
");
|
|
if (!$res) throw new Exception(mysqli_error($connect));
|
|
|
|
$existing = mysqli_fetch_assoc($res);
|
|
}
|
|
|
|
// 2. old_key fallback (key 변경 대응)
|
|
if (!$existing && !empty($opt['old_key'])) {
|
|
|
|
$oldDate = preg_replace('/[^0-9]/', '', $opt['old_key']['d_orderdate'] ?? '');
|
|
$oldAcc = mysqli_real_escape_string($connect, $opt['old_key']['d_accountno'] ?? '');
|
|
$oldJob = mysqli_real_escape_string($connect, $opt['old_key']['d_jobtype'] ?? '');
|
|
|
|
if ($oldDate && $oldAcc) {
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_orderdate = '{$oldDate}'
|
|
AND d_accountno = '{$oldAcc}'
|
|
AND d_jobtype = '{$oldJob}'
|
|
LIMIT 1
|
|
");
|
|
if (!$res) throw new Exception(mysqli_error($connect));
|
|
|
|
$existing = mysqli_fetch_assoc($res);
|
|
}
|
|
}
|
|
|
|
// 3. 기본 key 매칭
|
|
if (!$existing) {
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_orderdate = '{$d_orderdate}'
|
|
AND d_accountno = '{$d_accountno}'
|
|
AND d_jobtype = '{$d_jobtype}'
|
|
LIMIT 1
|
|
");
|
|
if (!$res) throw new Exception(mysqli_error($connect));
|
|
|
|
$existing = mysqli_fetch_assoc($res);
|
|
}
|
|
|
|
$isUpdate = (bool)$existing;
|
|
$beforeData = $existing ?: null;
|
|
|
|
// =========================
|
|
// DUPLICATE POLICY (중요)
|
|
// =========================
|
|
if ($isUpdate && empty($opt['allow_update'])) {
|
|
if (!$inTx) mysqli_query($connect, "ROLLBACK");
|
|
return null; // skip
|
|
}
|
|
|
|
// =========================
|
|
// normalize + snapshot
|
|
// =========================
|
|
$data = $this->normalizeDailyPayload($data, $existing ?: null);
|
|
$data = $this->hydrateDailySnapshot($data, $existing ?: null);
|
|
|
|
// estquantity 자동 계산 (입력때만 안그러면 d_estquantity 값이 0으로 덮어 씌워짐)
|
|
if (!$existing && empty($data['d_estquantity'])) {
|
|
$data['d_estquantity'] = $this->calculateEstQuantity($data, $existing);
|
|
}
|
|
|
|
// note 백업 (tbl_note 로 전달할 값)
|
|
$noteForUpsert = $data['d_note'] ?? '';
|
|
$pickupNoteForUpsert = $data['d_pickupnote'] ?? '';
|
|
$level = $data['d_level'] ?? 9;
|
|
|
|
// =========================
|
|
// 컬럼 필터링
|
|
// =========================
|
|
$allowed = $this->getDailyColumns();
|
|
$data = array_intersect_key($data, array_flip($allowed));
|
|
|
|
// =========================
|
|
// UPDATE
|
|
// =========================
|
|
if ($isUpdate) {
|
|
|
|
$setParts = [];
|
|
|
|
foreach ($data as $k => $v) {
|
|
|
|
if (in_array($k, ['d_orderdate','d_accountno','d_jobtype'], true)) continue;
|
|
if (!array_key_exists($k, $existing)) continue;
|
|
|
|
$setParts[] = "{$k}='" . mysqli_real_escape_string($connect, (string)$v) . "'";
|
|
}
|
|
|
|
$setParts[] = "d_modifydate='{$now14}'";
|
|
|
|
if (!empty($setParts)) {
|
|
|
|
// update는 무조건 d_uid 기준으로
|
|
$uid = (int)$existing['d_uid'];
|
|
|
|
$sql = "
|
|
UPDATE tbl_daily
|
|
SET " . implode(",", $setParts) . "
|
|
WHERE d_uid='{$uid}'
|
|
LIMIT 1
|
|
";
|
|
|
|
if (!mysqli_query($connect, $sql)) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
}
|
|
}
|
|
|
|
// =========================
|
|
// INSERT
|
|
// =========================
|
|
else {
|
|
|
|
$cols = [];
|
|
$vals = [];
|
|
|
|
foreach ($data as $k => $v) {
|
|
$cols[] = $k;
|
|
$vals[] = "'" . mysqli_real_escape_string($connect, (string)$v) . "'";
|
|
}
|
|
|
|
if (!in_array('d_modifydate', $cols)) {
|
|
$cols[] = 'd_modifydate';
|
|
$vals[] = "'{$now14}'";
|
|
}
|
|
|
|
$sql = "
|
|
INSERT INTO tbl_daily (" . implode(",", $cols) . ")
|
|
VALUES (" . implode(",", $vals) . ")
|
|
";
|
|
|
|
if (!mysqli_query($connect, $sql)) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
}
|
|
|
|
// =========================
|
|
// AFTER RELOAD (uid 기준)
|
|
// =========================
|
|
if ($isUpdate) {
|
|
$uid = (int)$existing['d_uid'];
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_uid='{$uid}'
|
|
LIMIT 1
|
|
");
|
|
} else {
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_orderdate='{$d_orderdate}'
|
|
AND d_accountno='{$d_accountno}'
|
|
AND d_jobtype = '{$d_jobtype}'
|
|
ORDER BY d_uid DESC
|
|
LIMIT 1
|
|
");
|
|
}
|
|
|
|
$afterData = mysqli_fetch_assoc($res);
|
|
// note
|
|
if ($noteForUpsert !== '') {
|
|
$afterData['d_note'] = $noteForUpsert;
|
|
$afterData['d_level'] = $level;
|
|
}
|
|
|
|
if ($pickupNoteForUpsert !== '') {
|
|
$afterData['d_pickupnote'] = $pickupNoteForUpsert;
|
|
$afterData['d_level'] = $level;
|
|
}
|
|
|
|
// =========================
|
|
// ERP OUTBOX
|
|
// =========================
|
|
$event_type = $isUpdate ? 'DAILY_UPDATED' : 'DAILY_CREATED';
|
|
|
|
$payload = mysqli_real_escape_string(
|
|
$connect,
|
|
json_encode([
|
|
'key' => [
|
|
'd_orderdate'=>$afterData['d_orderdate'],
|
|
'd_accountno'=>$afterData['d_accountno'],
|
|
'd_jobtype'=>$afterData['d_jobtype']
|
|
],
|
|
'before'=>$beforeData,
|
|
'after'=>$afterData
|
|
], JSON_UNESCAPED_UNICODE)
|
|
);
|
|
|
|
mysqli_query($connect, "
|
|
INSERT INTO tbl_erp_outbox
|
|
(eo_event_type, eo_key_date, eo_key_accountno, eo_key_jobtype, eo_payload, eo_status, eo_created_at)
|
|
VALUES
|
|
('{$event_type}',
|
|
'{$afterData['d_orderdate']}',
|
|
'{$afterData['d_accountno']}',
|
|
'{$afterData['d_jobtype']}',
|
|
'{$payload}',
|
|
'PENDING',
|
|
NOW())
|
|
");
|
|
|
|
// =========================
|
|
// AFTER WORK
|
|
// =========================
|
|
$this->runAfterSave($beforeData, $afterData, $opt);
|
|
|
|
// =========================
|
|
// COMMIT
|
|
// =========================
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "COMMIT");
|
|
}
|
|
|
|
return $afterData;
|
|
|
|
} catch (Exception $e) {
|
|
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "ROLLBACK");
|
|
}
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function isDuplicate($orderdate, $accountno, $jobtype): bool
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
$res = mysqli_query($connect, "
|
|
SELECT 1
|
|
FROM tbl_daily
|
|
WHERE d_orderdate='{$orderdate}'
|
|
AND d_accountno='{$accountno}'
|
|
AND d_jobtype = '{$jobtype}'
|
|
LIMIT 1
|
|
");
|
|
|
|
return (bool)mysqli_fetch_assoc($res);
|
|
}
|
|
|
|
private function normalizeDailyPayload(array $data, ?array $existing = null): array
|
|
{
|
|
// 날짜 정리
|
|
if (isset($data['d_orderdate'])) {
|
|
$data['d_orderdate'] = preg_replace('/[^0-9]/', '', $data['d_orderdate']);
|
|
}
|
|
|
|
if (isset($data['d_visitdate'])) {
|
|
$data['d_visitdate'] = preg_replace('/[^0-9]/', '', $data['d_visitdate']);
|
|
}
|
|
|
|
// 숫자 필드 기본값
|
|
$intFields = [
|
|
'd_quantity',
|
|
'd_sludge'
|
|
];
|
|
|
|
foreach ($intFields as $f) {
|
|
if (isset($data[$f])) {
|
|
$data[$f] = ($data[$f] === '' || $data[$f] === null)
|
|
? 0
|
|
: (int)$data[$f];
|
|
}
|
|
}
|
|
|
|
// 문자열 trim
|
|
$strFields = [
|
|
'd_pickupnote',
|
|
'd_note'
|
|
];
|
|
|
|
foreach ($strFields as $f) {
|
|
if (isset($data[$f])) {
|
|
$data[$f] = trim((string)$data[$f]);
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function hydrateDailySnapshot(array $data, ?array $existing = null): array
|
|
{
|
|
$connect = $GLOBALS['conn'] ?? null;
|
|
if (!$connect) {
|
|
throw new Exception("DB connection not found");
|
|
}
|
|
|
|
// =========================
|
|
// DRIVER SNAPSHOT
|
|
// =========================
|
|
$druid = isset($data['d_druid'])
|
|
? (int)$data['d_druid']
|
|
: (int)($existing['d_druid'] ?? 0);
|
|
|
|
// fallback: legacy member uid -> current driver uid
|
|
if ($druid <= 0) {
|
|
$driverUid = isset($data['d_driveruid'])
|
|
? (int)$data['d_driveruid']
|
|
: (int)($existing['d_driveruid'] ?? 0);
|
|
|
|
if ($driverUid > 0) {
|
|
$sqMap = qry("
|
|
SELECT
|
|
m.m_currentdriver AS dr_uid,
|
|
d.dr_initial
|
|
FROM tbl_member m
|
|
LEFT JOIN tbl_driver d
|
|
ON d.dr_uid = m.m_currentdriver
|
|
WHERE m.m_uid = '{$driverUid}'
|
|
LIMIT 1
|
|
");
|
|
$map = fetch_array($sqMap);
|
|
|
|
if ($map && (int)$map['dr_uid'] > 0) {
|
|
$druid = (int)$map['dr_uid'];
|
|
$data['d_druid'] = $druid;
|
|
$data['d_drinitial'] = $map['dr_initial'] ?? '';
|
|
}
|
|
}
|
|
}
|
|
|
|
// druid가 있으면 driver snapshot 확정
|
|
if ($druid > 0) {
|
|
$sqDriver = qry("
|
|
SELECT dr_initial
|
|
FROM tbl_driver
|
|
WHERE dr_uid = '{$druid}'
|
|
LIMIT 1
|
|
");
|
|
$driver = fetch_array($sqDriver);
|
|
|
|
if ($driver && !empty($driver['dr_initial'])) {
|
|
$data['d_druid'] = $druid;
|
|
$data['d_drinitial'] = $driver['dr_initial'];
|
|
} elseif ($existing) {
|
|
$data['d_druid'] = $existing['d_druid'] ?? 0;
|
|
$data['d_drinitial'] = $existing['d_drinitial'] ?? '';
|
|
}
|
|
}
|
|
|
|
// =========================
|
|
// CUSTOMER SNAPSHOT (확장)
|
|
// =========================
|
|
$customerUid = isset($data['d_customeruid'])
|
|
? (int)$data['d_customeruid']
|
|
: (int)($existing['d_customeruid'] ?? 0);
|
|
|
|
if ($customerUid > 0) {
|
|
$sqCustomer = qry("
|
|
SELECT
|
|
c_regionuid,
|
|
c_region,
|
|
c_fullcycle,
|
|
c_fullcycleforced,
|
|
c_fullcycleflag,
|
|
c_lastpickupdate,
|
|
c_lastpickupquantity,
|
|
c_lastpaiddate,
|
|
c_name,
|
|
c_paymenttype,
|
|
c_paymentcycle,
|
|
c_rate,
|
|
c_form_eu,
|
|
c_form_corsia,
|
|
c_maincontainer,
|
|
c_container,
|
|
c_location,
|
|
c_phone,
|
|
c_address,
|
|
c_city,
|
|
c_postal,
|
|
c_area
|
|
|
|
FROM tbl_customer
|
|
WHERE c_uid = '{$customerUid}'
|
|
LIMIT 1
|
|
");
|
|
$customer = fetch_array($sqCustomer);
|
|
|
|
if ($customer) {
|
|
$data['d_name'] = $customer['c_name'] ?? '';
|
|
$data['d_form_eu'] = $customer['c_form_eu'] ?? '';
|
|
$data['d_form_corsia'] = $customer['c_form_corsia'] ?? '';
|
|
$data['d_maincontainer'] = $customer['c_maincontainer'] ?? '';
|
|
$data['d_container'] = $customer['c_container'] ?? '';
|
|
$data['d_location'] = $customer['c_location'] ?? '';
|
|
$data['d_phone'] = $customer['c_phone'] ?? '';
|
|
$data['d_address'] = $customer['c_address'] ?? '';
|
|
$data['d_city'] = $customer['c_city'] ?? '';
|
|
$data['d_postal'] = $customer['c_postal'] ?? '';
|
|
$data['d_area'] = $customer['c_area'] ?? '';
|
|
$data['d_regionuid'] = (int)($customer['c_regionuid'] ?? 0);
|
|
$data['d_region'] = $customer['c_region'] ?? '';
|
|
$data['d_fullcycle'] = $customer['c_fullcycle'] ?? 0;
|
|
$data['d_fullcycleforced'] = $customer['c_fullcycleforced'] ?? 0;
|
|
$data['d_fullcycleflag'] = $customer['c_fullcycleflag'] ?? 0;
|
|
$data['d_lastpickupdate'] = $customer['c_lastpickupdate'] ?? '';
|
|
$data['d_lastpickupquantity'] = $customer['c_lastpickupquantity'] ?? 0;
|
|
$data['d_lastpaiddate'] = $customer['c_lastpaiddate'] ?? '';
|
|
$data['d_paymenttype'] = $customer['c_paymenttype'] ?? '';
|
|
$data['d_cycle'] = $customer['c_paymentcycle'] ?? '';
|
|
$data['d_rate'] = $customer['c_rate'] ?? '';
|
|
if (!isset($data['d_oil_2y'])) $data['d_oil_2y'] = 0;
|
|
if (!isset($data['d_oil_1y'])) $data['d_oil_1y'] = 0;
|
|
if (!isset($data['d_oil_0y'])) $data['d_oil_0y'] = 0;
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function getDailyColumns(): array
|
|
{
|
|
return [
|
|
'd_uid',
|
|
|
|
// key
|
|
'd_orderdate',
|
|
'd_accountno',
|
|
'd_customeruid',
|
|
'd_jobtype',
|
|
|
|
// driver / region
|
|
'd_driveruid',
|
|
'd_druid',
|
|
'd_drinitial',
|
|
'd_regionuid',
|
|
'd_region',
|
|
|
|
// basic status
|
|
'd_ordertype',
|
|
'd_visitdate',
|
|
'd_visit',
|
|
'd_status',
|
|
|
|
// qty / sludge / payment
|
|
'd_quantity',
|
|
'd_sludge',
|
|
'd_paymenttype',
|
|
'd_payamount',
|
|
'd_payeename',
|
|
'd_paystatus',
|
|
|
|
// customer snapshot
|
|
'd_name',
|
|
'd_form_eu',
|
|
'd_form_corsia',
|
|
'd_maincontainer',
|
|
'd_container',
|
|
'd_location',
|
|
'd_phone',
|
|
'd_address',
|
|
'd_city',
|
|
'd_postal',
|
|
'd_area',
|
|
|
|
// oil / cycle history snapshot
|
|
'd_oil_2y',
|
|
'd_oil_1y',
|
|
'd_oil_0y',
|
|
'd_fullcycle',
|
|
'd_fullcycleforced',
|
|
'd_fullcycleflag',
|
|
'd_lastpickupdate',
|
|
'd_lastpickupquantity',
|
|
'd_lastpaiddate',
|
|
|
|
// pricing / estimate
|
|
'd_cycle',
|
|
'd_rate',
|
|
'd_estquantity',
|
|
|
|
// audit
|
|
'd_createruid',
|
|
'd_createddate',
|
|
'd_inputdate',
|
|
'd_modifydate',
|
|
'd_ruid',
|
|
|
|
// misc
|
|
'd_payeesign',
|
|
'd_is_transfer'
|
|
];
|
|
}
|
|
|
|
private function calculateEstQuantity(array $data, ?array $existing = null): int
|
|
{
|
|
$customerUid = $data['d_customeruid'] ?? ($existing['d_customeruid'] ?? 0);
|
|
$orderdate = $data['d_orderdate'] ?? ($existing['d_orderdate'] ?? '');
|
|
|
|
if (!$customerUid || !$orderdate) return 0;
|
|
|
|
$sqCus = qry("
|
|
SELECT c_fullquantity, c_fullquantitydaily
|
|
FROM tbl_customer
|
|
WHERE c_uid = '{$customerUid}'
|
|
LIMIT 1
|
|
");
|
|
$customer = fetch_array($sqCus);
|
|
|
|
if (!$customer) return 0;
|
|
|
|
$today = new DateTime(date("Y-m-d"));
|
|
$searchDate = new DateTime(substr($orderdate,0,4)."-".substr($orderdate,4,2)."-".substr($orderdate,6,2));
|
|
|
|
$interval = $today->diff($searchDate);
|
|
$days = (int)$interval->days;
|
|
|
|
return max(
|
|
0,
|
|
($customer['c_fullquantity'] - $customer['c_fullquantitydaily']) +
|
|
($days * $customer['c_fullquantitydaily'])
|
|
);
|
|
}
|
|
|
|
private function runAfterSave(?array $before, array $after, array $opt = []): void
|
|
{
|
|
$this->updateCustomerFromDaily($before, $after);
|
|
$this->updateRequestFromDaily($after);
|
|
$this->upsertDailyNote($after);
|
|
$this->handlePickupCleanup($before, $after);
|
|
$this->handlePickupReset($before, $after);
|
|
}
|
|
|
|
private function runAfterDelete(array $before, array $opt = []): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
if (empty($before['d_customeruid'])) {
|
|
return;
|
|
}
|
|
|
|
$customerUid = mysqli_real_escape_string($connect, $before['d_customeruid']);
|
|
$orderdate = mysqli_real_escape_string($connect, $before['d_orderdate'] ?? '');
|
|
|
|
// 1) REQUEST 삭제
|
|
$this->deleteRequestByDaily($before);
|
|
|
|
// 2) CUSTOMER 재계산 (lastpick, lastpaid 등 포함)
|
|
$this->recalcCustomerFromDailyHistory($customerUid);
|
|
|
|
// 3) GHOST 재판정
|
|
$this->reEvaluateGhost($customerUid);
|
|
}
|
|
|
|
private function deleteRequestByDaily(array $before): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
$customerUid = (int)($before['d_customeruid'] ?? 0);
|
|
$orderdate = preg_replace('/[^0-9]/', '', $before['d_orderdate'] ?? '');
|
|
|
|
if ($customerUid <= 0 || $orderdate === '') {
|
|
return;
|
|
}
|
|
|
|
mysqli_query($connect, "
|
|
DELETE FROM tbl_request
|
|
WHERE r_customeruid = '{$customerUid}'
|
|
AND r_requestdate = '{$orderdate}'
|
|
");
|
|
}
|
|
|
|
private function updateCustomerFromDaily($before, $after)
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
if (empty($after['d_customeruid'])) return;
|
|
|
|
$customer_uid = mysqli_real_escape_string($connect, $after['d_customeruid']);
|
|
|
|
// 고객 현재 상태 조회
|
|
$qr = mysqli_query($connect, "
|
|
SELECT
|
|
c_paymenttype,
|
|
c_is_top,
|
|
c_fpickup,
|
|
c_schedule,
|
|
c_scheduleday
|
|
FROM tbl_customer
|
|
WHERE c_uid = '{$customer_uid}'
|
|
LIMIT 1
|
|
");
|
|
|
|
$customer = mysqli_fetch_assoc($qr);
|
|
if (!$customer) return;
|
|
|
|
$updateFields = [];
|
|
|
|
$ordertype = $after['d_ordertype'] ?? 'N';
|
|
$orderdate = $after['d_orderdate'] ?? '';
|
|
$visitdate = $after['d_visitdate'] ?? '';
|
|
$thisQuantity= (int)($after['d_quantity'] ?? 0);
|
|
$lastQuantity= (int)($after['d_lastpickupquantity'] ?? 0);
|
|
$paystatus = $after['d_paystatus'] ?? '';
|
|
$paymenttype = $after['d_paymenttype'] ?? '';
|
|
|
|
$isTop = $customer['c_is_top'] ?? '';
|
|
|
|
// --------------------------------------------------
|
|
// 0) Ghost Check
|
|
// --------------------------------------------------
|
|
if (
|
|
$thisQuantity > 0 &&
|
|
$lastQuantity > 0 &&
|
|
$thisQuantity < 10 &&
|
|
$lastQuantity < 10 &&
|
|
$isTop !== 'G'
|
|
) {
|
|
$updateFields[] = "c_is_top = 'G'";
|
|
}
|
|
|
|
if ($isTop === 'G' && $thisQuantity >= 10) {
|
|
$updateFields[] = "c_is_top = ''";
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// 1) c_fpickup 처리
|
|
// --------------------------------------------------
|
|
if (
|
|
empty($customer['c_fpickup']) &&
|
|
!empty($orderdate) &&
|
|
$thisQuantity >= 10
|
|
) {
|
|
$updateFields[] = "c_fpickup = '{$orderdate}'";
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// 2) orderflag 처리
|
|
// --------------------------------------------------
|
|
if (!empty($after['d_visit']) && $after['d_visit'] === 'Y') {
|
|
$updateFields[] = "c_orderdate = ''";
|
|
$updateFields[] = "c_orderflag = 0";
|
|
} else {
|
|
$updateFields[] = "c_orderdate = '{$orderdate}'";
|
|
$updateFields[] = "c_orderflag = 1";
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// 3) Scheduled 처리 (d_ordertype == 'S')
|
|
// --------------------------------------------------
|
|
if ($ordertype === 'S' && !empty($visitdate)) {
|
|
|
|
$schedule = $customer['c_schedule'] ?? '';
|
|
$scheduleDay = $customer['c_scheduleday'] ?? '';
|
|
|
|
if ($schedule > '1W' && $schedule <= '6W') {
|
|
|
|
$weekCount = (int)substr($schedule, 0, 1);
|
|
|
|
if ($weekCount > 0 && !empty($scheduleDay)) {
|
|
|
|
$dayParts = explode('|', $scheduleDay);
|
|
$dayOfWeek = $dayParts[0] ?? '';
|
|
|
|
$calcStr = "+{$weekCount} week {$dayOfWeek}";
|
|
$nextDate = date('Ymd', strtotime($calcStr, strtotime($visitdate)));
|
|
|
|
$updateFields[] = "c_schedulebasic = '{$nextDate}'";
|
|
}
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
// UPDATE 실행
|
|
// --------------------------------------------------
|
|
if (!empty($updateFields)) {
|
|
|
|
mysqli_query($connect, "
|
|
UPDATE tbl_customer
|
|
SET " . implode(",", $updateFields) . "
|
|
WHERE c_uid = '{$customer_uid}'
|
|
LIMIT 1
|
|
");
|
|
}
|
|
}
|
|
|
|
private function updateRequestFromDaily(array $after): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
if (empty($after['d_ordertype']) || $after['d_ordertype'] !== 'R') {
|
|
return;
|
|
}
|
|
|
|
if (empty($after['d_ruid'])) {
|
|
return;
|
|
}
|
|
|
|
// pickup 완료 조건 (방문 완료 + 상태 F)
|
|
if (
|
|
empty($after['d_visit']) ||
|
|
$after['d_visit'] !== 'Y' ||
|
|
empty($after['d_status']) ||
|
|
$after['d_status'] !== 'F'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
$r_uid = mysqli_real_escape_string($connect, $after['d_ruid']);
|
|
|
|
mysqli_query($connect, "
|
|
UPDATE tbl_request
|
|
SET r_status = 'F'
|
|
WHERE r_uid = '{$r_uid}'
|
|
LIMIT 1
|
|
");
|
|
}
|
|
|
|
private function upsertDailyNote(array $after): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
if (empty($after['d_uid'])) return;
|
|
|
|
// note는 saveDaily payload에 포함되어 있어야 함
|
|
if (empty($after['d_pickupnote']) && empty($after['d_note'])) {
|
|
return;
|
|
}
|
|
|
|
$noteText = trim($after['d_pickupnote'] ?? $after['d_note'] ?? '');
|
|
|
|
if (strlen($noteText) <= 1) return;
|
|
|
|
$dailyuid = mysqli_real_escape_string($connect, $after['d_uid']);
|
|
$customeruid = mysqli_real_escape_string($connect, $after['d_customeruid'] ?? '');
|
|
$memberuid = mysqli_real_escape_string($connect, $after['d_driveruid'] ?? '');
|
|
$noteEsc = mysqli_real_escape_string($connect, $noteText);
|
|
$now14 = date('YmdHis');
|
|
$level = mysqli_real_escape_string($connect, $after['d_level'] ?? 9);
|
|
|
|
// 기존 note 존재 여부 확인
|
|
$qr = mysqli_query($connect, "
|
|
SELECT n_uid
|
|
FROM tbl_note
|
|
WHERE n_dailyuid = '{$dailyuid}'
|
|
LIMIT 1
|
|
");
|
|
|
|
$existing = mysqli_fetch_assoc($qr);
|
|
|
|
if ($existing) {
|
|
// UPDATE
|
|
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 = '{$dailyuid}'
|
|
LIMIT 1
|
|
");
|
|
} else {
|
|
// INSERT
|
|
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}',
|
|
'{$dailyuid}',
|
|
'D',
|
|
{$level},
|
|
1,
|
|
'{$noteEsc}',
|
|
'{$now14}'
|
|
)
|
|
");
|
|
}
|
|
}
|
|
|
|
private function handlePickupCleanup(?array $before, array $after): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
$beforeQty = (int)($before['d_quantity'] ?? 0);
|
|
$afterQty = (int)($after['d_quantity'] ?? 0);
|
|
|
|
$status = $after['d_status'] ?? '';
|
|
$visit = $after['d_visit'] ?? '';
|
|
|
|
// 최초 pickup
|
|
$isFirstPickup = ($beforeQty < 10 && $afterQty >= 10);
|
|
|
|
if (!$isFirstPickup) return;
|
|
if ($status !== 'F' || $visit !== 'Y') return;
|
|
|
|
$customerUid = mysqli_real_escape_string($connect, $after['d_customeruid']);
|
|
$accountno = mysqli_real_escape_string($connect, $after['d_accountno']);
|
|
$today = date('Ymd');
|
|
$jobtype = mysqli_real_escape_string($connect, $after['d_jobtype'] ?? 'UCO');
|
|
|
|
// =========================
|
|
// 삭제 대상 조회
|
|
// =========================
|
|
$res = mysqli_query($connect, "
|
|
SELECT d_orderdate, d_accountno, d_jobtype
|
|
FROM tbl_daily
|
|
WHERE d_customeruid = '{$customerUid}'
|
|
AND d_orderdate > '{$today}'
|
|
AND d_status = 'A'
|
|
AND d_jobtype = '{$jobtype}'
|
|
");
|
|
|
|
if (!$res) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
|
|
// =========================
|
|
// 하나씩 deleteDaily 호출
|
|
// =========================
|
|
while ($row = mysqli_fetch_assoc($res)) {
|
|
|
|
try {
|
|
$this->deleteDaily(
|
|
$row['d_orderdate'],
|
|
$row['d_accountno'],
|
|
$row['d_jobtype'],
|
|
['in_tx' => true]
|
|
);
|
|
|
|
} catch (Exception $e) {
|
|
// 여기서 실패하면 전체 rollback 유도
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
error_log("[PickupCleanup] ERP-safe delete completed for customer={$accountno}");
|
|
}
|
|
|
|
private function handlePickupReset(?array $before, array $after): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
$beforeQty = (int)($before['d_quantity'] ?? 0);
|
|
$afterQty = (int)($after['d_quantity'] ?? 0);
|
|
|
|
$status = $after['d_status'] ?? '';
|
|
$visit = $after['d_visit'] ?? '';
|
|
|
|
$isFirstPickup = ($beforeQty < 10 && $afterQty >= 10);
|
|
|
|
if (!$isFirstPickup) return;
|
|
if ($status !== 'F' || $visit !== 'Y') return;
|
|
|
|
$customerUid = mysqli_real_escape_string($connect, $after['d_customeruid']);
|
|
$accountno = mysqli_real_escape_string($connect, $after['d_accountno']);
|
|
$orderdate = mysqli_real_escape_string($connect, $after['d_orderdate']);
|
|
|
|
$sql = "
|
|
UPDATE tbl_customer
|
|
SET
|
|
c_fullquantity = 0,
|
|
c_lastpickupdate = '{$orderdate}',
|
|
c_lastpickupquantity = '{$afterQty}'
|
|
WHERE c_uid = '{$customerUid}'
|
|
";
|
|
|
|
mysqli_query($connect, $sql);
|
|
|
|
error_log("[PickupReset] customer={$accountno} reset fullquantity + lastpickupdate={$orderdate}");
|
|
}
|
|
|
|
public function deleteDaily(string $orderdate, string $accountno, string $jobtype, array $opt = [])
|
|
{
|
|
$connect = $GLOBALS['conn'] ?? null;
|
|
if (!$connect) {
|
|
throw new Exception("DB connection not found");
|
|
}
|
|
|
|
$inTx = !empty($opt['in_tx']);
|
|
|
|
// =========================
|
|
// KEY NORMALIZE
|
|
// =========================
|
|
$orderdate = preg_replace('/[^0-9]/', '', $orderdate);
|
|
if (strlen($orderdate) !== 8) {
|
|
throw new Exception("invalid orderdate");
|
|
}
|
|
|
|
$accountno = mysqli_real_escape_string($connect, trim($accountno));
|
|
if ($accountno === '') {
|
|
throw new Exception("invalid accountno");
|
|
}
|
|
|
|
$jobtype = mysqli_real_escape_string($connect, trim($jobtype));
|
|
if ($jobtype === '') {
|
|
$jobtype = 'UCO';
|
|
}
|
|
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "START TRANSACTION");
|
|
}
|
|
|
|
try {
|
|
|
|
// =========================
|
|
// 1. 기존 데이터 조회
|
|
// =========================
|
|
$res = mysqli_query($connect, "
|
|
SELECT *
|
|
FROM tbl_daily
|
|
WHERE d_orderdate='{$orderdate}'
|
|
AND d_accountno='{$accountno}'
|
|
AND d_jobtype = '{$jobtype}'
|
|
LIMIT 1
|
|
");
|
|
if (!$res) throw new Exception(mysqli_error($connect));
|
|
|
|
$existing = mysqli_fetch_assoc($res);
|
|
|
|
if (!$existing) {
|
|
throw new Exception("Daily record not found");
|
|
}
|
|
|
|
// =========================
|
|
// 2. ERP OUTBOX 기록
|
|
// =========================
|
|
$payload = mysqli_real_escape_string(
|
|
$connect,
|
|
json_encode([
|
|
'key'=>[
|
|
'd_orderdate'=>$orderdate,
|
|
'd_accountno'=>$accountno,
|
|
'd_jobtype' =>$jobtype
|
|
],
|
|
'before'=>$existing
|
|
], JSON_UNESCAPED_UNICODE)
|
|
);
|
|
|
|
if (!mysqli_query($connect, "
|
|
INSERT INTO tbl_erp_outbox
|
|
(eo_event_type, eo_key_date, eo_key_accountno, eo_key_jobtype, eo_payload, eo_status, eo_created_at)
|
|
VALUES
|
|
('DAILY_DELETED','{$orderdate}','{$accountno}','{$jobtype}','{$payload}','PENDING',NOW())
|
|
")) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
|
|
// =========================
|
|
// 3. 물리 삭제
|
|
// =========================
|
|
if (!mysqli_query($connect, "
|
|
DELETE FROM tbl_daily
|
|
WHERE d_orderdate='{$orderdate}'
|
|
AND d_accountno='{$accountno}'
|
|
AND d_jobtype = '{$jobtype}'
|
|
LIMIT 1
|
|
")) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
|
|
if (mysqli_affected_rows($connect) === 0) {
|
|
throw new Exception("Delete failed");
|
|
}
|
|
|
|
// =========================
|
|
// AFTER WORK 중앙화
|
|
// =========================
|
|
$this->runAfterDelete($existing, $opt);
|
|
|
|
// =========================
|
|
// COMMIT
|
|
// =========================
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "COMMIT");
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (Exception $e) {
|
|
|
|
if (!$inTx) {
|
|
mysqli_query($connect, "ROLLBACK");
|
|
}
|
|
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
private function reEvaluateGhost(int $customerUid): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
|
|
$res = mysqli_query($connect, "
|
|
SELECT d_quantity, d_lastpickupquantity
|
|
FROM tbl_daily
|
|
WHERE d_customeruid = '{$customerUid}'
|
|
AND d_status = 'F'
|
|
ORDER BY d_orderdate DESC
|
|
LIMIT 1
|
|
");
|
|
|
|
$row = mysqli_fetch_assoc($res);
|
|
|
|
if (!$row) {
|
|
// 데이터 없으면 ghost 였던 것 해제
|
|
mysqli_query($connect, "
|
|
UPDATE tbl_customer
|
|
SET c_is_top = ''
|
|
WHERE c_uid = '{$customerUid}'
|
|
and c_is_top = 'G'
|
|
");
|
|
return;
|
|
}
|
|
|
|
$thisQuantity = (int)$row['d_quantity'];
|
|
$lastQuantity = (int)$row['d_lastpickupquantity'];
|
|
|
|
if ($thisQuantity < 10 && $lastQuantity < 10) {
|
|
mysqli_query($connect, "
|
|
UPDATE tbl_customer
|
|
SET c_is_top = 'G'
|
|
WHERE c_uid = '{$customerUid}'
|
|
");
|
|
} else {
|
|
mysqli_query($connect, "
|
|
UPDATE tbl_customer
|
|
SET c_is_top = ''
|
|
WHERE c_uid = '{$customerUid}'
|
|
and c_is_top = 'G'
|
|
");
|
|
}
|
|
}
|
|
|
|
private function recalcCustomerFromDailyHistory(string $customerUid): void
|
|
{
|
|
$connect = $GLOBALS['conn'];
|
|
$customerUidEsc = mysqli_real_escape_string($connect, $customerUid);
|
|
|
|
// 0) "오늘 포함 이후" 오더장 존재 여부 체크 (남아있으면 리셋 금지)
|
|
$today = date('Ymd');
|
|
|
|
$resFuture = mysqli_query($connect, "
|
|
SELECT 1
|
|
FROM tbl_daily
|
|
WHERE d_customeruid = '{$customerUidEsc}'
|
|
AND d_orderdate >= '{$today}'
|
|
AND d_status = 'A'
|
|
LIMIT 1
|
|
");
|
|
if (!$resFuture) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
$hasFutureOrder = (bool)mysqli_fetch_assoc($resFuture);
|
|
|
|
// 1) 마지막 완료 주문(F) 기준으로 last pickup 재계산
|
|
$res = mysqli_query($connect, "
|
|
SELECT
|
|
d_orderdate,
|
|
d_quantity,
|
|
d_sludge,
|
|
d_paymenttype,
|
|
d_paystatus
|
|
FROM tbl_daily
|
|
WHERE d_customeruid = '{$customerUidEsc}'
|
|
AND d_status = 'F'
|
|
ORDER BY d_orderdate DESC
|
|
LIMIT 1
|
|
");
|
|
if (!$res) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
|
|
$last = mysqli_fetch_assoc($res);
|
|
|
|
$lastPickupDate = '';
|
|
$lastPickupQty = 0;
|
|
$lastSludge = '';
|
|
$lastPaidDate = '';
|
|
|
|
if ($last) {
|
|
$lastPickupDate = $last['d_orderdate'];
|
|
$lastPickupQty = (int)$last['d_quantity'];
|
|
$lastSludge = (string)$last['d_sludge'];
|
|
|
|
if ($last['d_paymenttype'] === 'CA' && $last['d_paystatus'] === 'P') {
|
|
$lastPaidDate = $last['d_orderdate'];
|
|
}
|
|
}
|
|
|
|
// 2) 마지막 주문이 cash paid가 아니면, cash paid 마지막을 다시 찾음
|
|
if ($lastPaidDate === '') {
|
|
|
|
$res2 = mysqli_query($connect, "
|
|
SELECT d_orderdate
|
|
FROM tbl_daily
|
|
WHERE d_customeruid = '{$customerUidEsc}'
|
|
AND d_status = 'F'
|
|
AND d_paymenttype = 'CA'
|
|
AND d_paystatus = 'P'
|
|
ORDER BY d_orderdate DESC
|
|
LIMIT 1
|
|
");
|
|
if (!$res2) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
|
|
$paid = mysqli_fetch_assoc($res2);
|
|
if ($paid) {
|
|
$lastPaidDate = $paid['d_orderdate'];
|
|
}
|
|
}
|
|
|
|
// 3) UPDATE 구성
|
|
$setParts = [];
|
|
$setParts[] = "c_lastpickupdate=" . ($lastPickupDate ? "'{$lastPickupDate}'" : "''");
|
|
$setParts[] = "c_lastpickupquantity='{$lastPickupQty}'";
|
|
$setParts[] = "c_lastpaiddate=" . ($lastPaidDate ? "'{$lastPaidDate}'" : "''");
|
|
$setParts[] = "c_sludge='" . mysqli_real_escape_string($connect, $lastSludge) . "'";
|
|
|
|
// 4) orderflag/orderdate 리셋 정책 반영
|
|
// - 오늘 포함 이후 오더장이 남아있으면 유지 (아무것도 안함)
|
|
// - 없으면 리셋
|
|
if (!$hasFutureOrder) {
|
|
$setParts[] = "c_orderdate=''";
|
|
$setParts[] = "c_orderflag=0";
|
|
}
|
|
|
|
$sql = "
|
|
UPDATE tbl_customer
|
|
SET " . implode(",", $setParts) . "
|
|
WHERE c_uid = '{$customerUidEsc}'
|
|
LIMIT 1
|
|
";
|
|
|
|
if (!mysqli_query($connect, $sql)) {
|
|
throw new Exception(mysqli_error($connect));
|
|
}
|
|
}
|
|
|
|
/* ===============================
|
|
* 공통 JSON 응답
|
|
* =============================== */
|
|
|
|
private function jsonSuccess($data = [])
|
|
{
|
|
return json_encode([
|
|
'result' => 'success',
|
|
'data' => $data
|
|
]);
|
|
}
|
|
|
|
private function jsonError($msg)
|
|
{
|
|
return json_encode([
|
|
'result' => 'error',
|
|
'message'=> $msg
|
|
]);
|
|
}
|
|
|
|
} |