feat: improve admin master data management

This commit is contained in:
taekyoungc
2026-04-08 00:19:00 +09:00
parent 89f80edc5d
commit 984ddb403e
35 changed files with 490 additions and 320 deletions

View File

@@ -7,6 +7,7 @@ namespace App\Controllers\Admin;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\CodeKindModel; use App\Models\CodeKindModel;
use App\Models\CodeDetailModel; use App\Models\CodeDetailModel;
use App\Models\LocalGovernmentModel;
use CodeIgniter\HTTP\RedirectResponse; use CodeIgniter\HTTP\RedirectResponse;
use Config\Roles; use Config\Roles;
@@ -47,9 +48,22 @@ class CodeDetail extends BaseController
return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.'); return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.');
} }
$level = (int) session()->get('mb_level');
$canPlatformScope = Roles::isSuperAdminEquivalent($level);
$govs = $canPlatformScope
? model(LocalGovernmentModel::class)->where('lg_state', 1)->orderBy('lg_name', 'ASC')->findAll()
: [];
helper('admin');
return view('admin/layout', [ return view('admin/layout', [
'title' => '세부코드 등록 — ' . $kind->ck_name, 'title' => '세부코드 등록 — ' . $kind->ck_name,
'content' => view('admin/code_detail/create', ['kind' => $kind]), 'content' => view('admin/code_detail/create', [
'kind' => $kind,
'canPlatformScope' => $canPlatformScope,
'localGovernments' => $govs,
'effectiveLgIdx' => admin_effective_lg_idx(),
]),
]); ]);
} }
@@ -71,10 +85,50 @@ class CodeDetail extends BaseController
} }
$ckIdx = (int) $this->request->getPost('cd_ck_idx'); $ckIdx = (int) $this->request->getPost('cd_ck_idx');
$kind = $this->kindModel->find($ckIdx);
if ($kind === null) {
return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.');
}
helper('admin');
$level = (int) session()->get('mb_level');
if (Roles::isSuperAdminEquivalent($level)) {
$scope = $this->request->getPost('cd_scope') === 'local' ? 'local' : 'platform';
if ($scope === 'platform') {
$cdSource = 'platform';
$cdLgIdx = 0;
} else {
$cdLgIdx = (int) $this->request->getPost('cd_lg_idx');
if ($cdLgIdx < 1) {
return redirect()->back()->withInput()->with('error', '지자체 전용인 경우 소속 지자체를 선택해 주세요.');
}
$gov = model(LocalGovernmentModel::class)->find($cdLgIdx);
if ($gov === null) {
return redirect()->back()->withInput()->with('error', '유효하지 않은 지자체입니다.');
}
$cdSource = 'local';
}
} else {
$lg = admin_effective_lg_idx();
if ($lg === null || (int) $lg < 1) {
return redirect()->to(site_url('bag/code-kinds'))->with('error', '지자체를 선택한 뒤 등록해 주세요.');
}
$cdSource = 'local';
$cdLgIdx = (int) $lg;
}
$cdCode = (string) $this->request->getPost('cd_code');
$dup = $this->detailModel->where('cd_ck_idx', $ckIdx)->where('cd_code', $cdCode)->where('cd_lg_idx', $cdLgIdx)->first();
if ($dup !== null) {
return redirect()->back()->withInput()->with('error', '같은 종류·코드값·소속 범위에 이미 등록된 행이 있습니다.');
}
$this->detailModel->insert([ $this->detailModel->insert([
'cd_ck_idx' => $ckIdx, 'cd_ck_idx' => $ckIdx,
'cd_code' => $this->request->getPost('cd_code'), 'cd_source' => $cdSource,
'cd_lg_idx' => $cdLgIdx,
'cd_code' => $cdCode,
'cd_name' => $this->request->getPost('cd_name'), 'cd_name' => $this->request->getPost('cd_name'),
'cd_sort' => (int) ($this->request->getPost('cd_sort') ?: 0), 'cd_sort' => (int) ($this->request->getPost('cd_sort') ?: 0),
'cd_state' => 1, 'cd_state' => 1,
@@ -95,6 +149,11 @@ class CodeDetail extends BaseController
return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.'); return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.');
} }
helper('admin');
if (! Roles::canEditCodeDetailRow((int) session()->get('mb_level'), $item, admin_effective_lg_idx())) {
return redirect()->to(site_url('bag/code-details/' . $item->cd_ck_idx))->with('error', '이 세부코드를 수정할 권한이 없습니다.');
}
$kind = $this->kindModel->find($item->cd_ck_idx); $kind = $this->kindModel->find($item->cd_ck_idx);
return view('admin/layout', [ return view('admin/layout', [
@@ -117,6 +176,11 @@ class CodeDetail extends BaseController
return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.'); return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.');
} }
helper('admin');
if (! Roles::canEditCodeDetailRow((int) session()->get('mb_level'), $item, admin_effective_lg_idx())) {
return redirect()->to(site_url('bag/code-details/' . $item->cd_ck_idx))->with('error', '이 세부코드를 수정할 권한이 없습니다.');
}
$rules = [ $rules = [
'cd_name' => 'required|max_length[100]', 'cd_name' => 'required|max_length[100]',
'cd_sort' => 'permit_empty|is_natural', 'cd_sort' => 'permit_empty|is_natural',
@@ -147,6 +211,11 @@ class CodeDetail extends BaseController
return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.'); return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드를 찾을 수 없습니다.');
} }
helper('admin');
if (! Roles::canEditCodeDetailRow((int) session()->get('mb_level'), $item, admin_effective_lg_idx())) {
return redirect()->to(site_url('bag/code-details/' . $item->cd_ck_idx))->with('error', '이 세부코드를 삭제할 권한이 없습니다.');
}
$ckIdx = $item->cd_ck_idx; $ckIdx = $item->cd_ck_idx;
$this->detailModel->delete($id); $this->detailModel->delete($id);

View File

@@ -19,10 +19,10 @@ class CodeKind extends BaseController
$this->kindModel = model(CodeKindModel::class); $this->kindModel = model(CodeKindModel::class);
} }
private function redirectIfCannotManageCodeMaster(): ?RedirectResponse private function redirectIfCannotManageCodeKindMaster(): ?RedirectResponse
{ {
if (! Roles::canManageCodeMaster((int) session()->get('mb_level'))) { if (! Roles::canManageCodeKindMaster((int) session()->get('mb_level'))) {
return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 관리 권한이 없습니다.'); return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류는 super admin·본부 관리자만 관리할 수 있습니다.');
} }
return null; return null;
@@ -30,7 +30,7 @@ class CodeKind extends BaseController
public function create() public function create()
{ {
if ($r = $this->redirectIfCannotManageCodeMaster()) { if ($r = $this->redirectIfCannotManageCodeKindMaster()) {
return $r; return $r;
} }
@@ -42,7 +42,7 @@ class CodeKind extends BaseController
public function store() public function store()
{ {
if ($r = $this->redirectIfCannotManageCodeMaster()) { if ($r = $this->redirectIfCannotManageCodeKindMaster()) {
return $r; return $r;
} }
@@ -67,7 +67,7 @@ class CodeKind extends BaseController
public function edit(int $id) public function edit(int $id)
{ {
if ($r = $this->redirectIfCannotManageCodeMaster()) { if ($r = $this->redirectIfCannotManageCodeKindMaster()) {
return $r; return $r;
} }
@@ -84,7 +84,7 @@ class CodeKind extends BaseController
public function update(int $id) public function update(int $id)
{ {
if ($r = $this->redirectIfCannotManageCodeMaster()) { if ($r = $this->redirectIfCannotManageCodeKindMaster()) {
return $r; return $r;
} }
@@ -112,7 +112,7 @@ class CodeKind extends BaseController
public function delete(int $id) public function delete(int $id)
{ {
if ($r = $this->redirectIfCannotManageCodeMaster()) { if ($r = $this->redirectIfCannotManageCodeKindMaster()) {
return $r; return $r;
} }

View File

@@ -18,25 +18,19 @@ class Company extends BaseController
{ {
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if (!$lgIdx) { if (! $lgIdx) {
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.'); return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
} }
$list = $this->model->where('cp_lg_idx', $lgIdx)->orderBy('cp_idx', 'DESC')->paginate(20); $list = $this->model->where('cp_lg_idx', $lgIdx)->orderBy('cp_idx', 'DESC')->paginate(20);
$pager = $this->model->pager; $pager = $this->model->pager;
return view('admin/layout', [ return $this->renderWorkPage('업체 관리', 'admin/company/index', ['list' => $list, 'pager' => $pager]);
'title' => '업체 관리',
'content' => view('admin/company/index', ['list' => $list, 'pager' => $pager]),
]);
} }
public function create() public function create()
{ {
return view('admin/layout', [ return $this->renderWorkPage('업체 등록', 'admin/company/create');
'title' => '업체 등록',
'content' => view('admin/company/create'),
]);
} }
public function store() public function store()
@@ -66,29 +60,26 @@ class Company extends BaseController
'cp_regdate' => date('Y-m-d H:i:s'), 'cp_regdate' => date('Y-m-d H:i:s'),
]); ]);
return redirect()->to(site_url('admin/companies'))->with('success', '업체가 등록되었습니다.'); return redirect()->to(mgmt_url('companies'))->with('success', '업체가 등록되었습니다.');
} }
public function edit(int $id) public function edit(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/companies'))->with('error', '업체를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('companies'))->with('error', '업체를 찾을 수 없습니다.');
} }
return view('admin/layout', [ return $this->renderWorkPage('업체 수정', 'admin/company/edit', ['item' => $item]);
'title' => '업체 수정',
'content' => view('admin/company/edit', ['item' => $item]),
]);
} }
public function update(int $id) public function update(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/companies'))->with('error', '업체를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('companies'))->with('error', '업체를 찾을 수 없습니다.');
} }
$rules = [ $rules = [
@@ -110,18 +101,19 @@ class Company extends BaseController
'cp_state' => (int) $this->request->getPost('cp_state'), 'cp_state' => (int) $this->request->getPost('cp_state'),
]); ]);
return redirect()->to(site_url('admin/companies'))->with('success', '업체가 수정되었습니다.'); return redirect()->to(mgmt_url('companies'))->with('success', '업체가 수정되었습니다.');
} }
public function delete(int $id) public function delete(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->cp_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/companies'))->with('error', '업체를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('companies'))->with('error', '업체를 찾을 수 없습니다.');
} }
$this->model->delete($id); $this->model->delete($id);
return redirect()->to(site_url('admin/companies'))->with('success', '업체가 삭제되었습니다.');
return redirect()->to(mgmt_url('companies'))->with('success', '업체가 삭제되었습니다.');
} }
} }

