2026-03-26 14:30:45 +09:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace App\Controllers;
|
|
|
|
|
|
2026-03-30 15:07:09 +09:00
|
|
|
use CodeIgniter\Database\Exceptions\DatabaseException;
|
2026-04-08 00:20:09 +09:00
|
|
|
use CodeIgniter\HTTP\RedirectResponse;
|
2026-03-26 14:30:45 +09:00
|
|
|
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;
|
2026-03-26 16:13:07 +09:00
|
|
|
use App\Models\DesignatedShopModel;
|
2026-04-08 00:20:09 +09:00
|
|
|
use App\Models\LocalGovernmentModel;
|
2026-04-22 15:35:36 +09:00
|
|
|
use App\Models\ManagerModel;
|
2026-03-30 15:07:09 +09:00
|
|
|
use Config\Roles;
|
2026-03-26 14:30:45 +09:00
|
|
|
|
|
|
|
|
class Bag extends BaseController
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* 로그인 사용자의 지자체 PK 반환 (미로그인/미지정 시 null)
|
|
|
|
|
*/
|
|
|
|
|
private function lgIdx(): ?int
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
return admin_effective_lg_idx();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 15:35:36 +09:00
|
|
|
/**
|
|
|
|
|
* 입고 화면용 인계자: 제작업체(company) 담당자.
|
|
|
|
|
*
|
|
|
|
|
* @return array{senders: list<object>, defaultSenderIdx: int}
|
|
|
|
|
*/
|
|
|
|
|
private function receivingManagerPickers(int $lgIdx): array
|
|
|
|
|
{
|
|
|
|
|
$senders = model(ManagerModel::class, false)
|
|
|
|
|
->where('mg_lg_idx', $lgIdx)
|
|
|
|
|
->where('mg_state', 1)
|
|
|
|
|
->where('mg_dept_code', 'company')
|
|
|
|
|
->orderBy('mg_name', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
$sessionName = trim((string) (session()->get('mb_name') ?? ''));
|
|
|
|
|
$defaultSenderIdx = 0;
|
|
|
|
|
foreach ($senders as $s) {
|
|
|
|
|
if ((string) ($s->mg_name ?? '') === $sessionName) {
|
|
|
|
|
$defaultSenderIdx = (int) ($s->mg_idx ?? 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($defaultSenderIdx <= 0 && $senders !== []) {
|
|
|
|
|
$defaultSenderIdx = (int) ($senders[0]->mg_idx ?? 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'senders' => $senders,
|
|
|
|
|
'defaultSenderIdx' => $defaultSenderIdx,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 인수자 드롭다운: 맨 위에 현재 로그인 회원, 이어서 대행소(agency) 담당자.
|
|
|
|
|
* value 는 br_receiver_ref 로 전달: m_{mb_idx} | g_{mg_idx}
|
|
|
|
|
*
|
|
|
|
|
* @return array{receiverOptions: list<array{ref: string, label: string}>, defaultReceiverRef: string}
|
|
|
|
|
*/
|
|
|
|
|
private function receivingReceiverSelect(int $lgIdx): array
|
|
|
|
|
{
|
|
|
|
|
$sessionMbIdx = (int) (session()->get('mb_idx') ?? 0);
|
|
|
|
|
$sessionName = trim((string) (session()->get('mb_name') ?? ''));
|
|
|
|
|
$normalizeName = static fn (string $name): string => preg_replace('/\s+/u', '', trim($name)) ?? '';
|
|
|
|
|
$normSession = $normalizeName($sessionName);
|
|
|
|
|
|
|
|
|
|
$options = [];
|
|
|
|
|
if ($sessionMbIdx > 0) {
|
|
|
|
|
$label = $sessionName !== '' ? $sessionName : '로그인 사용자';
|
|
|
|
|
$options[] = ['ref' => 'm_' . $sessionMbIdx, 'label' => $label];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$agencyManagers = model(ManagerModel::class, false)
|
|
|
|
|
->where('mg_lg_idx', $lgIdx)
|
|
|
|
|
->where('mg_state', 1)
|
|
|
|
|
->where('mg_dept_code', 'agency')
|
|
|
|
|
->orderBy('mg_name', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
foreach ($agencyManagers as $rcv) {
|
|
|
|
|
$mgIdx = (int) ($rcv->mg_idx ?? 0);
|
|
|
|
|
$receiverName = trim((string) ($rcv->mg_name ?? ''));
|
|
|
|
|
if ($mgIdx <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ($normSession !== '' && $normalizeName($receiverName) === $normSession) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$options[] = ['ref' => 'g_' . $mgIdx, 'label' => $receiverName];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$defaultRef = $options !== [] ? (string) ($options[0]['ref'] ?? '') : '';
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'receiverOptions' => $options,
|
|
|
|
|
'defaultReceiverRef' => $defaultRef,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param list<array{ref: string, label: string}> $options
|
|
|
|
|
*/
|
|
|
|
|
private function sanitizeReceiverRef(array $options, string $ref): string
|
|
|
|
|
{
|
|
|
|
|
foreach ($options as $opt) {
|
|
|
|
|
if (($opt['ref'] ?? '') === $ref) {
|
|
|
|
|
return $ref;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function parseReceiverRefToStoredIdx(int $lgIdx, string $ref): int
|
|
|
|
|
{
|
|
|
|
|
$ref = trim($ref);
|
|
|
|
|
if (preg_match('/^m_(\d+)$/', $ref, $mm)) {
|
|
|
|
|
$mbIdx = (int) $mm[1];
|
|
|
|
|
if ($mbIdx <= 0 || $mbIdx !== (int) (session()->get('mb_idx') ?? 0)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $mbIdx;
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('/^g_(\d+)$/', $ref, $mg)) {
|
|
|
|
|
return $this->assertAgencyReceiverIdx($lgIdx, (int) $mg[1]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function assertAgencyReceiverIdx(int $lgIdx, int $mgIdx): int
|
|
|
|
|
{
|
|
|
|
|
if ($mgIdx <= 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
$row = model(ManagerModel::class, false)->where([
|
|
|
|
|
'mg_idx' => $mgIdx,
|
|
|
|
|
'mg_lg_idx' => $lgIdx,
|
|
|
|
|
'mg_state' => 1,
|
|
|
|
|
'mg_dept_code' => 'agency',
|
|
|
|
|
])->first();
|
|
|
|
|
|
|
|
|
|
return $row ? $mgIdx : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function resolveCompanySenderName(int $lgIdx, int $mgIdx): string
|
|
|
|
|
{
|
|
|
|
|
if ($mgIdx <= 0) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
$row = model(ManagerModel::class, false)->where([
|
|
|
|
|
'mg_idx' => $mgIdx,
|
|
|
|
|
'mg_lg_idx' => $lgIdx,
|
|
|
|
|
'mg_state' => 1,
|
|
|
|
|
'mg_dept_code' => 'company',
|
|
|
|
|
])->first();
|
|
|
|
|
|
|
|
|
|
return $row ? trim((string) ($row->mg_name ?? '')) : '';
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:30:45 +09:00
|
|
|
private function render(string $title, string $viewFile, array $data = []): string
|
|
|
|
|
{
|
|
|
|
|
return view('bag/layout/main', [
|
|
|
|
|
'title' => $title,
|
|
|
|
|
'content' => view($viewFile, $data),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ──────────────────────────────────────────────
|
2026-04-08 00:20:09 +09:00
|
|
|
// 기본정보관리 (단가·포장 단위 진입 허브)
|
2026-03-26 14:30:45 +09:00
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
public function basicInfo(): string
|
|
|
|
|
{
|
2026-04-08 00:20:09 +09:00
|
|
|
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',
|
2026-03-30 15:07:09 +09:00
|
|
|
];
|
2026-04-08 00:20:09 +09:00
|
|
|
$hasExplicitGetFilter = false;
|
|
|
|
|
foreach ($filterKeys as $fk) {
|
|
|
|
|
$v = $get[$fk] ?? null;
|
|
|
|
|
if ($v !== null && ! is_array($v) && trim((string) $v) !== '') {
|
|
|
|
|
$hasExplicitGetFilter = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-26 14:30:45 +09:00
|
|
|
|
2026-04-08 00:20:09 +09:00
|
|
|
$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) {
|
2026-03-30 15:07:09 +09:00
|
|
|
try {
|
2026-04-08 00:20:09 +09:00
|
|
|
$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 : '';
|
2026-03-30 15:07:09 +09:00
|
|
|
} catch (DatabaseException $e) {
|
2026-04-08 00:20:09 +09:00
|
|
|
log_message('error', '[prices] bag_price 조회 실패: ' . $e->getMessage());
|
2026-03-30 15:07:09 +09:00
|
|
|
}
|
2026-04-08 00:20:09 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = [];
|
|
|
|
|
if ($lgIdx) {
|
2026-03-30 15:07:09 +09:00
|
|
|
try {
|
2026-04-08 00:20:09 +09:00
|
|
|
$packagingUnits = model(PackagingUnitModel::class)->where('pu_lg_idx', $lgIdx)->orderBy('pu_bag_code', 'ASC')->findAll();
|
2026-03-30 15:07:09 +09:00
|
|
|
} catch (DatabaseException $e) {
|
2026-04-08 00:20:09 +09:00
|
|
|
log_message('error', '[packagingUnits] packaging_unit 조회 실패: ' . $e->getMessage());
|
2026-03-30 15:07:09 +09:00
|
|
|
}
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
2026-04-09 13:01:31 +09:00
|
|
|
return $this->render('포장 단위', 'bag/packaging_units', ['packagingUnits' => $packagingUnits]);
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
2026-03-30 15:07:09 +09:00
|
|
|
/**
|
|
|
|
|
* 기본코드 종류·세부코드 조회 전용 (사이트 메뉴 기본코드관리)
|
|
|
|
|
*/
|
|
|
|
|
public function codeKinds(): string
|
|
|
|
|
{
|
|
|
|
|
$kindModel = model(CodeKindModel::class);
|
|
|
|
|
$detailModel = model(CodeDetailModel::class);
|
2026-04-09 12:20:35 +09:00
|
|
|
$kinds = [];
|
2026-03-30 15:07:09 +09:00
|
|
|
$countMap = [];
|
2026-04-14 14:49:15 +09:00
|
|
|
$selectedKind = null;
|
|
|
|
|
$detailList = [];
|
|
|
|
|
$rowCanEdit = [];
|
2026-04-09 12:20:35 +09:00
|
|
|
$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', '기본코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
|
2026-03-30 15:07:09 +09:00
|
|
|
}
|
|
|
|
|
|
2026-04-08 00:20:09 +09:00
|
|
|
$level = (int) session()->get('mb_level');
|
2026-04-14 14:49:15 +09:00
|
|
|
$canManageDetails = Roles::canManageCodeMaster($level);
|
|
|
|
|
|
|
|
|
|
if ($kinds !== []) {
|
|
|
|
|
$selectedCkIdx = (int) ($this->request->getGet('ck_idx') ?? 0);
|
|
|
|
|
foreach ($kinds as $row) {
|
|
|
|
|
if ((int) $row->ck_idx === $selectedCkIdx) {
|
|
|
|
|
$selectedKind = $row;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($selectedKind === null) {
|
|
|
|
|
$selectedKind = $kinds[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($selectedKind !== null) {
|
|
|
|
|
$detailList = $detailModel->where('cd_ck_idx', (int) $selectedKind->ck_idx)
|
|
|
|
|
->filterByTenantScope($lgIdx)
|
|
|
|
|
->orderBy('cd_sort', 'ASC')
|
2026-04-22 15:35:36 +09:00
|
|
|
->orderBy('cd_code', 'ASC')
|
2026-04-14 14:49:15 +09:00
|
|
|
->orderBy('cd_idx', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
helper('admin');
|
|
|
|
|
$adminLg = admin_effective_lg_idx();
|
|
|
|
|
foreach ($detailList as $row) {
|
|
|
|
|
$rowCanEdit[$row->cd_idx] = Roles::canEditCodeDetailRow($level, $row, $adminLg);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-08 00:20:09 +09:00
|
|
|
|
2026-03-30 15:07:09 +09:00
|
|
|
return $this->render('기본코드관리', 'bag/code_kinds', [
|
2026-04-08 00:20:09 +09:00
|
|
|
'codeKinds' => $kinds,
|
|
|
|
|
'countMap' => $countMap,
|
|
|
|
|
'canManageKinds' => Roles::canManageCodeKindMaster($level),
|
2026-04-14 14:49:15 +09:00
|
|
|
'canManageDetails' => $canManageDetails,
|
|
|
|
|
'selectedKind' => $selectedKind,
|
|
|
|
|
'detailList' => $detailList,
|
|
|
|
|
'rowCanEdit' => $rowCanEdit,
|
2026-03-30 15:07:09 +09:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 기본코드 세부 목록 (사이트 레이아웃). 등록·수정·삭제 폼은 /admin/code-details/* 유지.
|
|
|
|
|
*/
|
|
|
|
|
public function codeDetails(int $ckIdx)
|
|
|
|
|
{
|
|
|
|
|
$kindModel = model(CodeKindModel::class);
|
|
|
|
|
$detailModel = model(CodeDetailModel::class);
|
2026-04-09 12:20:35 +09:00
|
|
|
$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', '세부코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
|
|
|
|
|
}
|
2026-03-30 15:07:09 +09:00
|
|
|
if ($kind === null) {
|
|
|
|
|
return redirect()->to(site_url('bag/code-kinds'))->with('error', '코드 종류를 찾을 수 없습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 00:20:09 +09:00
|
|
|
$lgIdx = $this->lgIdx();
|
2026-04-09 12:20:35 +09:00
|
|
|
try {
|
|
|
|
|
$list = $detailModel->where('cd_ck_idx', $ckIdx)
|
|
|
|
|
->filterByTenantScope($lgIdx)
|
|
|
|
|
->orderBy('cd_sort', 'ASC')
|
2026-04-22 15:35:36 +09:00
|
|
|
->orderBy('cd_code', 'ASC')
|
2026-04-09 12:20:35 +09:00
|
|
|
->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', '세부코드 조회 중 오류가 발생했습니다. 관리자에게 로그 확인을 요청해 주세요.');
|
|
|
|
|
}
|
2026-03-30 15:07:09 +09:00
|
|
|
|
2026-04-08 00:20:09 +09:00
|
|
|
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 . ')';
|
2026-03-30 15:07:09 +09:00
|
|
|
|
|
|
|
|
return $this->render($title, 'bag/code_details', [
|
2026-04-08 00:20:09 +09:00
|
|
|
'kind' => $kind,
|
|
|
|
|
'list' => $list,
|
|
|
|
|
'pager' => $pager,
|
|
|
|
|
'canManage' => $canManage,
|
|
|
|
|
'rowCanEdit' => $rowCanEdit,
|
2026-03-30 15:07:09 +09:00
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 14:30:45 +09:00
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
// 발주 입고 관리
|
|
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// 발주 목록
|
2026-04-22 15:35:36 +09:00
|
|
|
$orderBuilder = model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx)->whereLatestHead($lgIdx);
|
2026-03-26 14:30:45 +09:00
|
|
|
if ($startDate) $orderBuilder->where('bo_order_date >=', $startDate);
|
|
|
|
|
if ($endDate) $orderBuilder->where('bo_order_date <=', $endDate);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['orders'] = $orderBuilder->orderBy('bo_order_date', 'DESC')->paginate(20, 'orders');
|
|
|
|
|
$data['orderPager'] = model(BagOrderModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
|
|
|
|
|
// 발주별 품목 합계
|
|
|
|
|
$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);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['receivings'] = $recvBuilder->orderBy('br_receive_date', 'DESC')->paginate(20, 'receivings');
|
|
|
|
|
$data['recvPager'] = model(BagReceivingModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['list'] = $builder->orderBy('bi2_issue_date', 'DESC')->paginate(20);
|
|
|
|
|
$data['pager'] = model(BagIssueModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->render('불출 관리', 'bag/issue', $data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
// 재고 관리
|
|
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
public function inventory(): string
|
|
|
|
|
{
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
$data = ['list' => []];
|
|
|
|
|
|
|
|
|
|
if ($lgIdx) {
|
2026-03-26 16:40:49 +09:00
|
|
|
$invModel = model(BagInventoryModel::class);
|
|
|
|
|
$data['list'] = $invModel->where('bi_lg_idx', $lgIdx)->orderBy('bi_bag_code', 'ASC')->paginate(20);
|
|
|
|
|
$data['pager'] = $invModel->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['salesList'] = $saleBuilder->orderBy('bs_sale_date', 'DESC')->paginate(20, 'sales');
|
|
|
|
|
$data['salesPager'] = model(BagSaleModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
|
|
|
|
|
// 주문 접수
|
|
|
|
|
$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);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['orderList'] = $orderBuilder->orderBy('so_idx', 'DESC')->paginate(20, 'shoporders');
|
|
|
|
|
$data['orderPager'] = model(ShopOrderModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2026-03-26 16:40:49 +09:00
|
|
|
$data['result'] = $builder->orderBy('bs_sale_date', 'DESC')->paginate(20);
|
|
|
|
|
$data['pager'] = model(BagSaleModel::class)->pager;
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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', []);
|
|
|
|
|
}
|
2026-03-26 16:13:07 +09:00
|
|
|
|
2026-03-26 16:20:35 +09:00
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
// 재고 조정 (실사)
|
|
|
|
|
// ──────────────────────────────────────────────
|
|
|
|
|
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', '재고가 조정되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 16:13:07 +09:00
|
|
|
// ══════════════════════════════════════════════
|
|
|
|
|
// CRUD — 사이트 레이아웃으로 등록/처리 폼 제공
|
|
|
|
|
// ══════════════════════════════════════════════
|
|
|
|
|
|
|
|
|
|
// --- 불출 등록 ---
|
|
|
|
|
public function issueCreate(): string
|
|
|
|
|
{
|
2026-04-08 00:20:09 +09:00
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
|
|
|
|
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $this->lgIdx()) : [];
|
2026-03-26 16:13:07 +09:00
|
|
|
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();
|
2026-04-08 00:20:09 +09:00
|
|
|
$companies = $lgIdx
|
2026-04-22 15:35:36 +09:00
|
|
|
? model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '제작업체')->where('cp_state', 1)->findAll()
|
|
|
|
|
: [];
|
|
|
|
|
$associations = $lgIdx
|
|
|
|
|
? model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '협회')->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)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
|
|
|
|
$priceMapRows = $lgIdx ? model(BagPriceModel::class)->latestActiveMapByBagCode($lgIdx) : [];
|
|
|
|
|
$units = $lgIdx ? model(PackagingUnitModel::class)->where('pu_lg_idx', $lgIdx)->where('pu_state', 1)->findAll() : [];
|
|
|
|
|
$recentOrders = $lgIdx
|
|
|
|
|
? model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx)->whereLatestHead($lgIdx)->orderBy('bo_order_date', 'DESC')->orderBy('bo_idx', 'DESC')->findAll(12)
|
|
|
|
|
: [];
|
|
|
|
|
|
|
|
|
|
$companyMap = [];
|
|
|
|
|
foreach ($companies as $company) {
|
|
|
|
|
$companyMap[(int) $company->cp_idx] = (string) $company->cp_name;
|
|
|
|
|
}
|
|
|
|
|
$agencyMap = [];
|
|
|
|
|
foreach ($agencies as $agency) {
|
|
|
|
|
$agencyMap[(int) $agency->sa_idx] = '[' . ($agency->sa_kind ?? '') . '] ' . ($agency->sa_code ?? '') . ' — ' . ($agency->sa_name ?? '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$bagNameMap = [];
|
|
|
|
|
foreach ($bagCodes as $codeDetail) {
|
|
|
|
|
$bagNameMap[(string) $codeDetail->cd_code] = (string) $codeDetail->cd_name;
|
|
|
|
|
}
|
|
|
|
|
$priceMap = [];
|
|
|
|
|
foreach ($priceMapRows as $bagCode => $price) {
|
|
|
|
|
$priceMap[(string) $bagCode] = (float) ($price->bp_order_price ?? 0);
|
|
|
|
|
}
|
|
|
|
|
$unitMap = [];
|
|
|
|
|
foreach ($units as $unit) {
|
|
|
|
|
$unitMap[(string) $unit->pu_bag_code] = [
|
|
|
|
|
'boxPerPack' => (int) $unit->pu_box_per_pack,
|
|
|
|
|
'packPerSheet' => (int) $unit->pu_pack_per_sheet,
|
|
|
|
|
'totalPerBox' => (int) $unit->pu_total_per_box,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$bagReferenceRows = [];
|
|
|
|
|
foreach ($bagCodes as $codeDetail) {
|
|
|
|
|
$bagCode = (string) $codeDetail->cd_code;
|
|
|
|
|
$unit = $unitMap[$bagCode] ?? ['boxPerPack' => 0, 'packPerSheet' => 0, 'totalPerBox' => 0];
|
|
|
|
|
$bagReferenceRows[] = [
|
|
|
|
|
'code' => $bagCode,
|
|
|
|
|
'name' => (string) ($bagNameMap[$bagCode] ?? ''),
|
|
|
|
|
'orderPrice' => (float) ($priceMap[$bagCode] ?? 0),
|
|
|
|
|
'boxPerPack' => (int) $unit['boxPerPack'],
|
|
|
|
|
'packPerSheet' => (int) $unit['packPerSheet'],
|
|
|
|
|
'totalPerBox' => (int) $unit['totalPerBox'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'발주 등록',
|
|
|
|
|
'bag/create_bag_order',
|
|
|
|
|
array_merge(
|
|
|
|
|
compact(
|
|
|
|
|
'companies',
|
|
|
|
|
'associations',
|
|
|
|
|
'agencies',
|
|
|
|
|
'bagCodes',
|
|
|
|
|
'recentOrders',
|
|
|
|
|
'companyMap',
|
|
|
|
|
'agencyMap',
|
|
|
|
|
'bagReferenceRows'
|
|
|
|
|
),
|
|
|
|
|
['editMode' => false, 'editDefaults' => null]
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 발주 변경 허브: 발주월·변경 구분 선택 후 목록에서 발주를 선택 (GBMS 발주 변경 화면 흐름).
|
|
|
|
|
*/
|
|
|
|
|
public function orderChange(): string|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
$month = $this->request->getGet('month');
|
|
|
|
|
if ($month === null || $month === '' || ! is_string($month) || ! preg_match('/^\d{4}-\d{2}$/', $month)) {
|
|
|
|
|
$month = date('Y-m');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$hubMode = $this->request->getGet('hub_mode');
|
|
|
|
|
$hubMode = in_array($hubMode, ['price', 'meta', 'delete'], true) ? $hubMode : 'meta';
|
|
|
|
|
|
|
|
|
|
$companyMap = [];
|
|
|
|
|
if ($lgIdx) {
|
|
|
|
|
$companies = model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '제작업체')->where('cp_state', 1)->findAll();
|
|
|
|
|
foreach ($companies as $company) {
|
|
|
|
|
$companyMap[(int) $company->cp_idx] = (string) $company->cp_name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$monthOrders = [];
|
|
|
|
|
if ($lgIdx) {
|
|
|
|
|
$start = $month . '-01';
|
|
|
|
|
$end = date('Y-m-t', strtotime($start . ' 00:00:00'));
|
|
|
|
|
$monthOrders = model(BagOrderModel::class)
|
|
|
|
|
->where('bo_lg_idx', $lgIdx)
|
|
|
|
|
->whereLatestHead($lgIdx)
|
|
|
|
|
->where('bo_order_date >=', $start)
|
|
|
|
|
->where('bo_order_date <=', $end)
|
|
|
|
|
->whereIn('bo_status', ['normal', 'cancelled'])
|
|
|
|
|
->orderBy('bo_order_date', 'DESC')
|
|
|
|
|
->orderBy('bo_idx', 'DESC')
|
|
|
|
|
->findAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($hubMode === 'delete') {
|
|
|
|
|
foreach ($monthOrders as $row) {
|
|
|
|
|
if ((string) ($row->bo_status ?? '') === 'normal') {
|
|
|
|
|
return redirect()->to(
|
|
|
|
|
site_url('bag/order/revise/' . (int) $row->bo_idx . '?change_mode=delete')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($lgIdx) {
|
|
|
|
|
session()->setFlashdata('error', '해당 월에 삭제할 수 있는 발주(정상)가 없습니다.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'발주 변경',
|
|
|
|
|
'bag/order_change',
|
|
|
|
|
compact('month', 'hubMode', 'monthOrders', 'companyMap')
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function orderRevise(int $id): string|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
$orderModel = model(BagOrderModel::class);
|
|
|
|
|
$itemModel = model(\App\Models\BagOrderItemModel::class);
|
|
|
|
|
|
|
|
|
|
$target = $orderModel->find($id);
|
|
|
|
|
if (! $target || (int) $target->bo_lg_idx !== $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '수정할 발주를 찾을 수 없습니다.');
|
|
|
|
|
}
|
|
|
|
|
if ((string) ($target->bo_status ?? '') !== 'normal') {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '변경할 수 없는 발주입니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$changeMode = $this->request->getGet('change_mode');
|
|
|
|
|
$changeMode = in_array($changeMode, ['price', 'meta', 'delete'], true) ? $changeMode : 'meta';
|
|
|
|
|
|
|
|
|
|
$companies = $lgIdx
|
|
|
|
|
? model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '제작업체')->where('cp_state', 1)->findAll()
|
|
|
|
|
: [];
|
|
|
|
|
$associations = $lgIdx
|
|
|
|
|
? model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '협회')->where('cp_state', 1)->findAll()
|
2026-04-08 00:20:09 +09:00
|
|
|
: [];
|
|
|
|
|
$agencies = $lgIdx ? model(SalesAgencyModel::class)->where('sa_lg_idx', $lgIdx)->orderForDisplay()->findAll() : [];
|
2026-03-26 16:13:07 +09:00
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
2026-04-22 15:35:36 +09:00
|
|
|
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
|
|
|
|
$priceMapRows = $lgIdx ? model(BagPriceModel::class)->latestActiveMapByBagCode($lgIdx) : [];
|
|
|
|
|
$units = $lgIdx ? model(PackagingUnitModel::class)->where('pu_lg_idx', $lgIdx)->where('pu_state', 1)->findAll() : [];
|
|
|
|
|
$companyMap = [];
|
|
|
|
|
foreach ($companies as $company) {
|
|
|
|
|
$companyMap[(int) $company->cp_idx] = (string) $company->cp_name;
|
|
|
|
|
}
|
|
|
|
|
$agencyMap = [];
|
|
|
|
|
foreach ($agencies as $agency) {
|
|
|
|
|
$agencyMap[(int) $agency->sa_idx] = '[' . ($agency->sa_kind ?? '') . '] ' . ($agency->sa_code ?? '') . ' — ' . ($agency->sa_name ?? '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$bagNameMap = [];
|
|
|
|
|
foreach ($bagCodes as $codeDetail) {
|
|
|
|
|
$bagNameMap[(string) $codeDetail->cd_code] = (string) $codeDetail->cd_name;
|
|
|
|
|
}
|
|
|
|
|
$priceMap = [];
|
|
|
|
|
foreach ($priceMapRows as $bagCode => $price) {
|
|
|
|
|
$priceMap[(string) $bagCode] = (float) ($price->bp_order_price ?? 0);
|
|
|
|
|
}
|
|
|
|
|
$unitMap = [];
|
|
|
|
|
foreach ($units as $unit) {
|
|
|
|
|
$unitMap[(string) $unit->pu_bag_code] = [
|
|
|
|
|
'boxPerPack' => (int) $unit->pu_box_per_pack,
|
|
|
|
|
'packPerSheet' => (int) $unit->pu_pack_per_sheet,
|
|
|
|
|
'totalPerBox' => (int) $unit->pu_total_per_box,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$bagReferenceRows = [];
|
|
|
|
|
foreach ($bagCodes as $codeDetail) {
|
|
|
|
|
$bagCode = (string) $codeDetail->cd_code;
|
|
|
|
|
$unit = $unitMap[$bagCode] ?? ['boxPerPack' => 0, 'packPerSheet' => 0, 'totalPerBox' => 0];
|
|
|
|
|
$bagReferenceRows[] = [
|
|
|
|
|
'code' => $bagCode,
|
|
|
|
|
'name' => (string) ($bagNameMap[$bagCode] ?? ''),
|
|
|
|
|
'orderPrice' => (float) ($priceMap[$bagCode] ?? 0),
|
|
|
|
|
'boxPerPack' => (int) $unit['boxPerPack'],
|
|
|
|
|
'packPerSheet' => (int) $unit['packPerSheet'],
|
|
|
|
|
'totalPerBox' => (int) $unit['totalPerBox'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$items = $itemModel->where('boi_bo_idx', (int) $target->bo_idx)->orderBy('boi_idx', 'ASC')->findAll();
|
|
|
|
|
|
|
|
|
|
$orderReturnMonth = substr((string) ($target->bo_order_date ?? date('Y-m-d')), 0, 7);
|
|
|
|
|
$monthStart = $orderReturnMonth . '-01';
|
|
|
|
|
$monthEnd = date('Y-m-t', strtotime($monthStart . ' 00:00:00'));
|
|
|
|
|
$recentOrders = $lgIdx
|
|
|
|
|
? $orderModel->where('bo_lg_idx', $lgIdx)
|
|
|
|
|
->whereLatestHead($lgIdx)
|
|
|
|
|
->where('bo_order_date >=', $monthStart)
|
|
|
|
|
->where('bo_order_date <=', $monthEnd)
|
|
|
|
|
->whereIn('bo_status', ['normal', 'cancelled'])
|
|
|
|
|
->orderBy('bo_order_date', 'DESC')
|
|
|
|
|
->orderBy('bo_idx', 'DESC')
|
|
|
|
|
->findAll()
|
|
|
|
|
: [];
|
|
|
|
|
$itemCodes = [];
|
|
|
|
|
$itemQtyBoxes = [];
|
|
|
|
|
$itemQtySheets = [];
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$itemCodes[] = (string) ($item->boi_bag_code ?? '');
|
|
|
|
|
$itemQtyBoxes[] = (int) ($item->boi_qty_box ?? 0);
|
|
|
|
|
$itemQtySheets[] = (int) ($item->boi_qty_sheet ?? 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$savedLinePrices = [];
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$savedLinePrices[(string) ($item->boi_bag_code ?? '')] = (float) ($item->boi_unit_price ?? 0);
|
|
|
|
|
}
|
|
|
|
|
foreach ($bagReferenceRows as &$brow) {
|
|
|
|
|
$c = (string) ($brow['code'] ?? '');
|
|
|
|
|
if ($c !== '' && isset($savedLinePrices[$c])) {
|
|
|
|
|
$brow['orderPrice'] = $savedLinePrices[$c];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
unset($brow);
|
|
|
|
|
|
|
|
|
|
$orderLotNo = (string) ($target->bo_lot_no ?? '');
|
|
|
|
|
|
|
|
|
|
$editDefaults = [
|
|
|
|
|
'bo_source_idx' => (int) $target->bo_idx,
|
|
|
|
|
'bo_order_date' => (string) ($target->bo_order_date ?? date('Y-m-d')),
|
|
|
|
|
'bo_order_month_ui' => substr((string) ($target->bo_order_date ?? date('Y-m-d')), 0, 7),
|
|
|
|
|
'bo_fee_rate' => (string) ($target->bo_fee_rate ?? '0'),
|
|
|
|
|
'bo_association_idx' => (string) ($target->bo_association_idx ?? ''),
|
|
|
|
|
'bo_company_idx' => (string) ($target->bo_company_idx ?? ''),
|
|
|
|
|
'bo_agency_idx' => (string) ($target->bo_agency_idx ?? ''),
|
|
|
|
|
'item_bag_code' => $itemCodes,
|
|
|
|
|
'item_qty_box' => $itemQtyBoxes,
|
|
|
|
|
'item_qty_sheet' => $itemQtySheets,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'발주 변경',
|
|
|
|
|
'bag/create_bag_order',
|
|
|
|
|
compact(
|
|
|
|
|
'companies',
|
|
|
|
|
'associations',
|
|
|
|
|
'agencies',
|
|
|
|
|
'bagCodes',
|
|
|
|
|
'recentOrders',
|
|
|
|
|
'companyMap',
|
|
|
|
|
'agencyMap',
|
|
|
|
|
'bagReferenceRows',
|
|
|
|
|
'editDefaults',
|
|
|
|
|
'changeMode',
|
|
|
|
|
'orderReturnMonth',
|
|
|
|
|
'orderLotNo'
|
|
|
|
|
)
|
|
|
|
|
+ ['editMode' => true, 'hubReturn' => true]
|
|
|
|
|
);
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function orderStore()
|
|
|
|
|
{
|
|
|
|
|
$admin = new \App\Controllers\Admin\BagOrder();
|
|
|
|
|
$admin->initController($this->request, $this->response, service('logger'));
|
|
|
|
|
$result = $admin->store();
|
2026-04-22 15:35:36 +09:00
|
|
|
if ($result instanceof RedirectResponse) {
|
|
|
|
|
$success = session()->getFlashdata('success');
|
|
|
|
|
$error = session()->getFlashdata('error');
|
|
|
|
|
$errors = session()->getFlashdata('errors');
|
|
|
|
|
|
|
|
|
|
if (! empty($error) || ! empty($errors)) {
|
|
|
|
|
$sourceIdx = (int) ($this->request->getPost('bo_source_idx') ?? 0);
|
|
|
|
|
$reviseMode = (string) ($this->request->getPost('bo_change_mode') ?? 'meta');
|
|
|
|
|
$redirectUrl = $sourceIdx > 0
|
|
|
|
|
? site_url('bag/order/revise/' . $sourceIdx . '?change_mode=' . rawurlencode($reviseMode))
|
|
|
|
|
: site_url('bag/order/create');
|
|
|
|
|
|
|
|
|
|
return redirect()->to($redirectUrl)
|
|
|
|
|
->withInput()
|
|
|
|
|
->with('error', $error)
|
|
|
|
|
->with('errors', $errors);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$returnHub = (int) ($this->request->getPost('order_return_hub') ?? 0) === 1;
|
|
|
|
|
$returnMonth = (string) ($this->request->getPost('order_return_month') ?? '');
|
|
|
|
|
$sourceIdxPost = (int) ($this->request->getPost('bo_source_idx') ?? 0);
|
|
|
|
|
if ($returnHub && $sourceIdxPost > 0 && preg_match('/^\d{4}-\d{2}$/', $returnMonth)) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change?month=' . $returnMonth))
|
|
|
|
|
->with('success', $success ?? '발주가 저장되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/order/create'))
|
|
|
|
|
->with('success', $success);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$returnHub = (int) ($this->request->getPost('order_return_hub') ?? 0) === 1;
|
|
|
|
|
$returnMonth = (string) ($this->request->getPost('order_return_month') ?? '');
|
|
|
|
|
if ($returnHub && (int) ($this->request->getPost('bo_source_idx') ?? 0) > 0 && preg_match('/^\d{4}-\d{2}$/', $returnMonth)) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change?month=' . $returnMonth))->with('success', '발주가 저장되었습니다.');
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
2026-04-22 15:35:36 +09:00
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/order/create'))->with('success', '발주 등록되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function orderDeletePost()
|
|
|
|
|
{
|
|
|
|
|
$id = (int) ($this->request->getPost('bo_idx') ?? 0);
|
|
|
|
|
if ($id <= 0) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '삭제할 발주를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->orderDelete($id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function orderDelete(int $id)
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if ($lgIdx === null || $lgIdx <= 0) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
$orderModel = model(BagOrderModel::class);
|
|
|
|
|
$order = $orderModel->find($id);
|
|
|
|
|
if (! $order || (int) $order->bo_lg_idx !== $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '발주를 찾을 수 없습니다.');
|
|
|
|
|
}
|
|
|
|
|
if ((string) ($order->bo_status ?? '') !== 'normal') {
|
|
|
|
|
return redirect()->to(site_url('bag/order/change'))->with('error', '삭제할 수 없는 발주입니다.');
|
|
|
|
|
}
|
|
|
|
|
$month = substr((string) ($order->bo_order_date ?? date('Y-m-d')), 0, 7);
|
|
|
|
|
|
|
|
|
|
$admin = new \App\Controllers\Admin\BagOrder();
|
|
|
|
|
$admin->initController($this->request, $this->response, service('logger'));
|
|
|
|
|
$response = $admin->delete($id);
|
|
|
|
|
if ($response instanceof RedirectResponse) {
|
|
|
|
|
$msg = session()->getFlashdata('success') ?? '발주가 삭제 처리되었습니다.';
|
|
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/order/change?month=' . $month))->with('success', $msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/order/change?month=' . $month))->with('success', '처리되었습니다.');
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
|
|
|
|
|
2026-04-08 00:20:09 +09:00
|
|
|
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', '발주가 취소되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-26 16:13:07 +09:00
|
|
|
// --- 입고 처리 ---
|
|
|
|
|
public function receivingCreate(): string
|
2026-04-22 15:35:36 +09:00
|
|
|
{
|
|
|
|
|
return $this->receivingScanner();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function receivingStore()
|
|
|
|
|
{
|
|
|
|
|
return $this->receivingScannerStore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 발주 입고(스캐너 대체 수동입력)
|
|
|
|
|
* - 미입고가 남은 발주의 LOT·봉투(이름)로 조회 범위를 좁힌 뒤 입고 처리
|
|
|
|
|
* - 인수자: 대행소(agency) 담당자, 기본값 동명이면 로그인 사용자명과 일치하는 담당자
|
|
|
|
|
* - 인계자: 제작업체(company) 담당자
|
|
|
|
|
*/
|
|
|
|
|
public function receivingScanner(): string|RedirectResponse
|
2026-03-26 16:13:07 +09:00
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
2026-04-22 15:35:36 +09:00
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$companyIdx = (int) old('company_idx', (int) ($this->request->getGet('company_idx') ?? 0));
|
|
|
|
|
$lotNo = '';
|
|
|
|
|
$bagCode = '';
|
|
|
|
|
|
|
|
|
|
$companies = model(CompanyModel::class)
|
|
|
|
|
->where('cp_lg_idx', $lgIdx)
|
|
|
|
|
->where('cp_type', '제작업체')
|
|
|
|
|
->where('cp_state', 1)
|
|
|
|
|
->orderBy('cp_name', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
$defaultCompanyIdx = ! empty($companies)
|
|
|
|
|
? (int) ($companies[0]->cp_idx ?? 0)
|
|
|
|
|
: 0;
|
|
|
|
|
|
|
|
|
|
if ($companyIdx > 0) {
|
|
|
|
|
$validCompany = false;
|
|
|
|
|
foreach ($companies as $company) {
|
|
|
|
|
if ((int) ($company->cp_idx ?? 0) === $companyIdx) {
|
|
|
|
|
$validCompany = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (! $validCompany) {
|
|
|
|
|
$companyIdx = $defaultCompanyIdx;
|
|
|
|
|
}
|
|
|
|
|
} elseif ($defaultCompanyIdx > 0) {
|
|
|
|
|
// 초기 진입 시 드롭다운 최상단 제작업체를 기본 선택한다.
|
|
|
|
|
$companyIdx = $defaultCompanyIdx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$lotChoices = [];
|
|
|
|
|
$bagFilterOptions = $this->receivingBagFilterOptions($lgIdx, $companyIdx, '');
|
|
|
|
|
|
|
|
|
|
$pick = $this->receivingManagerPickers($lgIdx);
|
|
|
|
|
$recvSel = $this->receivingReceiverSelect($lgIdx);
|
|
|
|
|
$receiverRef = (string) old('br_receiver_ref', $recvSel['defaultReceiverRef']);
|
|
|
|
|
$receiverRef = $this->sanitizeReceiverRef($recvSel['receiverOptions'], $receiverRef);
|
|
|
|
|
if ($receiverRef === '') {
|
|
|
|
|
$receiverRef = $recvSel['defaultReceiverRef'];
|
|
|
|
|
}
|
|
|
|
|
$senderIdx = (int) old('br_sender_idx', $pick['defaultSenderIdx']);
|
|
|
|
|
|
|
|
|
|
$rows = $companyIdx > 0
|
|
|
|
|
? $this->buildReceivingCandidateRows($lgIdx, $companyIdx, '', true, '')
|
|
|
|
|
: [];
|
|
|
|
|
$rowsByKey = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$rowsByKey[(string) $row['row_key']] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'발주 입고(스캐너)',
|
|
|
|
|
'bag/receiving_scanner',
|
|
|
|
|
[
|
|
|
|
|
'companyIdx' => $companyIdx,
|
|
|
|
|
'companies' => $companies,
|
|
|
|
|
'lotNo' => '',
|
|
|
|
|
'bagCode' => '',
|
|
|
|
|
'bagFilterOptions' => $bagFilterOptions,
|
|
|
|
|
'lotChoices' => $lotChoices,
|
|
|
|
|
'receiverOptions' => $recvSel['receiverOptions'],
|
|
|
|
|
'receiverRef' => $receiverRef,
|
|
|
|
|
'senders' => $pick['senders'],
|
|
|
|
|
'senderIdx' => $senderIdx,
|
|
|
|
|
'rows' => $rows,
|
|
|
|
|
'rowsByKey' => $rowsByKey,
|
|
|
|
|
]
|
|
|
|
|
);
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 15:35:36 +09:00
|
|
|
public function receivingScannerStore(): RedirectResponse
|
2026-03-26 16:13:07 +09:00
|
|
|
{
|
2026-04-22 15:35:36 +09:00
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$receiveDate = (string) ($this->request->getPost('br_receive_date') ?? date('Y-m-d'));
|
|
|
|
|
$companyIdx = (int) ($this->request->getPost('company_idx') ?? 0);
|
|
|
|
|
$lotNo = '';
|
|
|
|
|
$filterBagCode = '';
|
|
|
|
|
$recvSel = $this->receivingReceiverSelect($lgIdx);
|
|
|
|
|
$receiverRef = (string) ($this->request->getPost('br_receiver_ref') ?? '');
|
|
|
|
|
$receiverRef = $this->sanitizeReceiverRef($recvSel['receiverOptions'], $receiverRef);
|
|
|
|
|
if ($receiverRef === '') {
|
|
|
|
|
$receiverRef = $recvSel['defaultReceiverRef'];
|
|
|
|
|
}
|
|
|
|
|
$receiverIdx = $this->parseReceiverRefToStoredIdx($lgIdx, $receiverRef);
|
|
|
|
|
$senderIdx = (int) ($this->request->getPost('br_sender_idx') ?? 0);
|
|
|
|
|
$inputQty = $this->request->getPost('receive_qty_sheet');
|
|
|
|
|
$inputQty = is_array($inputQty) ? $inputQty : [];
|
|
|
|
|
|
|
|
|
|
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $receiveDate)) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '입고일 형식을 확인해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
if ($companyIdx <= 0) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '제작업체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
if ($receiverIdx <= 0) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '인수자를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$senderResolved = $this->resolveCompanySenderName($lgIdx, $senderIdx);
|
|
|
|
|
|
|
|
|
|
$rows = $this->buildReceivingCandidateRows($lgIdx, $companyIdx, '', true, '');
|
|
|
|
|
$rowMap = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$rowMap[(string) $row['row_key']] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$insertRows = [];
|
|
|
|
|
foreach ($inputQty as $rowKey => $qtyRaw) {
|
|
|
|
|
$rowKey = (string) $rowKey;
|
|
|
|
|
$qty = (int) $qtyRaw;
|
|
|
|
|
if ($qty <= 0 || ! isset($rowMap[$rowKey])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$base = $rowMap[$rowKey];
|
|
|
|
|
$pending = (int) ($base['pending_qty_sheet'] ?? 0);
|
|
|
|
|
if ($pending <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ($qty > $pending) {
|
|
|
|
|
$qty = $pending;
|
|
|
|
|
}
|
|
|
|
|
$totalPerBox = max(1, (int) ($base['total_per_box'] ?? 1));
|
|
|
|
|
$qtyBox = intdiv($qty, $totalPerBox);
|
|
|
|
|
$sender = $senderResolved !== '' ? $senderResolved : (string) ($base['company_rep_name'] ?? '');
|
|
|
|
|
|
|
|
|
|
$insertRows[] = [
|
|
|
|
|
'br_bo_idx' => (int) $base['bo_idx'],
|
|
|
|
|
'br_lg_idx' => $lgIdx,
|
|
|
|
|
'br_bag_code' => (string) $base['bag_code'],
|
|
|
|
|
'br_bag_name' => (string) $base['bag_name'],
|
|
|
|
|
'br_qty_box' => $qtyBox,
|
|
|
|
|
'br_qty_sheet' => $qty,
|
|
|
|
|
'br_receive_date' => $receiveDate,
|
|
|
|
|
'br_receiver_idx' => $receiverIdx,
|
|
|
|
|
'br_sender_name' => $sender,
|
|
|
|
|
'br_type' => 'scanner',
|
|
|
|
|
'br_regdate' => date('Y-m-d H:i:s'),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($insertRows)) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '입고 처리할 수량을 입력해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$recvModel = model(BagReceivingModel::class);
|
|
|
|
|
$invModel = model(BagInventoryModel::class);
|
|
|
|
|
$db = \Config\Database::connect();
|
|
|
|
|
$db->transStart();
|
|
|
|
|
foreach ($insertRows as $row) {
|
|
|
|
|
$recvModel->insert($row);
|
|
|
|
|
$invModel->adjustQty(
|
|
|
|
|
$lgIdx,
|
|
|
|
|
(string) $row['br_bag_code'],
|
|
|
|
|
(string) $row['br_bag_name'],
|
|
|
|
|
(int) $row['br_qty_sheet']
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
$db->transComplete();
|
|
|
|
|
|
|
|
|
|
if (! $db->transStatus()) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '입고 처리 중 오류가 발생했습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$query = ['company_idx' => $companyIdx];
|
|
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/receiving/scanner') . '?' . http_build_query($query))
|
|
|
|
|
->with('success', count($insertRows) . '건 입고 처리되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 일괄 입고: LOT-봉투 행 기준 미입고량 전체 입고.
|
|
|
|
|
*/
|
|
|
|
|
public function receivingBatch(): string|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$companyIdx = (int) ($this->request->getGet('company_idx') ?? 0);
|
|
|
|
|
$bagCode = trim((string) ($this->request->getGet('bag_code') ?? ''));
|
|
|
|
|
|
|
|
|
|
$companies = model(CompanyModel::class)
|
|
|
|
|
->where('cp_lg_idx', $lgIdx)
|
|
|
|
|
->where('cp_type', '제작업체')
|
|
|
|
|
->where('cp_state', 1)
|
|
|
|
|
->orderBy('cp_name', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
|
|
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
|
|
|
|
$bagCodeOptions = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
|
|
|
|
$pick = $this->receivingManagerPickers($lgIdx);
|
|
|
|
|
$recvSel = $this->receivingReceiverSelect($lgIdx);
|
|
|
|
|
$receiverRef = (string) old('br_receiver_ref', $recvSel['defaultReceiverRef']);
|
|
|
|
|
$receiverRef = $this->sanitizeReceiverRef($recvSel['receiverOptions'], $receiverRef);
|
|
|
|
|
if ($receiverRef === '') {
|
|
|
|
|
$receiverRef = $recvSel['defaultReceiverRef'];
|
|
|
|
|
}
|
|
|
|
|
// 조회 화면에서는 입고완료 행도 함께 보여 미입고량 0을 확인할 수 있게 한다.
|
|
|
|
|
$rows = $this->buildReceivingCandidateRows($lgIdx, $companyIdx, $bagCode, false, '');
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'일괄 입고',
|
|
|
|
|
'bag/receiving_batch',
|
|
|
|
|
[
|
|
|
|
|
'companyIdx' => $companyIdx,
|
|
|
|
|
'bagCode' => $bagCode,
|
|
|
|
|
'companies' => $companies,
|
|
|
|
|
'bagCodeOptions' => $bagCodeOptions,
|
|
|
|
|
'receiverOptions' => $recvSel['receiverOptions'],
|
|
|
|
|
'receiverRef' => $receiverRef,
|
|
|
|
|
'senders' => $pick['senders'],
|
|
|
|
|
'senderIdx' => (int) old('br_sender_idx', $pick['defaultSenderIdx']),
|
|
|
|
|
'rows' => $rows,
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function receivingBatchStore(): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$companyIdx = (int) ($this->request->getPost('company_idx') ?? 0);
|
|
|
|
|
$bagCode = trim((string) ($this->request->getPost('bag_code') ?? ''));
|
|
|
|
|
$selected = $this->request->getPost('selected_rows');
|
|
|
|
|
$selected = is_array($selected) ? array_map('strval', $selected) : [];
|
|
|
|
|
$receiveDate = (string) ($this->request->getPost('br_receive_date') ?? date('Y-m-d'));
|
|
|
|
|
$recvSel = $this->receivingReceiverSelect($lgIdx);
|
|
|
|
|
$receiverRef = (string) ($this->request->getPost('br_receiver_ref') ?? '');
|
|
|
|
|
$receiverRef = $this->sanitizeReceiverRef($recvSel['receiverOptions'], $receiverRef);
|
|
|
|
|
if ($receiverRef === '') {
|
|
|
|
|
$receiverRef = $recvSel['defaultReceiverRef'];
|
|
|
|
|
}
|
|
|
|
|
$receiverIdx = $this->parseReceiverRefToStoredIdx($lgIdx, $receiverRef);
|
|
|
|
|
$senderIdx = (int) ($this->request->getPost('br_sender_idx') ?? 0);
|
|
|
|
|
|
|
|
|
|
if (empty($selected)) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '일괄 입고할 행을 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
if (! preg_match('/^\d{4}-\d{2}-\d{2}$/', $receiveDate)) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '입고일 형식을 확인해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
if ($receiverIdx <= 0) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '인수자를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$senderResolved = $this->resolveCompanySenderName($lgIdx, $senderIdx);
|
|
|
|
|
|
|
|
|
|
$rows = $this->buildReceivingCandidateRows($lgIdx, 0, '', true, '');
|
|
|
|
|
$rowMap = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$rowMap[(string) $row['row_key']] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$insertRows = [];
|
|
|
|
|
foreach ($selected as $rowKey) {
|
|
|
|
|
if (! isset($rowMap[$rowKey])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$base = $rowMap[$rowKey];
|
|
|
|
|
$qty = (int) ($base['pending_qty_sheet'] ?? 0);
|
|
|
|
|
if ($qty <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$totalPerBox = max(1, (int) ($base['total_per_box'] ?? 1));
|
|
|
|
|
$qtyBox = intdiv($qty, $totalPerBox);
|
|
|
|
|
$sender = $senderResolved !== '' ? $senderResolved : (string) ($base['company_rep_name'] ?? '');
|
|
|
|
|
|
|
|
|
|
$insertRows[] = [
|
|
|
|
|
'br_bo_idx' => (int) $base['bo_idx'],
|
|
|
|
|
'br_lg_idx' => $lgIdx,
|
|
|
|
|
'br_bag_code' => (string) $base['bag_code'],
|
|
|
|
|
'br_bag_name' => (string) $base['bag_name'],
|
|
|
|
|
'br_qty_box' => $qtyBox,
|
|
|
|
|
'br_qty_sheet' => $qty,
|
|
|
|
|
'br_receive_date' => $receiveDate,
|
|
|
|
|
'br_receiver_idx' => $receiverIdx,
|
|
|
|
|
'br_sender_name' => $sender,
|
|
|
|
|
'br_type' => 'batch',
|
|
|
|
|
'br_regdate' => date('Y-m-d H:i:s'),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($insertRows)) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '선택한 행에 입고할 수량이 없습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$recvModel = model(BagReceivingModel::class);
|
|
|
|
|
$invModel = model(BagInventoryModel::class);
|
|
|
|
|
$db = \Config\Database::connect();
|
|
|
|
|
$db->transStart();
|
|
|
|
|
foreach ($insertRows as $row) {
|
|
|
|
|
$recvModel->insert($row);
|
|
|
|
|
$invModel->adjustQty(
|
|
|
|
|
$lgIdx,
|
|
|
|
|
(string) $row['br_bag_code'],
|
|
|
|
|
(string) $row['br_bag_name'],
|
|
|
|
|
(int) $row['br_qty_sheet']
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
$db->transComplete();
|
|
|
|
|
|
|
|
|
|
if (! $db->transStatus()) {
|
|
|
|
|
return redirect()->back()->withInput()->with('error', '일괄 입고 처리 중 오류가 발생했습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return redirect()->to(site_url('bag/receiving/batch?company_idx=' . $companyIdx . '&bag_code=' . rawurlencode($bagCode)))
|
|
|
|
|
->with('success', count($insertRows) . '건 일괄 입고 처리되었습니다.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function receivingStatus(): string|RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$startDate = (string) ($this->request->getGet('start_date') ?? date('Y-m-01'));
|
|
|
|
|
$endDate = (string) ($this->request->getGet('end_date') ?? date('Y-m-d'));
|
|
|
|
|
$companyIdx = (int) ($this->request->getGet('company_idx') ?? 0);
|
|
|
|
|
$bagCode = trim((string) ($this->request->getGet('bag_code') ?? ''));
|
|
|
|
|
$receiveType = (string) ($this->request->getGet('receive_type') ?? 'all');
|
|
|
|
|
if (! in_array($receiveType, ['all', 'completed', 'pending'], true)) {
|
|
|
|
|
$receiveType = 'all';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$companies = model(CompanyModel::class)
|
|
|
|
|
->where('cp_lg_idx', $lgIdx)
|
|
|
|
|
->where('cp_type', '제작업체')
|
|
|
|
|
->where('cp_state', 1)
|
|
|
|
|
->orderBy('cp_name', 'ASC')
|
|
|
|
|
->findAll();
|
|
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
|
|
|
|
$bagCodeOptions = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
|
|
|
|
|
|
|
|
|
$rows = $this->buildReceivingStatusRows($lgIdx, $startDate, $endDate, $companyIdx, $bagCode, $receiveType);
|
|
|
|
|
$groupTotals = [];
|
|
|
|
|
$grandTotalReceive = 0;
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$key = (string) ($row['display_date'] ?? '');
|
|
|
|
|
if (! isset($groupTotals[$key])) {
|
|
|
|
|
$groupTotals[$key] = 0;
|
|
|
|
|
}
|
|
|
|
|
$groupTotals[$key] += (int) ($row['received_qty_sheet'] ?? 0);
|
|
|
|
|
$grandTotalReceive += (int) ($row['received_qty_sheet'] ?? 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->render(
|
|
|
|
|
'입고 현황',
|
|
|
|
|
'bag/receiving_status',
|
|
|
|
|
compact(
|
|
|
|
|
'startDate',
|
|
|
|
|
'endDate',
|
|
|
|
|
'companyIdx',
|
|
|
|
|
'bagCode',
|
|
|
|
|
'receiveType',
|
|
|
|
|
'companies',
|
|
|
|
|
'bagCodeOptions',
|
|
|
|
|
'rows',
|
|
|
|
|
'groupTotals',
|
|
|
|
|
'grandTotalReceive'
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function receivingStatusExport(): RedirectResponse
|
|
|
|
|
{
|
|
|
|
|
helper(['admin', 'export']);
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
if (! $lgIdx) {
|
|
|
|
|
return redirect()->to(site_url('bag/purchase-inbound'))->with('error', '지자체를 선택해 주세요.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$startDate = (string) ($this->request->getGet('start_date') ?? date('Y-m-01'));
|
|
|
|
|
$endDate = (string) ($this->request->getGet('end_date') ?? date('Y-m-d'));
|
|
|
|
|
$companyIdx = (int) ($this->request->getGet('company_idx') ?? 0);
|
|
|
|
|
$bagCode = trim((string) ($this->request->getGet('bag_code') ?? ''));
|
|
|
|
|
$receiveType = (string) ($this->request->getGet('receive_type') ?? 'all');
|
|
|
|
|
if (! in_array($receiveType, ['all', 'completed', 'pending'], true)) {
|
|
|
|
|
$receiveType = 'all';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rows = $this->buildReceivingStatusRows($lgIdx, $startDate, $endDate, $companyIdx, $bagCode, $receiveType);
|
|
|
|
|
$exportRows = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$exportRows[] = [
|
|
|
|
|
(string) ($row['display_date'] ?? ''),
|
|
|
|
|
(string) ($row['bag_name'] ?? ''),
|
|
|
|
|
(int) ($row['received_qty_sheet'] ?? 0),
|
|
|
|
|
(string) ($row['order_date'] ?? ''),
|
|
|
|
|
(int) ($row['order_qty_sheet'] ?? 0),
|
|
|
|
|
(string) ($row['order_no'] ?? ''),
|
|
|
|
|
(string) ($row['company_name'] ?? ''),
|
|
|
|
|
(string) ($row['receive_status_label'] ?? ''),
|
|
|
|
|
(string) ($row['agency_name'] ?? ''),
|
|
|
|
|
'',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export_xlsx(
|
|
|
|
|
'입고현황_' . date('Ymd'),
|
|
|
|
|
'입고현황',
|
|
|
|
|
['입고일자', '품명', '입고수량', '발주일자', '발주수량', '발주번호', '제작업체', '입고여부', '입고처', '비고'],
|
|
|
|
|
$exportRows
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 미입고 잔량이 있는 발주 LOT 목록(스캐너 입고용 드롭다운).
|
|
|
|
|
*
|
|
|
|
|
* @return list<array{lot_no: string, bo_idx: int, order_date: string, company_name: string, pending_lines: int}>
|
|
|
|
|
*/
|
|
|
|
|
private function buildReceivingPendingLotChoices(int $lgIdx, int $companyIdx = 0): array
|
|
|
|
|
{
|
|
|
|
|
$rows = $this->buildReceivingCandidateRows($lgIdx, $companyIdx, '', true, '');
|
|
|
|
|
$byLot = [];
|
|
|
|
|
foreach ($rows as $r) {
|
|
|
|
|
$lot = (string) ($r['lot_no'] ?? '');
|
|
|
|
|
if ($lot === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (! isset($byLot[$lot])) {
|
|
|
|
|
$byLot[$lot] = [
|
|
|
|
|
'lot_no' => $lot,
|
|
|
|
|
'bo_idx' => (int) ($r['bo_idx'] ?? 0),
|
|
|
|
|
'order_date' => (string) ($r['order_date'] ?? ''),
|
|
|
|
|
'company_name' => (string) ($r['company_name'] ?? ''),
|
|
|
|
|
'pending_lines' => 0,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$byLot[$lot]['pending_lines']++;
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
2026-04-22 15:35:36 +09:00
|
|
|
$list = array_values($byLot);
|
|
|
|
|
usort($list, static function (array $a, array $b): int {
|
|
|
|
|
$da = (string) ($a['order_date'] ?? '');
|
|
|
|
|
$db = (string) ($b['order_date'] ?? '');
|
|
|
|
|
if ($da === $db) {
|
|
|
|
|
return strcmp((string) ($b['lot_no'] ?? ''), (string) ($a['lot_no'] ?? ''));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp($db, $da);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeLotNoForReceiving(int $lgIdx, int $companyIdx, string $lotNo): string
|
|
|
|
|
{
|
|
|
|
|
$lotNo = trim($lotNo);
|
|
|
|
|
if ($lotNo === '') {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
foreach ($this->buildReceivingPendingLotChoices($lgIdx, $companyIdx) as $choice) {
|
|
|
|
|
if ((string) ($choice['lot_no'] ?? '') === $lotNo) {
|
|
|
|
|
return $lotNo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 선택 조건(제작업체 + LOT)에 해당하는 미입고 품목(봉투) 목록 — 조회 조건 드롭다운용.
|
|
|
|
|
*
|
|
|
|
|
* @return list<array{bag_code: string, bag_name: string}>
|
|
|
|
|
*/
|
|
|
|
|
private function receivingBagFilterOptions(int $lgIdx, int $companyIdx, string $lotNo = ''): array
|
|
|
|
|
{
|
|
|
|
|
if ($companyIdx <= 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
$allForFilter = $this->buildReceivingCandidateRows($lgIdx, $companyIdx, '', true, $lotNo);
|
|
|
|
|
$byCode = [];
|
|
|
|
|
foreach ($allForFilter as $r) {
|
|
|
|
|
$c = (string) ($r['bag_code'] ?? '');
|
|
|
|
|
if ($c === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (! isset($byCode[$c])) {
|
|
|
|
|
$byCode[$c] = (string) ($r['bag_name'] ?? '');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$list = [];
|
|
|
|
|
foreach ($byCode as $code => $name) {
|
|
|
|
|
$list[] = ['bag_code' => $code, 'bag_name' => $name];
|
|
|
|
|
}
|
|
|
|
|
usort($list, static fn (array $a, array $b): int => strcmp($a['bag_name'], $b['bag_name']));
|
|
|
|
|
|
|
|
|
|
return $list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeBagCodeForReceiving(int $lgIdx, int $companyIdx, string $lotNo, string $bagCode): string
|
|
|
|
|
{
|
|
|
|
|
$bagCode = trim($bagCode);
|
|
|
|
|
if ($bagCode === '' || $companyIdx <= 0) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
foreach ($this->receivingBagFilterOptions($lgIdx, $companyIdx, $lotNo) as $opt) {
|
|
|
|
|
if ($opt['bag_code'] === $bagCode) {
|
|
|
|
|
return $bagCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 입고 대상 후보(LOT-봉투행) 생성.
|
|
|
|
|
*
|
|
|
|
|
* @param string $lotNo 빈 문자열이면 LOT 제한 없음. 지정 시 해당 LOT(최신 헤드) 발주만.
|
|
|
|
|
*/
|
|
|
|
|
private function buildReceivingCandidateRows(int $lgIdx, int $companyIdx = 0, string $bagCode = '', bool $onlyPending = true, string $lotNo = ''): array
|
|
|
|
|
{
|
|
|
|
|
$orderBuilder = model(BagOrderModel::class)
|
|
|
|
|
->where('bo_lg_idx', $lgIdx)
|
|
|
|
|
->whereLatestHead($lgIdx)
|
|
|
|
|
->where('bo_status', 'normal')
|
|
|
|
|
->orderBy('bo_order_date', 'DESC')
|
|
|
|
|
->orderBy('bo_idx', 'DESC');
|
|
|
|
|
if ($lotNo !== '') {
|
|
|
|
|
$orderBuilder->where('bo_lot_no', $lotNo);
|
|
|
|
|
}
|
|
|
|
|
if ($companyIdx > 0) {
|
|
|
|
|
$orderBuilder->where('bo_company_idx', $companyIdx);
|
|
|
|
|
}
|
|
|
|
|
$orders = $orderBuilder->findAll();
|
|
|
|
|
if (empty($orders)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$orderIds = array_map(static fn($o) => (int) ($o->bo_idx ?? 0), $orders);
|
|
|
|
|
$companyMap = [];
|
|
|
|
|
foreach (model(CompanyModel::class)->where('cp_lg_idx', $lgIdx)->where('cp_type', '제작업체')->findAll() as $company) {
|
|
|
|
|
$companyMap[(int) ($company->cp_idx ?? 0)] = [
|
|
|
|
|
'name' => (string) ($company->cp_name ?? ''),
|
|
|
|
|
'rep' => (string) ($company->cp_rep_name ?? ''),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
$agencyMap = [];
|
|
|
|
|
foreach (model(SalesAgencyModel::class)->where('sa_lg_idx', $lgIdx)->orderForDisplay()->findAll() as $agency) {
|
|
|
|
|
$agencyMap[(int) ($agency->sa_idx ?? 0)] = (string) ($agency->sa_name ?? '');
|
|
|
|
|
}
|
|
|
|
|
$unitMap = [];
|
|
|
|
|
foreach (model(PackagingUnitModel::class)->where('pu_lg_idx', $lgIdx)->where('pu_state', 1)->findAll() as $unit) {
|
|
|
|
|
$unitMap[(string) ($unit->pu_bag_code ?? '')] = [
|
|
|
|
|
'pack_per_sheet' => (int) ($unit->pu_pack_per_sheet ?? 1),
|
|
|
|
|
'total_per_box' => (int) ($unit->pu_total_per_box ?? 1),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$itemBuilder = model(BagOrderItemModel::class)->whereIn('boi_bo_idx', $orderIds);
|
|
|
|
|
if ($bagCode !== '') {
|
|
|
|
|
$itemBuilder->where('boi_bag_code', $bagCode);
|
|
|
|
|
}
|
|
|
|
|
$items = $itemBuilder->orderBy('boi_bo_idx', 'DESC')->orderBy('boi_idx', 'ASC')->findAll();
|
|
|
|
|
|
|
|
|
|
$receivedRows = model(BagReceivingModel::class)
|
|
|
|
|
->select('br_bo_idx, br_bag_code, SUM(br_qty_sheet) as recv_qty_sheet, MAX(br_receive_date) as last_receive_date')
|
|
|
|
|
->where('br_lg_idx', $lgIdx)
|
|
|
|
|
->whereIn('br_bo_idx', $orderIds)
|
|
|
|
|
->groupBy('br_bo_idx, br_bag_code')
|
|
|
|
|
->findAll();
|
|
|
|
|
$receivedMap = [];
|
|
|
|
|
foreach ($receivedRows as $recv) {
|
|
|
|
|
$receivedMap[(int) ($recv->br_bo_idx ?? 0) . '|' . (string) ($recv->br_bag_code ?? '')] = [
|
|
|
|
|
'recv_qty_sheet' => (int) ($recv->recv_qty_sheet ?? 0),
|
|
|
|
|
'last_receive_date' => (string) ($recv->last_receive_date ?? ''),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$orderMap = [];
|
|
|
|
|
foreach ($orders as $order) {
|
|
|
|
|
$orderMap[(int) ($order->bo_idx ?? 0)] = $order;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rows = [];
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
|
$boIdx = (int) ($item->boi_bo_idx ?? 0);
|
|
|
|
|
if (! isset($orderMap[$boIdx])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$order = $orderMap[$boIdx];
|
|
|
|
|
$itemBagCode = (string) ($item->boi_bag_code ?? '');
|
|
|
|
|
$recv = $receivedMap[$boIdx . '|' . $itemBagCode] ?? ['recv_qty_sheet' => 0, 'last_receive_date' => ''];
|
|
|
|
|
$orderQtySheet = (int) ($item->boi_qty_sheet ?? 0);
|
|
|
|
|
$receivedQtySheet = min($orderQtySheet, (int) ($recv['recv_qty_sheet'] ?? 0));
|
|
|
|
|
$pendingQtySheet = max(0, $orderQtySheet - $receivedQtySheet);
|
|
|
|
|
if ($onlyPending && $pendingQtySheet <= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$unit = $unitMap[$itemBagCode] ?? ['pack_per_sheet' => 1, 'total_per_box' => 1];
|
|
|
|
|
$companyInfo = $companyMap[(int) ($order->bo_company_idx ?? 0)] ?? ['name' => '', 'rep' => ''];
|
|
|
|
|
|
|
|
|
|
$rows[] = [
|
|
|
|
|
'row_key' => $boIdx . '|' . $itemBagCode,
|
|
|
|
|
'bo_idx' => $boIdx,
|
|
|
|
|
'order_no' => sprintf('%06d', $boIdx),
|
|
|
|
|
'lot_no' => (string) ($order->bo_lot_no ?? ''),
|
|
|
|
|
'order_date' => (string) ($order->bo_order_date ?? ''),
|
|
|
|
|
'company_name' => (string) ($companyInfo['name'] ?? ''),
|
|
|
|
|
'company_rep_name' => (string) ($companyInfo['rep'] ?? ''),
|
|
|
|
|
'agency_name' => (string) ($agencyMap[(int) ($order->bo_agency_idx ?? 0)] ?? ''),
|
|
|
|
|
'bag_code' => $itemBagCode,
|
|
|
|
|
'bag_name' => (string) ($item->boi_bag_name ?? ''),
|
|
|
|
|
'order_qty_sheet' => $orderQtySheet,
|
|
|
|
|
'received_qty_sheet' => $receivedQtySheet,
|
|
|
|
|
'pending_qty_sheet' => $pendingQtySheet,
|
|
|
|
|
'pack_per_sheet' => max(1, (int) ($unit['pack_per_sheet'] ?? 1)),
|
|
|
|
|
'total_per_box' => max(1, (int) ($unit['total_per_box'] ?? 1)),
|
|
|
|
|
'last_receive_date' => (string) ($recv['last_receive_date'] ?? ''),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $rows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function buildReceivingStatusRows(
|
|
|
|
|
int $lgIdx,
|
|
|
|
|
string $startDate,
|
|
|
|
|
string $endDate,
|
|
|
|
|
int $companyIdx,
|
|
|
|
|
string $bagCode,
|
|
|
|
|
string $receiveType
|
|
|
|
|
): array {
|
|
|
|
|
$rows = $this->buildReceivingCandidateRows($lgIdx, $companyIdx, $bagCode, false, '');
|
|
|
|
|
$filtered = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$pendingQty = (int) ($row['pending_qty_sheet'] ?? 0);
|
|
|
|
|
$isCompleted = $pendingQty <= 0;
|
|
|
|
|
if ($receiveType === 'completed' && ! $isCompleted) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ($receiveType === 'pending' && $isCompleted) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$displayDate = (string) ($row['last_receive_date'] ?? '');
|
|
|
|
|
if ($displayDate === '') {
|
|
|
|
|
$displayDate = (string) ($row['order_date'] ?? '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($startDate !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate) && $displayDate < $startDate) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ($endDate !== '' && preg_match('/^\d{4}-\d{2}-\d{2}$/', $endDate) && $displayDate > $endDate) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$row['display_date'] = $displayDate;
|
|
|
|
|
$row['receive_status_label'] = $isCompleted ? '완료' : '미완료';
|
|
|
|
|
$filtered[] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usort($filtered, static function (array $a, array $b): int {
|
|
|
|
|
$da = (string) ($a['display_date'] ?? '');
|
|
|
|
|
$db = (string) ($b['display_date'] ?? '');
|
|
|
|
|
if ($da === $db) {
|
|
|
|
|
return strcmp((string) ($a['bag_name'] ?? ''), (string) ($b['bag_name'] ?? ''));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strcmp($da, $db);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $filtered;
|
2026-03-26 16:13:07 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- 판매 등록 ---
|
|
|
|
|
public function saleCreate(): string
|
|
|
|
|
{
|
|
|
|
|
helper('admin');
|
|
|
|
|
$lgIdx = $this->lgIdx();
|
|
|
|
|
$shops = $lgIdx ? model(DesignatedShopModel::class)->where('ds_lg_idx', $lgIdx)->where('ds_state', 1)->findAll() : [];
|
2026-04-08 00:20:09 +09:00
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
|
|
|
|
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
2026-03-26 16:13:07 +09:00
|
|
|
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() : [];
|
2026-04-08 00:20:09 +09:00
|
|
|
$kind = model(CodeKindModel::class)->where('ck_code', 'O')->first();
|
|
|
|
|
$bagCodes = $kind ? model(CodeDetailModel::class)->getByKind((int) $kind->ck_idx, true, $lgIdx) : [];
|
2026-03-26 16:13:07 +09:00
|
|
|
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', '주문 접수되었습니다.');
|
|
|
|
|
}
|
2026-03-26 14:30:45 +09:00
|
|
|
}
|