사이트·관리자 봉투 물류 기능(수불·통계·레포트·재고·발주)과 DB·메뉴·E2E를 운영 반영한다.
통계 분석(전년대비·월별·계절별), 수급계획·LOT 수불, 지정판매소·실사·메뉴 링크 등을 포함한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -43,8 +43,9 @@ class BagInventory extends BaseController
|
||||
];
|
||||
}
|
||||
|
||||
export_csv(
|
||||
'재고현황_' . date('Ymd') . '.csv',
|
||||
export_xlsx(
|
||||
'재고현황_' . date('Ymd') . '.xlsx',
|
||||
'재고현황',
|
||||
['번호', '봉투코드', '봉투명', '현재재고(낱장)', '최종갱신'],
|
||||
$rows
|
||||
);
|
||||
|
||||
@@ -4,17 +4,56 @@ namespace App\Controllers\Admin;
|
||||
|
||||
use App\Controllers\BaseController;
|
||||
use App\Models\BagIssueModel;
|
||||
use App\Models\BagIssueItemCodeModel;
|
||||
use App\Models\BagInventoryModel;
|
||||
use App\Models\CodeKindModel;
|
||||
use App\Models\CodeDetailModel;
|
||||
use App\Models\FreeRecipientModel;
|
||||
use App\Models\PackagingUnitModel;
|
||||
|
||||
class BagIssue extends BaseController
|
||||
{
|
||||
private BagIssueModel $issueModel;
|
||||
private BagIssueItemCodeModel $issueItemCodeModel;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->issueModel = model(BagIssueModel::class);
|
||||
$this->issueItemCodeModel = model(BagIssueItemCodeModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 낱장 수량을 품목코드 단위로 분해한다.
|
||||
*
|
||||
* @return array<int,array{issueCode:string,qty:int}>
|
||||
*/
|
||||
private function buildIssueCodeRows(int $bi2Idx, int $sheetQty, array $packUnit): array
|
||||
{
|
||||
$sheetQty = max(0, $sheetQty);
|
||||
if ($sheetQty <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$chunkSize = max(
|
||||
1,
|
||||
(int) ($packUnit['totalPerBox'] ?? 0),
|
||||
(int) ($packUnit['packPerSheet'] ?? 0)
|
||||
);
|
||||
|
||||
$rows = [];
|
||||
$remaining = $sheetQty;
|
||||
$seq = 1;
|
||||
while ($remaining > 0) {
|
||||
$qty = min($chunkSize, $remaining);
|
||||
$rows[] = [
|
||||
'issueCode' => sprintf('%d-%06d-%03d', (int) date('y'), $bi2Idx, $seq),
|
||||
'qty' => $qty,
|
||||
];
|
||||
$remaining -= $qty;
|
||||
$seq++;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public function index()
|
||||
@@ -62,48 +101,219 @@ class BagIssue extends BaseController
|
||||
'bi2_issue_type' => 'required|max_length[20]',
|
||||
'bi2_issue_date' => 'required|valid_date[Y-m-d]',
|
||||
'bi2_dest_name' => 'required|max_length[100]',
|
||||
'bi2_bag_code' => 'required|max_length[50]',
|
||||
'bi2_qty' => 'required|is_natural_no_zero',
|
||||
// 사이트 다건 입력(item_bag_code/item_qty)과 기존 관리자 단건 입력을 함께 허용
|
||||
'bi2_bag_code' => 'permit_empty|max_length[50]',
|
||||
'bi2_qty' => 'permit_empty|is_natural_no_zero',
|
||||
];
|
||||
if (! $this->validate($rules)) {
|
||||
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
$bagCode = $this->request->getPost('bi2_bag_code');
|
||||
$qty = (int) $this->request->getPost('bi2_qty');
|
||||
|
||||
$kindO = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
||||
$detail = $kindO ? model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $kindO->ck_idx, (string) $bagCode, $lgIdx) : null;
|
||||
$bagName = $detail ? $detail->cd_name : '';
|
||||
$issueType = trim((string) $this->request->getPost('bi2_issue_type'));
|
||||
$destType = trim((string) ($this->request->getPost('bi2_dest_type') ?? ''));
|
||||
$destName = trim((string) ($this->request->getPost('bi2_dest_name') ?? ''));
|
||||
$destDongCode = trim((string) ($this->request->getPost('bi2_dest_dong_code') ?? ''));
|
||||
|
||||
if ($destType === '') {
|
||||
$destType = '동사무소';
|
||||
}
|
||||
if ($issueType === '공공용' && $destType === '동사무소') {
|
||||
$destType = '구청';
|
||||
}
|
||||
|
||||
if ($issueType === '무료용' && $destDongCode !== '') {
|
||||
$existsFreeDong = model(FreeRecipientModel::class)
|
||||
->where('fr_lg_idx', $lgIdx)
|
||||
->where('fr_state', 1)
|
||||
->where('fr_dong_code', $destDongCode)
|
||||
->first();
|
||||
if (! $existsFreeDong) {
|
||||
return redirect()->back()->withInput()->with('error', '선택한 불출처는 무료용 대상이 아닙니다.');
|
||||
}
|
||||
}
|
||||
|
||||
$invRows = model(BagInventoryModel::class)
|
||||
->where('bi_lg_idx', $lgIdx)
|
||||
->where('bi_qty >', 0)
|
||||
->findAll();
|
||||
$inventoryMap = [];
|
||||
foreach ($invRows as $inv) {
|
||||
$code = (string) ($inv->bi_bag_code ?? '');
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$inventoryMap[$code] = [
|
||||
'qty' => (int) ($inv->bi_qty ?? 0),
|
||||
'name' => (string) ($inv->bi_bag_name ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
$unitRows = model(PackagingUnitModel::class)
|
||||
->where('pu_lg_idx', $lgIdx)
|
||||
->where('pu_state', 1)
|
||||
->findAll();
|
||||
$packMap = [];
|
||||
foreach ($unitRows as $unit) {
|
||||
$code = (string) ($unit->pu_bag_code ?? '');
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$packMap[$code] = [
|
||||
'packPerSheet' => max(1, (int) ($unit->pu_pack_per_sheet ?? 1)),
|
||||
'totalPerBox' => max(1, (int) ($unit->pu_total_per_box ?? 1)),
|
||||
];
|
||||
}
|
||||
|
||||
$items = [];
|
||||
$itemCodes = $this->request->getPost('item_bag_code');
|
||||
$itemQtys = $this->request->getPost('item_qty');
|
||||
$itemPacks = $this->request->getPost('item_pack');
|
||||
$itemCodes = is_array($itemCodes) ? $itemCodes : [];
|
||||
$itemQtys = is_array($itemQtys) ? $itemQtys : [];
|
||||
$itemPacks = is_array($itemPacks) ? $itemPacks : [];
|
||||
|
||||
$count = max(count($itemCodes), count($itemQtys), count($itemPacks));
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$bagCode = trim((string) ($itemCodes[$i] ?? ''));
|
||||
$qtyRaw = (int) ($itemQtys[$i] ?? 0);
|
||||
$pack = trim((string) ($itemPacks[$i] ?? 'sheet'));
|
||||
if ($bagCode === '' || $qtyRaw <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (! in_array($pack, ['box', 'pack', 'sheet'], true)) {
|
||||
$pack = 'sheet';
|
||||
}
|
||||
$packUnit = $packMap[$bagCode] ?? ['packPerSheet' => 1, 'totalPerBox' => 1];
|
||||
$sheetQty = $qtyRaw;
|
||||
if ($pack === 'box') {
|
||||
$sheetQty = $qtyRaw * (int) $packUnit['totalPerBox'];
|
||||
} elseif ($pack === 'pack') {
|
||||
$sheetQty = $qtyRaw * (int) $packUnit['packPerSheet'];
|
||||
}
|
||||
$sheetQty = max(1, (int) $sheetQty);
|
||||
|
||||
$detail = $kindO
|
||||
? model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $kindO->ck_idx, $bagCode, $lgIdx)
|
||||
: null;
|
||||
$bagName = $detail ? (string) ($detail->cd_name ?? '') : (string) ($inventoryMap[$bagCode]['name'] ?? '');
|
||||
if ($bagName === '') {
|
||||
$bagName = (string) $bagCode;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'bagCode' => $bagCode,
|
||||
'bagName' => $bagName,
|
||||
'pack' => $pack,
|
||||
'rawQty' => $qtyRaw,
|
||||
'sheetQty' => $sheetQty,
|
||||
];
|
||||
}
|
||||
|
||||
// 기존 관리자 단건 폼과의 호환
|
||||
if ($items === []) {
|
||||
$singleBagCode = trim((string) $this->request->getPost('bi2_bag_code'));
|
||||
$singleQty = (int) $this->request->getPost('bi2_qty');
|
||||
if ($singleBagCode !== '' && $singleQty > 0) {
|
||||
$detail = $kindO
|
||||
? model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $kindO->ck_idx, $singleBagCode, $lgIdx)
|
||||
: null;
|
||||
$bagName = $detail ? (string) ($detail->cd_name ?? '') : (string) ($inventoryMap[$singleBagCode]['name'] ?? '');
|
||||
if ($bagName === '') {
|
||||
$bagName = (string) $singleBagCode;
|
||||
}
|
||||
$items[] = [
|
||||
'bagCode' => $singleBagCode,
|
||||
'bagName' => $bagName,
|
||||
'pack' => 'sheet',
|
||||
'rawQty' => $singleQty,
|
||||
'sheetQty' => $singleQty,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($items === []) {
|
||||
return redirect()->back()->withInput()->with('error', '불출 품목을 1건 이상 입력해 주세요.');
|
||||
}
|
||||
|
||||
$requiredByBag = [];
|
||||
foreach ($items as $item) {
|
||||
$code = (string) $item['bagCode'];
|
||||
if (! isset($requiredByBag[$code])) {
|
||||
$requiredByBag[$code] = 0;
|
||||
}
|
||||
$requiredByBag[$code] += (int) $item['sheetQty'];
|
||||
}
|
||||
foreach ($requiredByBag as $code => $requiredQty) {
|
||||
$available = (int) ($inventoryMap[$code]['qty'] ?? 0);
|
||||
if ($available <= 0) {
|
||||
return redirect()->back()->withInput()->with('error', '입고 재고가 없는 봉투코드는 불출할 수 없습니다: ' . $code);
|
||||
}
|
||||
if ($available < $requiredQty) {
|
||||
return redirect()->back()->withInput()->with('error', '재고가 부족합니다: ' . $code . ' (재고 ' . number_format($available) . ', 요청 ' . number_format($requiredQty) . ')');
|
||||
}
|
||||
}
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$db->transStart();
|
||||
$hasIssueCodeTable = $db->tableExists('bag_issue_item_code');
|
||||
|
||||
$issueData = [
|
||||
'bi2_lg_idx' => $lgIdx,
|
||||
'bi2_year' => (int) $this->request->getPost('bi2_year'),
|
||||
'bi2_quarter' => (int) $this->request->getPost('bi2_quarter'),
|
||||
'bi2_issue_type' => $this->request->getPost('bi2_issue_type'),
|
||||
'bi2_issue_date' => $this->request->getPost('bi2_issue_date'),
|
||||
'bi2_dest_type' => $this->request->getPost('bi2_dest_type') ?? '',
|
||||
'bi2_dest_name' => $this->request->getPost('bi2_dest_name'),
|
||||
'bi2_bag_code' => $bagCode,
|
||||
'bi2_bag_name' => $bagName,
|
||||
'bi2_qty' => $qty,
|
||||
'bi2_status' => 'normal',
|
||||
'bi2_regdate' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$this->issueModel->insert($issueData);
|
||||
$bi2Idx = (int) $this->issueModel->getInsertID();
|
||||
|
||||
$issueYear = (int) $this->request->getPost('bi2_year');
|
||||
$issueQuarter = (int) $this->request->getPost('bi2_quarter');
|
||||
$issueDate = (string) $this->request->getPost('bi2_issue_date');
|
||||
$createdCount = 0;
|
||||
helper('audit');
|
||||
audit_log('create', 'bag_issue', $bi2Idx, null, array_merge($issueData, ['bi2_idx' => $bi2Idx]));
|
||||
foreach ($items as $item) {
|
||||
$issueData = [
|
||||
'bi2_lg_idx' => $lgIdx,
|
||||
'bi2_year' => $issueYear,
|
||||
'bi2_quarter' => $issueQuarter,
|
||||
'bi2_issue_type' => $issueType,
|
||||
'bi2_issue_date' => $issueDate,
|
||||
'bi2_dest_type' => $destType,
|
||||
'bi2_dest_name' => $destName,
|
||||
'bi2_bag_code' => (string) $item['bagCode'],
|
||||
'bi2_bag_name' => (string) $item['bagName'],
|
||||
'bi2_qty' => (int) $item['sheetQty'],
|
||||
'bi2_status' => 'normal',
|
||||
'bi2_regdate' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$this->issueModel->insert($issueData);
|
||||
$bi2Idx = (int) $this->issueModel->getInsertID();
|
||||
audit_log('create', 'bag_issue', $bi2Idx, null, array_merge($issueData, ['bi2_idx' => $bi2Idx]));
|
||||
|
||||
model(BagInventoryModel::class)->adjustQty($lgIdx, $bagCode, $bagName, -$qty);
|
||||
if ($hasIssueCodeTable) {
|
||||
$codeRows = $this->buildIssueCodeRows($bi2Idx, (int) $item['sheetQty'], $packMap[(string) $item['bagCode']] ?? []);
|
||||
foreach ($codeRows as $codeRow) {
|
||||
$this->issueItemCodeModel->insert([
|
||||
'bic_lg_idx' => $lgIdx,
|
||||
'bic_bi2_idx' => $bi2Idx,
|
||||
'bic_bag_code' => (string) $item['bagCode'],
|
||||
'bic_issue_code' => (string) $codeRow['issueCode'],
|
||||
'bic_qty' => (int) $codeRow['qty'],
|
||||
'bic_cancel_qty' => 0,
|
||||
'bic_state' => 'normal',
|
||||
'bic_regdate' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
model(BagInventoryModel::class)->adjustQty(
|
||||
$lgIdx,
|
||||
(string) $item['bagCode'],
|
||||
(string) $item['bagName'],
|
||||
-((int) $item['sheetQty'])
|
||||
);
|
||||
$createdCount++;
|
||||
}
|
||||
|
||||
$db->transComplete();
|
||||
|
||||
return redirect()->to(mgmt_url('bag-issues'))->with('success', '불출 처리되었습니다.');
|
||||
if (! $db->transStatus()) {
|
||||
return redirect()->back()->withInput()->with('error', '불출 처리 중 오류가 발생했습니다.');
|
||||
}
|
||||
|
||||
return redirect()->to(mgmt_url('bag-issues'))->with('success', $createdCount . '건 불출 처리되었습니다.');
|
||||
}
|
||||
|
||||
public function cancel(int $id)
|
||||
@@ -116,12 +326,38 @@ class BagIssue extends BaseController
|
||||
|
||||
$db = \Config\Database::connect();
|
||||
$db->transStart();
|
||||
$hasIssueCodeTable = $db->tableExists('bag_issue_item_code');
|
||||
|
||||
$before = (array) $item;
|
||||
$this->issueModel->update($id, ['bi2_status' => 'cancelled']);
|
||||
helper('audit');
|
||||
audit_log('update', 'bag_issue', $id, $before, ['bi2_status' => 'cancelled']);
|
||||
model(BagInventoryModel::class)->adjustQty((int) $item->bi2_lg_idx, $item->bi2_bag_code, $item->bi2_bag_name, (int) $item->bi2_qty);
|
||||
|
||||
$restoreQty = (int) $item->bi2_qty;
|
||||
if ($hasIssueCodeTable) {
|
||||
$codeRows = $db->table('bag_issue_item_code')
|
||||
->select('bic_idx, bic_qty, bic_cancel_qty')
|
||||
->where('bic_lg_idx', (int) $item->bi2_lg_idx)
|
||||
->where('bic_bi2_idx', $id)
|
||||
->get()
|
||||
->getResultArray();
|
||||
$restoreQty = 0;
|
||||
foreach ($codeRows as $codeRow) {
|
||||
$bicIdx = (int) ($codeRow['bic_idx'] ?? 0);
|
||||
$qty = (int) ($codeRow['bic_qty'] ?? 0);
|
||||
$oldCancel = (int) ($codeRow['bic_cancel_qty'] ?? 0);
|
||||
$restoreQty += max(0, $qty - $oldCancel);
|
||||
$db->table('bag_issue_item_code')
|
||||
->where('bic_idx', $bicIdx)
|
||||
->update([
|
||||
'bic_cancel_qty' => $qty,
|
||||
'bic_state' => 'cancelled',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
model(BagInventoryModel::class)->adjustQty((int) $item->bi2_lg_idx, $item->bi2_bag_code, $item->bi2_bag_name, $restoreQty);
|
||||
$this->issueModel->update($id, ['bi2_qty' => 0, 'bi2_status' => 'cancelled']);
|
||||
|
||||
$db->transComplete();
|
||||
|
||||
|
||||
@@ -138,11 +138,7 @@ class BagPrice extends BaseController
|
||||
if ($bagCode !== null && $bagCode !== '') {
|
||||
$queryForPager['bag_code'] = $bagCode;
|
||||
}
|
||||
$pagerPath = mgmt_url('bag-prices');
|
||||
if ($queryForPager !== []) {
|
||||
$pagerPath .= '?' . http_build_query($queryForPager);
|
||||
}
|
||||
$this->priceModel->pager->setPath($pagerPath);
|
||||
apply_pager_path($this->priceModel->pager, mgmt_path('bag-prices'), $queryForPager);
|
||||
|
||||
$kindO = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
||||
$bagCodes = $kindO
|
||||
|
||||
@@ -42,11 +42,7 @@ class Company extends BaseController
|
||||
if ($companyType !== '' && in_array($companyType, $typeOptions, true)) {
|
||||
$queryForPager['cp_type'] = $companyType;
|
||||
}
|
||||
$pagerPath = mgmt_url('companies');
|
||||
if ($queryForPager !== []) {
|
||||
$pagerPath .= '?' . http_build_query($queryForPager);
|
||||
}
|
||||
$pager->setPath($pagerPath);
|
||||
apply_pager_path($pager, mgmt_path('companies'), $queryForPager);
|
||||
|
||||
return $this->renderWorkPage('업체 관리', 'admin/company/index', [
|
||||
'list' => $list,
|
||||
|
||||
@@ -40,7 +40,10 @@ class Dashboard extends BaseController
|
||||
FROM bag_order_item GROUP BY boi_bo_idx
|
||||
) sub ON sub.boi_bo_idx = bo.bo_idx
|
||||
WHERE bo.bo_lg_idx = ? AND bo.bo_status = 'normal'
|
||||
", [$lgIdx])->getRow();
|
||||
AND (bo.bo_uuid, bo.bo_version) IN (
|
||||
SELECT bo_uuid, MAX(bo_version) FROM bag_order WHERE bo_lg_idx = ? GROUP BY bo_uuid
|
||||
)
|
||||
", [$lgIdx, $lgIdx])->getRow();
|
||||
$stats['order_count'] = (int) ($orderStats->cnt ?? 0);
|
||||
$stats['order_amount'] = (int) ($orderStats->total_amount ?? 0);
|
||||
|
||||
@@ -72,9 +75,12 @@ class Dashboard extends BaseController
|
||||
SELECT bo_idx, bo_lot_no, bo_order_date, bo_status
|
||||
FROM bag_order
|
||||
WHERE bo_lg_idx = ?
|
||||
AND (bo_uuid, bo_version) IN (
|
||||
SELECT bo_uuid, MAX(bo_version) FROM bag_order WHERE bo_lg_idx = ? GROUP BY bo_uuid
|
||||
)
|
||||
ORDER BY bo_order_date DESC, bo_idx DESC
|
||||
LIMIT 5
|
||||
", [$lgIdx])->getResult();
|
||||
", [$lgIdx, $lgIdx])->getResult();
|
||||
|
||||
// 최근 판매 5건
|
||||
$stats['recent_sales'] = $db->query("
|
||||
|
||||
@@ -16,6 +16,19 @@ class FreeRecipient extends BaseController
|
||||
$this->model = model(FreeRecipientModel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 무료용 대상 구분(스크린샷 기준): 사람뿐 아니라 동사무소 자체도 등록 가능.
|
||||
*
|
||||
* @return array<string,string>
|
||||
*/
|
||||
private function recipientTypeOptions(): array
|
||||
{
|
||||
return [
|
||||
'office' => '읍.면.동 사무소',
|
||||
'target' => '무료 대상자',
|
||||
];
|
||||
}
|
||||
|
||||
private function getCodeOptions(string $ckCode): array
|
||||
{
|
||||
helper('admin');
|
||||
@@ -33,16 +46,42 @@ class FreeRecipient extends BaseController
|
||||
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_type_code', 'ASC')
|
||||
->orderBy('fr_name', 'ASC')
|
||||
->orderBy('fr_idx', 'DESC')
|
||||
->paginate(20);
|
||||
$pager = $this->model->pager;
|
||||
$perPage = 20;
|
||||
$currentPage = (int) ($pager->getCurrentPage() ?: 1);
|
||||
$totalCount = (int) $this->model
|
||||
->where('fr_lg_idx', $lgIdx)
|
||||
->countAllResults();
|
||||
$dongNameMap = [];
|
||||
foreach ($this->getCodeOptions('D') as $dong) {
|
||||
$code = (string) ($dong->cd_code ?? '');
|
||||
if ($code === '') {
|
||||
continue;
|
||||
}
|
||||
$dongNameMap[$code] = (string) ($dong->cd_name ?? $code);
|
||||
}
|
||||
|
||||
return $this->renderWorkPage('무료용 대상자 관리', 'admin/free_recipient/index', ['list' => $list, 'pager' => $pager]);
|
||||
return $this->renderWorkPage('무료용 대상자 관리', 'admin/free_recipient/index', [
|
||||
'list' => $list,
|
||||
'pager' => $pager,
|
||||
'recipientTypeOptions' => $this->recipientTypeOptions(),
|
||||
'dongNameMap' => $dongNameMap,
|
||||
'totalCount' => $totalCount,
|
||||
'currentPage' => $currentPage,
|
||||
'perPage' => $perPage,
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return $this->renderWorkPage('무료용 대상자 등록', 'admin/free_recipient/create', [
|
||||
'typeCodes' => $this->getCodeOptions('H'),
|
||||
'recipientTypeOptions' => $this->recipientTypeOptions(),
|
||||
'dongCodes' => $this->getCodeOptions('D'),
|
||||
]);
|
||||
}
|
||||
@@ -85,7 +124,7 @@ class FreeRecipient extends BaseController
|
||||
|
||||
return $this->renderWorkPage('무료용 대상자 수정', 'admin/free_recipient/edit', [
|
||||
'item' => $item,
|
||||
'typeCodes' => $this->getCodeOptions('H'),
|
||||
'recipientTypeOptions' => $this->recipientTypeOptions(),
|
||||
'dongCodes' => $this->getCodeOptions('D'),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -63,6 +63,11 @@ class Menu extends BaseController
|
||||
}
|
||||
}
|
||||
|
||||
if ($effectiveMtIdx > 0 && $currentTypeCode === 'site') {
|
||||
$this->menuModel->pruneInventoryManagementMenus($effectiveMtIdx, $lgIdx);
|
||||
$list = $this->menuModel->getAllByType($effectiveMtIdx, $lgIdx);
|
||||
}
|
||||
|
||||
// 트리 순서대로 상위 메뉴 바로 아래에 하위 메뉴가 오도록 평탄화
|
||||
if (! empty($list)) {
|
||||
$tree = build_menu_tree($list);
|
||||
@@ -109,6 +114,10 @@ class Menu extends BaseController
|
||||
if ($mtIdx <= 0) {
|
||||
return $this->response->setJSON(['status' => 0, 'msg' => 'mt_idx required']);
|
||||
}
|
||||
$type = $this->typeModel->find($mtIdx);
|
||||
if ($type && (string) ($type->mt_code ?? '') === 'site') {
|
||||
$this->menuModel->pruneInventoryManagementMenus($mtIdx, $lgIdx);
|
||||
}
|
||||
$list = $this->menuModel->getAllByType($mtIdx, $lgIdx);
|
||||
return $this->response->setJSON(['status' => 1, 'data' => $list]);
|
||||
}
|
||||
@@ -153,6 +162,7 @@ class Menu extends BaseController
|
||||
if ($mmPidx > 0) {
|
||||
$this->menuModel->updateCnode($mmPidx, 1);
|
||||
}
|
||||
$this->menuModel->pruneInventoryManagementMenus($mtIdx, $lgIdx);
|
||||
$this->menuModel->syncTypeToAllLgs($mtIdx, $lgIdx);
|
||||
return redirect()->back()->with('success', '메뉴가 등록되었습니다.');
|
||||
}
|
||||
@@ -184,6 +194,7 @@ class Menu extends BaseController
|
||||
'mm_is_view' => $this->request->getPost('mm_is_view') ? 'Y' : 'N',
|
||||
];
|
||||
$this->menuModel->update($id, $data);
|
||||
$this->menuModel->pruneInventoryManagementMenus((int) $row->mt_idx, $lgIdx);
|
||||
$this->menuModel->syncTypeToAllLgs((int) $row->mt_idx, $lgIdx);
|
||||
return redirect()->back()->with('success', '메뉴가 수정되었습니다.');
|
||||
}
|
||||
@@ -207,6 +218,7 @@ class Menu extends BaseController
|
||||
}
|
||||
$result = $this->menuModel->deleteSafe($id);
|
||||
if ($result['ok']) {
|
||||
$this->menuModel->pruneInventoryManagementMenus((int) $row->mt_idx, $lgIdx);
|
||||
$this->menuModel->syncTypeToAllLgs((int) $row->mt_idx, $lgIdx);
|
||||
return redirect()->back()->with('success', '메뉴가 삭제되었습니다.');
|
||||
}
|
||||
@@ -234,6 +246,7 @@ class Menu extends BaseController
|
||||
$firstRow = $firstId > 0 ? $this->menuModel->find($firstId) : null;
|
||||
$this->menuModel->setOrder($ids, $lgIdx);
|
||||
if ($firstRow && (int) $firstRow->lg_idx === $lgIdx) {
|
||||
$this->menuModel->pruneInventoryManagementMenus((int) $firstRow->mt_idx, $lgIdx);
|
||||
$this->menuModel->syncTypeToAllLgs((int) $firstRow->mt_idx, $lgIdx);
|
||||
}
|
||||
return redirect()->back()->with('success', '순서가 적용되었습니다.');
|
||||
|
||||
@@ -55,11 +55,7 @@ class SalesAgency extends BaseController
|
||||
'sa_idx' => $saIdx,
|
||||
];
|
||||
$queryForPager = array_filter($queryForPager, static fn ($v) => $v !== null && $v !== '');
|
||||
$pagerPath = mgmt_url('sales-agencies');
|
||||
if ($queryForPager !== []) {
|
||||
$pagerPath .= '?' . http_build_query($queryForPager);
|
||||
}
|
||||
$pager->setPath($pagerPath);
|
||||
apply_pager_path($pager, mgmt_path('sales-agencies'), $queryForPager);
|
||||
|
||||
return $this->renderWorkPage('판매 대행소 관리', 'admin/sales_agency/index', [
|
||||
'list' => $list,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -57,8 +57,21 @@ class ShopOrder extends BaseController
|
||||
$shops = model(DesignatedShopModel::class)->where('ds_lg_idx', $lgIdx)->where('ds_state', 1)->findAll();
|
||||
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
||||
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
||||
$priceMap = model(BagPriceModel::class)->latestActiveMapByBagCode($lgIdx);
|
||||
$unitRows = model(PackagingUnitModel::class)
|
||||
->where('pu_lg_idx', $lgIdx)
|
||||
->where('pu_state', 1)
|
||||
->findAll();
|
||||
$unitMap = [];
|
||||
foreach ($unitRows as $unit) {
|
||||
$code = (string) ($unit->pu_bag_code ?? '');
|
||||
if ($code === '' || isset($unitMap[$code])) {
|
||||
continue;
|
||||
}
|
||||
$unitMap[$code] = $unit;
|
||||
}
|
||||
|
||||
return $this->renderWorkPage('주문 접수', 'admin/shop_order/create', compact('shops', 'bagCodes'));
|
||||
return $this->renderWorkPage('주문 접수', 'admin/shop_order/create', compact('shops', 'bagCodes', 'priceMap', 'unitMap'));
|
||||
}
|
||||
|
||||
public function store()
|
||||
@@ -81,7 +94,7 @@ class ShopOrder extends BaseController
|
||||
$dsIdx = (int) $this->request->getPost('so_ds_idx');
|
||||
$shop = model(DesignatedShopModel::class)->find($dsIdx);
|
||||
|
||||
$this->orderModel->insert([
|
||||
$orderData = [
|
||||
'so_lg_idx' => $lgIdx,
|
||||
'so_ds_idx' => $dsIdx,
|
||||
'so_ds_name' => $shop ? $shop->ds_name : '',
|
||||
@@ -91,8 +104,24 @@ class ShopOrder extends BaseController
|
||||
'so_status' => 'normal',
|
||||
'so_orderer_idx' => session()->get('mb_idx'),
|
||||
'so_regdate' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
];
|
||||
// shop_order.so_channel 이 아직 반영되지 않은 DB와의 호환 처리
|
||||
if ($db->fieldExists('so_channel', 'shop_order')) {
|
||||
$orderData['so_channel'] = 'phone';
|
||||
}
|
||||
|
||||
$insertOk = $this->orderModel->insert($orderData);
|
||||
if ($insertOk === false) {
|
||||
$db->transRollback();
|
||||
$errors = $this->orderModel->errors();
|
||||
$msg = ! empty($errors) ? implode(' / ', array_values($errors)) : '주문 저장에 실패했습니다.';
|
||||
return redirect()->back()->withInput()->with('error', $msg);
|
||||
}
|
||||
$soIdx = (int) $this->orderModel->getInsertID();
|
||||
if ($soIdx <= 0) {
|
||||
$db->transRollback();
|
||||
return redirect()->back()->withInput()->with('error', '주문번호 생성에 실패했습니다. DB 스키마를 확인해 주세요.');
|
||||
}
|
||||
|
||||
$bagCodes = $this->request->getPost('item_bag_code') ?? [];
|
||||
$qtys = $this->request->getPost('item_qty') ?? [];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,9 @@ class Home extends BaseController
|
||||
public function index()
|
||||
{
|
||||
if (session()->get('logged_in')) {
|
||||
return $this->dashboard();
|
||||
// 메인(/) 본문은 「요약(simple)」 대시보드로 노출한다.
|
||||
// 종래의 「종합·그래프(blend)」 본문은 /dashboard (또는 /dashboard/blend)로 이동.
|
||||
return $this->dashboardSimple();
|
||||
}
|
||||
|
||||
return view('welcome_message');
|
||||
@@ -28,6 +30,34 @@ class Home extends BaseController
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그인 후 메인 — 단순형 요약 대시보드. URL: /dashboard/simple
|
||||
* 기존 /dashboard 화면이 복잡하다는 피드백용으로, 핵심 지표·링크만 노출.
|
||||
*/
|
||||
public function dashboardSimple()
|
||||
{
|
||||
return view('bag/layout/main', [
|
||||
'title' => '업무 현황 · 요약',
|
||||
'content' => view('bag/lg_dashboard_simple', [
|
||||
'lgLabel' => $this->resolveLgLabel(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그인 후 메인 — 중간 밀도 대시보드. URL: /dashboard/compact
|
||||
* /dashboard 보다 단순하지만 simple 보다 정보량을 늘린 화면.
|
||||
*/
|
||||
public function dashboardCompact()
|
||||
{
|
||||
return view('bag/layout/main', [
|
||||
'title' => '업무 현황 · 컴팩트',
|
||||
'content' => view('bag/lg_dashboard_compact', [
|
||||
'lgLabel' => $this->resolveLgLabel(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 디자인 시안(기존 /dashboard 연결 화면)
|
||||
*/
|
||||
@@ -74,6 +104,20 @@ class Home extends BaseController
|
||||
return $this->dashboard();
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그인 후 메인 — 라이트(축약) 대시보드. URL: /dashboard/lite
|
||||
* dashboard_blend 의 일부 KPI/표/차트만 남긴 단순화 화면.
|
||||
*/
|
||||
public function dashboardLite()
|
||||
{
|
||||
return view('bag/layout/main', [
|
||||
'title' => '업무 현황 · 라이트',
|
||||
'content' => view('bag/dashboard_blend_lite_inner', [
|
||||
'lgLabel' => $this->resolveLgLabel(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 재고 조회(수불) 화면 (목업)
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user