View File

@@ -39,7 +39,7 @@ class DesignatedShop extends BaseController
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin')) return redirect()->to(work_area_home_url())
->with('error', '작업할 지자체가 선택되지 않았습니다. 지자체를 선택해 주세요.'); ->with('error', '작업할 지자체가 선택되지 않았습니다. 지자체를 선택해 주세요.');
} }
@@ -73,17 +73,14 @@ class DesignatedShop extends BaseController
$db = \Config\Database::connect(); $db = \Config\Database::connect();
$gugunCodes = $db->query("SELECT DISTINCT ds_gugun_code FROM designated_shop WHERE ds_lg_idx = ? AND ds_gugun_code != '' ORDER BY ds_gugun_code", [$lgIdx])->getResult(); $gugunCodes = $db->query("SELECT DISTINCT ds_gugun_code FROM designated_shop WHERE ds_lg_idx = ? AND ds_gugun_code != '' ORDER BY ds_gugun_code", [$lgIdx])->getResult();
return view('admin/layout', [ return $this->renderWorkPage('지정판매소 관리', 'admin/designated_shop/index', [
'title' => '지정판매소 관리', 'list' => $list,
'content' => view('admin/designated_shop/index', [ 'lgMap' => $lgMap,
'list' => $list, 'pager' => $pager,
'lgMap' => $lgMap, 'dsName' => $dsName ?? '',
'pager' => $pager, 'dsGugunCode' => $dsGugunCode ?? '',
'dsName' => $dsName ?? '', 'dsState' => $dsState ?? '',
'dsGugunCode' => $dsGugunCode ?? '', 'gugunCodes' => $gugunCodes,
'dsState' => $dsState ?? '',
'gugunCodes' => $gugunCodes,
]),
]); ]);
} }
@@ -92,7 +89,7 @@ class DesignatedShop extends BaseController
helper(['admin', 'export']); helper(['admin', 'export']);
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if (!$lgIdx) { if (!$lgIdx) {
return redirect()->to(site_url('admin/designated-shops'))->with('error', '지자체를 선택해 주세요.'); return redirect()->to(mgmt_url('designated-shops'))->with('error', '지자체를 선택해 주세요.');
} }
$list = $this->shopModel->where('ds_lg_idx', $lgIdx)->orderBy('ds_idx', 'DESC')->findAll(); $list = $this->shopModel->where('ds_lg_idx', $lgIdx)->orderBy('ds_idx', 'DESC')->findAll();
@@ -129,22 +126,19 @@ class DesignatedShop extends BaseController
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '작업할 지자체가 선택되지 않았습니다. 지자체를 선택해 주세요.'); ->with('error', '작업할 지자체가 선택되지 않았습니다. 지자체를 선택해 주세요.');
} }
$currentLg = $this->lgModel->find($lgIdx); $currentLg = $this->lgModel->find($lgIdx);
if ($currentLg === null) { if ($currentLg === null) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '선택한 지자체 정보를 찾을 수 없습니다.'); ->with('error', '선택한 지자체 정보를 찾을 수 없습니다.');
} }
return view('admin/layout', [ return $this->renderWorkPage('지정판매소 등록', 'admin/designated_shop/create', [
'title' => '지정판매소 등록', 'localGovs' => [],
'content' => view('admin/designated_shop/create', [ 'currentLg' => $currentLg,
'localGovs' => [],
'currentLg' => $currentLg,
]),
]); ]);
} }
@@ -154,7 +148,7 @@ class DesignatedShop extends BaseController
public function store() public function store()
{ {
if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) { if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '지정판매소 등록은 관리자만 가능합니다.'); ->with('error', '지정판매소 등록은 관리자만 가능합니다.');
} }
@@ -211,7 +205,7 @@ class DesignatedShop extends BaseController
$this->shopModel->insert($data); $this->shopModel->insert($data);
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('success', '지정판매소가 등록되었습니다.'); ->with('success', '지정판매소가 등록되었습니다.');
} }
@@ -222,31 +216,28 @@ class DesignatedShop extends BaseController
public function edit(int $id) public function edit(int $id)
{ {
if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) { if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '권한이 없습니다.'); ->with('error', '권한이 없습니다.');
} }
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '작업할 지자체가 선택되지 않았습니다.'); ->with('error', '작업할 지자체가 선택되지 않았습니다.');
} }
$shop = $this->shopModel->find($id); $shop = $this->shopModel->find($id);
if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) { if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '해당 지정판매소를 찾을 수 없거나 수정할 수 없습니다.'); ->with('error', '해당 지정판매소를 찾을 수 없거나 수정할 수 없습니다.');
} }
$currentLg = $this->lgModel->find($lgIdx); $currentLg = $this->lgModel->find($lgIdx);
return view('admin/layout', [ return $this->renderWorkPage('지정판매소 수정', 'admin/designated_shop/edit', [
'title' => '지정판매소 수정', 'shop' => $shop,
'content' => view('admin/designated_shop/edit', [ 'currentLg' => $currentLg,
'shop' => $shop,
'currentLg' => $currentLg,
]),
]); ]);
} }
@@ -256,20 +247,20 @@ class DesignatedShop extends BaseController
public function update(int $id) public function update(int $id)
{ {
if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) { if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '권한이 없습니다.'); ->with('error', '권한이 없습니다.');
} }
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '작업할 지자체가 선택되지 않았습니다.'); ->with('error', '작업할 지자체가 선택되지 않았습니다.');
} }
$shop = $this->shopModel->find($id); $shop = $this->shopModel->find($id);
if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) { if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '해당 지정판매소를 찾을 수 없거나 수정할 수 없습니다.'); ->with('error', '해당 지정판매소를 찾을 수 없거나 수정할 수 없습니다.');
} }
@@ -305,7 +296,7 @@ class DesignatedShop extends BaseController
$this->shopModel->update($id, $data); $this->shopModel->update($id, $data);
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('success', '지정판매소 정보가 수정되었습니다.'); ->with('success', '지정판매소 정보가 수정되었습니다.');
} }
@@ -316,26 +307,26 @@ class DesignatedShop extends BaseController
public function delete(int $id) public function delete(int $id)
{ {
if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) { if (! $this->isSuperAdmin() && ! $this->isLocalAdmin()) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '권한이 없습니다.'); ->with('error', '권한이 없습니다.');
} }
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '작업할 지자체가 선택되지 않았습니다.'); ->with('error', '작업할 지자체가 선택되지 않았습니다.');
} }
$shop = $this->shopModel->find($id); $shop = $this->shopModel->find($id);
if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) { if ($shop === null || (int) $shop->ds_lg_idx !== $lgIdx) {
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('error', '해당 지정판매소를 찾을 수 없거나 삭제할 수 없습니다.'); ->with('error', '해당 지정판매소를 찾을 수 없거나 삭제할 수 없습니다.');
} }
$this->shopModel->delete($id); $this->shopModel->delete($id);
return redirect()->to(site_url('admin/designated-shops')) return redirect()->to(mgmt_url('designated-shops'))
->with('success', '지정판매소가 삭제되었습니다.'); ->with('success', '지정판매소가 삭제되었습니다.');
} }
@@ -347,7 +338,7 @@ class DesignatedShop extends BaseController
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin')) return redirect()->to(work_area_home_url())
->with('error', '작업할 지자체가 선택되지 않았습니다.'); ->with('error', '작업할 지자체가 선택되지 않았습니다.');
} }
@@ -356,11 +347,8 @@ class DesignatedShop extends BaseController
->where('ds_state', 1) ->where('ds_state', 1)
->findAll(); ->findAll();
return view('admin/layout', [ return $this->renderWorkPage('지정판매소 지도', 'admin/designated_shop/map', [
'title' => '지정판매소 지도', 'shops' => $shops,
'content' => view('admin/designated_shop/map', [
'shops' => $shops,
]),
]); ]);
} }
@@ -372,7 +360,7 @@ class DesignatedShop extends BaseController
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if ($lgIdx === null || $lgIdx <= 0) { if ($lgIdx === null || $lgIdx <= 0) {
return redirect()->to(site_url('admin')) return redirect()->to(work_area_home_url())
->with('error', '작업할 지자체가 선택되지 않았습니다.'); ->with('error', '작업할 지자체가 선택되지 않았습니다.');
} }
@@ -400,14 +388,11 @@ class DesignatedShop extends BaseController
$totalActive = $this->shopModel->where('ds_lg_idx', $lgIdx)->where('ds_state', 1)->countAllResults(false); $totalActive = $this->shopModel->where('ds_lg_idx', $lgIdx)->where('ds_state', 1)->countAllResults(false);
$totalInactive = $this->shopModel->where('ds_lg_idx', $lgIdx)->where('ds_state !=', 1)->countAllResults(false); $totalInactive = $this->shopModel->where('ds_lg_idx', $lgIdx)->where('ds_state !=', 1)->countAllResults(false);
return view('admin/layout', [ return $this->renderWorkPage('지정판매소 현황', 'admin/designated_shop/status', [
'title' => '지정판매소 현황', 'newByYear' => $newByYear,
'content' => view('admin/designated_shop/status', [ 'cancelByYear' => $cancelByYear,
'newByYear' => $newByYear, 'totalActive' => $totalActive,
'cancelByYear' => $cancelByYear, 'totalInactive' => $totalInactive,
'totalActive' => $totalActive,
'totalInactive' => $totalInactive,
]),
]); ]);
} }

