Files
jongryangje/app/Controllers/Bag.php
taekyoungc 8e859f420d chore: show runtime DB server identity in db_diag
Add config and server-level DB identity fields (host/port/user, @@hostname/@@port/@@version) to packaging-units db_diag so production can verify the exact runtime DB endpoint.
2026-04-09 12:54:35 +09:00

892 lines
40 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Controllers;
use CodeIgniter\Database\Exceptions\DatabaseException;
use CodeIgniter\HTTP\RedirectResponse;
use App\Models\BagInventoryModel;
use App\Models\BagIssueModel;
use App\Models\BagOrderModel;
use App\Models\BagOrderItemModel;
use App\Models\BagPriceModel;
use App\Models\BagReceivingModel;
use App\Models\BagSaleModel;
use App\Models\CodeKindModel;
use App\Models\CodeDetailModel;
use App\Models\CompanyModel;
use App\Models\PackagingUnitModel;
use App\Models\SalesAgencyModel;
use App\Models\ShopOrderModel;
use App\Models\DesignatedShopModel;
use App\Models\LocalGovernmentModel;
use Config\Roles;
class Bag extends BaseController
{
/**
* 로그인 사용자의 지자체 PK 반환 (미로그인/미지정 시 null)
*/
private function lgIdx(): ?int
{
helper('admin');
return admin_effective_lg_idx();
}
private function render(string $title, string $viewFile, array $data = []): string
{
return view('bag/layout/main', [
'title' => $title,
'content' => view($viewFile, $data),
]);
}
// ──────────────────────────────────────────────
// 기본정보관리 (단가·포장 단위 진입 허브)
// ──────────────────────────────────────────────
public function basicInfo(): string
{
return $this->render('기본정보관리', 'bag/basic_info', []);
}
/** 봉투 단가 조회 (사이트) — 기간·봉투구분·봉투코드 필터, 적용기간 겹침, 페이징·인쇄 */
public function prices(): string|RedirectResponse
{
helper('admin');
if ($this->request->is('post')) {
$post = $this->request->getPost();
$pick = static function (array $src, string $key): ?string {
if (! array_key_exists($key, $src)) {
return null;
}
$v = $src[$key];
if ($v === null || is_array($v)) {
return null;
}
$s = trim((string) $v);
return $s === '' ? null : $s;
};
session()->setFlashdata('bag_prices_filter', [
'start_y' => $pick($post, 'start_y'),
'start_m' => $pick($post, 'start_m'),
'start_d' => $pick($post, 'start_d'),
'end_y' => $pick($post, 'end_y'),
'end_m' => $pick($post, 'end_m'),
'end_d' => $pick($post, 'end_d'),
'bag_kind_e' => $pick($post, 'bag_kind_e'),
'bag_code' => $pick($post, 'bag_code'),
]);
return redirect()->to(site_url('bag/prices'));
}
$lgIdx = $this->lgIdx();
$bagPrices = [];
$get = $this->request->getGet();
$flash = session()->getFlashdata('bag_prices_filter');
$readSrc = static function (array $src, string $key): ?string {
if (! array_key_exists($key, $src)) {
return null;
}
$v = $src[$key];
if ($v === null || is_array($v)) {
return null;
}
$s = trim((string) $v);
return $s === '' ? null : $s;
};
$filterKeys = [
'start_y', 'start_m', 'start_d',
'end_y', 'end_m', 'end_d',
'bag_kind_e', 'bag_code',
'start_date', 'end_date',
];
$hasExplicitGetFilter = false;
foreach ($filterKeys as $fk) {
$v = $get[$fk] ?? null;
if ($v !== null && ! is_array($v) && trim((string) $v) !== '') {
$hasExplicitGetFilter = true;
break;
}
}
$src = [];
if ($hasExplicitGetFilter) {
$src = $get;
} elseif (is_array($flash)) {
$src = $flash;
}
$sy = $readSrc($src, 'start_y');
$sm = $readSrc($src, 'start_m');
$sd = $readSrc($src, 'start_d');
$ey = $readSrc($src, 'end_y');
$em = $readSrc($src, 'end_m');
$ed = $readSrc($src, 'end_d');
$startDate = null;
if ($sy !== null && $sy !== '' && $sm !== null && $sm !== '' && $sd !== null && $sd !== '') {
$startDate = parse_ymd_from_triple($sy, $sm, $sd);
}
if ($startDate === null) {
$g = $readSrc($src, 'start_date');
$startDate = ($g !== null && $g !== '') ? $g : null;
}
$endDate = null;
if ($ey !== null && $ey !== '' && $em !== null && $em !== '' && $ed !== null && $ed !== '') {
$endDate = parse_ymd_from_triple($ey, $em, $ed);
}
if ($endDate === null) {
$g = $readSrc($src, 'end_date');
$endDate = ($g !== null && $g !== '') ? $g : null;
}
$startParts = ['y' => '', 'm' => '', 'd' => ''];
$endParts = ['y' => '', 'm' => '', 'd' => ''];
if ($startDate !== null && preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $startDate, $m)) {
$startParts = ['y' => $m[1], 'm' => (int) $m[2], 'd' => (int) $m[3]];
} elseif ($sy !== null && $sm !== null && $sd !== null && $sy !== '' && $sm !== '' && $sd !== '') {
$iy = (int) $sy;
$im = (int) $sm;
$id = (int) $sd;
if ($iy >= 1000 && $iy <= 9999 && $im >= 1 && $im <= 12 && $id >= 1 && $id <= 31) {
$startParts = ['y' => (string) $iy, 'm' => $im, 'd' => $id];
}
}
if ($endDate !== null && preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $endDate, $m)) {
$endParts = ['y' => $m[1], 'm' => (int) $m[2], 'd' => (int) $m[3]];
} elseif ($ey !== null && $em !== null && $ed !== null && $ey !== '' && $em !== '' && $ed !== '') {
$iy = (int) $ey;
$im = (int) $em;
$id = (int) $ed;
if ($iy >= 1000 && $iy <= 9999 && $im >= 1 && $im <= 12 && $id >= 1 && $id <= 31) {
$endParts = ['y' => (string) $iy, 'm' => $im, 'd' => $id];
}
}
$dateYearMin = (int) date('Y') - 12;
$dateYearMax = (int) date('Y') + 2;
$bagKindE = $readSrc($src, 'bag_kind_e');
$bagCode = $readSrc($src, 'bag_code');
$pager = null;
$bagCodes = [];
$bagKindOpts = [];
$printLines = [];
$printLgName = '';
if ($lgIdx !== null) {
try {
$priceModel = model(BagPriceModel::class);
$builder = $priceModel->where('bp_lg_idx', $lgIdx);
if (($startDate !== null && $startDate !== '') || ($endDate !== null && $endDate !== '')) {
$qStart = ($startDate !== null && $startDate !== '') ? $startDate : $endDate;
$qEnd = ($endDate !== null && $endDate !== '') ? $endDate : $startDate;
if (strcmp((string) $qStart, (string) $qEnd) > 0) {
[$qStart, $qEnd] = [$qEnd, $qStart];
}
$builder->where('bp_start_date <=', $qEnd);
$builder->groupStart()
->where('bp_end_date IS NULL')
->orWhere('bp_end_date >=', $qStart)
->groupEnd();
}
if ($bagKindE !== null && $bagKindE !== '') {
$ek = model(CodeKindModel::class)->where('ck_code', 'E')->first();
if ($ek) {
$eDetail = model(CodeDetailModel::class)
->where('cd_ck_idx', (int) $ek->ck_idx)
->where('cd_code', $bagKindE)
->where('cd_state', 1)
->first();
if ($eDetail !== null) {
$builder->like('bp_bag_code', (string) $bagKindE, 'after');
}
}
}
if ($bagCode !== null && $bagCode !== '') {
$ok = model(CodeKindModel::class)->where('ck_code', 'O')->first();
if ($ok) {
$oDetail = model(CodeDetailModel::class)->findResolvedByKindAndCode((int) $ok->ck_idx, (string) $bagCode, $lgIdx);
if ($oDetail !== null) {
$builder->where('bp_bag_code', $bagCode);
}
}
}
$bagPrices = $builder->orderBy('bp_bag_code', 'ASC')->orderBy('bp_start_date', 'DESC')->paginate(20);
$queryForPager = [];
$tripleS = $sy !== null && $sy !== '' && $sm !== null && $sm !== '' && $sd !== null && $sd !== '';
$tripleE = $ey !== null && $ey !== '' && $em !== null && $em !== '' && $ed !== null && $ed !== '';
if ($tripleS) {
$queryForPager['start_y'] = $sy;
$queryForPager['start_m'] = $sm;
$queryForPager['start_d'] = $sd;
} else {
$legacyS = $readSrc($src, 'start_date');
if ($legacyS !== null) {
$queryForPager['start_date'] = $legacyS;
}
}
if ($tripleE) {
$queryForPager['end_y'] = $ey;
$queryForPager['end_m'] = $em;
$queryForPager['end_d'] = $ed;
} else {
$legacyE = $readSrc($src, 'end_date');
if ($legacyE !== null) {
$queryForPager['end_date'] = $legacyE;
}
}
if ($bagKindE !== null && $bagKindE !== '') {
$queryForPager['bag_kind_e'] = $bagKindE;
}
if ($bagCode !== null && $bagCode !== '') {
$queryForPager['bag_code'] = $bagCode;
}
$queryForPager = array_filter(
$queryForPager,
static fn ($v) => $v !== null && $v !== ''
);
$pagerPath = site_url('bag/prices');
if ($queryForPager !== []) {
$pagerPath .= '?' . http_build_query($queryForPager);
}
$priceModel->pager->setPath($pagerPath);
$pager = $priceModel->pager;
$kindO = model(CodeKindModel::class)->where('ck_code', 'O')->first();
$bagCodes = $kindO
? model(CodeDetailModel::class)->getByKind((int) $kindO->ck_idx, true, $lgIdx)
: [];
$kindE = model(CodeKindModel::class)->where('ck_code', 'E')->first();
$bagKindOpts = $kindE
? model(CodeDetailModel::class)->getByKind((int) $kindE->ck_idx, true, null)
: [];
$lgRow = model(LocalGovernmentModel::class)->find($lgIdx);
$printLgName = $lgRow !== null ? $lgRow->lg_name : '';
} catch (DatabaseException $e) {
log_message('error', '[prices] bag_price 조회 실패: ' . $e->getMessage());
}
}
if (($startDate !== null && $startDate !== '') || ($endDate !== null && $endDate !== '')) {
$qs = ($startDate !== null && $startDate !== '') ? $startDate : $endDate;
$qe = ($endDate !== null && $endDate !== '') ? $endDate : $startDate;
if (strcmp((string) $qs, (string) $qe) > 0) {
[$qs, $qe] = [$qe, $qs];
}
$printLines[] = '조회기간(적용기간 겹침): ' . format_ymd_korean($qs) . ' ~ ' . format_ymd_korean($qe);
}
if ($bagKindE !== null && $bagKindE !== '') {
foreach ($bagKindOpts as $cd) {
if ((string) $cd->cd_code === (string) $bagKindE) {
$printLines[] = '봉투구분: ' . $cd->cd_name . ' (' . $bagKindE . ')';
break;
}
}
}
if ($bagCode !== null && $bagCode !== '') {
$printLines[] = '봉투코드: ' . $bagCode;
}
$viewData = [
'lgIdx' => $lgIdx,
'bagPrices' => $bagPrices,
'pager' => $pager,
'startDate' => $startDate,
'endDate' => $endDate,
'startParts' => $startParts,
'endParts' => $endParts,
'dateYearMin' => $dateYearMin,
'dateYearMax' => $dateYearMax,
'bag_kind_e' => $bagKindE,
'bag_code' => $bagCode,
'bag_codes' => $bagCodes,
'bag_kind_options' => $bagKindOpts,
'printExtraLines' => $printLines,
];
if ($printLgName !== '') {
$viewData['printLgName'] = $printLgName;
}
return $this->render('봉투 단가', 'bag/prices', $viewData);
}
/** 포장 단위 조회 (사이트) */
public function packagingUnits(): string
{
$lgIdx = $this->lgIdx();
$packagingUnits = [];
$dbDiag = null;
if ($lgIdx) {
try {
$packagingUnits = model(PackagingUnitModel::class)->where('pu_lg_idx', $lgIdx)->orderBy('pu_bag_code', 'ASC')->findAll();
} catch (DatabaseException $e) {
log_message('error', '[packagingUnits] packaging_unit 조회 실패: ' . $e->getMessage());
}
}
if ($this->request->getGet('db_diag') === '1') {
$dbDiag = [
'lg_idx' => $lgIdx,
'cfg_host' => null,
'cfg_port' => null,
'cfg_user' => null,
'db_name' => null,
'mysql_host' => null,
'mysql_port' => null,
'mysql_version' => null,
'packaging_unit' => null,
'code_kind' => null,
'code_detail' => null,
'error' => null,
];
try {
$db = db_connect();
$cfg = config('Database')->default ?? [];
$dbDiag['cfg_host'] = $cfg['hostname'] ?? null;
$dbDiag['cfg_port'] = isset($cfg['port']) ? (string) $cfg['port'] : null;
$dbDiag['cfg_user'] = $cfg['username'] ?? null;
$dbDiag['db_name'] = $db->database;
$meta = $db->query('SELECT @@hostname AS mysql_host, @@port AS mysql_port, @@version AS mysql_version, DATABASE() AS db_name')->getRowArray();
if (is_array($meta)) {
$dbDiag['mysql_host'] = $meta['mysql_host'] ?? null;
$dbDiag['mysql_port'] = isset($meta['mysql_port']) ? (string) $meta['mysql_port'] : null;
$dbDiag['mysql_version'] = $meta['mysql_version'] ?? null;
if (! empty($meta['db_name'])) {
$dbDiag['db_name'] = (string) $meta['db_name'];
}
}
$dbDiag['packaging_unit'] = (int) $db->table('packaging_unit')->where('pu_lg_idx', (int) $lgIdx)->countAllResults();
$dbDiag['code_kind'] = (int) $db->table('code_kind')->countAllResults();
$dbDiag['code_detail'] = (int) $db->table('code_detail')->countAllResults();
} catch (\Throwable $e) {
$dbDiag['error'] = $e->getMessage();
log_message('error', '[packagingUnits][db_diag] {type}: {message}', [
'type' => $e::class,
'message' => $e->getMessage(),
]);
}
}
return $this->render('포장 단위', 'bag/packaging_units', [
'packagingUnits' => $packagingUnits,
'dbDiag' => $dbDiag,
]);
}
/**
* 기본코드 종류·세부코드 조회 전용 (사이트 메뉴 기본코드관리)
*/
public function codeKinds(): string
{
$kindModel = model(CodeKindModel::class);
$detailModel = model(CodeDetailModel::class);
$kinds = [];
$countMap = [];
$lgIdx = $this->lgIdx();
try {
$kinds = $kindModel->orderBy('ck_code', 'ASC')->findAll();
foreach ($kinds as $row) {
$countMap[$row->ck_idx] = (int) $detailModel->where('cd_ck_idx', $row->ck_idx)
->filterByTenantScope($lgIdx)
->countAllResults();
}
} catch (\Throwable $e) {
log_message('error', '[codeKinds] 실패: {type} {message} @ {file}:{line} / lg={lg}, user={user}, level={level}', [
'type' => $e::class,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'lg' => $lgIdx !== null ? (string) $lgIdx : 'null',
'user' => (string) (session()->get('mb_id') ?? ''),
'level' => (string) (session()->get('mb_level') ?? ''),
]);
session()->setFlashdata('error', '기본코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
}
$level = (int) session()->get('mb_level');
return $this->render('기본코드관리', 'bag/code_kinds', [
'codeKinds' => $kinds,
'countMap' => $countMap,
'canManageKinds' => Roles::canManageCodeKindMaster($level),
]);
}
/**
* 기본코드 세부 목록 (사이트 레이아웃). 등록·수정·삭제 폼은 /admin/code-details/* 유지.
*/
public function codeDetails(int $ckIdx)
{
$kindModel = model(CodeKindModel::class);
$detailModel = model(CodeDetailModel::class);
$kind = null;
try {
$kind = $kindModel->find($ckIdx);
} catch (\Throwable $e) {
log_message('error', '[codeDetails] kind 조회 실패: {type} {message} @ {file}:{line} / ck={ck}, user={user}, level={level}', [
'type' => $e::class,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'ck' => (string) $ckIdx,
'user' => (string) (session()->get('mb_id') ?? ''),
'level' => (string) (session()->get('mb_level') ?? ''),
]);
return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
}
if ($kind === null) {
return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.');
}
$lgIdx = $this->lgIdx();
try {
$list = $detailModel->where('cd_ck_idx', $ckIdx)
->filterByTenantScope($lgIdx)
->orderBy('cd_sort', 'ASC')
->orderBy('cd_idx', 'ASC')
->paginate(20);
$pager = $detailModel->pager;
} catch (\Throwable $e) {
log_message('error', '[codeDetails] list 조회 실패: {type} {message} @ {file}:{line} / ck={ck}, lg={lg}, user={user}, level={level}', [
'type' => $e::class,
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'ck' => (string) $ckIdx,
'lg' => $lgIdx !== null ? (string) $lgIdx : 'null',
'user' => (string) (session()->get('mb_id') ?? ''),
'level' => (string) (session()->get('mb_level') ?? ''),
]);
return redirect()->to(site_url('bag/code-kinds'))->with('error', '세부코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
}
helper('admin');
$level = (int) session()->get('mb_level');
$adminLg = admin_effective_lg_idx();
$canManage = Roles::canManageCodeMaster($level);
$rowCanEdit = [];
foreach ($list as $row) {
$rowCanEdit[$row->cd_idx] = Roles::canEditCodeDetailRow($level, $row, $adminLg);
}
$title = ($canManage ? '세부코드 관리' : '세부코드 조회') . ' — ' . $kind->ck_name . ' (' . $kind->ck_code . ')';
return $this->render($title, 'bag/code_details', [
'kind' => $kind,
'list' => $list,
'pager' => $pager,
'canManage' => $canManage,
'rowCanEdit' => $rowCanEdit,
]);
}
// ──────────────────────────────────────────────
// 발주 입고 관리
// ──────────────────────────────────────────────
public function purchaseInbound(): string
{
$lgIdx = $this->lgIdx();
$data = ['orders' => [], 'receivings' => [], 'startDate' => null, 'endDate' => null];
if ($lgIdx) {
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$data['startDate'] = $startDate;
$data['endDate'] = $endDate;
// 발주 목록
$orderBuilder = model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx);
if ($startDate) $orderBuilder->where('bo_order_date >=', $startDate);
if ($endDate) $orderBuilder->where('bo_order_date <=', $endDate);
$data['orders'] = $orderBuilder->orderBy('bo_order_date', 'DESC')->paginate(20, 'orders');
$data['orderPager'] = model(BagOrderModel::class)->pager;
// 발주별 품목 합계
$itemSummary = [];
foreach ($data['orders'] as $order) {
$items = model(BagOrderItemModel::class)->where('boi_bo_idx', $order->bo_idx)->findAll();
$totalQty = 0;
$totalAmt = 0;
foreach ($items as $it) {
$totalQty += (int) $it->boi_qty_sheet;
$totalAmt += (float) $it->boi_amount;
}
$itemSummary[$order->bo_idx] = ['qty' => $totalQty, 'amount' => $totalAmt, 'count' => count($items)];
}
$data['itemSummary'] = $itemSummary;
// 입고 목록
$recvBuilder = model(BagReceivingModel::class)->where('br_lg_idx', $lgIdx);
if ($startDate) $recvBuilder->where('br_receive_date >=', $startDate);
if ($endDate) $recvBuilder->where('br_receive_date <=', $endDate);
$data['receivings'] = $recvBuilder->orderBy('br_receive_date', 'DESC')->paginate(20, 'receivings');
$data['recvPager'] = model(BagReceivingModel::class)->pager;
}
return $this->render('발주 입고 관리', 'bag/purchase_inbound', $data);
}
// ──────────────────────────────────────────────
// 불출 관리
// ──────────────────────────────────────────────
public function issue(): string
{
$lgIdx = $this->lgIdx();
$data = ['list' => [], 'startDate' => null, 'endDate' => null];
if ($lgIdx) {
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$data['startDate'] = $startDate;
$data['endDate'] = $endDate;
$builder = model(BagIssueModel::class)->where('bi2_lg_idx', $lgIdx);
if ($startDate) $builder->where('bi2_issue_date >=', $startDate);
if ($endDate) $builder->where('bi2_issue_date <=', $endDate);
$data['list'] = $builder->orderBy('bi2_issue_date', 'DESC')->paginate(20);
$data['pager'] = model(BagIssueModel::class)->pager;
}
return $this->render('불출 관리', 'bag/issue', $data);
}
// ──────────────────────────────────────────────
// 재고 관리
// ──────────────────────────────────────────────
public function inventory(): string
{
$lgIdx = $this->lgIdx();
$data = ['list' => []];
if ($lgIdx) {
$invModel = model(BagInventoryModel::class);
$data['list'] = $invModel->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code', 'ASC')->paginate(20);
$data['pager'] = $invModel->pager;
}
return $this->render('재고 관리', 'bag/inventory', $data);
}
// ──────────────────────────────────────────────
// 판매 관리
// ──────────────────────────────────────────────
public function sales(): string
{
$lgIdx = $this->lgIdx();
$data = ['salesList' => [], 'orderList' => [], 'startDate' => null, 'endDate' => null];
if ($lgIdx) {
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$data['startDate'] = $startDate;
$data['endDate'] = $endDate;
// 판매/반품
$saleBuilder = model(BagSaleModel::class)->where('bs_lg_idx', $lgIdx);
if ($startDate) $saleBuilder->where('bs_sale_date >=', $startDate);
if ($endDate) $saleBuilder->where('bs_sale_date <=', $endDate);
$data['salesList'] = $saleBuilder->orderBy('bs_sale_date', 'DESC')->paginate(20, 'sales');
$data['salesPager'] = model(BagSaleModel::class)->pager;
// 주문 접수
$orderBuilder = model(ShopOrderModel::class)->where('so_lg_idx', $lgIdx);
if ($startDate) $orderBuilder->where('so_delivery_date >=', $startDate);
if ($endDate) $orderBuilder->where('so_delivery_date <=', $endDate);
$data['orderList'] = $orderBuilder->orderBy('so_idx', 'DESC')->paginate(20, 'shoporders');
$data['orderPager'] = model(ShopOrderModel::class)->pager;
}
return $this->render('판매 관리', 'bag/sales', $data);
}
// ──────────────────────────────────────────────
// 판매 현황
// ──────────────────────────────────────────────
public function salesStats(): string
{
$lgIdx = $this->lgIdx();
$data = ['result' => [], 'startDate' => null, 'endDate' => null];
if ($lgIdx) {
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$data['startDate'] = $startDate;
$data['endDate'] = $endDate;
$builder = model(BagSaleModel::class)->where('bs_lg_idx', $lgIdx)->where('bs_type', 'sale');
if ($startDate) $builder->where('bs_sale_date >=', $startDate);
if ($endDate) $builder->where('bs_sale_date <=', $endDate);
$data['result'] = $builder->orderBy('bs_sale_date', 'DESC')->paginate(20);
$data['pager'] = model(BagSaleModel::class)->pager;
}
return $this->render('판매 현황', 'bag/sales_stats', $data);
}
// ──────────────────────────────────────────────
// 봉투 수불 관리
// ──────────────────────────────────────────────
public function flow(): string
{
$lgIdx = $this->lgIdx();
$data = ['receiving' => [], 'sales' => [], 'issues' => [], 'inventory' => [], 'startDate' => null, 'endDate' => null];
if ($lgIdx) {
$startDate = $this->request->getGet('start_date');
$endDate = $this->request->getGet('end_date');
$data['startDate'] = $startDate;
$data['endDate'] = $endDate;
$data['inventory'] = model(BagInventoryModel::class)->where('bi_lg_idx', $lgIdx)->findAll();
$recvBuilder = model(BagReceivingModel::class)->where('br_lg_idx', $lgIdx);
if ($startDate) $recvBuilder->where('br_receive_date >=', $startDate);
if ($endDate) $recvBuilder->where('br_receive_date <=', $endDate);
$data['receiving'] = $recvBuilder->findAll();
$saleBuilder = model(BagSaleModel::class)->where('bs_lg_idx', $lgIdx);
if ($startDate) $saleBuilder->where('bs_sale_date >=', $startDate);
if ($endDate) $saleBuilder->where('bs_sale_date <=', $endDate);
$data['sales'] = $saleBuilder->findAll();
$issueBuilder = model(BagIssueModel::class)->where('bi2_lg_idx', $lgIdx);
if ($startDate) $issueBuilder->where('bi2_issue_date >=', $startDate);
if ($endDate) $issueBuilder->where('bi2_issue_date <=', $endDate);
$data['issues'] = $issueBuilder->findAll();
}
return $this->render('봉투 수불 관리', 'bag/flow', $data);
}
// ──────────────────────────────────────────────
// 통계 분석 관리
// ──────────────────────────────────────────────
public function analytics(): string
{
return $this->render('통계 분석 관리', 'bag/analytics', []);
}
// ──────────────────────────────────────────────
// 창 (프로그램 창 관리 - 추후)
// ──────────────────────────────────────────────
public function window(): string
{
return $this->render('창', 'bag/window', []);
}
// ──────────────────────────────────────────────
// 도움말
// ──────────────────────────────────────────────
public function help(): string
{
return $this->render('도움말', 'bag/help', []);
}
// ──────────────────────────────────────────────
// 재고 조정 (실사)
// ──────────────────────────────────────────────
public function inventoryAdjust(): string
{
$lgIdx = $this->lgIdx();
$inventory = $lgIdx ? model(BagInventoryModel::class)->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code')->findAll() : [];
return $this->render('재고 조정', 'bag/inventory_adjust', compact('inventory'));
}
public function inventoryAdjustStore()
{
helper('admin');
$lgIdx = $this->lgIdx();
if (! $lgIdx) {
return redirect()->to(site_url('bag/inventory'))->with('error', '지자체를 선택해 주세요.');
}
$rules = [
'bag_code' => 'required|max_length[50]',
'adjust_type' => 'required|in_list[set,add,sub]',
'qty' => 'required|is_natural',
];
if (! $this->validate($rules)) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$bagCode = $this->request->getPost('bag_code');
$type = $this->request->getPost('adjust_type');
$qty = (int) $this->request->getPost('qty');
$invModel = model(BagInventoryModel::class);
$existing = $invModel->where('bi_lg_idx', $lgIdx)->where('bi_bag_code', $bagCode)->first();
if ($type === 'set') {
if ($existing) {
$invModel->update($existing->bi_idx, ['bi_qty' => $qty, 'bi_updated_at' => date('Y-m-d H:i:s')]);
}
} elseif ($type === 'add') {
$bagName = $existing ? $existing->bi_bag_name : '';
$invModel->adjustQty($lgIdx, $bagCode, $bagName, $qty);
} elseif ($type === 'sub') {
$bagName = $existing ? $existing->bi_bag_name : '';
$invModel->adjustQty($lgIdx, $bagCode, $bagName, -$qty);
}
return redirect()->to(site_url('bag/inventory'))->with('success', '재고가 조정되었습니다.');
}
// ══════════════════════════════════════════════
// CRUD — 사이트 레이아웃으로 등록/처리 폼 제공
// ══════════════════════════════════════════════
// --- 불출 등록 ---
public function issueCreate(): string
{
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $this->lgIdx()) : [];
return $this->render('불출 처리', 'bag/create_bag_issue', compact('bagCodes'));
}
public function issueStore()
{
$admin = new \App\Controllers\Admin\BagIssue();
$admin->initController($this->request, $this->response, service('logger'));
$result = $admin->store();
if ($result instanceof \CodeIgniter\HTTP\RedirectResponse) {
$to = (string) $result->getHeaderLine('Location');
$to = str_replace('/admin/bag-issues', '/bag/issue', $to);
return redirect()->to($to)->with('success', session()->getFlashdata('success'))->with('errors', session()->getFlashdata('errors'));
}
return redirect()->to(site_url('bag/issue'))->with('success', '불출 처리되었습니다.');
}
public function issueCancel(int $id)
{
$admin = new \App\Controllers\Admin\BagIssue();
$admin->initController($this->request, $this->response, service('logger'));
$admin->cancel($id);
return redirect()->to(site_url('bag/issue'))->with('success', session()->getFlashdata('success') ?? '취소되었습니다.');
}
// --- 발주 등록 ---
public function orderCreate(): string
{
helper('admin');
$lgIdx = $this->lgIdx();
$companies = $lgIdx
? model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->whereIn('cp_type', ['제작업체', 'manufacturer'])->where('cp_state', 1)->findAll()
: [];
$agencies = $lgIdx ? model(SalesAgencyModel::class)->where('sa_lg_idx', $lgIdx)->orderForDisplay()->findAll() : [];
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
$bagCodes = $kind ? model(CodeDetailModel::class)->where('cd_ck_idx', $kind->ck_idx)->where('cd_state', 1)->orderBy('cd_sort')->findAll() : [];
return $this->render('발주 등록', 'bag/create_bag_order', compact('companies', 'agencies', 'bagCodes'));
}
public function orderStore()
{
$admin = new \App\Controllers\Admin\BagOrder();
$admin->initController($this->request, $this->response, service('logger'));
$result = $admin->store();
if ($result instanceof \CodeIgniter\HTTP\RedirectResponse) {
return redirect()->to(site_url('bag/purchase-inbound'))->with('success', session()->getFlashdata('success'))->with('errors', session()->getFlashdata('errors'));
}
return redirect()->to(site_url('bag/purchase-inbound'))->with('success', '발주 등록되었습니다.');
}
public function orderCancel(int $id)
{
helper('admin');
$lgIdx = $this->lgIdx();
if (!$lgIdx) {
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 확인할 수 없습니다.');
}
$orderModel = model(BagOrderModel::class);
$order = $orderModel->find($id);
if (!$order || (int) $order->bo_lg_idx !== $lgIdx) {
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '발주를 찾을 수 없습니다.');
}
$before = (array) $order;
$orderModel->update($id, ['bo_status' => 'cancelled', 'bo_moddate' => date('Y-m-d H:i:s')]);
helper('audit');
audit_log('update', 'bag_order', $id, $before, ['bo_status' => 'cancelled']);
return redirect()->to(site_url('bag/purchase-inbound'))->with('success', '발주가 취소되었습니다.');
}
// --- 입고 처리 ---
public function receivingCreate(): string
{
helper('admin');
$lgIdx = $this->lgIdx();
$orders = $lgIdx ? model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx)->where('bo_status', 'normal')->orderBy('bo_order_date', 'DESC')->findAll() : [];
return $this->render('입고 처리', 'bag/create_bag_receiving', compact('orders'));
}
public function receivingStore()
{
$admin = new \App\Controllers\Admin\BagReceiving();
$admin->initController($this->request, $this->response, service('logger'));
$result = $admin->store();
if ($result instanceof \CodeIgniter\HTTP\RedirectResponse) {
return redirect()->to(site_url('bag/purchase-inbound'))->with('success', session()->getFlashdata('success'))->with('errors', session()->getFlashdata('errors'));
}
return redirect()->to(site_url('bag/purchase-inbound'))->with('success', '입고 처리되었습니다.');
}
// --- 판매 등록 ---
public function saleCreate(): string
{
helper('admin');
$lgIdx = $this->lgIdx();
$shops = $lgIdx ? 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) : [];
return $this->render('판매 등록', 'bag/create_bag_sale', compact('shops', 'bagCodes'));
}
public function saleStore()
{
$admin = new \App\Controllers\Admin\BagSale();
$admin->initController($this->request, $this->response, service('logger'));
$result = $admin->store();
if ($result instanceof \CodeIgniter\HTTP\RedirectResponse) {
return redirect()->to(site_url('bag/sales'))->with('success', session()->getFlashdata('success'))->with('errors', session()->getFlashdata('errors'));
}
return redirect()->to(site_url('bag/sales'))->with('success', '판매 등록되었습니다.');
}
// --- 주문 접수 ---
public function shopOrderCreate(): string
{
helper('admin');
$lgIdx = $this->lgIdx();
$shops = $lgIdx ? 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) : [];
return $this->render('주문 접수', 'bag/create_shop_order', compact('shops', 'bagCodes'));
}
public function shopOrderStore()
{
$admin = new \App\Controllers\Admin\ShopOrder();
$admin->initController($this->request, $this->response, service('logger'));
$result = $admin->store();
if ($result instanceof \CodeIgniter\HTTP\RedirectResponse) {
return redirect()->to(site_url('bag/sales'))->with('success', session()->getFlashdata('success'))->with('errors', session()->getFlashdata('errors'));
}
return redirect()->to(site_url('bag/sales'))->with('success', '주문 접수되었습니다.');
}
}