feat: 워크스페이스 탭 자동복원 + 주문 입금확인 토글 + 메뉴 개명(전화접수→주문접수)

종량제3 세션 작업.
- 워크스페이스 탭 계정별 저장·자동복원(member_workspace_tabs 자동생성, pref/workspace-tabs)
- 주문접수 관리 입금확인(so_paid) 수동 토글(phoneOrderMarkPaid, order/phone/manage/paid)
- 메뉴 '전화 접수 관리' → '주문접수관리' 개명(피드백 #35) + 시드/개명 SQL

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
taekyoungc
2026-07-23 08:04:45 +09:00
parent dc5c38d242
commit 215d87f991
9 changed files with 221 additions and 23 deletions

View File

@@ -274,6 +274,70 @@ SQL);
return $this->response->setJSON(['ok' => true, 'csrf' => csrf_hash()]);
}
/** member_workspace_tabs 테이블 보장(없으면 생성) */
private function ensureWorkspaceTabsTable(\CodeIgniter\Database\BaseConnection $db): void
{
if ($db->tableExists('member_workspace_tabs')) {
return;
}
$db->query(<<<'SQL'
CREATE TABLE IF NOT EXISTS `member_workspace_tabs` (
`mwt_mb_idx` INT UNSIGNED NOT NULL,
`mwt_state` MEDIUMTEXT NULL,
`mwt_updated` DATETIME NOT NULL,
PRIMARY KEY (`mwt_mb_idx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
SQL);
}
/** 계정별 워크스페이스 탭 상태 조회(자동 복원용). 없으면 'null'. */
public function loadWorkspaceTabsState(int $mbIdx): string
{
if ($mbIdx <= 0) {
return 'null';
}
$db = \Config\Database::connect();
if (! $db->tableExists('member_workspace_tabs')) {
return 'null';
}
$row = $db->table('member_workspace_tabs')->select('mwt_state')->where('mwt_mb_idx', $mbIdx)->get()->getRowArray();
$s = (string) ($row['mwt_state'] ?? '');
return $s !== '' ? $s : 'null';
}
/** 계정별 워크스페이스 탭 상태 저장 (자동 복원) */
public function saveWorkspaceTabs(): ResponseInterface
{
$mbIdx = (int) (session()->get('mb_idx') ?? 0);
if ($mbIdx <= 0) {
return $this->response->setJSON(['ok' => false, 'csrf' => csrf_hash()]);
}
$state = (string) ($this->request->getPost('state') ?? '');
// 크기 제한 + JSON 유효성
if (strlen($state) > 30000) {
return $this->response->setJSON(['ok' => false, 'csrf' => csrf_hash()]);
}
if ($state !== '') {
json_decode($state);
if (json_last_error() !== JSON_ERROR_NONE) {
return $this->response->setJSON(['ok' => false, 'csrf' => csrf_hash()]);
}
}
$db = \Config\Database::connect();
$this->ensureWorkspaceTabsTable($db);
$now = date('Y-m-d H:i:s');
$exists = $db->table('member_workspace_tabs')->where('mwt_mb_idx', $mbIdx)->countAllResults();
if ($exists > 0) {
$db->table('member_workspace_tabs')->where('mwt_mb_idx', $mbIdx)
->update(['mwt_state' => $state, 'mwt_updated' => $now]);
} else {
$db->table('member_workspace_tabs')->insert(['mwt_mb_idx' => $mbIdx, 'mwt_state' => $state, 'mwt_updated' => $now]);
}
return $this->response->setJSON(['ok' => true, 'csrf' => csrf_hash()]);
}
// ──────────────────────────────────────────────
// 기본정보관리 (단가·포장 단위 진입 허브)
// ──────────────────────────────────────────────
@@ -7896,6 +7960,37 @@ SQL;
return redirect()->to(site_url('bag/order/phone/manage'))->with('success', '주문 수정 저장이 완료되었습니다.');
}
/**
* 주문접수 관리 — 입금확인(so_paid) 수동 토글(JSON 응답).
* 입금확인이 완료되어야 포장(스캔)이 가능하다.
*/
public function phoneOrderMarkPaid()
{
$lgIdx = $this->lgIdx();
if (! $lgIdx) {
return $this->response->setJSON(['ok' => false, 'message' => '지자체를 선택해 주세요.', 'csrf' => csrf_hash()]);
}
$soIdx = (int) ($this->request->getPost('so_idx') ?? 0);
$paid = ((int) ($this->request->getPost('paid') ?? 0)) === 1 ? 1 : 0;
if ($soIdx <= 0) {
return $this->response->setJSON(['ok' => false, 'message' => '주문 값이 올바르지 않습니다.', 'csrf' => csrf_hash()]);
}
$orderModel = model(ShopOrderModel::class);
$order = $orderModel->where('so_idx', $soIdx)->where('so_lg_idx', $lgIdx)->first();
if (! $order) {
return $this->response->setJSON(['ok' => false, 'message' => '선택한 주문을 찾을 수 없습니다.', 'csrf' => csrf_hash()]);
}
if ((string) ($order->so_status ?? '') === 'cancelled') {
return $this->response->setJSON(['ok' => false, 'message' => '취소된 주문은 입금상태를 변경할 수 없습니다.', 'csrf' => csrf_hash()]);
}
$orderModel->update($soIdx, ['so_paid' => $paid]);
return $this->response->setJSON(['ok' => true, 'so_idx' => $soIdx, 'paid' => $paid, 'csrf' => csrf_hash()]);
}
/**
* 전화접수 관리 — 봉투코드 스캔으로 즉시 판매·포장 처리(JSON 응답).
* 선택된 주문의 판매소로 판매 기록(bag_sale) + 재고 차감 + 팩코드 sold 전환 +
@@ -7921,6 +8016,11 @@ SQL;
return $this->response->setJSON(['ok' => false, 'message' => '선택한 주문을 사용할 수 없습니다.', 'csrf' => csrf_hash()]);
}
// 입금확인(so_paid=1)이 안 된 주문은 포장(판매) 스캔 불가.
if ((int) ($order->so_paid ?? 0) !== 1) {
return $this->response->setJSON(['ok' => false, 'message' => '입금확인이 완료되지 않은 주문입니다. 입금확인 후 포장하세요.', 'csrf' => csrf_hash()]);
}
$scan = $this->resolveDesignatedSaleBarcode($lgIdx, $barcode);
if (! ($scan['ok'] ?? false)) {
return $this->response->setJSON(array_merge($scan, ['csrf' => csrf_hash()]));