dbmsv4 init...5
This commit is contained in:
parent
fbc62329ae
commit
0c780611c9
@ -27,11 +27,11 @@ class Layout extends BaseConfig
|
||||
],
|
||||
'stylesheets' => [
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
|
||||
'<link href="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">',
|
||||
'<link rel="stylesheet" href="/css/common/style.css" />',
|
||||
],
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"></script>',
|
||||
],
|
||||
'footerScripts' => []
|
||||
],
|
||||
@ -56,13 +56,13 @@ class Layout extends BaseConfig
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="/css/common/style.css" />',
|
||||
],
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
|
||||
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
|
||||
],
|
||||
'footerScripts' => []
|
||||
@ -87,7 +87,7 @@ class Layout extends BaseConfig
|
||||
'<link rel="icon" href="/favicon.ico">',
|
||||
'<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css">',
|
||||
'<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />',
|
||||
'<link rel="stylesheet" href="/assets/tagify/dist/tagify.css">',
|
||||
@ -96,7 +96,7 @@ class Layout extends BaseConfig
|
||||
'javascripts' => [
|
||||
'<script src="//cdn.jsdelivr.net/npm/jquery@3.7.0/dist/jquery.min.js"></script>',
|
||||
'<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>',
|
||||
'<script src="//cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>',
|
||||
'<script src="/assets/tinymce/tinymce.min.js" referrerpolicy="origin"></script>',
|
||||
'<script src="/assets/tagify/dist/tagify.js"></script>'
|
||||
|
||||
@ -92,7 +92,7 @@ class ServiceController extends CustomerController
|
||||
try {
|
||||
$db->transException(true)->transStart();
|
||||
//추가할 대체서버 정의
|
||||
$serverinfo_uid = $this->request->getGet('serverinfo_uid');
|
||||
$serverinfo_uid = $this->request->getPost('serverinfo_uid');
|
||||
if (!$serverinfo_uid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 변경할 대체서버가 정의되지 않았습니다.");
|
||||
}
|
||||
@ -105,7 +105,7 @@ class ServiceController extends CustomerController
|
||||
service('equipment_serverservice')->attatchToService($entity, $serverinfo_uid);
|
||||
// 트랜잭션 완료 및 커밋
|
||||
$db->transComplete();
|
||||
return $this->action_redirect_process('info', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가가 완료되었습니다.");
|
||||
return static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가가 완료되었습니다.";
|
||||
} catch (\Throwable $e) {
|
||||
$db->transRollback(); // 예외 발생 시 수동으로 롤백
|
||||
return $this->action_redirect_process('error', static::class . '->' . __FUNCTION__ . "에서 {$this->getTitle()} 대체서버 추가 오류:" . $e->getMessage());
|
||||
|
||||
@ -7,7 +7,6 @@ use Psr\Log\LoggerInterface;
|
||||
use CodeIgniter\HTTP\RedirectResponse;
|
||||
use CodeIgniter\HTTP\RequestInterface;
|
||||
use CodeIgniter\HTTP\ResponseInterface;
|
||||
use App\Entities\Equipment\ServerEntity;
|
||||
|
||||
class ServerController extends EquipmentController
|
||||
{
|
||||
|
||||
@ -12,7 +12,6 @@ class PaymentEntity extends CommonEntity
|
||||
protected array $nullableFields = [
|
||||
'serviceinfo_uid',
|
||||
'serverpartinfo_uid',
|
||||
'pay',
|
||||
];
|
||||
protected $attributes = [
|
||||
'serviceinfo_uid' => null,
|
||||
@ -22,7 +21,7 @@ class PaymentEntity extends CommonEntity
|
||||
'billing' => "",
|
||||
'billing_at' => "",
|
||||
'billing_month' => 0,
|
||||
'pay' => null,
|
||||
'pay' => "",
|
||||
'status' => '',
|
||||
'content' => ''
|
||||
];
|
||||
@ -67,7 +66,7 @@ class PaymentEntity extends CommonEntity
|
||||
{
|
||||
return $this->billing_month ?? 0;
|
||||
}
|
||||
public function getPay(): int|null
|
||||
public function getPay(): string|null
|
||||
{
|
||||
return $this->pay ?? null;
|
||||
}
|
||||
|
||||
@ -238,7 +238,6 @@ abstract class CommonService
|
||||
try {
|
||||
// INSERT 시 Entity의 PK는 0 또는 NULL이어야 함 (DB가 ID를 생성하도록)
|
||||
$initialPK = $entity->getPK();
|
||||
// dd($entity);
|
||||
$result = $this->model->save($entity);
|
||||
// 최종적으로 DB에 반영된 PK를 반환받습니다. (UPDATE이면 기존 PK, INSERT이면 새 PK)
|
||||
$entity->{$this->getPKField()} = $this->handle_save_result($result, $initialPK);
|
||||
|
||||
@ -79,16 +79,23 @@ class ServiceService extends CustomerService
|
||||
|
||||
final protected function updateAmount(ServiceEntity $entity): ServiceEntity
|
||||
{
|
||||
$serverUid = $entity->getServerInfoUid();
|
||||
// ✅ 서버 미지정(해지/분리 상태) 방어: 계산 시도 자체를 막는다
|
||||
if (empty($serverUid)) { // null, 0, '' 모두 방어
|
||||
throw new RuntimeException(
|
||||
static::class . '->' . __FUNCTION__
|
||||
. " 오류: 서비스({$entity->getPK()})에 serverinfo_uid가 없습니다. (해지/분리 상태)"
|
||||
);
|
||||
}
|
||||
$entity->amount = $this->getCalculatedAmount(
|
||||
$entity->getRack(),
|
||||
$entity->getLine(),
|
||||
$entity->getSale(),
|
||||
$entity->getServerInfoUid()
|
||||
(int) $serverUid
|
||||
);
|
||||
|
||||
// dd($entity);
|
||||
if (!$this->model->save($entity)) {
|
||||
$errors = $this->model->errors();
|
||||
throw new RuntimeException("금액 업데이트 중 DB 저장 오류: " . implode(', ', $errors));
|
||||
throw new RuntimeException("금액 업데이트 중 DB 저장 오류: " . implode(', ', $this->model->errors()));
|
||||
}
|
||||
return $entity;
|
||||
}
|
||||
@ -117,7 +124,7 @@ class ServiceService extends CustomerService
|
||||
final public function recalcAmountAndSyncPaymentInternal(ServiceEntity $old, ServiceEntity $current): ServiceEntity
|
||||
{
|
||||
$current = $this->updateAmount($current);
|
||||
service('paymentservice')->upsertByService($old, $current);
|
||||
service('paymentservice')->setByService($old, $current);
|
||||
return $current;
|
||||
}
|
||||
|
||||
@ -163,15 +170,11 @@ class ServiceService extends CustomerService
|
||||
|
||||
protected function modify_process($entity, array $formDatas): ServiceEntity
|
||||
{
|
||||
if (empty($formDatas['site'])) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 사이트가 지정되지 않았습니다.");
|
||||
}
|
||||
if (empty($formDatas['serverinfo_uid'])) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 서버가 지정되지 않았습니다.");
|
||||
}
|
||||
|
||||
$oldEntity = clone $entity;
|
||||
|
||||
$formDatas['code'] = $entity->getCode();
|
||||
$formDatas['amount'] = 0;
|
||||
|
||||
|
||||
@ -50,14 +50,14 @@ class ServerPartService extends EquipmentService
|
||||
return $entity;
|
||||
}
|
||||
|
||||
private function getBillingAtByServiceEntity(ServiceEntity $serviceEntity, string $billing): string
|
||||
private function getBillingAtByServiceEntity(ServiceEntity $serviceEntity, string $billing): ?string
|
||||
{
|
||||
if ($billing === PAYMENT['BILLING']['MONTH']) {
|
||||
return $serviceEntity->getBillingAt();
|
||||
} elseif ($billing === PAYMENT['BILLING']['ONETIME']) {
|
||||
return date("Y-m-d");
|
||||
}
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$billing}에 해당하는 청구방식을 찾을수 없습니다.");
|
||||
return null;
|
||||
}
|
||||
|
||||
private function setPartTitleByPartEntity(array $formDatas): string
|
||||
@ -86,6 +86,8 @@ class ServerPartService extends EquipmentService
|
||||
|
||||
$formDatas['title'] = $this->setPartTitleByPartEntity($formDatas);
|
||||
|
||||
// ✅ ServerPart는 serverinfo_uid 기준으로 움직인다.
|
||||
// serviceinfo_uid는 serverEntity의 "현재 상태"에서만 참고한다.
|
||||
$serviceEntity = null;
|
||||
if ($serverEntity->getServiceInfoUid()) {
|
||||
$serviceEntity = service('customer_serviceservice')->getEntity($serverEntity->getServiceInfoUid());
|
||||
@ -104,34 +106,53 @@ class ServerPartService extends EquipmentService
|
||||
|
||||
/**
|
||||
* ✅ MONTH 파트 변경 시:
|
||||
* - "서비스가 유지중"이면: amount 재계산 + 월 Payment upsert (internal)
|
||||
* - "서버가 서비스에서 분리된 상태(server.serviceinfo_uid==null)"이면: amount 재계산만 + 월 Payment는 TERMINATED 처리
|
||||
* - "서비스가 유지중"이면: (serverinfo_uid 기준) 현재 서버가 붙은 서비스로 amount 재계산 + 월 Payment upsert
|
||||
* - "서버가 서비스에서 분리된 상태(server.serviceinfo_uid==null)"이면: 월 미납 결제 status=TERMINATED (amount 재계산 금지)
|
||||
*
|
||||
* 핵심: ServerPartService는 'serviceUid'가 아니라 'serverUid'를 기준으로 판단한다.
|
||||
*/
|
||||
private function syncMonthlyServiceAndPayment(?int $serviceUid, ?ServiceEntity $oldServiceSnapshot = null, bool $serverDetached = false): void
|
||||
{
|
||||
if (!$serviceUid)
|
||||
return;
|
||||
|
||||
$svcService = service('customer_serviceservice');
|
||||
|
||||
$current = $svcService->getEntity($serviceUid);
|
||||
if (!$current instanceof ServiceEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serviceUid}에 해당하는 서비스정보을 찾을수 없습니다.");
|
||||
private function syncMonthlyServiceAndPaymentByServer(
|
||||
int $serverUid,
|
||||
?ServiceEntity $oldServiceSnapshot = null,
|
||||
bool $serverDetached = false
|
||||
): void {
|
||||
$server = service('equipment_serverservice')->getEntity($serverUid);
|
||||
if (!$server instanceof ServerEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serverUid} 서버정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
$old = $oldServiceSnapshot instanceof ServiceEntity ? $oldServiceSnapshot : clone $current;
|
||||
|
||||
// ✅ 해지 케이스
|
||||
if ($serverDetached) {
|
||||
// ✅ 해지 케이스: payment upsert 금지(0원화 방지), 월 미납은 TERMINATED
|
||||
$svcService->recalcAmountInternal($current);
|
||||
service('paymentservice')->terminateUnpaidMonthlyByService($old);
|
||||
if ($oldServiceSnapshot instanceof ServiceEntity) {
|
||||
service('paymentservice')->terminateUnpaidMonthlyByService($oldServiceSnapshot);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$serviceUid = $server->getServiceInfoUid();
|
||||
if (!$serviceUid) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: 서비스가 정의되지 않은 서버정보입니다.");
|
||||
}
|
||||
|
||||
$svcService = service('customer_serviceservice');
|
||||
$current = $svcService->getEntity($serviceUid);
|
||||
if (!$current instanceof ServiceEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$serviceUid} 서비스정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
// ✅ 핵심 추가: 메인서버만 amount/payment 동기화 허용
|
||||
if ((int) $current->getServerInfoUid() !== (int) $serverUid) {
|
||||
// 대체서버(alternative)에서는 서비스금액/결제 동기화 금지
|
||||
return; // 또는 정책상 필요하면 예외로 변경 가능
|
||||
// throw new RuntimeException("메인서버가 아닌 서버({$serverUid})에서 월동기화 금지");
|
||||
}
|
||||
|
||||
// ✅ 일반 케이스: amount + payment upsert
|
||||
$old = $oldServiceSnapshot instanceof ServiceEntity ? $oldServiceSnapshot : clone $current;
|
||||
$svcService->recalcAmountAndSyncPaymentInternal($old, $current);
|
||||
}
|
||||
|
||||
|
||||
protected function create_process(array $formDatas): CommonEntity
|
||||
{
|
||||
$serverEntity = service('equipment_serverservice')->getEntity($formDatas['serverinfo_uid']);
|
||||
@ -146,18 +167,18 @@ class ServerPartService extends EquipmentService
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: Return Type은 ServerPartEntity만 가능");
|
||||
}
|
||||
|
||||
// dd($entity);
|
||||
$this->getPartService($entity->getType())->attachToServerPart($entity);
|
||||
|
||||
// ✅ 서버가 서비스에 붙어 있을 때만 결제/동기화
|
||||
if ($entity->getServiceInfoUid()) {
|
||||
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
|
||||
$this->syncMonthlyServiceAndPayment($entity->getServiceInfoUid(), null, false);
|
||||
$this->syncMonthlyServiceAndPaymentByServer((int) $entity->getServerInfoUid());
|
||||
}
|
||||
|
||||
if ($entity->getBilling() == PAYMENT['BILLING']['ONETIME']) {
|
||||
service('paymentservice')->createByServerPart($entity);
|
||||
}
|
||||
}
|
||||
|
||||
return $entity;
|
||||
}
|
||||
|
||||
@ -179,17 +200,18 @@ class ServerPartService extends EquipmentService
|
||||
|
||||
$this->getPartService($entity->getType())->modifyServerPart($oldEntity, $entity);
|
||||
|
||||
$serviceUidsToSync = [];
|
||||
// ✅ MONTH 동기화는 serviceinfo_uid가 아니라 serverinfo_uid 기준
|
||||
$serverUidsToSync = [];
|
||||
|
||||
if ($oldEntity->getServiceInfoUid() && $oldEntity->getBilling() == PAYMENT['BILLING']['MONTH']) {
|
||||
$serviceUidsToSync[(int) $oldEntity->getServiceInfoUid()] = true;
|
||||
if ($oldEntity->getBilling() == PAYMENT['BILLING']['MONTH'] && $oldEntity->getServerInfoUid()) {
|
||||
$serverUidsToSync[(int) $oldEntity->getServerInfoUid()] = true;
|
||||
}
|
||||
if ($entity->getServiceInfoUid() && $entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
|
||||
$serviceUidsToSync[(int) $entity->getServiceInfoUid()] = true;
|
||||
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH'] && $entity->getServerInfoUid()) {
|
||||
$serverUidsToSync[(int) $entity->getServerInfoUid()] = true;
|
||||
}
|
||||
|
||||
foreach (array_keys($serviceUidsToSync) as $svcUid) {
|
||||
$this->syncMonthlyServiceAndPayment((int) $svcUid, null, false);
|
||||
foreach (array_keys($serverUidsToSync) as $serverUid) {
|
||||
$this->syncMonthlyServiceAndPaymentByServer((int) $serverUid);
|
||||
}
|
||||
|
||||
if ($entity->getBilling() == PAYMENT['BILLING']['ONETIME']) {
|
||||
@ -214,8 +236,9 @@ class ServerPartService extends EquipmentService
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: Return Type은 ServerPartEntity만 가능");
|
||||
}
|
||||
|
||||
if ($old->getServiceInfoUid() !== null && $old->getBilling() == PAYMENT['BILLING']['MONTH']) {
|
||||
$this->syncMonthlyServiceAndPayment($old->getServiceInfoUid(), null, false);
|
||||
// ✅ MONTH면 서버 기준으로 서비스/결제 동기화
|
||||
if ($old->getBilling() == PAYMENT['BILLING']['MONTH'] && $old->getServerInfoUid()) {
|
||||
$this->syncMonthlyServiceAndPaymentByServer((int) $old->getServerInfoUid());
|
||||
}
|
||||
|
||||
// ✅ ONETIME 미납 결제 삭제는 "보류" (여기서는 아무것도 안함)
|
||||
@ -256,61 +279,37 @@ class ServerPartService extends EquipmentService
|
||||
|
||||
/**
|
||||
* ✅ 서버 해지/분리 시 서버파트 회수 처리
|
||||
* - MONTH 파트 삭제되면:
|
||||
* - server.serviceinfo_uid == null (분리 완료) 인 경우에만:
|
||||
* - 월 미납 payment는 삭제/0원수정 금지, status=TERMINATED
|
||||
* - service.amount는 재계산만(결제 upsert 금지)
|
||||
* - BASE 제외 전부 삭제(정책상 OK)
|
||||
* - MONTH 삭제가 있었다면:
|
||||
* - server.serviceinfo_uid == null 인 상태에서만 TERMINATED 처리 (amount 재계산 금지)
|
||||
* - ONETIME 미납 삭제는 보류
|
||||
*
|
||||
* @param ServiceEntity|null $oldServiceEntity 분리 전 서비스 스냅샷(필수 권장)
|
||||
* @param ServiceEntity|null $oldServiceEntity 분리 전 서비스 스냅샷(권장: TERMINATED 매칭용)
|
||||
*/
|
||||
public function detachFromServer(ServerEntity $serverEntity, ?ServiceEntity $oldServiceEntity = null): void
|
||||
{
|
||||
// 서버가 분리되기 전 serviceinfo_uid를 잃을 수 있으므로, oldServiceEntity로 serviceUid 확보
|
||||
$serviceUid = $oldServiceEntity instanceof ServiceEntity ? $oldServiceEntity->getPK() : $serverEntity->getServiceInfoUid();
|
||||
|
||||
$monthlyChanged = false;
|
||||
|
||||
foreach ($this->getEntities([
|
||||
'serverinfo_uid' => $serverEntity->getPK(),
|
||||
"billing !=" => PAYMENT['BILLING']['BASE']
|
||||
]) as $entity) {
|
||||
|
||||
foreach ($this->getEntities(['serverinfo_uid' => $serverEntity->getPK(), "billing !=" => PAYMENT['BILLING']['BASE']]) as $entity) {
|
||||
if (!$entity instanceof ServerPartEntity)
|
||||
continue;
|
||||
|
||||
$this->getPartService($entity->getType())->detachFromServerPart($entity);
|
||||
|
||||
if ($entity->getBilling() == PAYMENT['BILLING']['MONTH']) {
|
||||
$monthlyChanged = true;
|
||||
}
|
||||
|
||||
// ✅ 일괄 삭제(추가 동기화는 아래에서 한 번만)
|
||||
parent::delete_process($entity);
|
||||
}
|
||||
|
||||
if (!$monthlyChanged || !$serviceUid) {
|
||||
if (!$monthlyChanged) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ "서버가 서비스에서 분리된 경우"에만 payment TERMINATED 처리
|
||||
// ✅ 분리 완료 후(server.serviceinfo_uid == null)에서만 TERMINATED 처리
|
||||
$serverDetached = ($serverEntity->getServiceInfoUid() === null);
|
||||
|
||||
if ($serverDetached) {
|
||||
// oldServiceEntity가 없으면(호환 호출), 여기서라도 스냅샷 시도(가능하면)
|
||||
if (!$oldServiceEntity instanceof ServiceEntity) {
|
||||
$svc = service('customer_serviceservice')->getEntity($serviceUid);
|
||||
if ($svc instanceof ServiceEntity) {
|
||||
$oldServiceEntity = clone $svc;
|
||||
}
|
||||
}
|
||||
if ($oldServiceEntity instanceof ServiceEntity) {
|
||||
$this->syncMonthlyServiceAndPayment($serviceUid, $oldServiceEntity, true);
|
||||
$this->syncMonthlyServiceAndPaymentByServer((int) $serverEntity->getPK(), $oldServiceEntity, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 서비스 유지중인 경우: amount + payment upsert
|
||||
$this->syncMonthlyServiceAndPayment($serviceUid, null, false);
|
||||
// ✅ (이 케이스는 정상 플로우에서는 거의 없지만) 서비스 유지중이면 정상 upsert
|
||||
$this->syncMonthlyServiceAndPaymentByServer((int) $serverEntity->getPK());
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,73 @@ class ServerService extends EquipmentService
|
||||
return ServerEntity::class;
|
||||
}
|
||||
|
||||
final public function getTotalServiceCount(array $where = []): array
|
||||
{
|
||||
$totalCounts = [
|
||||
'chiba_summary' => 0,
|
||||
'tokyo_summary' => 0,
|
||||
'all_summary' => 0,
|
||||
'normal' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'defence' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'dedicated' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'alternative' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'vpn' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'event' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
'test' => ['chiba' => 0, 'tokyo' => 0, 'summary' => 0],
|
||||
];
|
||||
$builder = $this->model->select("serverinfo.type, COUNT(CASE WHEN serviceinfo.location = 'chiba' THEN 1 END) AS chiba, COUNT(CASE WHEN serviceinfo.location = 'tokyo' THEN 1 END) AS tokyo, COUNT(CASE WHEN serviceinfo.location IN ('chiba', 'tokyo') THEN 1 END) AS summary")
|
||||
->join('serviceinfo', 'serviceinfo.uid = serverinfo.serviceinfo_uid')
|
||||
->where($where)
|
||||
->groupBy('serverinfo.type')
|
||||
->builder();
|
||||
// echo $builder->getCompiledSelect(false) . "<BR>";
|
||||
//초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
|
||||
// dd($rows);
|
||||
foreach ($builder->get()->getResult() as $row) {
|
||||
$totalCounts[$row->type]['chiba'] = $row->chiba;
|
||||
$totalCounts[$row->type]['tokyo'] = $row->tokyo;
|
||||
$totalCounts[$row->type]['summary'] += $row->summary;
|
||||
$totalCounts['chiba_summary'] += $row->chiba;
|
||||
$totalCounts['tokyo_summary'] += $row->tokyo;
|
||||
$totalCounts['all_summary'] = $totalCounts['chiba_summary'] + $totalCounts['tokyo_summary'];
|
||||
}
|
||||
// dd($totalCounts);
|
||||
return $totalCounts;
|
||||
}
|
||||
//전체 검색어에 따른 서버정보를 검색 후 해당하는 서비스리스트를 가져온다.
|
||||
final public function getSearchServices(string $keyword): array
|
||||
{
|
||||
$builder = $this->model->distinct()->select('serverinfo.serviceinfo_uid AS serviceinfo_uid')
|
||||
->join('clientinfo', 'clientinfo.uid = serverinfo.clientinfo_uid')
|
||||
->join('serverpartinfo', 'serverpartinfo.clientinfo_uid = clientinfo.uid', 'left')
|
||||
->groupStart()
|
||||
->like('clientinfo.name', $keyword, 'both', null, true) // escape=true
|
||||
->orLike('serverinfo.code', $keyword, 'both', null, true)
|
||||
->orLike('serverinfo.ip', $keyword, 'both', null, true)
|
||||
->orLike('serverinfo.title', $keyword, 'both', null, true)
|
||||
->orLike('serverpartinfo.title', $keyword, 'both', null, true)
|
||||
->groupEnd()
|
||||
->builder();
|
||||
// echo $builder->getCompiledSelect(false); //초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
|
||||
$rows = $builder->get()->getResult();
|
||||
if (!count($rows)) {
|
||||
return [];
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
//서버 Title별 카운트수
|
||||
final public function getStockCount(): array
|
||||
{
|
||||
$builder = $this->model->select('title,COUNT(*) AS cnt')->groupBy('title')->builder();
|
||||
// echo $builder->getCompiledSelect(false); //초기화 없이 SQL만 보고 싶을 때: getCompiledSelect(false) ← 꼭 false!
|
||||
// dd($builder->get()->getResult());
|
||||
$rows = [];
|
||||
foreach ($builder->get()->getResult() as $row) {
|
||||
$rows[$row->title] = $row->cnt;
|
||||
}
|
||||
return $rows;
|
||||
}
|
||||
|
||||
//총서버금액
|
||||
final public function getCalculatedAmount(int $uid): int
|
||||
{
|
||||
@ -43,11 +110,10 @@ class ServerService extends EquipmentService
|
||||
if (!$entity instanceof ServerEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ . "에서 오류발생: {$uid} 서버 정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
$serverPartService = service('equipment_serverpartservice');
|
||||
$caculatedAmount = $entity->getPrice();
|
||||
|
||||
foreach ($serverPartService->getEntities(['serverinfo_uid' => $entity->getPK(), 'billing' => PAYMENT['BILLING']['MONTH']]) as $serverPartEntity) {
|
||||
log_message('debug', $serverPartEntity->getCustomTitle() . '::' . $serverPartEntity->getCalculatedAmount());
|
||||
$caculatedAmount += $serverPartEntity->getCalculatedAmount();
|
||||
}
|
||||
return $caculatedAmount;
|
||||
@ -189,4 +255,33 @@ class ServerService extends EquipmentService
|
||||
// - ONETIME 미납 삭제는 보류
|
||||
service('equipment_serverpartservice')->detachFromServer($entity, $oldServiceEntity);
|
||||
}
|
||||
|
||||
// Service 메인서버 교체 시 서버 정합성 유지용
|
||||
public function modifyByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): void
|
||||
{
|
||||
// 기존 메인서버
|
||||
$oldServer = $this->getEntity($oldServiceEntity->getServerInfoUid());
|
||||
if (!$oldServer instanceof ServerEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ .
|
||||
"에서 오류발생: {$oldServiceEntity->getServerInfoUid()} 기존 메인 서버정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
// 신규 메인서버
|
||||
$newServer = $this->getEntity($serviceEntity->getServerInfoUid());
|
||||
if (!$newServer instanceof ServerEntity) {
|
||||
throw new RuntimeException(static::class . '->' . __FUNCTION__ .
|
||||
"에서 오류발생: {$serviceEntity->getServerInfoUid()} 신규 메인 서버정보를 찾을수 없습니다.");
|
||||
}
|
||||
|
||||
// 신규 서버를 서비스에 붙임(occupied 처리 + client/service uid 세팅)
|
||||
// type은 기존 메인서버의 type을 승계 (normal/dedicated/defence 등)
|
||||
$this->attatchToService($serviceEntity, $newServer->getPK(), [
|
||||
'type' => $oldServer->getType()
|
||||
]);
|
||||
|
||||
// 기존 메인서버는 대체 서버로 변경(서비스에 남겨둘지/분리할지는 정책에 따라 다름)
|
||||
// 여기서는 기존 동작 유지: alternative로만 바꿈
|
||||
parent::modify_process($oldServer, ['type' => 'alternative']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -123,22 +123,21 @@ class PaymentService extends CommonService
|
||||
}
|
||||
|
||||
//서비스정보로 결제정보 생성 또는 수정 (일반 운영용: upsert 유지)
|
||||
public function upsertByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): PaymentEntity
|
||||
public function setByService(ServiceEntity $oldServiceEntity, ServiceEntity $serviceEntity): PaymentEntity
|
||||
{
|
||||
$formDatas = $this->getFormDatasFromService($serviceEntity);
|
||||
$entity = $this->getEntity([
|
||||
'serviceinfo_uid' => $oldServiceEntity->getPK(),
|
||||
'billing' => PAYMENT['BILLING']['MONTH'],
|
||||
'billing_at' => $oldServiceEntity->getBillingAt(),
|
||||
'status' => STATUS['UNPAID']
|
||||
]);
|
||||
|
||||
$formDatas = $this->getFormDatasFromService($serviceEntity);
|
||||
|
||||
if (!$entity instanceof PaymentEntity) {
|
||||
return $this->create_process($formDatas);
|
||||
}
|
||||
//매칭되는게 있으면
|
||||
if ($entity instanceof PaymentEntity) {
|
||||
return $this->modify_process($entity, $formDatas);
|
||||
}
|
||||
return $this->create_process($formDatas);
|
||||
}
|
||||
|
||||
//일회성,선결제,쿠폰,포인트 입력 관련
|
||||
protected function create_process(array $formDatas): PaymentEntity
|
||||
|
||||
@ -91,7 +91,7 @@ trait UtilTrait
|
||||
final public function alertTrait(string $msg, $url = null): string
|
||||
{
|
||||
$safeMsg = json_encode($msg, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG);
|
||||
$js = "alert({$safeMsg});";
|
||||
$js = "console.log({$safeMsg}); alert({$safeMsg});";
|
||||
switch ($url) {
|
||||
case 'close':
|
||||
$js .= "window.close();";
|
||||
@ -110,7 +110,8 @@ trait UtilTrait
|
||||
|
||||
final public function getPasswordStringTrait($length = 8, $characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
{
|
||||
return substr(str_shuffle($characters), 0, $length);;
|
||||
return substr(str_shuffle($characters), 0, $length);
|
||||
;
|
||||
}
|
||||
|
||||
// byte값을 알아보기 쉽게 변환
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
<?= session('message') ? session('message') : ""; ?>
|
||||
<div class="rounded border border-gray p-2 mt-3">
|
||||
<div style="font-size:15px">미지급 1회성정보</div>
|
||||
<table class="table table-bordered table-hover table-striped">
|
||||
@ -17,16 +18,28 @@
|
||||
<?php $paymentCellDatas['entity'] = $entity ?>
|
||||
<tr class="text-left">
|
||||
<td><?= $cnt ?></td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('serviceinfo_uid', $entity->getServiceInfoUid(), $paymentCellDatas) ?></td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('serviceinfo_uid', $entity->getServiceInfoUid(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('title', $entity->getTitle(), $paymentCellDatas) ?>
|
||||
<div><?= $paymentCellDatas['helper']->getFieldView('content', $entity->getContent(), $paymentCellDatas) ?></div>
|
||||
</td>
|
||||
<td class="text-end"><?= $paymentCellDatas['helper']->getFieldView('amount', $entity->getAmount(), $paymentCellDatas) ?></td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('billing', $entity->getBilling(), $paymentCellDatas) ?></td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('pay', $entity->getPay(), $paymentCellDatas) ?></td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('create_at', $entity->getCreatedAt(), $paymentCellDatas) ?></td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getFieldView('user_uid', $entity->getUserUid(), $paymentCellDatas) ?></td>
|
||||
<td class="text-end">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('amount', $entity->getAmount(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('billing', $entity->getBilling(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('pay', $entity->getPay(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('create_at', $entity->getCreatedAt(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?= $paymentCellDatas['helper']->getFieldView('user_uid', $entity->getUserUid(), $paymentCellDatas) ?>
|
||||
</td>
|
||||
<td class="text-center"><?= $paymentCellDatas['helper']->getListButton('paid', "", $paymentCellDatas) ?></td>
|
||||
</tr>
|
||||
<?php $cnt-- ?>
|
||||
|
||||
@ -2,26 +2,12 @@
|
||||
<?php foreach ($serverCellDatas['entities'] as $entity): ?>
|
||||
<?php $serverCellDatas['entity'] = $entity ?>
|
||||
<tr class="text-center">
|
||||
<th style="width: 250px">
|
||||
<th style="width: 150px">
|
||||
<?= $serverCellDatas['serviceEntity']->getServerInfoUid() == $entity->getPK() ? "📌" : "<a href=\"/admin/customer/service/changeServer/{$serverCellDatas['serviceEntity']->getPK()}?serverinfo_uid={$entity->getPK()}\">✔️</a>" ?>
|
||||
<?= $serverCellDatas['serverPartHelper']->getFieldView('SERVER', "", ['serverEntity' => $entity]) ?>
|
||||
<?= "<a href=\"/admin/customer/service/terminateServer/{$serverCellDatas['serviceEntity']->getPK()}?serverinfo_uid={$entity->getPK()}\">❌</a>" ?>
|
||||
</th>
|
||||
<th style=" width: 250px">
|
||||
<?= $serverCellDatas['serverPartHelper']->getListButton('CPU', 'CPU', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
/ <?= $serverCellDatas['serverPartHelper']->getListButton('RAM', 'RAM', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
/
|
||||
<?= $serverCellDatas['serverPartHelper']->getListButton('DISK', 'DISK', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
</th>
|
||||
<th style="width: 200px">
|
||||
<?= $serverCellDatas['serverPartHelper']->getListButton('IP', '추가IP', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
</th>
|
||||
<th style="width: 200px">
|
||||
<?= $serverCellDatas['serverPartHelper']->getListButton('CS', 'CS', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
</th>
|
||||
<th style="width: 200px">
|
||||
<?= $serverCellDatas['serverPartHelper']->getListButton('SOFTWARE', 'SOFTWARE', ['serverinfo_uid' => $entity->getPK()]) ?>
|
||||
</th>
|
||||
<th>파트정보</th>
|
||||
</tr>
|
||||
<tr class="text-center">
|
||||
<td nowrap>
|
||||
@ -38,11 +24,12 @@
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= view_cell("\App\Cells\Equipment\ServerPartCell::parttable", [
|
||||
'serverinfo_uid' => $entity->getPK(),
|
||||
'types' => SERVERPART['ALL_PARTTYPES'],
|
||||
'template' => 'detail'
|
||||
'types' => SERVERPART['ALL_PARTTYPES']
|
||||
]) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
@ -15,12 +15,22 @@
|
||||
<table class="table table-bordered table-striped m-0 p-0">
|
||||
<?php foreach ($serverPartCellDatas['types'] as $type): ?>
|
||||
<tr class="m-0 p-0">
|
||||
<th class="text-end m-0 p-0" width="15%"><?= $serverPartCellDatas['helper']->getListButton($type, '', $serverPartCellDatas) ?></th>
|
||||
<td class="text-start m-0 p-0">
|
||||
<th class="text-end m-0 p-0" width="10%">
|
||||
<?= $serverPartCellDatas['helper']->getListButton($type, '', $serverPartCellDatas) ?>
|
||||
</th>
|
||||
<th>
|
||||
<table class="table table-striped m-0 p-0">
|
||||
<?php foreach ($htmls[$type] as $html): ?>
|
||||
<?= $html['view'] ?>[<?= number_format($html['amount']) ?>원]<?= $html['entity']->getBilling() == PAYMENT['BILLING']['BASE'] ? "" : "<a href=\"/admin/equipment/serverpart/delete/{$html['entity']->getPK()}\">❌</a>" ?><BR>
|
||||
<?php endforeach ?>
|
||||
<tr>
|
||||
<td class="text-start m-0 p-0"><?= $html['view'] ?></td>
|
||||
<td class="text-end m-0 p-0 w-25"><?= number_format($html['amount']) ?>원</td>
|
||||
<td class="text-center m-0 p-0 w-5">
|
||||
<?= $html['entity']->getBilling() == PAYMENT['BILLING']['BASE'] ? "" : "<a href=\"/admin/equipment/serverpart/delete/{$html['entity']->getPK()}\">❌</a>" ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
</th>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
Loading…
Reference in New Issue
Block a user