goiintra/public_html/assets/api/customer.php

1029 lines
28 KiB
PHP

<?php
require_once getenv("DOCUMENT_ROOT")."/assets/conf.inc.php";
require_once getenv("DOCUMENT_ROOT")."/assets/dbCon.php";
require_once getenv("DOCUMENT_ROOT")."/assets/api/daily.php";
class CustomerService extends CONF
{
/* =====================================================
* CREATE / UPDATE CUSTOMER
* ===================================================== */
public function saveCustomer(array $data, array $opt = []): array
{
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
$inTx = !empty($opt['in_tx']);
if (!$inTx) {
mysqli_query($connect, "START TRANSACTION");
}
try {
$mode = $data['mode'] ?? 'create';
if (!in_array($mode, ['create', 'update'])) {
throw new Exception("invalid mode");
}
$beforeData = null;
$isUpdate = false;
// mode 는 필드가 아니니 지워라.
unset($data['mode']);
// =====================================
// UPDATE 기존 데이터 조회
// =====================================
if ($mode === 'update') {
$c_uid = (int)($data['c_uid'] ?? 0);
unset($data['c_uid']);
if ($c_uid <= 0) {
throw new Exception("invalid customer uid");
}
$res = mysqli_query($connect, "
SELECT *
FROM tbl_customer
WHERE c_uid = '{$c_uid}'
LIMIT 1
");
if (!$res) {
throw new Exception(mysqli_error($connect));
}
$beforeData = mysqli_fetch_assoc($res);
if (!$beforeData) {
throw new Exception("customer not found");
}
$isUpdate = true;
}
// =====================================
// NORMALIZE
// =====================================
$data = $this->normalizeCustomerPayload($data, $beforeData);
// =====================================
// HYDRATE
// =====================================
$data = $this->hydrateCustomerSnapshot($data, $beforeData);
// =====================================
// CREATE
// =====================================
if (!$isUpdate) {
$columns = [];
$values = [];
foreach ($data as $k => $v) {
$columns[] = $k;
$values[] = "'" . mysqli_real_escape_string($connect, (string)$v) . "'";
}
$sql = "
INSERT INTO tbl_customer
(
" . implode(',', $columns) . "
)
VALUES
(
" . implode(',', $values) . "
)
";
if (!mysqli_query($connect, $sql)) {
throw new Exception(mysqli_error($connect));
}
$c_uid = mysqli_insert_id($connect);
}
// =====================================
// UPDATE
// =====================================
else {
// 주소/우편번호가 바뀌면 좌표 초기화
$this->handleGeoReset($beforeData, $data);
$setParts = [];
foreach ($data as $k => $v) {
if ($v === null) {
$setParts[] = "{$k}=NULL";
continue;
}
$vEsc = mysqli_real_escape_string(
$connect,
(string)$v
);
$setParts[] = "{$k}='{$vEsc}'";
}
$sql = "
UPDATE tbl_customer
SET
" . implode(',', $setParts) . "
WHERE c_uid='{$beforeData['c_uid']}'
LIMIT 1
";
if (!mysqli_query($connect, $sql)) {
throw new Exception(mysqli_error($connect));
}
$c_uid = (int)$beforeData['c_uid'];
}
// =====================================
// AFTER RELOAD
// =====================================
$res = mysqli_query($connect, "
SELECT *
FROM tbl_customer
WHERE c_uid='{$c_uid}'
LIMIT 1
");
if (!$res) {
throw new Exception(mysqli_error($connect));
}
$afterData = mysqli_fetch_assoc($res);
// =====================================
// OUTBOX
// =====================================
$actionType = $isUpdate
? 'UPDATED'
: 'CREATED';
if (!$isUpdate|| $this->hasCustomerChanges($beforeData, $afterData)) {
$this->enqueueOutbox(
$actionType,
$beforeData,
$afterData
);
}
// =====================================
// AFTER WORK
// =====================================
if (empty($opt['skip_after'])) {
$this->runAfterChange(
$beforeData,
$afterData,
$data,
$opt
);
}
// =====================================
// COMMIT
// =====================================
if (!$inTx) {
mysqli_query($connect, "COMMIT");
}
return $afterData;
} catch (Exception $e) {
if (!$inTx) {
mysqli_query($connect, "ROLLBACK");
}
throw $e;
}
}
/* =====================================================
* PATCH CUSTOMER
* ===================================================== */
public function patchCustomer(
int $customerUid,
array $fields,
array $opt = []
): array {
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
$inTx = !empty($opt['in_tx']);
if (!$inTx) {
mysqli_query($connect, "START TRANSACTION");
}
try {
$customerUid = (int)$customerUid;
if ($customerUid <= 0) {
throw new Exception("invalid customer uid");
}
// =====================================
// BEFORE
// =====================================
$res = mysqli_query($connect, "SELECT * FROM tbl_customer WHERE c_uid='{$customerUid}' LIMIT 1");
if (!$res) {
throw new Exception(mysqli_error($connect));
}
$beforeData = mysqli_fetch_assoc($res);
if (!$beforeData) {
throw new Exception("customer not found");
}
// =====================================
// normalize
// =====================================
$fields = $this->normalizeCustomerPayload(
$fields,
$beforeData
);
if (empty($fields)) {
return $beforeData;
}
$fields['c_updateddate'] = date("YmdHis");
// =====================================
// GEO RESET
// =====================================
$this->handleGeoReset(
$beforeData,
$fields
);
// =====================================
// UPDATE
// =====================================
$setParts = [];
foreach ($fields as $k => $v) {
if ($v === null) {
$setParts[] = "{$k}=NULL";
continue;
}
$vEsc = mysqli_real_escape_string(
$connect,
(string)$v
);
$setParts[] = "{$k}='{$vEsc}'";
}
$sql = "
UPDATE tbl_customer
SET
" . implode(',', $setParts) . "
WHERE c_uid='{$customerUid}'
LIMIT 1
";
if (!mysqli_query($connect, $sql)) {
throw new Exception(mysqli_error($connect));
}
// =====================================
// AFTER
// =====================================
$res = mysqli_query($connect, "
SELECT *
FROM tbl_customer
WHERE c_uid='{$customerUid}'
LIMIT 1
");
if (!$res) {
throw new Exception(mysqli_error($connect));
}
$afterData = mysqli_fetch_assoc($res);
// =====================================
// OUTBOX
// =====================================
if ($this->hasCustomerChanges($beforeData, $afterData)) {
$this->enqueueOutbox(
'UPDATED',
$beforeData,
$afterData
);
}
// =====================================
// AFTER WORK
// =====================================
if (empty($opt['skip_after'])) {
$this->runAfterChange(
$beforeData,
$afterData,
$fields,
$opt
);
}
if (!$inTx) {
mysqli_query($connect, "COMMIT");
}
return $afterData;
} catch (Exception $e) {
if (!$inTx) {
mysqli_query($connect, "ROLLBACK");
}
throw $e;
}
}
/* =====================================================
* DELETE CUSTOMER (사용 안함)
* ===================================================== */
public function deleteCustomer(
int $customerUid,
array $opt = []
): bool {
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
$customerUid = (int)$customerUid;
if ($customerUid <= 0) {
throw new Exception("invalid customer uid");
}
$inTx = !empty($opt['in_tx']);
if (!$inTx) {
mysqli_query($connect, "START TRANSACTION");
}
try {
$res = mysqli_query($connect, "
SELECT *
FROM tbl_customer
WHERE c_uid='{$customerUid}'
LIMIT 1
");
if (!$res) {
throw new Exception(mysqli_error($connect));
}
$beforeData = mysqli_fetch_assoc($res);
if (!$beforeData) {
throw new Exception("customer not found");
}
$sql = "
UPDATE tbl_customer
SET
c_status='D'
WHERE c_uid='{$customerUid}'
LIMIT 1
";
if (!mysqli_query($connect, $sql)) {
throw new Exception(mysqli_error($connect));
}
$res = mysqli_query($connect, "
SELECT *
FROM tbl_customer
WHERE c_uid='{$customerUid}'
LIMIT 1
");
$afterData = mysqli_fetch_assoc($res);
$this->enqueueOutbox(
'DELETED',
$beforeData,
$afterData
);
if (!$inTx) {
mysqli_query($connect, "COMMIT");
}
return true;
} catch (Exception $e) {
if (!$inTx) {
mysqli_query($connect, "ROLLBACK");
}
throw $e;
}
}
/* =====================================================
* OUTBOX
* ===================================================== */
private function enqueueOutbox(
string $actionType,
?array $beforeData,
?array $afterData
): void {
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
$key1 = $afterData['c_accountno']
?? $beforeData['c_accountno']
?? '';
$payload = mysqli_real_escape_string(
$connect,
json_encode([
'entity' => 'CUSTOMER',
'action' => $actionType,
'key' => [
'c_accountno' => $key1
],
'before' => $beforeData,
'after' => $afterData
], JSON_UNESCAPED_UNICODE)
);
$sql = "
INSERT INTO tbl_erp_outbox
(
eo_entity_type,
eo_action_type,
eo_key1,
eo_payload,
eo_status,
eo_created_at
)
VALUES
(
'CUSTOMER',
'{$actionType}',
'{$key1}',
'{$payload}',
'PENDING',
NOW()
)
";
if (!mysqli_query($connect, $sql)) {
throw new Exception(mysqli_error($connect));
}
}
/* =====================================================
* AFTER SAVE
* ===================================================== */
private function runAfterChange(
?array $before,
array $after,
array $changedFields = [],
array $opt = []
): void {
// =========================
// DAILY pricing sync
// =========================
$pricingFields = [
'c_rate',
'c_paymentcycle',
'c_paymenttype'
];
$hasPricingChange = false;
foreach ($pricingFields as $f) {
if (
array_key_exists($f, $changedFields)
||
(($before[$f] ?? null) != ($after[$f] ?? null))
) {
$hasPricingChange = true;
break;
}
}
if ($hasPricingChange) {
$this->syncFutureDailyPricing(
$before,
$after
);
}
// =========================
// DRIVER sync
// =========================
if (
array_key_exists('c_driveruid', $changedFields)
||
(($before['c_driveruid'] ?? null) != ($after['c_driveruid'] ?? null))
) {
$this->syncFutureDriver(
$before,
$after
);
}
}
/* =====================================================
* DRIVER SYNC
* ===================================================== */
private function syncFutureDriver(
?array $before,
array $after
): void {
if (!$before) {
return;
}
$oldDriver = $before['c_driveruid'] ?? '';
$newDriver = $after['c_driveruid'] ?? '';
if ($oldDriver == $newDriver) {
return;
}
$customerUid = (int)$after['c_uid'];
$today = date("Ymd");
$dailyService = new DailyService();
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
// FUTURE DAILY
$qry = "
SELECT
d_uid,
d_orderdate,
d_accountno
FROM tbl_daily
WHERE d_customeruid = '{$customerUid}'
AND d_orderdate >= '{$today}'
";
$result = mysqli_query($connect, $qry);
while ($row = mysqli_fetch_assoc($result)) {
$dailyService->saveDaily(
[
'd_orderdate' => $row['d_orderdate'],
'd_accountno' => $row['d_accountno'],
'd_driveruid' => $newDriver,
'd_updateruid' => $after['c_updatedby']
?? $after['c_createdby']
?? null
],
[
'match_uid' => $row['d_uid'],
'allow_update' => true
]
);
}
// FUTURE REQUEST
mysqli_query($connect, "
UPDATE tbl_request
SET r_driveruid='{$newDriver}'
WHERE r_customeruid='{$customerUid}'
AND r_requestdate >= '{$today}'
");
}
/* =====================================================
* FUTURE DAILY PRICING (위에거 포함한 bulk update 로직 만드는 거 추후에 작업)
* ===================================================== */
private function syncFutureDailyPricing(
?array $before,
array $after
): void {
if (!$before) {
return;
}
$rateChanged = (string)($before['c_rate'] ?? '') !== (string)($after['c_rate'] ?? '');
$cycleChanged = (string)($before['c_paymentcycle'] ?? '') !== (string)($after['c_paymentcycle'] ?? '');
$paymentChanged = (string)($before['c_paymenttype'] ?? '') !== (string)($after['c_paymenttype'] ?? '');
if (
!$rateChanged
&& !$cycleChanged
&& !$paymentChanged
) {
return;
}
$customerUid = (int)$after['c_uid'];
$today = date("Ymd");
$dailyService = new DailyService();
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
$qry = "
SELECT
d_uid,
d_orderdate,
d_accountno
FROM tbl_daily
WHERE d_customeruid = '{$customerUid}'
AND d_orderdate > '{$today}'
";
$result = mysqli_query($connect, $qry);
while ($row = mysqli_fetch_assoc($result)) {
$payload = [
'd_orderdate' => $row['d_orderdate'],
'd_accountno' => $row['d_accountno'],
'd_updateruid' =>
$after['c_updatedby']
?? $after['c_createdby']
?? null
];
if ($rateChanged) {
$payload['d_rate'] = $after['c_rate'];
}
if ($cycleChanged) {
$payload['d_cycle'] = $after['c_paymentcycle'];
}
if ($paymentChanged) {
$payload['d_paymenttype'] = $after['c_paymenttype'];
}
$dailyService->saveDaily(
$payload,
[
'match_uid' => $row['d_uid'],
'allow_update' => true
]
);
}
}
/* =====================================================
* GEO RESET
* ===================================================== */
private function handleGeoReset(
array $before,
array &$fields
): void {
$beforeAddress = trim(
(string)($before['c_address'] ?? '')
);
$beforePostal = trim(
(string)($before['c_postal'] ?? '')
);
$newAddress = trim(
(string)(
$fields['c_address']
?? $beforeAddress
)
);
$newPostal = trim(
(string)(
$fields['c_postal']
?? $beforePostal
)
);
if (
$beforeAddress !== $newAddress
||
$beforePostal !== $newPostal
) {
$fields['c_geolat'] = null;
$fields['c_geolon'] = null;
}
}
/* =====================================================
* NORMALIZE
* ===================================================== */
private function normalizeCustomerPayload(
array $data,
?array $existing = null
): array {
$dateFields = [
'c_contractdate',
'c_installdate',
'c_salescommissiondate',
'c_removaldate',
'c_inactivedate',
'c_exchangedate',
'c_switchformdate',
'c_forceddate'
];
foreach ($dateFields as $f) {
if (!isset($data[$f])) {
continue;
}
$data[$f] = preg_replace(
'/[^0-9]/',
'',
(string)$data[$f]
);
}
$strFields = [
'c_name',
'c_address',
'c_city',
'c_postal',
'c_location',
'c_comment_ri',
'c_comment_ci',
'c_email',
'c_phone',
'c_phoneext',
'c_cell',
'c_createdby',
'c_updatedby'
];
foreach ($strFields as $f) {
if (!isset($data[$f])) {
continue;
}
$data[$f] = trim(
str_replace(
"\\",
'',
(string)$data[$f]
)
);
}
if (isset($data['c_phone'])) {
$data['c_phone'] = str_replace(
'-',
'',
$data['c_phone']
);
}
// c_time_restricted: Y/N 로 강제 (MIS 전용 필드)
// - hasCustomerChanges() 의 $businessFields 에 없으므로 ERP outbox 로 안 나감
if (isset($data['c_time_restricted'])) {
$v = $data['c_time_restricted'];
$data['c_time_restricted'] =
($v === 'Y' || $v === '1' || $v === 1 || $v === true) ? 'Y' : 'N';
}
return $data;
}
/* =====================================================
* HYDRATE
* ===================================================== */
private function hydrateCustomerSnapshot(
array $data,
?array $existing = null
): array {
$connect = $GLOBALS['conn'] ?? null;
if (!$connect) {
throw new Exception("DB connection not found");
}
// driver -> region snapshot
if (!empty($data['c_driveruid'])) {
$driveruid = mysqli_real_escape_string(
$connect,
(string)$data['c_driveruid']
);
$res = mysqli_query($connect, "
SELECT
m_regionuid,
m_region
FROM tbl_member
WHERE m_uid='{$driveruid}'
LIMIT 1
");
if ($res) {
$driver = mysqli_fetch_assoc($res);
if ($driver) {
$data['c_regionuid'] = $driver['m_regionuid'];
$data['c_region'] = $driver['m_region'];
}
}
}
// created date
if (empty($existing)) {
if (empty($data['c_createddate'])) {
$data['c_createddate'] = date("YmdHis");
}
}
$data['c_updateddate'] = date("YmdHis");
return $data;
}
private function hasCustomerChanges(
?array $before,
?array $after
): bool {
if (!$before || !$after) {
return true;
}
$businessFields = [
// 기본
'c_accountno',
'c_name',
'c_status',
'c_form_eu',
'c_form_corsia',
'c_rate',
'c_paymenttype',
'c_hstno',
'c_driveruid',
// 지역
'c_regionuid',
'c_area',
'c_address',
'c_mailingaddr',
'c_postal',
'c_city',
'c_province',
'c_geolat',
'c_geolon',
// 연락처
'c_email',
'c_phone',
'c_phoneext',
// 계약
'c_contractdate',
'c_contractby',
'c_installdate',
'c_salesperson',
'c_salescommissiondate',
'c_salesmethod',
// Full Cycle (erp 에서 의미없는 변화)
// 'c_fullcycle',
// 'c_fullquantity',
// 'c_fullcycleflag',
// 'c_fullcycleforced',
// 'c_forceddate',
// 'c_crondate',
// Schedule
'c_schedule',
'c_scheduleday',
// Location
'c_location',
// Pickup
'c_lastpickupdate',
'c_lastpickupquantity',
'c_sludge',
// Comment
'c_comment_ri',
'c_comment_ci'
];
foreach ($businessFields as $field) {
$beforeVal = trim((string)($before[$field] ?? ''));
$afterVal = trim((string)($after[$field] ?? ''));
if ($beforeVal !== $afterVal) {
return true;
}
}
return false;
}
}
/* =====================================================================
* 이 파일을 "직접 호출"했을 때만 동작하는 경량 디스패처.
* (다른 코드에서 require 될 땐 위 CustomerService 클래스만 제공)
*
* action = patchTimeRestricted
* - c_time_restricted (Y|N) 만 patchCustomer() 로 갱신
* - c_time_restricted 는 hasCustomerChanges()의 $businessFields 에 없으므로
* ERP outbox 로 나가지 않음
* - 입력: POST c_uid, value(Y|N) / 출력: { ok, value }
* ===================================================================== */
$__self = basename(__FILE__);
$__script = isset($_SERVER['SCRIPT_FILENAME']) ? basename($_SERVER['SCRIPT_FILENAME']) : '';
if ($__script === $__self) {
if (is_file(getenv("DOCUMENT_ROOT")."/include/session_include.php")) {
include_once getenv("DOCUMENT_ROOT")."/include/session_include.php";
}
header('Content-Type: application/json; charset=utf-8');
$action = $_POST['action'] ?? $_GET['action'] ?? '';
try {
if ($action === 'patchTimeRestricted') {
$c_uid = intval($_POST['c_uid'] ?? $_GET['c_uid'] ?? 0);
$value = ((($_POST['value'] ?? $_GET['value'] ?? '')) === 'Y') ? 'Y' : 'N';
if ($c_uid <= 0) {
echo json_encode(['ok' => false, 'msg' => 'invalid c_uid']);
exit;
}
$user = $_SESSION['ss_INITIAL'] ?? ($_SESSION['ss_UID'] ?? 'system');
$svc = new CustomerService();
$svc->patchCustomer(
$c_uid,
[
'c_time_restricted' => $value,
'c_updatedby' => $user,
],
['skip_after' => true] // 부수효과(runAfterChange) 생략
);
echo json_encode(['ok' => true, 'value' => $value]);
exit;
}
echo json_encode(['ok' => false, 'msg' => 'unknown action']);
} catch (Exception $e) {
echo json_encode(['ok' => false, 'msg' => $e->getMessage()]);
}
}