지정판매소 주소·지도 연동과 관련 설정을 반영
지정판매소 등록/수정/목록에 카카오 주소 검색 및 지도 연동 컴포넌트를 적용하고, 관련 모델·SQL 스크립트·테스트 설정을 함께 정리해 기능 동작 기반을 맞췄다. Made-with: Cursor
This commit is contained in:
10
.cursor/rules/dependency-security.mdc
Normal file
10
.cursor/rules/dependency-security.mdc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
description: 패키지 설치 전 승인·안정성 확인 및 공급망 보안 습관
|
||||||
|
alwaysApply: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# 의존성·패키지 보안
|
||||||
|
|
||||||
|
- **새 패키지(npm, Composer 등)를 설치·추가하기 전에 반드시 사용자에게 먼저 물어본다.** 자동으로 `npm install`, `composer require` 등을 실행하지 않는다(사용자가 명시적으로 요청한 경우만).
|
||||||
|
- 새 버전을 제안할 때는 **공식 레지스트리(npmjs.org, packagist.org) 출처**인지 확인하고, **출시된 지 최소 며칠(가이드: 7일) 이상 지난 안정(stable) 버전**을 우선 제안한다. 방금 출시된 버전은 typosquat·피싱 패키지 위험이 있어 사용자에게 그 점을 짚어 준다.
|
||||||
|
- 락 파일(`package-lock.json`, `composer.lock`)을 대량 수정하거나 생소한 패키지를 넣지 않는다. 이상하면 사용자에게 중단하고 확인을 요청한다.
|
||||||
26
app/Config/Kakao.php
Normal file
26
app/Config/Kakao.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Config;
|
||||||
|
|
||||||
|
use CodeIgniter\Config\BaseConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 카카오 Developers — 내 애플리케이션 — 앱 키 — JavaScript 키
|
||||||
|
* .env 예: kakao.javascriptKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
*/
|
||||||
|
class Kakao extends BaseConfig
|
||||||
|
{
|
||||||
|
public string $javascriptKey = '';
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$v = env('kakao.javascriptKey');
|
||||||
|
if (is_string($v) && $v !== '') {
|
||||||
|
$this->javascriptKey = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,16 +17,25 @@ class DesignatedShopModel extends Model
|
|||||||
'ds_name',
|
'ds_name',
|
||||||
'ds_biz_no',
|
'ds_biz_no',
|
||||||
'ds_rep_name',
|
'ds_rep_name',
|
||||||
|
'ds_biz_type',
|
||||||
|
'ds_biz_kind',
|
||||||
'ds_va_number',
|
'ds_va_number',
|
||||||
|
'ds_va_bank',
|
||||||
|
'ds_va_account',
|
||||||
'ds_zip',
|
'ds_zip',
|
||||||
'ds_addr',
|
'ds_addr',
|
||||||
'ds_addr_jibun',
|
'ds_addr_jibun',
|
||||||
|
'ds_addr_detail',
|
||||||
'ds_tel',
|
'ds_tel',
|
||||||
'ds_rep_phone',
|
'ds_rep_phone',
|
||||||
'ds_email',
|
'ds_email',
|
||||||
'ds_gugun_code',
|
'ds_gugun_code',
|
||||||
|
'ds_zone_code',
|
||||||
|
'ds_branch_no',
|
||||||
'ds_designated_at',
|
'ds_designated_at',
|
||||||
'ds_state',
|
'ds_state',
|
||||||
|
'ds_state_changed_at',
|
||||||
|
'ds_change_reason',
|
||||||
'ds_regdate',
|
'ds_regdate',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<span class="text-sm font-bold text-gray-700">지정판매소 등록</span>
|
<span class="text-sm font-bold text-gray-700">지정판매소 등록</span>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
||||||
<form action="<?= mgmt_url('designated-shops/store') ?>" method="POST" class="space-y-4">
|
<form id="designated-shop-create-form" action="<?= mgmt_url('designated-shops/store') ?>" method="POST" class="space-y-4">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
<?php if (! empty($localGovs)): ?>
|
<?php if (! empty($localGovs)): ?>
|
||||||
@@ -23,14 +23,18 @@
|
|||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
||||||
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
||||||
|
<span class="text-gray-500 ml-1"><?= esc(trim((string) ($currentLg->lg_sido ?? '') . ' ' . (string) ($currentLg->lg_gugun ?? ''))) ?></span>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="ds_lg_idx" value="<?= esc($currentLg->lg_idx) ?>"/>
|
<input type="hidden" name="ds_lg_idx" value="<?= esc($currentLg->lg_idx) ?>"/>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<input type="hidden" name="addr_search_sido" id="addr_search_sido" value="<?= esc((string) old('addr_search_sido', '')) ?>"/>
|
||||||
|
<input type="hidden" name="addr_search_sigungu" id="addr_search_sigungu" value="<?= esc((string) old('addr_search_sigungu', '')) ?>"/>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">판매소번호</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">판매소번호</label>
|
||||||
<div class="text-sm text-gray-600">등록 시 자동 부여 (지자체코드 + 일련번호 3자리)</div>
|
<div class="text-sm text-gray-600">등록 시 자동 부여 (주소 기준 기본코드 B·C·D 조합 + 일련번호 3자리)</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
@@ -49,23 +53,50 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">업태</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_number" type="text" value="<?= esc(old('ds_va_number')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_type" type="text" value="<?= esc(old('ds_biz_type')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">업종</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_kind" type="text" value="<?= esc(old('ds_biz_kind')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌(은행)</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_va_bank" type="text" value="<?= esc(old('ds_va_bank')) ?>" placeholder="예: 농협"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">계좌번호</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_account" type="text" value="<?= esc(old('ds_va_account')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-start gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28 pt-1.5">주소</label>
|
||||||
|
<p class="text-xs text-gray-600 max-w-lg leading-relaxed">우편번호 옆 <strong>주소 검색</strong>으로만 지정합니다(직접 입력 불가). <strong>지도</strong>는 카카오맵에서 현재 입력된 주소를 검색해 엽니다. 관할이 아니면 검색 직후 안내 후 반영되지 않습니다. 동·호 등은 아래 <strong>상세주소</strong>에 입력하세요.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32" name="ds_zip" type="text" value="<?= esc(old('ds_zip')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32 bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_zip" id="ds_zip" type="text" value="<?= esc(old('ds_zip')) ?>" maxlength="10" autocomplete="postal-code" readonly tabindex="-1"/>
|
||||||
|
<button type="button" id="btn-ds-kakao-postcode" class="no-print border border-btn-print-border text-gray-700 px-3 py-1.5 rounded-sm text-sm hover:bg-gray-50 transition shrink-0">주소 검색</button>
|
||||||
|
<?= view('components/kakao_map_link_button', ['buttonId' => 'btn-ds-kakao-map-create']) ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr" type="text" value="<?= esc(old('ds_addr')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_addr" id="ds_addr" type="text" value="<?= esc(old('ds_addr')) ?>" readonly tabindex="-1"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr_jibun" type="text" value="<?= esc(old('ds_addr_jibun')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_addr_jibun" id="ds_addr_jibun" type="text" value="<?= esc(old('ds_addr_jibun')) ?>" readonly tabindex="-1"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">상세주소</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full" name="ds_addr_detail" id="ds_addr_detail" type="text" value="<?= esc(old('ds_addr_detail')) ?>" maxlength="200" placeholder="동·호수·층 등"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
@@ -88,11 +119,31 @@
|
|||||||
<div class="text-sm text-gray-600">해당 지자체(구·군) 코드로 등록 시 자동 설정</div>
|
<div class="text-sm text-gray-600">해당 지자체(구·군) 코드로 등록 시 자동 설정</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">구역</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-72" name="ds_zone_code" type="text" value="<?= esc(old('ds_zone_code')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">종사업장번호</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_branch_no" type="text" value="<?= esc(old('ds_branch_no')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc(old('ds_designated_at')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc(old('ds_designated_at')) ?>"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">변경일자</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_state_changed_at" type="date" value="<?= esc(old('ds_state_changed_at')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-start gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28 pt-1.5">변경사유</label>
|
||||||
|
<textarea class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 min-h-[4rem]" name="ds_change_reason" rows="3"><?= esc(old('ds_change_reason')) ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2 pt-2">
|
<div class="flex gap-2 pt-2">
|
||||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
||||||
<a href="<?= mgmt_url('designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
<a href="<?= mgmt_url('designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||||
@@ -100,3 +151,17 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?= view('components/kakao_map_modal', ['kakaoJavascriptKey' => $kakaoJavascriptKey ?? '']) ?>
|
||||||
|
|
||||||
|
<?= view('components/kakao_address_search', [
|
||||||
|
'buttonId' => 'btn-ds-kakao-postcode',
|
||||||
|
'zipName' => 'ds_zip',
|
||||||
|
'roadName' => 'ds_addr',
|
||||||
|
'jibunName' => 'ds_addr_jibun',
|
||||||
|
'sidoFieldName' => 'addr_search_sido',
|
||||||
|
'sigunguFieldName' => 'addr_search_sigungu',
|
||||||
|
'tenantScope' => $addrTenantScope ?? ['lg_sido' => '', 'lg_gugun' => ''],
|
||||||
|
'roadBaseOnly' => true,
|
||||||
|
'detailFieldName' => 'ds_addr_detail',
|
||||||
|
]) ?>
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,35 @@ if ($shop === null) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($key) : ($shop->{$key} ?? $default);
|
$v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($key) : ($shop->{$key} ?? $default);
|
||||||
|
$vaAccountDefault = (isset($shop->ds_va_account) && (string) $shop->ds_va_account !== '')
|
||||||
|
? (string) $shop->ds_va_account
|
||||||
|
: (string) ($shop->ds_va_number ?? '');
|
||||||
|
$dateField = static function (string $key) use ($shop, $v): string {
|
||||||
|
$s = (string) $v($key);
|
||||||
|
if ($s === '' || str_starts_with($s, '0000')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $s;
|
||||||
|
};
|
||||||
?>
|
?>
|
||||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||||
<span class="text-sm font-bold text-gray-700">지정판매소 수정</span>
|
<span class="text-sm font-bold text-gray-700">지정판매소 수정</span>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-3xl">
|
||||||
<form action="<?= mgmt_url('designated-shops/update/' . (int) $shop->ds_idx) ?>" method="POST" class="space-y-4">
|
<form id="designated-shop-edit-form" action="<?= mgmt_url('designated-shops/update/' . (int) $shop->ds_idx) ?>" method="POST" class="space-y-4">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<input type="hidden" name="addr_search_sido" id="addr_search_sido" value="<?= esc((string) old('addr_search_sido', $currentLg !== null ? (string) ($currentLg->lg_sido ?? '') : '')) ?>"/>
|
||||||
|
<input type="hidden" name="addr_search_sigungu" id="addr_search_sigungu" value="<?= esc((string) old('addr_search_sigungu', $currentLg !== null ? (string) ($currentLg->lg_gugun ?? '') : '')) ?>"/>
|
||||||
|
|
||||||
<?php if ($currentLg !== null): ?>
|
<?php if ($currentLg !== null): ?>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">지자체</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">지자체</label>
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
<span class="font-semibold"><?= esc($currentLg->lg_name) ?></span>
|
||||||
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
<span class="text-gray-500 ml-2">(<?= esc($currentLg->lg_code) ?>)</span>
|
||||||
|
<span class="text-gray-500 ml-1"><?= esc(trim((string) ($currentLg->lg_sido ?? '') . ' ' . (string) ($currentLg->lg_gugun ?? ''))) ?></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -49,23 +64,50 @@ $v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($k
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">업태</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_number" type="text" value="<?= esc($v('ds_va_number')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_type" type="text" value="<?= esc($v('ds_biz_type')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">업종</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_biz_kind" type="text" value="<?= esc($v('ds_biz_kind')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">가상계좌(은행)</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_va_bank" type="text" value="<?= esc($v('ds_va_bank')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">계좌번호</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_va_account" type="text" value="<?= esc((old('ds_va_account') !== null && old('ds_va_account') !== '') ? old('ds_va_account') : $vaAccountDefault) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-start gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28 pt-1.5">주소</label>
|
||||||
|
<p class="text-xs text-gray-600 max-w-lg leading-relaxed">우편·도로명·지번은 <strong>주소 검색</strong>으로만 바꿀 수 있습니다(직접 입력 불가). <strong>지도</strong>는 카카오맵에서 현재 입력된 주소를 검색해 엽니다. 관할이 아니면 검색 직후 안내 후 반영되지 않습니다. 동·호 등은 <strong>상세주소</strong>에 입력하세요.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">우편번호</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32" name="ds_zip" type="text" value="<?= esc($v('ds_zip')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32 bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_zip" id="ds_zip" type="text" value="<?= esc($v('ds_zip')) ?>" maxlength="10" readonly tabindex="-1"/>
|
||||||
|
<button type="button" id="btn-ds-kakao-postcode-edit" class="no-print border border-btn-print-border text-gray-700 px-3 py-1.5 rounded-sm text-sm hover:bg-gray-50 transition shrink-0">주소 검색</button>
|
||||||
|
<?= view('components/kakao_map_link_button', ['buttonId' => 'btn-ds-kakao-map-edit']) ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">도로명주소</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr" type="text" value="<?= esc($v('ds_addr')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_addr" id="ds_addr" type="text" value="<?= esc($v('ds_addr')) ?>" readonly tabindex="-1"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">지번주소</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96" name="ds_addr_jibun" type="text" value="<?= esc($v('ds_addr_jibun')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full bg-gray-100 text-gray-800 cursor-not-allowed" name="ds_addr_jibun" id="ds_addr_jibun" type="text" value="<?= esc($v('ds_addr_jibun')) ?>" readonly tabindex="-1"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">상세주소</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 max-w-full" name="ds_addr_detail" id="ds_addr_detail" type="text" value="<?= esc($v('ds_addr_detail')) ?>" maxlength="200" placeholder="동·호수·층 등"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
@@ -83,6 +125,16 @@ $v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($k
|
|||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_email" type="email" value="<?= esc($v('ds_email')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="ds_email" type="email" value="<?= esc($v('ds_email')) ?>"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">구역</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-72" name="ds_zone_code" type="text" value="<?= esc($v('ds_zone_code')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">종사업장번호</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" name="ds_branch_no" type="text" value="<?= esc($v('ds_branch_no')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
<label class="block text-sm font-bold text-gray-700 w-28">지정일자</label>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc($v('ds_designated_at')) ?>"/>
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_designated_at" type="date" value="<?= esc($v('ds_designated_at')) ?>"/>
|
||||||
@@ -97,9 +149,33 @@ $v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($k
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28">변경일자</label>
|
||||||
|
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-40" name="ds_state_changed_at" type="date" value="<?= esc($dateField('ds_state_changed_at')) ?>"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap items-start gap-2">
|
||||||
|
<label class="block text-sm font-bold text-gray-700 w-28 pt-1.5">변경사유</label>
|
||||||
|
<textarea class="border border-gray-300 rounded px-3 py-1.5 text-sm w-96 min-h-[4rem]" name="ds_change_reason" rows="3"><?= esc($v('ds_change_reason')) ?></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2 pt-2">
|
<div class="flex gap-2 pt-2">
|
||||||
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
|
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
|
||||||
<a href="<?= mgmt_url('designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
<a href="<?= mgmt_url('designated-shops') ?>" class="bg-white text-black border border-btn-print-border px-4 py-1.5 rounded-sm text-sm shadow hover:bg-gray-50 transition">목록</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?= view('components/kakao_map_modal', ['kakaoJavascriptKey' => $kakaoJavascriptKey ?? '']) ?>
|
||||||
|
|
||||||
|
<?= view('components/kakao_address_search', [
|
||||||
|
'buttonId' => 'btn-ds-kakao-postcode-edit',
|
||||||
|
'zipName' => 'ds_zip',
|
||||||
|
'roadName' => 'ds_addr',
|
||||||
|
'jibunName' => 'ds_addr_jibun',
|
||||||
|
'sidoFieldName' => 'addr_search_sido',
|
||||||
|
'sigunguFieldName' => 'addr_search_sigungu',
|
||||||
|
'tenantScope' => $addrTenantScope ?? ['lg_sido' => '', 'lg_gugun' => ''],
|
||||||
|
'roadBaseOnly' => true,
|
||||||
|
'detailFieldName' => 'ds_addr_detail',
|
||||||
|
]) ?>
|
||||||
|
|||||||
@@ -1,19 +1,186 @@
|
|||||||
<?= view('components/print_header', ['printTitle' => '지정판매소 목록']) ?>
|
<?php $readOnly = ! empty($readOnly); ?>
|
||||||
|
<?= view('components/print_header', ['printTitle' => $readOnly ? '지정판매소 조회 목록' : '지정판매소 목록']) ?>
|
||||||
|
<style>
|
||||||
|
/* 목록 위 → 지정판매소 정보 아래 (가로 2열 없음) */
|
||||||
|
.ds-split {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
.ds-list-panel {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 42vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.ds-detail-panel {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 12rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background: #f5f5f5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.ds-panel-title {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: linear-gradient(180deg, #fafafa 0%, #e9ecef 100%);
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.ds-summary-bar {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: #fff3cd;
|
||||||
|
border: 1px solid #ffc107;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.ds-row-selected td { background-color: #cce5ff !important; }
|
||||||
|
.ds-detail-inner { padding: 10px; overflow: auto; flex: 1; }
|
||||||
|
/* 원본 지정판매소 정보: 라벨 고정폭 + 2열 값(우측 값이 더 넓음), 주소·개인전화는 전폭 */
|
||||||
|
.ds-detail-form {
|
||||||
|
font-size: 12px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.ds-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 0;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.ds-detail-form > .ds-row:last-child { border-bottom: none; }
|
||||||
|
/* 그 외 2+2 동일 비율 (상호명 | 우편번호 등) */
|
||||||
|
.ds-row-4-even {
|
||||||
|
grid-template-columns: 5.5rem minmax(0, 1fr) 5.5rem minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
/* 판매소번호 전폭 행 — 값을 우편·주소 필드처럼 넓게 */
|
||||||
|
.ds-value-shop-wide {
|
||||||
|
font-weight: 600;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
font-size: 13px;
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
/* 라벨 | 값(나머지 전체) — 도로명·지번·개인전화·이메일 */
|
||||||
|
.ds-row-wide {
|
||||||
|
grid-template-columns: 5.5rem minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
.ds-row-wide-tall .ds-field-value {
|
||||||
|
min-height: 3.25rem;
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
/* 도로명주소 + 카카오맵 버튼 */
|
||||||
|
.ds-field-value-with-map {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.ds-field-value-with-map .ds-addr-text {
|
||||||
|
flex: 1 1 12rem;
|
||||||
|
min-width: 0;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
.ds-field-label {
|
||||||
|
background: #eef2f5;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.ds-field-value {
|
||||||
|
padding: 5px 8px;
|
||||||
|
background: #fff;
|
||||||
|
word-break: break-word;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.ds-row-4-even > *:nth-child(4n) { border-right: none; }
|
||||||
|
.ds-row-wide > .ds-field-value { border-right: none; }
|
||||||
|
.ds-field-hint {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #b91c1c;
|
||||||
|
margin-top: 4px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.ds-row-4-even { grid-template-columns: 5rem 1fr; }
|
||||||
|
}
|
||||||
|
.ds-detail-actions { padding: 10px; border-top: 1px solid #ccc; background: #eee; }
|
||||||
|
.ds-detail-info-wrap { overflow-x: auto; }
|
||||||
|
.ds-detail-info-wrap .data-table th { white-space: nowrap; }
|
||||||
|
.ds-detail-info-wrap th.ds-col-tight-head,
|
||||||
|
.ds-detail-info-wrap td.ds-col-tight-cell {
|
||||||
|
max-width: 6.5rem;
|
||||||
|
width: 6.5rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ds-list-panel .ds-col-tight {
|
||||||
|
max-width: 6rem;
|
||||||
|
width: 6rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ds-list-panel .ds-col-zip {
|
||||||
|
width: 4.5rem;
|
||||||
|
max-width: 5rem;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ds-list-panel .ds-col-addr-list {
|
||||||
|
max-width: 11rem;
|
||||||
|
min-width: 6rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ds-list-panel .ds-col-detail-list {
|
||||||
|
max-width: 8rem;
|
||||||
|
min-width: 4rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.ds-ro-road-btn { margin-left: 6px; vertical-align: middle; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$listBasePath = $readOnly ? 'designated-shops/browse' : 'designated-shops';
|
||||||
|
?>
|
||||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||||
<span class="text-sm font-bold text-gray-700">지정판매소 목록</span>
|
<span class="text-sm font-bold text-gray-700"><?= $readOnly ? '지정판매소 조회' : '지정판매소 관리' ?></span>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
|
<?php if ($readOnly): ?>
|
||||||
<a href="<?= mgmt_url('designated-shops/export') ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
<a href="<?= mgmt_url('designated-shops/export') ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
||||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
<button type="button" onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (! $readOnly): ?>
|
||||||
<a href="<?= mgmt_url('designated-shops/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">지정판매소 등록</a>
|
<a href="<?= mgmt_url('designated-shops/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">지정판매소 등록</a>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<!-- P2-15: 다조건 검색 -->
|
|
||||||
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
||||||
<form method="GET" action="<?= mgmt_url('designated-shops') ?>" class="flex flex-wrap items-center gap-2">
|
<form method="GET" action="<?= mgmt_url($listBasePath) ?>" class="flex flex-wrap items-center gap-2">
|
||||||
|
<span class="text-sm font-semibold text-gray-700 mr-1">지정판매소 검색</span>
|
||||||
<label class="text-sm text-gray-600">상호명</label>
|
<label class="text-sm text-gray-600">상호명</label>
|
||||||
<input type="text" name="ds_name" value="<?= esc($dsName ?? '') ?>" placeholder="상호명 검색" class="border border-gray-300 rounded px-2 py-1 text-sm w-40"/>
|
<input type="text" name="ds_name" value="<?= esc($dsName ?? '') ?>" placeholder="상호명" class="border border-gray-300 rounded px-2 py-1 text-sm w-36"/>
|
||||||
<label class="text-sm text-gray-600">구군코드</label>
|
<label class="text-sm text-gray-600">구군코드</label>
|
||||||
<select name="ds_gugun_code" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
<select name="ds_gugun_code" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||||
<option value="">전체</option>
|
<option value="">전체</option>
|
||||||
@@ -29,48 +196,351 @@
|
|||||||
<option value="3" <?= ($dsState ?? '') === '3' ? 'selected' : '' ?>>직권해지</option>
|
<option value="3" <?= ($dsState ?? '') === '3' ? 'selected' : '' ?>>직권해지</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||||
<a href="<?= mgmt_url('designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">초기화</a>
|
<a href="<?= mgmt_url($listBasePath) ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">초기화</a>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 overflow-auto mt-2">
|
|
||||||
<table class="w-full data-table">
|
<?php
|
||||||
|
$sc = $stateCounts ?? ['total' => 0, 1 => 0, 2 => 0, 3 => 0];
|
||||||
|
?>
|
||||||
|
<div class="ds-summary-bar no-print mx-2 mt-2 rounded-sm">
|
||||||
|
건수 : <?= (int) ($sc['total'] ?? 0) ?>
|
||||||
|
(정상 : <?= (int) ($sc[1] ?? 0) ?> / 폐업 : <?= (int) ($sc[2] ?? 0) ?> / 해지 : <?= (int) ($sc[3] ?? 0) ?>)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ds-split no-print mx-2 mb-2 mt-2 flex-1 min-h-0">
|
||||||
|
<div class="ds-list-panel">
|
||||||
|
<div class="ds-panel-title shrink-0">지정판매소 리스트</div>
|
||||||
|
<div class="overflow-auto flex-1 min-h-0">
|
||||||
|
<table class="w-full data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-14">번호</th>
|
||||||
|
<th class="w-24">구·군</th>
|
||||||
|
<th class="w-24">지정일</th>
|
||||||
|
<th class="w-24">구역</th>
|
||||||
|
<th class="ds-col-tight">대표자명</th>
|
||||||
|
<th class="ds-col-tight">상호명</th>
|
||||||
|
<th class="ds-col-zip">우편번호</th>
|
||||||
|
<th class="text-left">주소</th>
|
||||||
|
<th class="text-left">상세주소</th>
|
||||||
|
<th class="w-28">사업자번호</th>
|
||||||
|
<th class="w-28">전화</th>
|
||||||
|
<th class="w-16">상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="ds-list-body" class="text-right">
|
||||||
|
<?php foreach ($list as $i => $row): ?>
|
||||||
|
<?php
|
||||||
|
$sn = (string) ($row->ds_shop_no ?? '');
|
||||||
|
if (preg_match('/(\d{3})$/', $sn, $m)) {
|
||||||
|
$shortNo = $m[1];
|
||||||
|
} elseif ($sn !== '' && strlen($sn) >= 3) {
|
||||||
|
$shortNo = substr($sn, -3);
|
||||||
|
} else {
|
||||||
|
$shortNo = $sn;
|
||||||
|
}
|
||||||
|
$st = (int) ($row->ds_state ?? 1);
|
||||||
|
$stLabel = $st === 1 ? '' : ($st === 2 ? '폐업' : '해지');
|
||||||
|
$ggLabel = (string) ($row->ds_gugun_code ?? '');
|
||||||
|
$da = $row->ds_designated_at ?? null;
|
||||||
|
$daDisp = ($da !== null && $da !== '' && (string) $da !== '0000-00-00') ? substr((string) $da, 0, 10) : '';
|
||||||
|
$zone = (string) ($row->ds_zone_code ?? '');
|
||||||
|
$zipList = trim((string) ($row->ds_zip ?? ''));
|
||||||
|
$roadL = trim((string) ($row->ds_addr ?? ''));
|
||||||
|
$jibunL = trim((string) ($row->ds_addr_jibun ?? ''));
|
||||||
|
$addrMainList = $roadL !== '' ? $roadL : $jibunL;
|
||||||
|
$addrDetailList = trim((string) ($row->ds_addr_detail ?? ''));
|
||||||
|
?>
|
||||||
|
<tr class="ds-list-row cursor-pointer" data-row-index="<?= (int) $i ?>" role="button" tabindex="0">
|
||||||
|
<td class="text-center"><?= esc($shortNo) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs"><?= esc($ggLabel) ?></td>
|
||||||
|
<td class="text-center text-xs"><?= esc($daDisp) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs"><?= esc($zone) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs ds-col-tight" title="<?= esc($row->ds_rep_name ?? '') ?>"><?= esc($row->ds_rep_name ?? '') ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs ds-col-tight" title="<?= esc($row->ds_name ?? '') ?>"><?= esc($row->ds_name ?? '') ?></td>
|
||||||
|
<td class="text-center text-xs ds-col-zip" title="<?= esc($zipList) ?>"><?= esc($zipList) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs ds-col-addr-list" title="<?= esc($addrMainList) ?>"><?= esc($addrMainList) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs ds-col-detail-list" title="<?= esc($addrDetailList) ?>"><?= esc($addrDetailList) ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs"><?= esc($row->ds_biz_no ?? '') ?></td>
|
||||||
|
<td class="text-left pl-1 text-xs"><?= esc($row->ds_tel ?? '') ?></td>
|
||||||
|
<td class="text-center <?= $st === 2 ? 'text-pink-600 font-medium' : ($st === 3 ? 'text-orange-700' : '') ?>"><?= esc($stLabel) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ds-detail-panel">
|
||||||
|
<div class="ds-panel-title shrink-0">지정판매소 정보</div>
|
||||||
|
<div class="ds-detail-inner" id="ds-detail-box">
|
||||||
|
<p id="ds-detail-placeholder" class="text-sm text-gray-500 py-6 text-center">위 목록에서 행을 선택하세요.</p>
|
||||||
|
<div id="ds-detail-fields" class="hidden">
|
||||||
|
<div class="ds-detail-info-wrap">
|
||||||
|
<table class="w-full data-table text-sm" id="ds-detail-info-table" aria-label="지정판매소 상세">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>판매소번호</th>
|
||||||
|
<th class="ds-col-tight-head">상호명</th>
|
||||||
|
<th>우편번호</th>
|
||||||
|
<th>사업자번호</th>
|
||||||
|
<th>일반전화</th>
|
||||||
|
<th class="ds-col-tight-head">대표자명</th>
|
||||||
|
<th>이메일</th>
|
||||||
|
<th>업태</th>
|
||||||
|
<th>업종</th>
|
||||||
|
<th>지정일자</th>
|
||||||
|
<th>지자체</th>
|
||||||
|
<th>도로명주소</th>
|
||||||
|
<th>지번주소</th>
|
||||||
|
<th>상세주소</th>
|
||||||
|
<th>개인전화</th>
|
||||||
|
<th>구코드</th>
|
||||||
|
<th>구역</th>
|
||||||
|
<th>가상계좌(은행)</th>
|
||||||
|
<th>계좌번호</th>
|
||||||
|
<th>종사업장번호</th>
|
||||||
|
<th>변경일자</th>
|
||||||
|
<th>영업상태</th>
|
||||||
|
<th>등록일시</th>
|
||||||
|
<th>변경사유</th>
|
||||||
|
<th class="no-print w-14">지도</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="text-left" data-ro="ds_shop_no">—</td>
|
||||||
|
<td class="text-left ds-col-tight-cell" data-ro="ds_name">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_zip">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_biz_no">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_tel">—</td>
|
||||||
|
<td class="text-left ds-col-tight-cell" data-ro="ds_rep_name">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_email">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_biz_type">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_biz_kind">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_designated_at">—</td>
|
||||||
|
<td class="text-left" data-ro="lg_name">—</td>
|
||||||
|
<td class="text-left min-w-[10rem]"><span data-ro="ds_addr">—</span></td>
|
||||||
|
<td class="text-left" data-ro="ds_addr_jibun">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_addr_detail">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_rep_phone">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_gugun_code">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_zone_code">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_va_bank">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_va_account">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_branch_no">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_state_changed_at">—</td>
|
||||||
|
<td class="text-left" data-ro="state_label">—</td>
|
||||||
|
<td class="text-left" data-ro="ds_regdate">—</td>
|
||||||
|
<td class="text-left min-w-[8rem]" data-ro="ds_change_reason">—</td>
|
||||||
|
<td class="text-center no-print">
|
||||||
|
<button type="button" class="border border-btn-print-border text-gray-700 px-2 py-0.5 rounded-sm text-xs hover:bg-gray-50" id="ds-ro-map-btn">지도</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php if (! $readOnly): ?>
|
||||||
|
<div class="ds-detail-actions no-print flex flex-wrap items-center gap-3 shrink-0">
|
||||||
|
<a id="ds-edit-link" href="#" class="text-blue-700 hover:underline text-sm font-medium pointer-events-none opacity-40">수정</a>
|
||||||
|
<form id="ds-delete-form" method="POST" action="" class="inline" onsubmit="return confirm('이 지정판매소를 삭제하시겠습니까?');">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<button type="submit" id="ds-delete-btn" class="text-red-600 hover:underline text-sm pointer-events-none opacity-40" disabled>삭제</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (isset($pager)): ?>
|
||||||
|
<div class="mt-2 mb-2 mx-2 no-print"><?= $pager->links() ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?= view('components/kakao_map_modal', ['kakaoJavascriptKey' => $kakaoJavascriptKey ?? '']) ?>
|
||||||
|
|
||||||
|
<script type="application/json" id="ds-detail-json"><?= $detailRowsJson ?? '[]' ?></script>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var raw = document.getElementById('ds-detail-json');
|
||||||
|
var rows = [];
|
||||||
|
try {
|
||||||
|
rows = JSON.parse(raw.textContent || '[]');
|
||||||
|
} catch (e) {
|
||||||
|
rows = [];
|
||||||
|
}
|
||||||
|
var readOnly = <?= json_encode($readOnly) ?>;
|
||||||
|
var body = document.getElementById('ds-list-body');
|
||||||
|
var placeholder = document.getElementById('ds-detail-placeholder');
|
||||||
|
var fieldsWrap = document.getElementById('ds-detail-fields');
|
||||||
|
var infoTable = document.getElementById('ds-detail-info-table');
|
||||||
|
var editLink = readOnly ? null : document.getElementById('ds-edit-link');
|
||||||
|
var delForm = readOnly ? null : document.getElementById('ds-delete-form');
|
||||||
|
var delBtn = readOnly ? null : document.getElementById('ds-delete-btn');
|
||||||
|
// mgmt_url() 이 path 를 trim 하므로 'edit/33' 이 아니라 'edit33' 로 붙지 않게 슬래시를 넣음
|
||||||
|
var editBase = <?= json_encode(mgmt_url('designated-shops/edit')) ?>;
|
||||||
|
var delBase = <?= json_encode(mgmt_url('designated-shops/delete')) ?>;
|
||||||
|
|
||||||
|
function textVal(v) {
|
||||||
|
return (v === '' || v == null) ? '—' : String(v);
|
||||||
|
}
|
||||||
|
function buildKakaoMapSearchQuery(d) {
|
||||||
|
var road = String(d.ds_addr || '').trim();
|
||||||
|
var jibun = String(d.ds_addr_jibun || '').trim();
|
||||||
|
var detail = String(d.ds_addr_detail || '').trim();
|
||||||
|
var q = road || jibun;
|
||||||
|
if (detail) {
|
||||||
|
q = q ? (q + ' ' + detail) : detail;
|
||||||
|
}
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
function fillDetailInfoTable(d) {
|
||||||
|
if (!infoTable) return;
|
||||||
|
infoTable.querySelectorAll('[data-ro]').forEach(function (el) {
|
||||||
|
var k = el.getAttribute('data-ro');
|
||||||
|
var v = d[k];
|
||||||
|
if (k === 'ds_va_account') {
|
||||||
|
v = d.ds_va_account || d.ds_va_number || '';
|
||||||
|
}
|
||||||
|
el.textContent = textVal(v);
|
||||||
|
});
|
||||||
|
window.__dsDetailForMap = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectIndex(idx) {
|
||||||
|
if (!rows.length || idx < 0 || idx >= rows.length) return;
|
||||||
|
var d = rows[idx];
|
||||||
|
Array.prototype.forEach.call(body.querySelectorAll('tr.ds-list-row'), function (tr) {
|
||||||
|
tr.classList.remove('ds-row-selected');
|
||||||
|
});
|
||||||
|
var tr = body.querySelector('tr[data-row-index="' + idx + '"]');
|
||||||
|
if (tr) tr.classList.add('ds-row-selected');
|
||||||
|
|
||||||
|
placeholder.classList.add('hidden');
|
||||||
|
fieldsWrap.classList.remove('hidden');
|
||||||
|
fillDetailInfoTable(d);
|
||||||
|
|
||||||
|
if (!readOnly && editLink && delForm && delBtn) {
|
||||||
|
var id = d.ds_idx;
|
||||||
|
editLink.href = editBase + '/' + id;
|
||||||
|
editLink.classList.remove('pointer-events-none', 'opacity-40');
|
||||||
|
delForm.action = delBase + '/' + id;
|
||||||
|
delBtn.disabled = false;
|
||||||
|
delBtn.classList.remove('pointer-events-none', 'opacity-40');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body) {
|
||||||
|
body.addEventListener('click', function (e) {
|
||||||
|
var tr = e.target.closest('tr.ds-list-row');
|
||||||
|
if (!tr) return;
|
||||||
|
var idx = parseInt(tr.getAttribute('data-row-index'), 10);
|
||||||
|
if (!isNaN(idx)) selectIndex(idx);
|
||||||
|
});
|
||||||
|
body.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key !== 'Enter' && e.key !== ' ') return;
|
||||||
|
var tr = e.target.closest('tr.ds-list-row');
|
||||||
|
if (!tr) return;
|
||||||
|
e.preventDefault();
|
||||||
|
var idx = parseInt(tr.getAttribute('data-row-index'), 10);
|
||||||
|
if (!isNaN(idx)) selectIndex(idx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapBtnRo = document.getElementById('ds-ro-map-btn');
|
||||||
|
if (mapBtnRo) {
|
||||||
|
mapBtnRo.addEventListener('click', function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
var d = window.__dsDetailForMap;
|
||||||
|
if (!d) return;
|
||||||
|
var q = buildKakaoMapSearchQuery(d);
|
||||||
|
if (!q) {
|
||||||
|
window.alert('표시할 주소 정보가 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof window.openDesignatedShopKakaoMap === 'function') {
|
||||||
|
window.openDesignatedShopKakaoMap(q);
|
||||||
|
} else {
|
||||||
|
window.open('https://map.kakao.com/link/search/' + encodeURIComponent(q), '_blank', 'noopener,noreferrer');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows.length > 0) {
|
||||||
|
selectIndex(0);
|
||||||
|
} else if (!readOnly && editLink && delBtn) {
|
||||||
|
editLink.classList.add('pointer-events-none', 'opacity-40');
|
||||||
|
delBtn.disabled = true;
|
||||||
|
delBtn.classList.add('pointer-events-none', 'opacity-40');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- 인쇄용: 전체 테이블 -->
|
||||||
|
<div class="hidden print:block print:p-4">
|
||||||
|
<table class="w-full data-table text-xs">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-16">번호</th>
|
<th>번호</th>
|
||||||
<th>지자체</th>
|
<th>지자체</th>
|
||||||
<th>판매소번호</th>
|
<th>구·군</th>
|
||||||
|
<th>지정일</th>
|
||||||
|
<th>구역</th>
|
||||||
|
<th>대표자명</th>
|
||||||
<th>상호명</th>
|
<th>상호명</th>
|
||||||
<th>대표자</th>
|
<th>우편번호</th>
|
||||||
|
<th>주소</th>
|
||||||
|
<th>상세주소</th>
|
||||||
<th>사업자번호</th>
|
<th>사업자번호</th>
|
||||||
|
<th>전화</th>
|
||||||
|
<th>판매소번호</th>
|
||||||
<th>가상계좌</th>
|
<th>가상계좌</th>
|
||||||
<th>상태</th>
|
<th>상태</th>
|
||||||
<th>등록일</th>
|
<th>등록일</th>
|
||||||
<th class="w-28">작업</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="text-right">
|
<tbody>
|
||||||
<?php foreach ($list as $row): ?>
|
<?php foreach ($list as $row): ?>
|
||||||
|
<?php
|
||||||
|
$snP = (string) ($row->ds_shop_no ?? '');
|
||||||
|
if (preg_match('/(\d{3})$/', $snP, $mP)) {
|
||||||
|
$shortNoP = $mP[1];
|
||||||
|
} elseif ($snP !== '' && strlen($snP) >= 3) {
|
||||||
|
$shortNoP = substr($snP, -3);
|
||||||
|
} else {
|
||||||
|
$shortNoP = $snP;
|
||||||
|
}
|
||||||
|
$daP = $row->ds_designated_at ?? null;
|
||||||
|
$daDispP = ($daP !== null && $daP !== '' && (string) $daP !== '0000-00-00') ? substr((string) $daP, 0, 10) : '';
|
||||||
|
$stP = (int) ($row->ds_state ?? 1);
|
||||||
|
$stLabP = $stP === 1 ? '정상' : ($stP === 2 ? '폐업' : '직권해지');
|
||||||
|
$zipP = trim((string) ($row->ds_zip ?? ''));
|
||||||
|
$roadP = trim((string) ($row->ds_addr ?? ''));
|
||||||
|
$jibP = trim((string) ($row->ds_addr_jibun ?? ''));
|
||||||
|
$addrP = $roadP !== '' ? $roadP : $jibP;
|
||||||
|
$detP = trim((string) ($row->ds_addr_detail ?? ''));
|
||||||
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><?= esc($row->ds_idx) ?></td>
|
<td class="text-center"><?= esc($shortNoP) ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($lgMap[$row->ds_lg_idx] ?? '') ?></td>
|
<td class="text-left"><?= esc($lgMap[$row->ds_lg_idx] ?? '') ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_shop_no) ?></td>
|
<td class="text-left"><?= esc($row->ds_gugun_code ?? '') ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_name) ?></td>
|
<td class="text-center"><?= esc($daDispP) ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_rep_name) ?></td>
|
<td class="text-left"><?= esc($row->ds_zone_code ?? '') ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_biz_no) ?></td>
|
<td class="text-left"><?= esc($row->ds_rep_name ?? '') ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_va_number) ?></td>
|
<td class="text-left"><?= esc($row->ds_name ?? '') ?></td>
|
||||||
<td class="text-center"><?= (int) $row->ds_state === 1 ? '정상' : ((int) $row->ds_state === 2 ? '폐업' : '직권해지') ?></td>
|
<td class="text-left"><?= esc($zipP) ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($row->ds_regdate ?? '') ?></td>
|
<td class="text-left"><?= esc($addrP) ?></td>
|
||||||
<td class="text-center">
|
<td class="text-left"><?= esc($detP) ?></td>
|
||||||
<a href="<?= mgmt_url('designated-shops/edit/' . (int) $row->ds_idx) ?>" class="text-blue-600 hover:underline text-sm">수정</a>
|
<td class="text-left"><?= esc($row->ds_biz_no ?? '') ?></td>
|
||||||
<form action="<?= mgmt_url('designated-shops/delete/' . (int) $row->ds_idx) ?>" method="POST" class="inline ml-1" onsubmit="return confirm('이 지정판매소를 삭제하시겠습니까?');">
|
<td class="text-left"><?= esc($row->ds_tel ?? '') ?></td>
|
||||||
<?= csrf_field() ?>
|
<td class="text-left"><?= esc($row->ds_shop_no) ?></td>
|
||||||
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
|
<td class="text-left"><?= esc($row->ds_va_number) ?></td>
|
||||||
</form>
|
<td class="text-center"><?= esc($stLabP) ?></td>
|
||||||
</td>
|
<td class="text-left"><?= esc($row->ds_regdate ?? '') ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php if (isset($pager)): ?><div class="mt-3"><?= $pager->links() ?></div><?php endif; ?>
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
<div id="kakao-map" class="w-full border border-gray-300 mt-2" style="height:600px;"></div>
|
<div id="kakao-map" class="w-full border border-gray-300 mt-2" style="height:600px;"></div>
|
||||||
<div class="mt-2 text-sm text-gray-500">총 <?= count($shops) ?>개 판매소 표시</div>
|
<div class="mt-2 text-sm text-gray-500">총 <?= count($shops) ?>개 판매소 표시</div>
|
||||||
|
|
||||||
<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=KAKAO_APP_KEY&libraries=services"></script>
|
<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=<?= esc($kakaoJavascriptKey ?? '', 'attr') ?>&libraries=services"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
var mapContainer = document.getElementById('kakao-map');
|
var mapContainer = document.getElementById('kakao-map');
|
||||||
if (typeof kakao === 'undefined' || typeof kakao.maps === 'undefined') {
|
if (typeof kakao === 'undefined' || typeof kakao.maps === 'undefined') {
|
||||||
mapContainer.innerHTML = '<div class="flex items-center justify-center h-full text-gray-400">카카오맵 API 키를 설정해 주세요.</div>';
|
mapContainer.innerHTML = '<div class="flex items-center justify-center h-full text-gray-500 text-sm px-4 text-center">카카오맵을 불러올 수 없습니다. Kakao Developers → 제품 설정에서 「Kakao Map」을 켜고, 플랫폼(Web)에 이 사이트 URL을 등록했는지 확인하세요.</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,10 +133,13 @@ $userNav = session_user_nav_display();
|
|||||||
<?php if (! empty($navItem->children)): ?>
|
<?php if (! empty($navItem->children)): ?>
|
||||||
<div class="absolute left-0 top-full z-[200] -mt-1 pt-2 hidden group-hover:block group-focus-within:block min-w-[12rem]">
|
<div class="absolute left-0 top-full z-[200] -mt-1 pt-2 hidden group-hover:block group-focus-within:block min-w-[12rem]">
|
||||||
<div class="bg-white border border-gray-200 rounded shadow-lg py-1">
|
<div class="bg-white border border-gray-200 rounded shadow-lg py-1">
|
||||||
|
<?php
|
||||||
|
$activeChild = site_nav_active_child_for_parent($navItem, $currentPath, $dashboardPathAliases);
|
||||||
|
?>
|
||||||
<?php foreach ($navItem->children as $child): ?>
|
<?php foreach ($navItem->children as $child): ?>
|
||||||
<?php
|
<?php
|
||||||
$childLink = site_nav_resolved_link_path($child->mm_link ?? null, $child->mm_name ?? null);
|
$childLink = site_nav_resolved_link_path($child->mm_link ?? null, $child->mm_name ?? null);
|
||||||
$childCurrent = menu_link_matches_request($child->mm_link ?? null, $currentPath, $dashboardPathAliases);
|
$childCurrent = $activeChild !== null && $child === $activeChild;
|
||||||
?>
|
?>
|
||||||
<?php if ($childLink !== ''): ?>
|
<?php if ($childLink !== ''): ?>
|
||||||
<a href="<?= base_url($childLink) ?>"
|
<a href="<?= base_url($childLink) ?>"
|
||||||
|
|||||||
126
app/Views/components/kakao_address_search.php
Normal file
126
app/Views/components/kakao_address_search.php
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/** @var string $buttonId 주소 검색 버튼 id */
|
||||||
|
$buttonId = $buttonId ?? 'btn-kakao-postcode';
|
||||||
|
/** @var string $zipName 우편번호 input name */
|
||||||
|
$zipName = $zipName ?? 'ds_zip';
|
||||||
|
/** @var string $roadName 도로명 input name */
|
||||||
|
$roadName = $roadName ?? 'ds_addr';
|
||||||
|
/** @var string $jibunName 지번 input name */
|
||||||
|
$jibunName = $jibunName ?? 'ds_addr_jibun';
|
||||||
|
/** @var string $sidoFieldName 카카오 시·도 → hidden name (비우면 미설정) */
|
||||||
|
$sidoFieldName = $sidoFieldName ?? '';
|
||||||
|
/** @var string $sigunguFieldName 카카오 시·군·구 → hidden name */
|
||||||
|
$sigunguFieldName = $sigunguFieldName ?? '';
|
||||||
|
/** @var string $detailFieldName 상세주소 input name (건물명 등, 비우면 미사용) */
|
||||||
|
$detailFieldName = $detailFieldName ?? '';
|
||||||
|
/**
|
||||||
|
* @var array{lg_sido?: string, lg_gugun?: string}|null $tenantScope 지자체 관할 검사(비우면 미검사)
|
||||||
|
*/
|
||||||
|
$tenantScope = $tenantScope ?? null;
|
||||||
|
/** @var bool $roadBaseOnly true면 도로명에 건물명 괄호 미부착(상세로 이전) */
|
||||||
|
$roadBaseOnly = ! empty($roadBaseOnly);
|
||||||
|
?>
|
||||||
|
<script src="https://t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var btnId = <?= json_encode($buttonId, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var zipName = <?= json_encode($zipName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var roadName = <?= json_encode($roadName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var jibunName = <?= json_encode($jibunName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var sidoFieldName = <?= json_encode($sidoFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var sigunguFieldName = <?= json_encode($sigunguFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var detailFieldName = <?= json_encode($detailFieldName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var roadBaseOnly = <?= $roadBaseOnly ? 'true' : 'false' ?>;
|
||||||
|
var tenantScope = <?= json_encode($tenantScope ?? (object) [], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
|
||||||
|
function compactStr(s) {
|
||||||
|
return String(s || '').replace(/\s+/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenMatches(needle, primary, blob) {
|
||||||
|
var n = compactStr(needle);
|
||||||
|
if (!n) return true;
|
||||||
|
var b = compactStr(blob);
|
||||||
|
if (b.indexOf(n) !== -1) return true;
|
||||||
|
var p = compactStr(primary);
|
||||||
|
if (p.indexOf(n) !== -1) return true;
|
||||||
|
if (n.indexOf(p) !== -1 && p) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addressAllowedByTenant(data) {
|
||||||
|
var lgSido = tenantScope && tenantScope.lg_sido ? String(tenantScope.lg_sido) : '';
|
||||||
|
var lgGugun = tenantScope && tenantScope.lg_gugun ? String(tenantScope.lg_gugun) : '';
|
||||||
|
if (!lgSido && !lgGugun) return true;
|
||||||
|
var sido = data.sido || '';
|
||||||
|
var sigungu = data.sigungu || '';
|
||||||
|
var road = data.roadAddress || '';
|
||||||
|
var jibun = data.jibunAddress || '';
|
||||||
|
var zip = data.zonecode || '';
|
||||||
|
var blob = sido + ' ' + sigungu + ' ' + road + ' ' + jibun + ' ' + zip;
|
||||||
|
if (lgSido && !tokenMatches(lgSido, sido, blob)) return false;
|
||||||
|
if (lgGugun && !tokenMatches(lgGugun, sigungu, blob)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind() {
|
||||||
|
var btn = document.getElementById(btnId);
|
||||||
|
if (!btn) return;
|
||||||
|
var form = btn.closest('form');
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
function field(n) {
|
||||||
|
return form.querySelector('[name="' + n + '"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
if (typeof daum === 'undefined' || !daum.Postcode) {
|
||||||
|
window.alert('주소 검색 스크립트를 불러오지 못했습니다. 네트워크를 확인해 주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new daum.Postcode({
|
||||||
|
oncomplete: function (data) {
|
||||||
|
if (!addressAllowedByTenant(data)) {
|
||||||
|
window.alert('작업 중인 지자체 관할이 아닌 주소입니다. 해당 시·구 주소를 검색해 주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var zipEl = field(zipName);
|
||||||
|
var roadEl = field(roadName);
|
||||||
|
var jibunEl = field(jibunName);
|
||||||
|
if (zipEl) zipEl.value = data.zonecode || '';
|
||||||
|
|
||||||
|
var roadAddr = data.roadAddress || '';
|
||||||
|
if (!roadBaseOnly && data.buildingName !== '') {
|
||||||
|
roadAddr += (roadAddr !== '' ? ' (' + data.buildingName + ')' : data.buildingName);
|
||||||
|
}
|
||||||
|
if (roadEl) roadEl.value = roadAddr;
|
||||||
|
|
||||||
|
if (jibunEl) jibunEl.value = data.jibunAddress || '';
|
||||||
|
|
||||||
|
if (sidoFieldName) {
|
||||||
|
var sidoEl = field(sidoFieldName);
|
||||||
|
if (sidoEl) sidoEl.value = data.sido || '';
|
||||||
|
}
|
||||||
|
if (sigunguFieldName) {
|
||||||
|
var sigEl = field(sigunguFieldName);
|
||||||
|
if (sigEl) sigEl.value = data.sigungu || '';
|
||||||
|
}
|
||||||
|
if (detailFieldName && roadBaseOnly) {
|
||||||
|
var detEl = field(detailFieldName);
|
||||||
|
if (detEl) detEl.value = data.buildingName || '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', bind);
|
||||||
|
} else {
|
||||||
|
bind();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
47
app/Views/components/kakao_map_link_button.php
Normal file
47
app/Views/components/kakao_map_link_button.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/** @var string $buttonId 버튼 id (폼마다 고유) */
|
||||||
|
$buttonId = $buttonId ?? 'btn-kakao-map-open';
|
||||||
|
/** @var string $label 버튼 텍스트 */
|
||||||
|
$label = $label ?? '지도';
|
||||||
|
?>
|
||||||
|
<button type="button" id="<?= esc($buttonId, 'attr') ?>" class="no-print border border-btn-print-border text-gray-700 px-3 py-1.5 rounded-sm text-sm hover:bg-gray-50 transition shrink-0" title="카카오맵에서 이 주소 검색"><?= esc($label) ?></button>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var bid = <?= json_encode($buttonId, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
function bind() {
|
||||||
|
var btn = document.getElementById(bid);
|
||||||
|
if (!btn) return;
|
||||||
|
btn.addEventListener('click', function () {
|
||||||
|
var form = btn.closest('form');
|
||||||
|
if (!form) return;
|
||||||
|
function val(name) {
|
||||||
|
var el = form.querySelector('[name="' + name + '"]');
|
||||||
|
return el ? String(el.value || '').trim() : '';
|
||||||
|
}
|
||||||
|
var road = val('ds_addr');
|
||||||
|
var jibun = val('ds_addr_jibun');
|
||||||
|
var detail = val('ds_addr_detail');
|
||||||
|
var q = road || jibun;
|
||||||
|
if (detail) {
|
||||||
|
q = q ? (q + ' ' + detail) : detail;
|
||||||
|
}
|
||||||
|
if (!q) {
|
||||||
|
window.alert('주소 검색으로 도로명·지번을 먼저 입력한 뒤 지도를 열 수 있습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof window.openDesignatedShopKakaoMap === 'function') {
|
||||||
|
window.openDesignatedShopKakaoMap(q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
window.open('https://map.kakao.com/link/search/' + encodeURIComponent(q), '_blank', 'noopener,noreferrer');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', bind);
|
||||||
|
} else {
|
||||||
|
bind();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
153
app/Views/components/kakao_map_modal.php
Normal file
153
app/Views/components/kakao_map_modal.php
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
$key = trim((string) ($kakaoJavascriptKey ?? ''));
|
||||||
|
?>
|
||||||
|
<div id="kakao-map-modal" class="hidden fixed inset-0 z-[300] flex items-center justify-center p-4" aria-hidden="true" role="dialog" aria-modal="true" aria-labelledby="kakao-map-modal-title">
|
||||||
|
<div class="absolute inset-0 bg-black/50" id="kakao-map-modal-backdrop"></div>
|
||||||
|
<div class="relative z-[301] w-full max-w-2xl max-h-[90vh] flex flex-col rounded border border-gray-300 bg-white shadow-lg overflow-hidden">
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0">
|
||||||
|
<span id="kakao-map-modal-title" class="text-sm font-bold text-gray-800">위치</span>
|
||||||
|
<button type="button" id="kakao-map-modal-close" class="text-gray-600 hover:text-gray-900 text-xl leading-none px-1" aria-label="닫기">×</button>
|
||||||
|
</div>
|
||||||
|
<div id="kakao-map-modal-container" class="w-full bg-gray-100" style="min-height: 380px; height: 50vh;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var APP_KEY = <?= json_encode($key, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT) ?>;
|
||||||
|
var modal = document.getElementById('kakao-map-modal');
|
||||||
|
var backdrop = document.getElementById('kakao-map-modal-backdrop');
|
||||||
|
var btnClose = document.getElementById('kakao-map-modal-close');
|
||||||
|
var mapContainer = document.getElementById('kakao-map-modal-container');
|
||||||
|
var mapInstance = null;
|
||||||
|
var markerInstance = null;
|
||||||
|
var scriptLoading = false;
|
||||||
|
var pendingAfterLoad = [];
|
||||||
|
|
||||||
|
function hideModal() {
|
||||||
|
if (!modal) return;
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
if (!modal) return;
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
modal.setAttribute('aria-hidden', 'false');
|
||||||
|
}
|
||||||
|
|
||||||
|
function runPending() {
|
||||||
|
var q = pendingAfterLoad;
|
||||||
|
pendingAfterLoad = [];
|
||||||
|
q.forEach(function (fn) {
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureScript(cb) {
|
||||||
|
if (!APP_KEY) {
|
||||||
|
window.alert('카카오맵 JavaScript 키가 설정되지 않았습니다. .env에 kakao.javascriptKey를 설정해 주세요. (Kakao Developers → 앱 키 → JavaScript 키)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof kakao !== 'undefined' && kakao.maps) {
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingAfterLoad.push(cb);
|
||||||
|
if (scriptLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scriptLoading = true;
|
||||||
|
var s = document.createElement('script');
|
||||||
|
s.charset = 'UTF-8';
|
||||||
|
s.async = true;
|
||||||
|
// 동적 삽입 시 autoload=false 후 kakao.maps.load() 필수 (카카오 웹 가이드)
|
||||||
|
s.src = 'https://dapi.kakao.com/v2/maps/sdk.js?appkey=' + encodeURIComponent(APP_KEY) + '&libraries=services&autoload=false';
|
||||||
|
s.onload = function () {
|
||||||
|
scriptLoading = false;
|
||||||
|
if (typeof kakao === 'undefined' || !kakao.maps || typeof kakao.maps.load !== 'function') {
|
||||||
|
pendingAfterLoad = [];
|
||||||
|
window.alert(
|
||||||
|
'카카오맵 API를 불러올 수 없습니다.\n\n' +
|
||||||
|
'Kakao Developers → 내 애플리케이션 → 해당 앱 → 「제품 설정」에서 「Kakao Map」(지도) / 로컬 API를 사용 설정으로 켜 주세요.\n' +
|
||||||
|
'(비활성 시 서버에서 OPEN_MAP_AND_LOCAL 오류가 납니다.)\n\n' +
|
||||||
|
'또한 플랫폼(Web)에 이 사이트 주소(예: http://localhost:8080)가 등록되어 있어야 합니다.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kakao.maps.load(function () {
|
||||||
|
runPending();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
s.onerror = function () {
|
||||||
|
scriptLoading = false;
|
||||||
|
pendingAfterLoad = [];
|
||||||
|
window.alert(
|
||||||
|
'카카오맵 스크립트를 불러오지 못했습니다.\n\n' +
|
||||||
|
'• 네트워크·차단(광고 차단) 확인\n' +
|
||||||
|
'• Kakao Developers → 제품 설정에서 「Kakao Map」활성화\n' +
|
||||||
|
'• 플랫폼(Web)에 접속 중인 URL 등록'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
document.head.appendChild(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (btnClose) {
|
||||||
|
btnClose.addEventListener('click', hideModal);
|
||||||
|
}
|
||||||
|
if (backdrop) {
|
||||||
|
backdrop.addEventListener('click', hideModal);
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key !== 'Escape' || !modal || modal.classList.contains('hidden')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hideModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.openDesignatedShopKakaoMap = function (addressQuery) {
|
||||||
|
var q = String(addressQuery || '').trim();
|
||||||
|
if (!q) {
|
||||||
|
window.alert('주소가 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ensureScript(function () {
|
||||||
|
if (typeof kakao === 'undefined' || !kakao.maps || !kakao.maps.services) {
|
||||||
|
window.alert('카카오맵을 초기화할 수 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var geocoder = new kakao.maps.services.Geocoder();
|
||||||
|
geocoder.addressSearch(q, function (result, status) {
|
||||||
|
if (status !== kakao.maps.services.Status.OK || !result || !result[0]) {
|
||||||
|
window.alert('주소를 지도에서 찾을 수 없습니다.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
|
||||||
|
showModal();
|
||||||
|
if (!mapInstance) {
|
||||||
|
mapInstance = new kakao.maps.Map(mapContainer, {
|
||||||
|
center: coords,
|
||||||
|
level: 3
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mapInstance.setCenter(coords);
|
||||||
|
mapInstance.setLevel(3);
|
||||||
|
}
|
||||||
|
if (markerInstance) {
|
||||||
|
markerInstance.setMap(null);
|
||||||
|
}
|
||||||
|
markerInstance = new kakao.maps.Marker({ position: coords, map: mapInstance });
|
||||||
|
setTimeout(function () {
|
||||||
|
if (mapInstance) {
|
||||||
|
mapInstance.relayout();
|
||||||
|
mapInstance.setCenter(coords);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -11,7 +11,7 @@ module.exports = defineConfig({
|
|||||||
timeout: 60000,
|
timeout: 60000,
|
||||||
|
|
||||||
use: {
|
use: {
|
||||||
baseURL: 'http://localhost:8045',
|
baseURL: process.env.PLAYWRIGHT_BASE_URL || 'http://localhost:8045',
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
locale: 'ko-KR',
|
locale: 'ko-KR',
|
||||||
|
|||||||
@@ -29,16 +29,25 @@ CREATE TABLE IF NOT EXISTS `designated_shop` (
|
|||||||
`ds_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '상호명',
|
`ds_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '상호명',
|
||||||
`ds_biz_no` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '사업자번호',
|
`ds_biz_no` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '사업자번호',
|
||||||
`ds_rep_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대표자명',
|
`ds_rep_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대표자명',
|
||||||
`ds_va_number` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '고정 가상계좌 번호',
|
`ds_biz_type` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업태',
|
||||||
|
`ds_biz_kind` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업종',
|
||||||
|
`ds_va_number` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '가상계좌(표시용 번호, 계좌번호와 동기화 가능)',
|
||||||
|
`ds_va_bank` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '가상계좌(은행)',
|
||||||
|
`ds_va_account` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '계좌번호',
|
||||||
`ds_zip` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '우편번호',
|
`ds_zip` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '우편번호',
|
||||||
`ds_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '도로명주소',
|
`ds_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '도로명주소',
|
||||||
`ds_addr_jibun` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '지번주소',
|
`ds_addr_jibun` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '지번주소',
|
||||||
|
`ds_addr_detail` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '상세주소(동·호 등)',
|
||||||
`ds_tel` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '일반전화',
|
`ds_tel` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '일반전화',
|
||||||
`ds_rep_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '개인전화',
|
`ds_rep_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '개인전화',
|
||||||
`ds_email` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '이메일',
|
`ds_email` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '이메일',
|
||||||
`ds_gugun_code` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '구코드',
|
`ds_gugun_code` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '구코드',
|
||||||
|
`ds_zone_code` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '구역',
|
||||||
|
`ds_branch_no` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '종사업장번호',
|
||||||
`ds_designated_at` DATE NULL DEFAULT NULL COMMENT '지정일자',
|
`ds_designated_at` DATE NULL DEFAULT NULL COMMENT '지정일자',
|
||||||
`ds_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=정상, 2=폐업, 3=직권해지',
|
`ds_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=정상, 2=폐업, 3=직권해지',
|
||||||
|
`ds_state_changed_at` DATE NULL DEFAULT NULL COMMENT '변경일자',
|
||||||
|
`ds_change_reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '변경사유',
|
||||||
`ds_regdate` DATETIME NOT NULL COMMENT '등록일시',
|
`ds_regdate` DATETIME NOT NULL COMMENT '등록일시',
|
||||||
PRIMARY KEY (`ds_idx`),
|
PRIMARY KEY (`ds_idx`),
|
||||||
KEY `idx_ds_lg_idx` (`ds_lg_idx`),
|
KEY `idx_ds_lg_idx` (`ds_lg_idx`),
|
||||||
|
|||||||
5
writable/database/designated_shop_addr_detail.sql
Normal file
5
writable/database/designated_shop_addr_detail.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
-- 지정판매소 상세주소(건물명·동·호 등) — 주소 검색으로 채운 도로명/지번과 별도 입력
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
ALTER TABLE `designated_shop`
|
||||||
|
ADD COLUMN `ds_addr_detail` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '상세주소(동·호 등)' AFTER `ds_addr_jibun`;
|
||||||
108
writable/database/designated_shop_ensure_app_columns.sql
Normal file
108
writable/database/designated_shop_ensure_app_columns.sql
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
-- 지정판매소: 앱(DesignatedShopModel / Admin\DesignatedShop)이 기대하는 컬럼을
|
||||||
|
-- 없을 때만 추가합니다. 기존 DB를 login_tables.sql 최신 정의와 맞출 때 사용.
|
||||||
|
-- 실행 예: mysql -h 127.0.0.1 -u USER -p DBNAME < writable/database/designated_shop_ensure_app_columns.sql
|
||||||
|
--
|
||||||
|
-- kr_address 등 외부 테이블 불필요. INFORMATION_SCHEMA 로 존재 여부만 확인합니다.
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
SET @db = DATABASE();
|
||||||
|
|
||||||
|
-- ds_biz_type
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_biz_type') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_biz_type` VARCHAR(100) NOT NULL DEFAULT '''' COMMENT ''업태'' AFTER `ds_rep_name`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_biz_kind
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_biz_kind') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_biz_kind` VARCHAR(100) NOT NULL DEFAULT '''' COMMENT ''업종'' AFTER `ds_biz_type`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_va_bank (ds_va_number 뒤 — 없으면 ds_biz_kind 뒤에 붙임)
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_va_bank') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
IF((SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_va_number') > 0,
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_va_bank` VARCHAR(80) NOT NULL DEFAULT '''' COMMENT ''가상계좌(은행)'' AFTER `ds_va_number`',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_va_bank` VARCHAR(80) NOT NULL DEFAULT '''' COMMENT ''가상계좌(은행)'' AFTER `ds_biz_kind`'
|
||||||
|
)
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_va_account
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_va_account') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_va_account` VARCHAR(50) NOT NULL DEFAULT '''' COMMENT ''계좌번호'' AFTER `ds_va_bank`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_addr_detail
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_addr_detail') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_addr_detail` VARCHAR(200) NOT NULL DEFAULT '''' COMMENT ''상세주소(동·호 등)'' AFTER `ds_addr_jibun`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_zone_code
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_zone_code') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_zone_code` VARCHAR(80) NOT NULL DEFAULT '''' COMMENT ''구역'' AFTER `ds_gugun_code`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_branch_no
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_branch_no') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_branch_no` VARCHAR(50) NOT NULL DEFAULT '''' COMMENT ''종사업장번호'' AFTER `ds_zone_code`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_state_changed_at
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_state_changed_at') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_state_changed_at` DATE NULL DEFAULT NULL COMMENT ''변경일자'' AFTER `ds_state`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_change_reason
|
||||||
|
SET @s = (SELECT IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_change_reason') > 0,
|
||||||
|
'SELECT 1',
|
||||||
|
'ALTER TABLE `designated_shop` ADD COLUMN `ds_change_reason` VARCHAR(500) NOT NULL DEFAULT '''' COMMENT ''변경사유'' AFTER `ds_state_changed_at`'
|
||||||
|
));
|
||||||
|
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
-- ds_va_number 뒤에 va_bank를 넣었을 수 있음 — 구 스키마에 ds_designated_at 등만 있는 경우
|
||||||
|
UPDATE `designated_shop`
|
||||||
|
SET `ds_va_account` = `ds_va_number`
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_va_account'
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'designated_shop' AND COLUMN_NAME = 'ds_va_number'
|
||||||
|
)
|
||||||
|
AND (`ds_va_account` = '' OR `ds_va_account` IS NULL)
|
||||||
|
AND `ds_va_number` IS NOT NULL
|
||||||
|
AND `ds_va_number` != '';
|
||||||
25
writable/database/designated_shop_extended_columns.sql
Normal file
25
writable/database/designated_shop_extended_columns.sql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-- 지정판매소 확장 컬럼 (업태·업종·구역·종사업장·가상계좌 은행/계좌·변경일자·변경사유)
|
||||||
|
-- 기존 DB: mysql ... < writable/database/designated_shop_extended_columns.sql
|
||||||
|
-- 컬럼이 이미 있으면 수동으로 스킵하거나 에러 무시 후 진행
|
||||||
|
--
|
||||||
|
-- 권장: 컬럼 유무를 자동 판별하려면 대신
|
||||||
|
-- writable/database/designated_shop_ensure_app_columns.sql
|
||||||
|
-- 를 실행하세요(여러 번 실행해도 안전).
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
ALTER TABLE `designated_shop`
|
||||||
|
ADD COLUMN `ds_biz_type` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업태' AFTER `ds_rep_name`,
|
||||||
|
ADD COLUMN `ds_biz_kind` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업종' AFTER `ds_biz_type`,
|
||||||
|
ADD COLUMN `ds_zone_code` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '구역' AFTER `ds_gugun_code`,
|
||||||
|
ADD COLUMN `ds_branch_no` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '종사업장번호' AFTER `ds_zone_code`,
|
||||||
|
ADD COLUMN `ds_va_bank` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '가상계좌(은행)' AFTER `ds_va_number`,
|
||||||
|
ADD COLUMN `ds_va_account` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '계좌번호' AFTER `ds_va_bank`,
|
||||||
|
ADD COLUMN `ds_state_changed_at` DATE NULL DEFAULT NULL COMMENT '변경일자' AFTER `ds_state`,
|
||||||
|
ADD COLUMN `ds_change_reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '변경사유' AFTER `ds_state_changed_at`;
|
||||||
|
|
||||||
|
UPDATE `designated_shop`
|
||||||
|
SET `ds_va_account` = `ds_va_number`
|
||||||
|
WHERE (`ds_va_account` = '' OR `ds_va_account` IS NULL)
|
||||||
|
AND `ds_va_number` IS NOT NULL
|
||||||
|
AND `ds_va_number` != '';
|
||||||
@@ -106,16 +106,25 @@ CREATE TABLE IF NOT EXISTS `designated_shop` (
|
|||||||
`ds_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '상호명',
|
`ds_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '상호명',
|
||||||
`ds_biz_no` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '사업자번호',
|
`ds_biz_no` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '사업자번호',
|
||||||
`ds_rep_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대표자명',
|
`ds_rep_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대표자명',
|
||||||
`ds_va_number` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '고정 가상계좌 번호',
|
`ds_biz_type` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업태',
|
||||||
|
`ds_biz_kind` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '업종',
|
||||||
|
`ds_va_number` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '가상계좌(표시용 번호, 계좌번호와 동기화 가능)',
|
||||||
|
`ds_va_bank` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '가상계좌(은행)',
|
||||||
|
`ds_va_account` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '계좌번호',
|
||||||
`ds_zip` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '우편번호',
|
`ds_zip` VARCHAR(10) NOT NULL DEFAULT '' COMMENT '우편번호',
|
||||||
`ds_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '도로명주소',
|
`ds_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '도로명주소',
|
||||||
`ds_addr_jibun` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '지번주소',
|
`ds_addr_jibun` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '지번주소',
|
||||||
|
`ds_addr_detail` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '상세주소(동·호 등)',
|
||||||
`ds_tel` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '일반전화',
|
`ds_tel` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '일반전화',
|
||||||
`ds_rep_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '개인전화',
|
`ds_rep_phone` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '개인전화',
|
||||||
`ds_email` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '이메일',
|
`ds_email` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '이메일',
|
||||||
`ds_gugun_code` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '구코드',
|
`ds_gugun_code` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '구코드',
|
||||||
|
`ds_zone_code` VARCHAR(80) NOT NULL DEFAULT '' COMMENT '구역',
|
||||||
|
`ds_branch_no` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '종사업장번호',
|
||||||
`ds_designated_at` DATE NULL DEFAULT NULL COMMENT '지정일자',
|
`ds_designated_at` DATE NULL DEFAULT NULL COMMENT '지정일자',
|
||||||
`ds_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=정상, 2=폐업, 3=직권해지',
|
`ds_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=정상, 2=폐업, 3=직권해지',
|
||||||
|
`ds_state_changed_at` DATE NULL DEFAULT NULL COMMENT '변경일자',
|
||||||
|
`ds_change_reason` VARCHAR(500) NOT NULL DEFAULT '' COMMENT '변경사유',
|
||||||
`ds_regdate` DATETIME NOT NULL COMMENT '등록일시',
|
`ds_regdate` DATETIME NOT NULL COMMENT '등록일시',
|
||||||
PRIMARY KEY (`ds_idx`),
|
PRIMARY KEY (`ds_idx`),
|
||||||
KEY `idx_ds_lg_idx` (`ds_lg_idx`),
|
KEY `idx_ds_lg_idx` (`ds_lg_idx`),
|
||||||
|
|||||||
43
writable/database/seed_designated_shops_test_30.sql
Normal file
43
writable/database/seed_designated_shops_test_30.sql
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
-- 테스트 지정판매소 30건 (동일 스크립트 재실행 시 기존 ZZTEST 행 삭제 후 삽입)
|
||||||
|
-- 기본: ds_lg_idx = 1 (북구청), ds_gugun_code = 110209 — 환경에 맞게 아래 DELETE/INSERT의 1, 110209만 조정하세요.
|
||||||
|
-- 실행: mysql -h 127.0.0.1 -u jongryangje -p jongryangje_dev < writable/database/seed_designated_shops_test_30.sql
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
|
||||||
|
DELETE FROM `designated_shop` WHERE `ds_shop_no` LIKE 'ZZTEST-%';
|
||||||
|
|
||||||
|
INSERT INTO `designated_shop` (
|
||||||
|
`ds_lg_idx`, `ds_mb_idx`, `ds_shop_no`, `ds_name`, `ds_biz_no`, `ds_rep_name`,
|
||||||
|
`ds_va_number`, `ds_zip`, `ds_addr`, `ds_addr_jibun`, `ds_tel`, `ds_rep_phone`, `ds_email`,
|
||||||
|
`ds_gugun_code`, `ds_designated_at`, `ds_state`, `ds_regdate`
|
||||||
|
) VALUES
|
||||||
|
(1, NULL, 'ZZTEST-00001', '테스트편의점 01', '784-12-00001', '김테01', '', '41590', '대구광역시 북구 테스트로 1', '대구 북구 테스트동 1', '053-000-0001', '01010000001', 'seed01@test.local', '110209', '2026-01-01', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00002', '테스트편의점 02', '784-12-00002', '김테02', '', '41590', '대구광역시 북구 테스트로 2', '대구 북구 테스트동 2', '053-000-0002', '01010000002', 'seed02@test.local', '110209', '2026-01-02', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00003', '테스트편의점 03', '784-12-00003', '김테03', '', '41590', '대구광역시 북구 테스트로 3', '대구 북구 테스트동 3', '053-000-0003', '01010000003', 'seed03@test.local', '110209', '2026-01-03', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00004', '테스트편의점 04', '784-12-00004', '김테04', '', '41590', '대구광역시 북구 테스트로 4', '대구 북구 테스트동 4', '053-000-0004', '01010000004', 'seed04@test.local', '110209', '2026-01-04', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00005', '테스트편의점 05', '784-12-00005', '김테05', '', '41590', '대구광역시 북구 테스트로 5', '대구 북구 테스트동 5', '053-000-0005', '01010000005', 'seed05@test.local', '110209', '2026-01-05', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00006', '테스트편의점 06', '784-12-00006', '김테06', '', '41590', '대구광역시 북구 테스트로 6', '대구 북구 테스트동 6', '053-000-0006', '01010000006', 'seed06@test.local', '110209', '2026-01-06', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00007', '테스트편의점 07', '784-12-00007', '김테07', '', '41590', '대구광역시 북구 테스트로 7', '대구 북구 테스트동 7', '053-000-0007', '01010000007', 'seed07@test.local', '110209', '2026-01-07', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00008', '테스트편의점 08', '784-12-00008', '김테08', '', '41590', '대구광역시 북구 테스트로 8', '대구 북구 테스트동 8', '053-000-0008', '01010000008', 'seed08@test.local', '110209', '2026-01-08', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00009', '테스트편의점 09', '784-12-00009', '김테09', '', '41590', '대구광역시 북구 테스트로 9', '대구 북구 테스트동 9', '053-000-0009', '01010000009', 'seed09@test.local', '110209', '2026-01-09', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00010', '테스트편의점 10', '784-12-00010', '김테10', '', '41590', '대구광역시 북구 테스트로 10', '대구 북구 테스트동 10', '053-000-0010', '01010000010', 'seed10@test.local', '110209', '2026-01-10', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00011', '테스트마트 11', '784-12-00011', '이테11', '', '41590', '대구광역시 북구 테스트로 11', '대구 북구 테스트동 11', '053-000-0011', '01010000011', 'seed11@test.local', '110209', '2026-01-11', 2, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00012', '테스트마트 12', '784-12-00012', '이테12', '', '41590', '대구광역시 북구 테스트로 12', '대구 북구 테스트동 12', '053-000-0012', '01010000012', 'seed12@test.local', '110209', '2026-01-12', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00013', '테스트마트 13', '784-12-00013', '이테13', '', '41590', '대구광역시 북구 테스트로 13', '대구 북구 테스트동 13', '053-000-0013', '01010000013', 'seed13@test.local', '110209', '2026-01-13', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00014', '테스트마트 14', '784-12-00014', '이테14', '', '41590', '대구광역시 북구 테스트로 14', '대구 북구 테스트동 14', '053-000-0014', '01010000014', 'seed14@test.local', '110209', '2026-01-14', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00015', '테스트마트 15', '784-12-00015', '이테15', '', '41590', '대구광역시 북구 테스트로 15', '대구 북구 테스트동 15', '053-000-0015', '01010000015', 'seed15@test.local', '110209', '2026-01-15', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00016', '테스트슈퍼 16', '784-12-00016', '박테16', '', '41590', '대구광역시 북구 테스트로 16', '대구 북구 테스트동 16', '053-000-0016', '01010000016', 'seed16@test.local', '110209', '2026-01-16', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00017', '테스트슈퍼 17', '784-12-00017', '박테17', '', '41590', '대구광역시 북구 테스트로 17', '대구 북구 테스트동 17', '053-000-0017', '01010000017', 'seed17@test.local', '110209', '2026-01-17', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00018', '테스트슈퍼 18', '784-12-00018', '박테18', '', '41590', '대구광역시 북구 테스트로 18', '대구 북구 테스트동 18', '053-000-0018', '01010000018', 'seed18@test.local', '110209', '2026-01-18', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00019', '테스트슈퍼 19', '784-12-00019', '박테19', '', '41590', '대구광역시 북구 테스트로 19', '대구 북구 테스트동 19', '053-000-0019', '01010000019', 'seed19@test.local', '110209', '2026-01-19', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00020', '테스트슈퍼 20', '784-12-00020', '박테20', '', '41590', '대구광역시 북구 테스트로 20', '대구 북구 테스트동 20', '053-000-0020', '01010000020', 'seed20@test.local', '110209', '2026-01-20', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00021', '테스트상회 21', '784-12-00021', '최테21', '', '41590', '대구광역시 북구 테스트로 21', '대구 북구 테스트동 21', '053-000-0021', '01010000021', 'seed21@test.local', '110209', '2026-01-21', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00022', '테스트상회 22', '784-12-00022', '최테22', '', '41590', '대구광역시 북구 테스트로 22', '대구 북구 테스트동 22', '053-000-0022', '01010000022', 'seed22@test.local', '110209', '2026-01-22', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00023', '테스트상회 23', '784-12-00023', '최테23', '', '41590', '대구광역시 북구 테스트로 23', '대구 북구 테스트동 23', '053-000-0023', '01010000023', 'seed23@test.local', '110209', '2026-01-23', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00024', '테스트상회 24', '784-12-00024', '최테24', '', '41590', '대구광역시 북구 테스트로 24', '대구 북구 테스트동 24', '053-000-0024', '01010000024', 'seed24@test.local', '110209', '2026-01-24', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00025', '테스트상회 25', '784-12-00025', '최테25', '', '41590', '대구광역시 북구 테스트로 25', '대구 북구 테스트동 25', '053-000-0025', '01010000025', 'seed25@test.local', '110209', '2026-01-25', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00026', '테스트복합 26', '784-12-00026', '정테26', '', '41590', '대구광역시 북구 테스트로 26', '대구 북구 테스트동 26', '053-000-0026', '01010000026', 'seed26@test.local', '110209', '2026-01-26', 3, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00027', '테스트복합 27', '784-12-00027', '정테27', '', '41590', '대구광역시 북구 테스트로 27', '대구 북구 테스트동 27', '053-000-0027', '01010000027', 'seed27@test.local', '110209', '2026-01-27', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00028', '테스트복합 28', '784-12-00028', '정테28', '', '41590', '대구광역시 북구 테스트로 28', '대구 북구 테스트동 28', '053-000-0028', '01010000028', 'seed28@test.local', '110209', '2026-01-28', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00029', '테스트복합 29', '784-12-00029', '정테29', '', '41590', '대구광역시 북구 테스트로 29', '대구 북구 테스트동 29', '053-000-0029', '01010000029', 'seed29@test.local', '110209', '2026-01-29', 1, NOW()),
|
||||||
|
(1, NULL, 'ZZTEST-00030', '테스트복합 30', '784-12-00030', '정테30', '', '41590', '대구광역시 북구 테스트로 30', '대구 북구 테스트동 30', '053-000-0030', '01010000030', 'seed30@test.local', '110209', '2026-01-30', 1, NOW());
|
||||||
56
writable/database/seed_tester_accounts_trash_host.sql
Normal file
56
writable/database/seed_tester_accounts_trash_host.sql
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
-- 테스터 계정 (비밀번호: test1234!) — 관리자 회원 등록과 동일 필드 구성
|
||||||
|
-- 비밀번호 해시: PHP password_hash('test1234!', PASSWORD_DEFAULT)
|
||||||
|
-- tester_local → local_government 중구청 (lg_idx=10, lg_code=110201)
|
||||||
|
-- 실행 예: mysql -h 116.122.157.166 -P 3306 -u jongryangje -p jongryangje_dev < writable/database/seed_tester_accounts_trash_host.sql
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET @pw := '$2y$10$D.rk9Dtce7qitSCaPO0W2.DROcEwpe3otxE.QF0qWPb63bCBhtE5u';
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
DELETE mar FROM member_approval_request mar
|
||||||
|
INNER JOIN member m ON m.mb_idx = mar.mb_idx
|
||||||
|
WHERE m.mb_id IN ('tester_badmin', 'tester_admin', 'tester_local', 'tester_shop', 'tester_user');
|
||||||
|
|
||||||
|
INSERT INTO `member` (
|
||||||
|
`mb_id`, `mb_passwd`, `mb_totp_secret`, `mb_totp_enabled`,
|
||||||
|
`mb_name`, `mb_email`, `mb_phone`, `mb_lang`,
|
||||||
|
`mb_level`, `mb_group`, `mb_lg_idx`, `mb_state`, `mb_regdate`
|
||||||
|
) VALUES
|
||||||
|
('tester_badmin', @pw, NULL, 0, '테스터본부', 'tester_badmin@test.com', '010-0000-0005', 'ko', 5, '', NULL, 1, NOW()),
|
||||||
|
('tester_admin', @pw, NULL, 0, '테스터관리자', 'tester_admin@test.com', '010-0000-0001', 'ko', 4, '', NULL, 1, NOW()),
|
||||||
|
('tester_local', @pw, NULL, 0, '테스터지자체(중구)', 'tester_local@test.com', '010-0000-0002', 'ko', 3, '', 10, 1, NOW()),
|
||||||
|
('tester_shop', @pw, NULL, 0, '테스터판매소', 'tester_shop@test.com', '010-0000-0003', 'ko', 2, '', NULL, 1, NOW()),
|
||||||
|
('tester_user', @pw, NULL, 0, '테스터사용자', 'tester_user@test.com', '010-0000-0004', 'ko', 1, '', NULL, 1, NOW())
|
||||||
|
AS new
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
`mb_passwd` = new.`mb_passwd`,
|
||||||
|
`mb_totp_secret` = NULL,
|
||||||
|
`mb_totp_enabled` = 0,
|
||||||
|
`mb_name` = new.`mb_name`,
|
||||||
|
`mb_email` = new.`mb_email`,
|
||||||
|
`mb_phone` = new.`mb_phone`,
|
||||||
|
`mb_level` = new.`mb_level`,
|
||||||
|
`mb_group` = new.`mb_group`,
|
||||||
|
`mb_lg_idx` = new.`mb_lg_idx`,
|
||||||
|
`mb_state` = 1;
|
||||||
|
|
||||||
|
INSERT INTO `member_approval_request` (
|
||||||
|
`mb_idx`, `mar_requested_level`, `mar_status`, `mar_request_note`,
|
||||||
|
`mar_reject_reason`, `mar_requested_at`, `mar_requested_by`,
|
||||||
|
`mar_processed_at`, `mar_processed_by`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
m.`mb_idx`,
|
||||||
|
m.`mb_level`,
|
||||||
|
'approved',
|
||||||
|
'테스트 계정 시드',
|
||||||
|
NULL,
|
||||||
|
NOW(),
|
||||||
|
m.`mb_idx`,
|
||||||
|
NOW(),
|
||||||
|
m.`mb_idx`
|
||||||
|
FROM `member` m
|
||||||
|
WHERE m.`mb_id` IN ('tester_badmin', 'tester_admin', 'tester_local', 'tester_shop', 'tester_user');
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
Reference in New Issue
Block a user