View File

@@ -18,35 +18,32 @@ class FreeRecipient extends BaseController
private function getCodeOptions(string $ckCode): array private function getCodeOptions(string $ckCode): array
{ {
$kind = model(CodeKindModel::class)->where('ck_code', $ckCode)->first(); helper('admin');
return $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true) : []; $lgIdx = admin_effective_lg_idx();
$kind = model(CodeKindModel::class)->where('ck_code', $ckCode)->first();
return $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
} }
public function index() public function index()
{ {
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if (!$lgIdx) { if (! $lgIdx) {
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.'); return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
} }
$list = $this->model->where('fr_lg_idx', $lgIdx)->orderBy('fr_idx', 'DESC')->paginate(20); $list = $this->model->where('fr_lg_idx', $lgIdx)->orderBy('fr_idx', 'DESC')->paginate(20);
$pager = $this->model->pager; $pager = $this->model->pager;
return view('admin/layout', [ return $this->renderWorkPage('무료용 대상자 관리', 'admin/free_recipient/index', ['list' => $list, 'pager' => $pager]);
'title' => '무료용 대상자 관리',
'content' => view('admin/free_recipient/index', ['list' => $list, 'pager' => $pager]),
]);
} }
public function create() public function create()
{ {
return view('admin/layout', [ return $this->renderWorkPage('무료용 대상자 등록', 'admin/free_recipient/create', [
'title' => '무료용 대상자 등록', 'typeCodes' => $this->getCodeOptions('H'),
'content' => view('admin/free_recipient/create', [ 'dongCodes' => $this->getCodeOptions('D'),
'typeCodes' => $this->getCodeOptions('H'),
'dongCodes' => $this->getCodeOptions('D'),
]),
]); ]);
} }
@@ -75,24 +72,21 @@ class FreeRecipient extends BaseController
'fr_regdate' => date('Y-m-d H:i:s'), 'fr_regdate' => date('Y-m-d H:i:s'),
]); ]);
return redirect()->to(site_url('admin/free-recipients'))->with('success', '무료용 대상자가 등록되었습니다.'); return redirect()->to(mgmt_url('free-recipients'))->with('success', '무료용 대상자가 등록되었습니다.');
} }
public function edit(int $id) public function edit(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/free-recipients'))->with('error', '대상자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('free-recipients'))->with('error', '대상자를 찾을 수 없습니다.');
} }
return view('admin/layout', [ return $this->renderWorkPage('무료용 대상자 수정', 'admin/free_recipient/edit', [
'title' => '무료용 대상자 수정', 'item' => $item,
'content' => view('admin/free_recipient/edit', [ 'typeCodes' => $this->getCodeOptions('H'),
'item' => $item, 'dongCodes' => $this->getCodeOptions('D'),
'typeCodes' => $this->getCodeOptions('H'),
'dongCodes' => $this->getCodeOptions('D'),
]),
]); ]);
} }
@@ -100,8 +94,8 @@ class FreeRecipient extends BaseController
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/free-recipients'))->with('error', '대상자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('free-recipients'))->with('error', '대상자를 찾을 수 없습니다.');
} }
$rules = [ $rules = [
@@ -123,18 +117,19 @@ class FreeRecipient extends BaseController
'fr_state' => (int) $this->request->getPost('fr_state'), 'fr_state' => (int) $this->request->getPost('fr_state'),
]); ]);
return redirect()->to(site_url('admin/free-recipients'))->with('success', '무료용 대상자가 수정되었습니다.'); return redirect()->to(mgmt_url('free-recipients'))->with('success', '무료용 대상자가 수정되었습니다.');
} }
public function delete(int $id) public function delete(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->fr_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/free-recipients'))->with('error', '대상자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('free-recipients'))->with('error', '대상자를 찾을 수 없습니다.');
} }
$this->model->delete($id); $this->model->delete($id);
return redirect()->to(site_url('admin/free-recipients'))->with('success', '무료용 대상자가 삭제되었습니다.');
return redirect()->to(mgmt_url('free-recipients'))->with('success', '무료용 대상자가 삭제되었습니다.');
} }
} }

View File

@@ -18,8 +18,11 @@ class Manager extends BaseController
private function getCodeOptions(string $ckCode): array private function getCodeOptions(string $ckCode): array
{ {
$kind = model(CodeKindModel::class)->where('ck_code', $ckCode)->first(); helper('admin');
return $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true) : []; $lgIdx = admin_effective_lg_idx();
$kind = model(CodeKindModel::class)->where('ck_code', $ckCode)->first();
return $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
} }
public function index() public function index()
@@ -27,32 +30,28 @@ class Manager extends BaseController
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if (!$lgIdx) { if (!$lgIdx) {
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.'); helper('admin');
return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
} }
$list = $this->model->where('mg_lg_idx', $lgIdx)->orderBy('mg_idx', 'DESC')->paginate(20); $list = $this->model->where('mg_lg_idx', $lgIdx)->orderBy('mg_idx', 'DESC')->paginate(20);
$pager = $this->model->pager; $pager = $this->model->pager;
return view('admin/layout', [ return $this->renderWorkPage('담당자 관리', 'admin/manager/index', ['list' => $list, 'pager' => $pager]);
'title' => '담당자 관리',
'content' => view('admin/manager/index', ['list' => $list, 'pager' => $pager]),
]);
} }
public function create() public function create()
{ {
return view('admin/layout', [ return $this->renderWorkPage('담당자 등록', 'admin/manager/create', [
'title' => '담당자 등록', 'deptCodes' => $this->getCodeOptions('S'),
'content' => view('admin/manager/create', [ 'positionCodes' => $this->getCodeOptions('T'),
'deptCodes' => $this->getCodeOptions('S'),
'positionCodes' => $this->getCodeOptions('T'),
]),
]); ]);
} }
public function store() public function store()
{ {
helper('admin'); helper(['admin', 'url']);
$rules = [ $rules = [
'mg_name' => 'required|max_length[50]', 'mg_name' => 'required|max_length[50]',
'mg_tel' => 'permit_empty|max_length[20]', 'mg_tel' => 'permit_empty|max_length[20]',
@@ -75,33 +74,30 @@ class Manager extends BaseController
'mg_regdate' => date('Y-m-d H:i:s'), 'mg_regdate' => date('Y-m-d H:i:s'),
]); ]);
return redirect()->to(site_url('admin/managers'))->with('success', '담당자가 등록되었습니다.'); return redirect()->to(mgmt_url('managers'))->with('success', '담당자가 등록되었습니다.');
} }
public function edit(int $id) public function edit(int $id)
{ {
helper('admin'); helper(['admin', 'url']);
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) { if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/managers'))->with('error', '담당자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('managers'))->with('error', '담당자를 찾을 수 없습니다.');
} }
return view('admin/layout', [ return $this->renderWorkPage('담당자 수정', 'admin/manager/edit', [
'title' => '담당자 수정', 'item' => $item,
'content' => view('admin/manager/edit', [ 'deptCodes' => $this->getCodeOptions('S'),
'item' => $item, 'positionCodes' => $this->getCodeOptions('T'),
'deptCodes' => $this->getCodeOptions('S'),
'positionCodes' => $this->getCodeOptions('T'),
]),
]); ]);
} }
public function update(int $id) public function update(int $id)
{ {
helper('admin'); helper(['admin', 'url']);
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) { if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/managers'))->with('error', '담당자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('managers'))->with('error', '담당자를 찾을 수 없습니다.');
} }
$rules = [ $rules = [
@@ -122,18 +118,19 @@ class Manager extends BaseController
'mg_state' => (int) $this->request->getPost('mg_state'), 'mg_state' => (int) $this->request->getPost('mg_state'),
]); ]);
return redirect()->to(site_url('admin/managers'))->with('success', '담당자가 수정되었습니다.'); return redirect()->to(mgmt_url('managers'))->with('success', '담당자가 수정되었습니다.');
} }
public function delete(int $id) public function delete(int $id)
{ {
helper('admin'); helper(['admin', 'url']);
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) { if (!$item || (int) $item->mg_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/managers'))->with('error', '담당자를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('managers'))->with('error', '담당자를 찾을 수 없습니다.');
} }
$this->model->delete($id); $this->model->delete($id);
return redirect()->to(site_url('admin/managers'))->with('success', '담당자가 삭제되었습니다.');
return redirect()->to(mgmt_url('managers'))->with('success', '담당자가 삭제되었습니다.');
} }
} }

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Controllers\Admin; namespace App\Controllers\Admin;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@@ -7,6 +9,8 @@ use App\Models\SalesAgencyModel;
class SalesAgency extends BaseController class SalesAgency extends BaseController
{ {
private const SCHEMA_ERROR = '판매 대행소 테이블에 sa_kind·sa_code 컬럼이 없습니다. DB에 writable/database/sales_agency_migrate_to_kind_code_name.sql(또는 신규용 sales_agency_tables.sql)을 적용한 뒤 다시 시도해 주세요.';
private SalesAgencyModel $model; private SalesAgencyModel $model;
public function __construct() public function __construct()
@@ -18,106 +22,122 @@ class SalesAgency extends BaseController
{ {
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx(); $lgIdx = admin_effective_lg_idx();
if (!$lgIdx) { if (! $lgIdx) {
return redirect()->to(site_url('admin'))->with('error', '지자체를 선택해 주세요.'); return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
} }
$list = $this->model->where('sa_lg_idx', $lgIdx)->orderBy('sa_idx', 'DESC')->paginate(20); $list = $this->model->where('sa_lg_idx', $lgIdx)->orderForDisplay()->paginate(20);
$pager = $this->model->pager; $pager = $this->model->pager;
return view('admin/layout', [ return $this->renderWorkPage('판매 대행소 관리', 'admin/sales_agency/index', ['list' => $list, 'pager' => $pager]);
'title' => '판매 대행소 관리',
'content' => view('admin/sales_agency/index', ['list' => $list, 'pager' => $pager]),
]);
} }
public function create() public function create()
{ {
return view('admin/layout', [ helper('admin');
'title' => '판매 대행소 등록', if (! admin_effective_lg_idx()) {
'content' => view('admin/sales_agency/create'), return redirect()->to(work_area_home_url())->with('error', '지자체를 선택해 주세요.');
]); }
return $this->renderWorkPage('판매 대행소 등록', 'admin/sales_agency/create');
} }
public function store() public function store()
{ {
helper('admin'); helper('admin');
$lgIdx = admin_effective_lg_idx();
if (! $lgIdx) {
return redirect()->to(mgmt_url('sales-agencies'))->with('error', '지자체를 선택해 주세요.');
}
if (! $this->model->hasKindCodeColumns()) {
return redirect()->back()->withInput()->with('error', self::SCHEMA_ERROR);
}
$rules = [ $rules = [
'sa_name' => 'required|max_length[100]', 'sa_kind' => 'required|max_length[50]',
'sa_biz_no' => 'permit_empty|max_length[20]', 'sa_code' => 'required|max_length[50]',
'sa_rep_name' => 'permit_empty|max_length[50]', 'sa_name' => 'required|max_length[100]',
'sa_tel' => 'permit_empty|max_length[20]',
'sa_addr' => 'permit_empty|max_length[255]',
]; ];
if (! $this->validate($rules)) { if (! $this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors()); return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
} }
$code = trim((string) $this->request->getPost('sa_code'));
if ($this->model->where('sa_lg_idx', $lgIdx)->where('sa_code', $code)->first() !== null) {
return redirect()->back()->withInput()->with('error', '동일 지자체에 같은 대행소 코드가 이미 있습니다.');
}
$this->model->insert([ $this->model->insert([
'sa_lg_idx' => admin_effective_lg_idx(), 'sa_lg_idx' => $lgIdx,
'sa_name' => $this->request->getPost('sa_name'), 'sa_kind' => trim((string) $this->request->getPost('sa_kind')),
'sa_biz_no' => $this->request->getPost('sa_biz_no') ?? '', 'sa_code' => $code,
'sa_rep_name' => $this->request->getPost('sa_rep_name') ?? '', 'sa_name' => trim((string) $this->request->getPost('sa_name')),
'sa_tel' => $this->request->getPost('sa_tel') ?? '', 'sa_regdate' => date('Y-m-d H:i:s'),
'sa_addr' => $this->request->getPost('sa_addr') ?? '',
'sa_state' => 1,
'sa_regdate' => date('Y-m-d H:i:s'),
]); ]);
return redirect()->to(site_url('admin/sales-agencies'))->with('success', '판매 대행소가 등록되었습니다.'); return redirect()->to(mgmt_url('sales-agencies'))->with('success', '판매 대행소가 등록되었습니다.');
} }
public function edit(int $id) public function edit(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $item = $this->model->find($id);
if (!$item || (int) $item->sa_lg_idx !== admin_effective_lg_idx()) { if (! $item || (int) $item->sa_lg_idx !== admin_effective_lg_idx()) {
return redirect()->to(site_url('admin/sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.'); return redirect()->to(mgmt_url('sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.');
} }
return view('admin/layout', [ return $this->renderWorkPage('판매 대행소 수정', 'admin/sales_agency/edit', ['item' => $item]);
'title' => '판매 대행소 수정',
'content' => view('admin/sales_agency/edit', ['item' => $item]),
]);
} }
public function update(int $id) public function update(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $lgIdx = admin_effective_lg_idx();
if (!$item || (int) $item->sa_lg_idx !== admin_effective_lg_idx()) { $item = $this->model->find($id);
return redirect()->to(site_url('admin/sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.'); if (! $item || ! $lgIdx || (int) $item->sa_lg_idx !== $lgIdx) {
return redirect()->to(mgmt_url('sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.');
}
if (! $this->model->hasKindCodeColumns()) {
return redirect()->back()->withInput()->with('error', self::SCHEMA_ERROR);
} }
$rules = [ $rules = [
'sa_name' => 'required|max_length[100]', 'sa_kind' => 'required|max_length[50]',
'sa_state' => 'required|in_list[0,1]', 'sa_code' => 'required|max_length[50]',
'sa_name' => 'required|max_length[100]',
]; ];
if (! $this->validate($rules)) { if (! $this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors()); return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
} }
$code = trim((string) $this->request->getPost('sa_code'));
$dup = $this->model->where('sa_lg_idx', $lgIdx)->where('sa_code', $code)->where('sa_idx !=', $id)->first();
if ($dup !== null) {
return redirect()->back()->withInput()->with('error', '동일 지자체에 같은 대행소 코드가 이미 있습니다.');
}
$this->model->update($id, [ $this->model->update($id, [
'sa_name' => $this->request->getPost('sa_name'), 'sa_kind' => trim((string) $this->request->getPost('sa_kind')),
'sa_biz_no' => $this->request->getPost('sa_biz_no') ?? '', 'sa_code' => $code,
'sa_rep_name' => $this->request->getPost('sa_rep_name') ?? '', 'sa_name' => trim((string) $this->request->getPost('sa_name')),
'sa_tel' => $this->request->getPost('sa_tel') ?? '',
'sa_addr' => $this->request->getPost('sa_addr') ?? '',
'sa_state' => (int) $this->request->getPost('sa_state'),
]); ]);
return redirect()->to(site_url('admin/sales-agencies'))->with('success', '판매 대행소가 수정되었습니다.'); return redirect()->to(mgmt_url('sales-agencies'))->with('success', '판매 대행소가 수정되었습니다.');
} }
public function delete(int $id) public function delete(int $id)
{ {
helper('admin'); helper('admin');
$item = $this->model->find($id); $lgIdx = admin_effective_lg_idx();
if (!$item || (int) $item->sa_lg_idx !== admin_effective_lg_idx()) { $item = $this->model->find($id);
return redirect()->to(site_url('admin/sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.'); if (! $item || ! $lgIdx || (int) $item->sa_lg_idx !== $lgIdx) {
return redirect()->to(mgmt_url('sales-agencies'))->with('error', '대행소를 찾을 수 없습니다.');
} }
$this->model->delete($id); $this->model->delete($id);
return redirect()->to(site_url('admin/sales-agencies'))->with('success', '판매 대행소가 삭제되었습니다.');
return redirect()->to(mgmt_url('sales-agencies'))->with('success', '삭제되었습니다.');
} }
} }

View File

@@ -121,8 +121,10 @@ class User extends BaseController
if (! $member) { if (! $member) {
return redirect()->to(site_url('admin/users'))->with('error', '회원을 찾을 수 없습니다.'); return redirect()->to(site_url('admin/users'))->with('error', '회원을 찾을 수 없습니다.');
} }
$member->mb_email = pii_decrypt($member->mb_email ?? ''); $email = pii_decrypt($member->mb_email ?? '');
$member->mb_phone = pii_decrypt($member->mb_phone ?? ''); $phone = pii_decrypt($member->mb_phone ?? '');
$member->mb_email = $email;
$member->mb_phone = $phone;
return view('admin/layout', [ return view('admin/layout', [
'title' => '회원 수정', 'title' => '회원 수정',
'content' => view('admin/user/edit', [ 'content' => view('admin/user/edit', [

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Models; namespace App\Models;
use CodeIgniter\Model; use CodeIgniter\Model;
@@ -12,6 +14,8 @@ class CodeDetailModel extends Model
protected $useTimestamps = false; protected $useTimestamps = false;
protected $allowedFields = [ protected $allowedFields = [
'cd_ck_idx', 'cd_ck_idx',
'cd_source',
'cd_lg_idx',
'cd_code', 'cd_code',
'cd_name', 'cd_name',
'cd_sort', 'cd_sort',
@@ -20,14 +24,50 @@ class CodeDetailModel extends Model
]; ];
/** /**
* 특정 코드 종류의 세부코드 목록 * 목록 조회: 플랫폼(0) + (선택) 해당 지자체 행
*
* @param int|null $effectiveLgIdx null 또는 1 미만이면 플랫폼 공통만
*/ */
public function getByKind(int $ckIdx, bool $activeOnly = false): array public function filterByTenantScope(?int $effectiveLgIdx): self
{ {
$builder = $this->where('cd_ck_idx', $ckIdx); if ($effectiveLgIdx === null || $effectiveLgIdx < 1) {
if ($activeOnly) { return $this->where('cd_lg_idx', 0);
$builder->where('cd_state', 1);
} }
return $builder->orderBy('cd_sort', 'ASC')->findAll();
return $this->groupStart()
->where('cd_lg_idx', 0)
->orWhere('cd_lg_idx', $effectiveLgIdx)
->groupEnd();
}
/**
* 특정 코드 종류의 세부코드 목록
*
* @param int|null $effectiveLgIdx 테넌트 범위 (null=플랫폼만)
*/
public function getByKind(int $ckIdx, bool $activeOnly = false, ?int $effectiveLgIdx = null): array
{
$this->where('cd_ck_idx', $ckIdx);
$this->filterByTenantScope($effectiveLgIdx);
if ($activeOnly) {
$this->where('cd_state', 1);
}
return $this->orderBy('cd_sort', 'ASC')->orderBy('cd_idx', 'ASC')->findAll();
}
/**
* 동일 세부코드값: 지자체 전용이 있으면 우선, 없으면 플랫폼
*/
public function findResolvedByKindAndCode(int $ckIdx, string $code, ?int $effectiveLgIdx): ?object
{
if ($effectiveLgIdx !== null && $effectiveLgIdx > 0) {
$local = $this->where('cd_ck_idx', $ckIdx)->where('cd_code', $code)->where('cd_lg_idx', $effectiveLgIdx)->first();
if ($local !== null) {
return $local;
}
}
return $this->where('cd_ck_idx', $ckIdx)->where('cd_code', $code)->where('cd_lg_idx', 0)->first();
} }
} }

View File

@@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Models; namespace App\Models;
use CodeIgniter\Model; use CodeIgniter\Model;
@@ -11,7 +13,34 @@ class SalesAgencyModel extends Model
protected $returnType = 'object'; protected $returnType = 'object';
protected $useTimestamps = false; protected $useTimestamps = false;
protected $allowedFields = [ protected $allowedFields = [
'sa_lg_idx', 'sa_name', 'sa_biz_no', 'sa_rep_name', 'sa_lg_idx',
'sa_tel', 'sa_addr', 'sa_state', 'sa_regdate', 'sa_kind',
'sa_code',
'sa_name',
'sa_regdate',
]; ];
/** sales_agency 테이블에 sa_kind, sa_code 컬럼이 있는지(마이그레이션 적용 여부). */
public function hasKindCodeColumns(): bool
{
static $cache = null;
if ($cache === null) {
$cols = db_connect()->getFieldNames($this->table);
$cache = in_array('sa_kind', $cols, true) && in_array('sa_code', $cols, true);
}
return $cache;
}
/**
* 신규 스키마면 구분·코드 순, 아니면 명·PK 순(옛 DB 호환).
*
* @return $this
*/
public function orderForDisplay()
{
return $this->hasKindCodeColumns()
? $this->orderBy('sa_kind', 'ASC')->orderBy('sa_code', 'ASC')
: $this->orderBy('sa_name', 'ASC')->orderBy('sa_idx', 'ASC');
}
} }

View File

@@ -30,6 +30,39 @@
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-24" name="cd_sort" type="number" value="<?= esc(old('cd_sort', '0')) ?>" min="0"/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-24" name="cd_sort" type="number" value="<?= esc(old('cd_sort', '0')) ?>" min="0"/>
</div> </div>
<?php if (! empty($canPlatformScope)): ?>
<div class="space-y-2 border-t border-gray-200 pt-3">
<p class="text-sm font-bold text-gray-700">등록 범위</p>
<label class="flex items-center gap-2 text-sm">
<input type="radio" name="cd_scope" value="platform" <?= old('cd_scope', 'platform') === 'platform' ? 'checked' : '' ?> class="cd-scope-radio"/>
플랫폼 공통 (CSV·시드와 동일 — 전 지자체, super/본부만 이후 수정)
</label>
<label class="flex items-center gap-2 text-sm">
<input type="radio" name="cd_scope" value="local" <?= old('cd_scope') === 'local' ? 'checked' : '' ?> class="cd-scope-radio"/>
지자체 전용 (해당 지자체 관리자가 수정·삭제)
</label>
<div id="cd-lg-wrap" class="<?= old('cd_scope') === 'local' ? '' : 'hidden' ?> flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-28">소속 지자체</label>
<select name="cd_lg_idx" class="border border-gray-300 rounded px-3 py-1.5 text-sm min-w-[14rem]">
<option value="">선택</option>
<?php foreach ($localGovernments as $gov): ?>
<option value="<?= (int) $gov->lg_idx ?>" <?= (string) old('cd_lg_idx') === (string) $gov->lg_idx ? 'selected' : '' ?>><?= esc($gov->lg_name) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<script>
(function () {
document.querySelectorAll('.cd-scope-radio').forEach(function (r) {
r.addEventListener('change', function () {
var w = document.getElementById('cd-lg-wrap');
if (w) w.classList.toggle('hidden', document.querySelector('input[name="cd_scope"][value="local"]:checked') === null);
});
});
})();
</script>
<?php endif; ?>
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
<a href="<?= base_url('bag/code-details/' . (int) $kind->ck_idx) ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= base_url('bag/code-details/' . (int) $kind->ck_idx) ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>

View File

@@ -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="<?= base_url('admin/companies/store') ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('companies/store') ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -42,7 +42,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
<a href="<?= base_url('admin/companies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('companies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -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="<?= base_url('admin/companies/update/' . (int) $item->cp_idx) ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('companies/update/' . (int) $item->cp_idx) ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -50,7 +50,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
<a href="<?= base_url('admin/companies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('companies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -4,7 +4,7 @@
<span class="text-sm font-bold text-gray-700">업체 관리</span> <span class="text-sm font-bold text-gray-700">업체 관리</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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 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>
<a href="<?= base_url('admin/companies/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('companies/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>
</div> </div>
</div> </div>
</section> </section>
@@ -35,8 +35,8 @@
<td class="text-left pl-2"><?= esc($row->cp_addr) ?></td> <td class="text-left pl-2"><?= esc($row->cp_addr) ?></td>
<td class="text-center"><?= (int) $row->cp_state === 1 ? '사용' : '미사용' ?></td> <td class="text-center"><?= (int) $row->cp_state === 1 ? '사용' : '미사용' ?></td>
<td class="text-center"> <td class="text-center">
<a href="<?= base_url('admin/companies/edit/' . (int) $row->cp_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a> <a href="<?= mgmt_url('companies/edit/' . (int) $row->cp_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a>
<form action="<?= base_url('admin/companies/delete/' . (int) $row->cp_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');"> <form action="<?= mgmt_url('companies/delete/' . (int) $row->cp_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button> <button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
</form> </form>

View File

@@ -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="<?= base_url('admin/designated-shops/store') ?>" method="POST" class="space-y-4"> <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)): ?>
@@ -95,7 +95,7 @@
<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="<?= base_url('admin/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 File

@@ -10,7 +10,7 @@ $v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($k
<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="<?= base_url('admin/designated-shops/update/' . (int) $shop->ds_idx) ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('designated-shops/update/' . (int) $shop->ds_idx) ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<?php if ($currentLg !== null): ?> <?php if ($currentLg !== null): ?>
@@ -99,7 +99,7 @@ $v = fn ($key, $default = '') => old($key) !== null && old($key) !== '' ? old($k
<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="<?= base_url('admin/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 File

@@ -3,15 +3,15 @@
<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">지정판매소 목록</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<a href="<?= base_url('admin/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 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>
<a href="<?= base_url('admin/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>
</div> </div>
</div> </div>
</section> </section>
<!-- P2-15: 다조건 검색 --> <!-- 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="<?= base_url('admin/designated-shops') ?>" class="flex flex-wrap items-center gap-2"> <form method="GET" action="<?= mgmt_url('designated-shops') ?>" class="flex flex-wrap items-center gap-2">
<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-40"/>
<label class="text-sm text-gray-600">구군코드</label> <label class="text-sm text-gray-600">구군코드</label>
@@ -29,7 +29,7 @@
<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="<?= base_url('admin/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('designated-shops') ?>" 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"> <div class="border border-gray-300 overflow-auto mt-2">
@@ -61,8 +61,8 @@
<td class="text-center"><?= (int) $row->ds_state === 1 ? '정상' : ((int) $row->ds_state === 2 ? '폐업' : '직권해지') ?></td> <td class="text-center"><?= (int) $row->ds_state === 1 ? '정상' : ((int) $row->ds_state === 2 ? '폐업' : '직권해지') ?></td>
<td class="text-left pl-2"><?= esc($row->ds_regdate ?? '') ?></td> <td class="text-left pl-2"><?= esc($row->ds_regdate ?? '') ?></td>
<td class="text-center"> <td class="text-center">
<a href="<?= base_url('admin/designated-shops/edit/' . (int) $row->ds_idx) ?>" class="text-blue-600 hover:underline text-sm">수정</a> <a href="<?= mgmt_url('designated-shops/edit/' . (int) $row->ds_idx) ?>" class="text-blue-600 hover:underline text-sm">수정</a>
<form action="<?= base_url('admin/designated-shops/delete/' . (int) $row->ds_idx) ?>" method="POST" class="inline ml-1" onsubmit="return confirm('이 지정판매소를 삭제하시겠습니까?');"> <form action="<?= mgmt_url('designated-shops/delete/' . (int) $row->ds_idx) ?>" method="POST" class="inline ml-1" onsubmit="return confirm('이 지정판매소를 삭제하시겠습니까?');">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button> <button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
</form> </form>

View File

@@ -2,7 +2,7 @@
<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">지정판매소 지도</span>
<a href="<?= base_url('admin/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('designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">목록으로</a>
</div> </div>
</section> </section>
<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>

View File

@@ -4,7 +4,7 @@
<span class="text-sm font-bold text-gray-700">지정판매소 현황 (신규/취소)</span> <span class="text-sm font-bold text-gray-700">지정판매소 현황 (신규/취소)</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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 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>
<a href="<?= base_url('admin/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('designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">목록으로</a>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -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="<?= base_url('admin/free-recipients/store') ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('free-recipients/store') ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -56,7 +56,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
<a href="<?= base_url('admin/free-recipients') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('free-recipients') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -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="<?= base_url('admin/free-recipients/update/' . (int) $item->fr_idx) ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('free-recipients/update/' . (int) $item->fr_idx) ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -64,7 +64,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
<a href="<?= base_url('admin/free-recipients') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('free-recipients') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -4,7 +4,7 @@
<span class="text-sm font-bold text-gray-700">무료용 대상자 관리</span> <span class="text-sm font-bold text-gray-700">무료용 대상자 관리</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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 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>
<a href="<?= base_url('admin/free-recipients/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('free-recipients/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>
</div> </div>
</div> </div>
</section> </section>
@@ -37,8 +37,8 @@
<td class="text-center"><?= esc($row->fr_end_date) ?></td> <td class="text-center"><?= esc($row->fr_end_date) ?></td>
<td class="text-center"><?= (int) $row->fr_state === 1 ? '사용' : '미사용' ?></td> <td class="text-center"><?= (int) $row->fr_state === 1 ? '사용' : '미사용' ?></td>
<td class="text-center"> <td class="text-center">
<a href="<?= base_url('admin/free-recipients/edit/' . (int) $row->fr_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a> <a href="<?= mgmt_url('free-recipients/edit/' . (int) $row->fr_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a>
<form action="<?= base_url('admin/free-recipients/delete/' . (int) $row->fr_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');"> <form action="<?= mgmt_url('free-recipients/delete/' . (int) $row->fr_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button> <button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
</form> </form>
@@ -46,7 +46,12 @@
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php if (empty($list)): ?> <?php if (empty($list)): ?>
<tr><td colspan="10" class="text-center text-gray-400 py-4">등록된 데이터가 없습니다.</td></tr> <tr>
<td colspan="10" class="text-center text-gray-500 py-4 text-sm space-y-1">
<p>등록된 데이터가 없습니다.</p>
<p class="text-gray-400">다른 지자체를 선택 중이면 해당 지자체 기준으로만 조회됩니다. Super Admin 은 상단에서 작업 지자체를 바꿔 보세요.</p>
</td>
</tr>
<?php endif; ?> <?php endif; ?>
</tbody> </tbody>
</table> </table>

View File

@@ -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="<?= base_url('admin/managers/store') ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('managers/store') ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -51,7 +51,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
<a href="<?= base_url('admin/managers') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('managers') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -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="<?= base_url('admin/managers/update/' . (int) $item->mg_idx) ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('managers/update/' . (int) $item->mg_idx) ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<div class="flex flex-wrap items-center gap-2"> <div class="flex flex-wrap items-center gap-2">
@@ -59,7 +59,7 @@
<div class="flex gap-2 pt-2"> <div class="flex gap-2 pt-2">
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
<a href="<?= base_url('admin/managers') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('managers') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -4,7 +4,7 @@
<span class="text-sm font-bold text-gray-700">담당자 관리</span> <span class="text-sm font-bold text-gray-700">담당자 관리</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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 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>
<a href="<?= base_url('admin/managers/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('managers/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>
</div> </div>
</div> </div>
</section> </section>
@@ -35,8 +35,8 @@
<td class="text-center"><?= esc($row->mg_email) ?></td> <td class="text-center"><?= esc($row->mg_email) ?></td>
<td class="text-center"><?= (int) $row->mg_state === 1 ? '사용' : '미사용' ?></td> <td class="text-center"><?= (int) $row->mg_state === 1 ? '사용' : '미사용' ?></td>
<td class="text-center"> <td class="text-center">
<a href="<?= base_url('admin/managers/edit/' . (int) $row->mg_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a> <a href="<?= mgmt_url('managers/edit/' . (int) $row->mg_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a>
<form action="<?= base_url('admin/managers/delete/' . (int) $row->mg_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');"> <form action="<?= mgmt_url('managers/delete/' . (int) $row->mg_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button> <button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
</form> </form>

View File

@@ -4,6 +4,27 @@ $list = $list ?? [];
$mtIdx = (int) ($mtIdx ?? 0); $mtIdx = (int) ($mtIdx ?? 0);
$mtCode = (string) ($mtCode ?? ''); $mtCode = (string) ($mtCode ?? '');
$levelNames = $levelNames ?? []; $levelNames = $levelNames ?? [];
helper('admin');
$adminMenusNavPath = current_nav_request_path();
/**
* 메뉴 관리 목록용: 저장된 mm_link → 실제 href (외부 http(s) 또는 base_url).
*/
$adminMenuListResolveHref = static function (string $rawLink) use ($adminMenusNavPath): string {
$rawLink = trim($rawLink);
if ($rawLink === '') {
return '';
}
if (preg_match('#^https?://#i', $rawLink)) {
return $rawLink;
}
$pathSeg = menu_link_preferred_href_path($rawLink, $adminMenusNavPath);
if ($pathSeg === '') {
$pathSeg = normalize_menu_link_for_url($rawLink);
}
return $pathSeg !== '' ? base_url($pathSeg) : '';
};
?> ?>
<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">
@@ -48,6 +69,10 @@ $levelNames = $levelNames ?? [];
</thead> </thead>
<tbody> <tbody>
<?php foreach ($list as $i => $row): ?> <?php foreach ($list as $i => $row): ?>
<?php
$rawLink = trim((string) $row->mm_link);
$listItemHref = $rawLink !== '' ? $adminMenuListResolveHref($rawLink) : '';
?>
<tr class="menu-row" data-mm-idx="<?= (int) $row->mm_idx ?>" data-mm-pidx="<?= (int) $row->mm_pidx ?>" data-mm-dep="<?= (int) $row->mm_dep ?>"> <tr class="menu-row" data-mm-idx="<?= (int) $row->mm_idx ?>" data-mm-pidx="<?= (int) $row->mm_pidx ?>" data-mm-dep="<?= (int) $row->mm_dep ?>">
<td class="text-center align-middle"> <td class="text-center align-middle">
<span class="menu-drag-handle cursor-move text-gray-400 select-none" title="드래그해서 순서를 변경하세요">↕</span> <span class="menu-drag-handle cursor-move text-gray-400 select-none" title="드래그해서 순서를 변경하세요">↕</span>
@@ -67,9 +92,21 @@ $levelNames = $levelNames ?? [];
└─ └─
<?php endif; ?> <?php endif; ?>
</span> </span>
<?php if ($listItemHref !== ''): ?>
<a href="<?= esc($listItemHref) ?>" class="ml-1 text-gray-900 hover:text-blue-700 hover:underline font-medium" target="_blank" rel="noopener noreferrer"><?= esc($row->mm_name) ?></a>
<?php else: ?>
<span class="ml-1"><?= esc($row->mm_name) ?></span> <span class="ml-1"><?= esc($row->mm_name) ?></span>
<?php endif; ?>
</td>
<td class="text-left pl-2 text-xs">
<?php if ($rawLink === ''): ?>
<span class="text-gray-400">—</span>
<?php elseif ($listItemHref !== ''): ?>
<a href="<?= esc($listItemHref) ?>" class="text-blue-600 hover:underline font-medium break-all" target="_blank" rel="noopener noreferrer"><?= esc($rawLink) ?></a>
<?php else: ?>
<span class="text-amber-700" title="URL로 해석되지 않는 링크입니다"><?= esc($rawLink) ?></span>
<?php endif; ?>
</td> </td>
<td class="text-left pl-2 text-xs"><?= esc($row->mm_link) ?></td>
<td class="text-left pl-2 text-xs"> <td class="text-left pl-2 text-xs">
<?php <?php
if ((string) $row->mm_level === '') { if ((string) $row->mm_level === '') {

View File

@@ -2,37 +2,27 @@
<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="<?= base_url('admin/sales-agencies/store') ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('sales-agencies/store') ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<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">대행소 <span class="text-red-500">*</span></label> <label class="block text-sm font-bold text-gray-700 w-28">대행소 구분 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_name" type="text" value="<?= esc(old('sa_name')) ?>" required/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_kind" type="text" value="<?= esc(old('sa_kind')) ?>" required maxlength="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">대행소 코드 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_biz_no" type="text" value="<?= esc(old('sa_biz_no')) ?>"/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_code" type="text" value="<?= esc(old('sa_code')) ?>" required maxlength="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">대행소 명 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_rep_name" type="text" value="<?= esc(old('sa_rep_name')) ?>"/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_name" type="text" value="<?= esc(old('sa_name')) ?>" required maxlength="100"/>
</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="sa_tel" type="text" value="<?= esc(old('sa_tel')) ?>"/>
</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" name="sa_addr" type="text" value="<?= esc(old('sa_addr')) ?>"/>
</div> </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-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
<a href="<?= base_url('admin/sales-agencies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('sales-agencies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -2,45 +2,27 @@
<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="<?= base_url('admin/sales-agencies/update/' . (int) $item->sa_idx) ?>" method="POST" class="space-y-4"> <form action="<?= mgmt_url('sales-agencies/update/' . (int) $item->sa_idx) ?>" method="POST" class="space-y-4">
<?= csrf_field() ?> <?= csrf_field() ?>
<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">대행소 <span class="text-red-500">*</span></label> <label class="block text-sm font-bold text-gray-700 w-28">대행소 구분 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_name" type="text" value="<?= esc(old('sa_name', $item->sa_name)) ?>" required/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_kind" type="text" value="<?= esc(old('sa_kind', $item->sa_kind ?? '')) ?>" required maxlength="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">대행소 코드 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_biz_no" type="text" value="<?= esc(old('sa_biz_no', $item->sa_biz_no)) ?>"/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_code" type="text" value="<?= esc(old('sa_code', $item->sa_code ?? '')) ?>" required maxlength="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">대행소 명 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_rep_name" type="text" value="<?= esc(old('sa_rep_name', $item->sa_rep_name)) ?>"/> <input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="sa_name" type="text" value="<?= esc(old('sa_name', $item->sa_name)) ?>" required maxlength="100"/>
</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="sa_tel" type="text" value="<?= esc(old('sa_tel', $item->sa_tel)) ?>"/>
</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" name="sa_addr" type="text" value="<?= esc(old('sa_addr', $item->sa_addr)) ?>"/>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-28">상태 <span class="text-red-500">*</span></label>
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32" name="sa_state" required>
<option value="1" <?= (int) old('sa_state', $item->sa_state) === 1 ? 'selected' : '' ?>>사용</option>
<option value="0" <?= (int) old('sa_state', $item->sa_state) === 0 ? 'selected' : '' ?>>미사용</option>
</select>
</div> </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-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button> <button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">수정</button>
<a href="<?= base_url('admin/sales-agencies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a> <a href="<?= mgmt_url('sales-agencies') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
</div> </div>
</form> </form>
</div> </div>

View File

@@ -4,7 +4,7 @@
<span class="text-sm font-bold text-gray-700">판매 대행소 관리</span> <span class="text-sm font-bold text-gray-700">판매 대행소 관리</span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<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 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>
<a href="<?= base_url('admin/sales-agencies/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('sales-agencies/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>
</div> </div>
</div> </div>
</section> </section>
@@ -13,12 +13,9 @@
<thead> <thead>
<tr> <tr>
<th class="w-16">번호</th> <th class="w-16">번호</th>
<th>대행소</th> <th>대행소 구분</th>
<th>사업자번호</th> <th>대행소 코드</th>
<th>대표자</th> <th>대행소 명</th>
<th>전화</th>
<th>주소</th>
<th class="w-20">상태</th>
<th class="w-36">작업</th> <th class="w-36">작업</th>
</tr> </tr>
</thead> </thead>
@@ -26,15 +23,12 @@
<?php foreach ($list as $row): ?> <?php foreach ($list as $row): ?>
<tr> <tr>
<td class="text-center"><?= esc($row->sa_idx) ?></td> <td class="text-center"><?= esc($row->sa_idx) ?></td>
<td class="text-left pl-2"><?= esc($row->sa_kind ?? '') ?></td>
<td class="text-center"><?= esc($row->sa_code ?? '') ?></td>
<td class="text-left pl-2"><?= esc($row->sa_name) ?></td> <td class="text-left pl-2"><?= esc($row->sa_name) ?></td>
<td class="text-center"><?= esc($row->sa_biz_no) ?></td>
<td class="text-center"><?= esc($row->sa_rep_name) ?></td>
<td class="text-center"><?= esc($row->sa_tel) ?></td>
<td class="text-left pl-2"><?= esc($row->sa_addr) ?></td>
<td class="text-center"><?= (int) $row->sa_state === 1 ? '정상' : '미사용' ?></td>
<td class="text-center"> <td class="text-center">
<a href="<?= base_url('admin/sales-agencies/edit/' . (int) $row->sa_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a> <a href="<?= mgmt_url('sales-agencies/edit/' . (int) $row->sa_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">수정</a>
<form action="<?= base_url('admin/sales-agencies/delete/' . (int) $row->sa_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');"> <form action="<?= mgmt_url('sales-agencies/delete/' . (int) $row->sa_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');">
<?= csrf_field() ?> <?= csrf_field() ?>
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button> <button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
</form> </form>
@@ -42,7 +36,7 @@
</tr> </tr>
<?php endforeach; ?> <?php endforeach; ?>
<?php if (empty($list)): ?> <?php if (empty($list)): ?>
<tr><td colspan="8" class="text-center text-gray-400 py-4">등록된 데이터가 없습니다.</td></tr> <tr><td colspan="5" class="text-center text-gray-400 py-4">등록된 데이터가 없습니다.</td></tr>
<?php endif; ?> <?php endif; ?>
</tbody> </tbody>
</table> </table>

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/> <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>회원가입 - 쓰레기봉투 물류시스템</title> <title>회원가입 - 종량제 시스템</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&amp;display=swap" rel="stylesheet"/>
<script> <script>
@@ -26,14 +26,7 @@ tailwind.config = {
</head> </head>
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased"> <body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased">
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0"> <header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0">
<div class="flex items-center gap-2"> <?= view('components/header_brand') ?>
<div class="w-6 h-6 flex items-center justify-center shrink-0">
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
</svg>
</div>
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
</div>
</header> </header>
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0"> <div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
회원가입 회원가입
@@ -102,6 +95,6 @@ tailwind.config = {
</form> </form>
</section> </section>
</main> </main>
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">쓰레기봉투 물류시스템</footer> <footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 shrink-0">종량제 시스템</footer>
</body> </body>
</html> </html>

View File

@@ -21,13 +21,17 @@ CREATE TABLE IF NOT EXISTS `code_kind` (
CREATE TABLE IF NOT EXISTS `code_detail` ( CREATE TABLE IF NOT EXISTS `code_detail` (
`cd_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '세부코드 PK', `cd_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '세부코드 PK',
`cd_ck_idx` INT UNSIGNED NOT NULL COMMENT 'code_kind FK', `cd_ck_idx` INT UNSIGNED NOT NULL COMMENT 'code_kind FK',
`cd_source` ENUM('platform','local') NOT NULL DEFAULT 'platform' COMMENT 'platform=전역 시드, local=지자체 등록',
`cd_lg_idx` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '0=전 지자체 공통',
`cd_code` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '세부 코드', `cd_code` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '세부 코드',
`cd_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '세부 명칭', `cd_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '세부 명칭',
`cd_sort` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '정렬 순서', `cd_sort` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '정렬 순서',
`cd_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=사용, 0=미사용', `cd_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=사용, 0=미사용',
`cd_regdate` DATETIME NOT NULL COMMENT '등록일시', `cd_regdate` DATETIME NOT NULL COMMENT '등록일시',
PRIMARY KEY (`cd_idx`), PRIMARY KEY (`cd_idx`),
KEY `idx_cd_ck_idx` (`cd_ck_idx`) KEY `idx_cd_ck_idx` (`cd_ck_idx`),
KEY `idx_cd_lg_idx` (`cd_lg_idx`),
UNIQUE KEY `uk_cd_tenant` (`cd_ck_idx`, `cd_code`, `cd_lg_idx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='세부 기본코드'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='세부 기본코드';

View File

@@ -1,6 +1,8 @@
-- ============================================ -- ============================================
-- 업체 테이블 (P2-11, P2-12) -- 업체 테이블 (P2-11, P2-12)
-- ============================================ -- ============================================
-- 로컬 DB에 없으면:
-- mysql --default-character-set=utf8mb4 -u root -p jongryangje_dev < writable/database/company_tables.sql
CREATE TABLE IF NOT EXISTS `company` ( CREATE TABLE IF NOT EXISTS `company` (
`cp_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT, `cp_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT,

View File

@@ -1,6 +1,9 @@
-- ============================================ -- ============================================
-- 담당자 테이블 (P2-09, P2-10) -- 담당자 테이블 (P2-09, P2-10)
-- ============================================ -- ============================================
-- 로컬 DB에 아직 없으면 아래처럼 실행 (DB 이름은 .env 와 동일하게):
-- mysql -u root -p jongryangje_dev < writable/database/manager_tables.sql
-- 선행: local_government 등 지자체 데이터가 있으면 됨 (FK 제약 없음).
CREATE TABLE IF NOT EXISTS `manager` ( CREATE TABLE IF NOT EXISTS `manager` (
`mg_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT, `mg_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT,

View File

@@ -43,7 +43,7 @@ INSERT INTO `menu` (`mt_idx`, `lg_idx`, `mm_name`, `mm_link`, `mm_pidx`, `mm_dep
(1, 1, '메뉴', 'admin/menus', 0, 0, 5, 0, '', 'Y'), (1, 1, '메뉴', 'admin/menus', 0, 0, 5, 0, '', 'Y'),
(1, 1, '지자체 전환', 'admin/select-local-government', 0, 0, 6, 0, '', 'Y'), (1, 1, '지자체 전환', 'admin/select-local-government', 0, 0, 6, 0, '', 'Y'),
(1, 1, '지자체', 'admin/local-governments', 0, 0, 7, 0, '', 'Y'), (1, 1, '지자체', 'admin/local-governments', 0, 0, 7, 0, '', 'Y'),
(1, 1, '지정판매소', 'admin/designated-shops', 0, 0, 8, 0, '3', 'Y'), (1, 1, '지정판매소', 'bag/designated-shops', 0, 0, 8, 0, '3', 'Y'),
-- mt_idx=2 (site) 기본 1차 사이트 메뉴 (링크는 추후 실제 라우트로 변경) -- mt_idx=2 (site) 기본 1차 사이트 메뉴 (링크는 추후 실제 라우트로 변경)
(2, 1, '기본정보관리', 'bag/basic-info', 0, 0, 0, 0, '', 'Y'), (2, 1, '기본정보관리', 'bag/basic-info', 0, 0, 0, 0, '', 'Y'),

View File

@@ -1,17 +1,15 @@
-- ============================================ -- ============================================
-- 판매 대행소 테이블 (P2-07, P2-08) -- 판매 대행소 테이블 (P2-07, P2-08) — 구분·코드·명만
-- ============================================ -- ============================================
CREATE TABLE IF NOT EXISTS `sales_agency` ( CREATE TABLE IF NOT EXISTS `sales_agency` (
`sa_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT, `sa_idx` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`sa_lg_idx` INT UNSIGNED NOT NULL COMMENT '지자체 FK', `sa_lg_idx` INT UNSIGNED NOT NULL COMMENT '지자체 FK',
`sa_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '대행소', `sa_kind` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대행소 구분',
`sa_biz_no` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '사업자번호', `sa_code` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '대행소 코드',
`sa_rep_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '표자', `sa_name` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '행소 ',
`sa_tel` VARCHAR(20) NOT NULL DEFAULT '' COMMENT '전화번호',
`sa_addr` VARCHAR(255) NOT NULL DEFAULT '' COMMENT '주소',
`sa_state` TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '1=정상, 0=미사용',
`sa_regdate` DATETIME NOT NULL, `sa_regdate` DATETIME NOT NULL,
PRIMARY KEY (`sa_idx`), PRIMARY KEY (`sa_idx`),
UNIQUE KEY `uk_sa_lg_code` (`sa_lg_idx`, `sa_code`),
KEY `idx_sa_lg_idx` (`sa_lg_idx`) KEY `idx_sa_lg_idx` (`sa_lg_idx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='판매 대행소'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='판매 대행소';