Compare commits
4 Commits
5c89c963ee
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21e7b91871 | ||
|
|
c708d30660 | ||
|
|
215d4d2c7c | ||
|
|
6db9d119c1 |
@@ -26,8 +26,18 @@ $routes->get('bag/code-details/(:num)', 'Bag::codeDetails/$1');
|
|||||||
// 옛 주소 호환: 세부 목록만 사이트로 이동
|
// 옛 주소 호환: 세부 목록만 사이트로 이동
|
||||||
$routes->get('admin/code-details/(:num)', 'Admin\CodeDetail::index/$1');
|
$routes->get('admin/code-details/(:num)', 'Admin\CodeDetail::index/$1');
|
||||||
$routes->get('bag/purchase-inbound', 'Bag::purchaseInbound');
|
$routes->get('bag/purchase-inbound', 'Bag::purchaseInbound');
|
||||||
$routes->get('bag/issue', 'Bag::issue');
|
$routes->get('bag/issue', 'Bag::issueLegacy');
|
||||||
|
$routes->get('bag/issue/cancel', 'Bag::issue');
|
||||||
$routes->get('bag/inventory', 'Bag::inventory');
|
$routes->get('bag/inventory', 'Bag::inventory');
|
||||||
|
$routes->get('bag/inventory/export', 'Bag::inventoryExport');
|
||||||
|
$routes->get('bag/inventory/inspection-select', 'Bag::inspectionSelect');
|
||||||
|
$routes->get('bag/inventory/inspection-work', 'Bag::inspectionWork');
|
||||||
|
$routes->post('bag/inventory/inspection-run', 'Bag::inspectionRun');
|
||||||
|
$routes->post('bag/inventory/inspection-select/save', 'Bag::inspectionSelectSave');
|
||||||
|
$routes->post('bag/inventory/inspection-select/confirm', 'Bag::inspectionSelectConfirm');
|
||||||
|
$routes->get('bag/inventory/inspection/(:num)', 'Bag::inspectionDetail/$1');
|
||||||
|
$routes->post('bag/inventory/inspection/(:num)/save', 'Bag::inspectionSave/$1');
|
||||||
|
$routes->post('bag/inventory/inspection/(:num)/apply', 'Bag::inspectionApply/$1');
|
||||||
$routes->get('bag/sales', 'Bag::sales');
|
$routes->get('bag/sales', 'Bag::sales');
|
||||||
$routes->get('bag/sales-stats', 'Bag::salesStats');
|
$routes->get('bag/sales-stats', 'Bag::salesStats');
|
||||||
$routes->get('bag/flow', 'Bag::flow');
|
$routes->get('bag/flow', 'Bag::flow');
|
||||||
@@ -41,11 +51,24 @@ $routes->post('bag/inventory/adjust', 'Bag::inventoryAdjustStore');
|
|||||||
$routes->get('bag/issue/create', 'Bag::issueCreate');
|
$routes->get('bag/issue/create', 'Bag::issueCreate');
|
||||||
$routes->post('bag/issue/store', 'Bag::issueStore');
|
$routes->post('bag/issue/store', 'Bag::issueStore');
|
||||||
$routes->post('bag/issue/cancel/(:num)', 'Bag::issueCancel/$1');
|
$routes->post('bag/issue/cancel/(:num)', 'Bag::issueCancel/$1');
|
||||||
|
$routes->post('bag/issue/cancel-save', 'Bag::issueCancelSave');
|
||||||
$routes->get('bag/order/create', 'Bag::orderCreate');
|
$routes->get('bag/order/create', 'Bag::orderCreate');
|
||||||
|
$routes->get('bag/order/change', 'Bag::orderChange');
|
||||||
|
$routes->get('bag/order/revise/(:num)', 'Bag::orderRevise/$1');
|
||||||
|
$routes->get('bag/order/lot-seed', 'Bag::orderLotSeed');
|
||||||
|
$routes->post('bag/order/lot-seed/generate', 'Bag::orderLotSeedGenerate');
|
||||||
$routes->post('bag/order/store', 'Bag::orderStore');
|
$routes->post('bag/order/store', 'Bag::orderStore');
|
||||||
$routes->post('bag/order/cancel/(:num)', 'Bag::orderCancel/$1');
|
$routes->post('bag/order/cancel/(:num)', 'Bag::orderCancel/$1');
|
||||||
|
$routes->post('bag/order/delete', 'Bag::orderDeletePost');
|
||||||
|
$routes->post('bag/order/delete/(:num)', 'Bag::orderDelete/$1');
|
||||||
$routes->get('bag/receiving/create', 'Bag::receivingCreate');
|
$routes->get('bag/receiving/create', 'Bag::receivingCreate');
|
||||||
$routes->post('bag/receiving/store', 'Bag::receivingStore');
|
$routes->post('bag/receiving/store', 'Bag::receivingStore');
|
||||||
|
$routes->get('bag/receiving/scanner', 'Bag::receivingScanner');
|
||||||
|
$routes->post('bag/receiving/scanner/store', 'Bag::receivingScannerStore');
|
||||||
|
$routes->get('bag/receiving/batch', 'Bag::receivingBatch');
|
||||||
|
$routes->post('bag/receiving/batch/store', 'Bag::receivingBatchStore');
|
||||||
|
$routes->get('bag/receiving/status', 'Bag::receivingStatus');
|
||||||
|
$routes->get('bag/receiving/status/export', 'Bag::receivingStatusExport');
|
||||||
$routes->get('bag/sale/create', 'Bag::saleCreate');
|
$routes->get('bag/sale/create', 'Bag::saleCreate');
|
||||||
$routes->post('bag/sale/store', 'Bag::saleStore');
|
$routes->post('bag/sale/store', 'Bag::saleStore');
|
||||||
$routes->get('bag/shop-order/create', 'Bag::shopOrderCreate');
|
$routes->get('bag/shop-order/create', 'Bag::shopOrderCreate');
|
||||||
@@ -108,6 +131,7 @@ $routes->group('bag', ['filter' => 'adminAuth'], static function ($routes): void
|
|||||||
$routes->get('bag-orders/export', 'Admin\BagOrder::export');
|
$routes->get('bag-orders/export', 'Admin\BagOrder::export');
|
||||||
$routes->get('bag-orders', 'Admin\BagOrder::index');
|
$routes->get('bag-orders', 'Admin\BagOrder::index');
|
||||||
$routes->get('bag-orders/create', 'Admin\BagOrder::create');
|
$routes->get('bag-orders/create', 'Admin\BagOrder::create');
|
||||||
|
$routes->get('bag-orders/revise/(:num)', 'Admin\BagOrder::revise/$1');
|
||||||
$routes->post('bag-orders/store', 'Admin\BagOrder::store');
|
$routes->post('bag-orders/store', 'Admin\BagOrder::store');
|
||||||
$routes->get('bag-orders/detail/(:num)', 'Admin\BagOrder::detail/$1');
|
$routes->get('bag-orders/detail/(:num)', 'Admin\BagOrder::detail/$1');
|
||||||
$routes->post('bag-orders/cancel/(:num)', 'Admin\BagOrder::cancel/$1');
|
$routes->post('bag-orders/cancel/(:num)', 'Admin\BagOrder::cancel/$1');
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class BagReceiving extends BaseController
|
|||||||
return redirect()->to(mgmt_url('bag-receivings'))->with('error', '지자체를 선택해 주세요.');
|
return redirect()->to(mgmt_url('bag-receivings'))->with('error', '지자체를 선택해 주세요.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$orders = model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx)->where('bo_status', 'normal')->orderBy('bo_order_date', 'DESC')->findAll();
|
$orders = model(BagOrderModel::class)->where('bo_lg_idx', $lgIdx)->whereLatestHead($lgIdx)->where('bo_status', 'normal')->orderBy('bo_order_date', 'DESC')->findAll();
|
||||||
|
|
||||||
return $this->renderWorkPage('입고 처리', 'admin/bag_receiving/create', compact('orders'));
|
return $this->renderWorkPage('입고 처리', 'admin/bag_receiving/create', compact('orders'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
helper('admin');
|
helper('admin');
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
@@ -93,6 +96,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function list()
|
public function list()
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus(true)) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
return $this->response->setJSON(['status' => 0, 'msg' => '지자체를 선택하세요.']);
|
return $this->response->setJSON(['status' => 0, 'msg' => '지자체를 선택하세요.']);
|
||||||
@@ -112,6 +118,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function store()
|
public function store()
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
return redirect()->to(base_url('admin/select-local-government'))
|
return redirect()->to(base_url('admin/select-local-government'))
|
||||||
@@ -144,6 +153,7 @@ class Menu extends BaseController
|
|||||||
if ($mmPidx > 0) {
|
if ($mmPidx > 0) {
|
||||||
$this->menuModel->updateCnode($mmPidx, 1);
|
$this->menuModel->updateCnode($mmPidx, 1);
|
||||||
}
|
}
|
||||||
|
$this->menuModel->syncTypeToAllLgs($mtIdx, $lgIdx);
|
||||||
return redirect()->back()->with('success', '메뉴가 등록되었습니다.');
|
return redirect()->back()->with('success', '메뉴가 등록되었습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +162,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function update(int $id)
|
public function update(int $id)
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
return redirect()->to(base_url('admin/select-local-government'))
|
return redirect()->to(base_url('admin/select-local-government'))
|
||||||
@@ -171,6 +184,7 @@ class Menu extends BaseController
|
|||||||
'mm_is_view' => $this->request->getPost('mm_is_view') ? 'Y' : 'N',
|
'mm_is_view' => $this->request->getPost('mm_is_view') ? 'Y' : 'N',
|
||||||
];
|
];
|
||||||
$this->menuModel->update($id, $data);
|
$this->menuModel->update($id, $data);
|
||||||
|
$this->menuModel->syncTypeToAllLgs((int) $row->mt_idx, $lgIdx);
|
||||||
return redirect()->back()->with('success', '메뉴가 수정되었습니다.');
|
return redirect()->back()->with('success', '메뉴가 수정되었습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +193,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function delete(int $id)
|
public function delete(int $id)
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
return redirect()->to(base_url('admin/select-local-government'))
|
return redirect()->to(base_url('admin/select-local-government'))
|
||||||
@@ -190,6 +207,7 @@ class Menu extends BaseController
|
|||||||
}
|
}
|
||||||
$result = $this->menuModel->deleteSafe($id);
|
$result = $this->menuModel->deleteSafe($id);
|
||||||
if ($result['ok']) {
|
if ($result['ok']) {
|
||||||
|
$this->menuModel->syncTypeToAllLgs((int) $row->mt_idx, $lgIdx);
|
||||||
return redirect()->back()->with('success', '메뉴가 삭제되었습니다.');
|
return redirect()->back()->with('success', '메뉴가 삭제되었습니다.');
|
||||||
}
|
}
|
||||||
return redirect()->back()->with('error', $result['msg']);
|
return redirect()->back()->with('error', $result['msg']);
|
||||||
@@ -200,6 +218,9 @@ class Menu extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function move()
|
public function move()
|
||||||
{
|
{
|
||||||
|
if ($deny = $this->denyUnlessLevel4Plus()) {
|
||||||
|
return $deny;
|
||||||
|
}
|
||||||
$lgIdx = admin_effective_lg_idx();
|
$lgIdx = admin_effective_lg_idx();
|
||||||
if ($lgIdx === null) {
|
if ($lgIdx === null) {
|
||||||
return redirect()->to(base_url('admin/select-local-government'))
|
return redirect()->to(base_url('admin/select-local-government'))
|
||||||
@@ -209,7 +230,12 @@ class Menu extends BaseController
|
|||||||
if (! is_array($ids) || empty($ids)) {
|
if (! is_array($ids) || empty($ids)) {
|
||||||
return redirect()->back()->with('error', '순서를 적용할 메뉴가 없습니다.');
|
return redirect()->back()->with('error', '순서를 적용할 메뉴가 없습니다.');
|
||||||
}
|
}
|
||||||
|
$firstId = (int) ($ids[0] ?? 0);
|
||||||
|
$firstRow = $firstId > 0 ? $this->menuModel->find($firstId) : null;
|
||||||
$this->menuModel->setOrder($ids, $lgIdx);
|
$this->menuModel->setOrder($ids, $lgIdx);
|
||||||
|
if ($firstRow && (int) $firstRow->lg_idx === $lgIdx) {
|
||||||
|
$this->menuModel->syncTypeToAllLgs((int) $firstRow->mt_idx, $lgIdx);
|
||||||
|
}
|
||||||
return redirect()->back()->with('success', '순서가 적용되었습니다.');
|
return redirect()->back()->with('success', '순서가 적용되었습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,4 +292,27 @@ class Menu extends BaseController
|
|||||||
|
|
||||||
return (int) $types[0]->mt_idx;
|
return (int) $types[0]->mt_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴 관리는 레벨4 이상(슈퍼/본부 관리자)만 허용.
|
||||||
|
*
|
||||||
|
* @return \CodeIgniter\HTTP\RedirectResponse|\CodeIgniter\HTTP\ResponseInterface|null
|
||||||
|
*/
|
||||||
|
private function denyUnlessLevel4Plus(bool $json = false)
|
||||||
|
{
|
||||||
|
$level = (int) session()->get('mb_level');
|
||||||
|
if (Roles::isSuperAdminEquivalent($level)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($json) {
|
||||||
|
return $this->response->setJSON([
|
||||||
|
'status' => 0,
|
||||||
|
'msg' => '메뉴 관리는 레벨4 이상만 접근할 수 있습니다.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to(base_url('admin/dashboard'))
|
||||||
|
->with('error', '메뉴 관리는 레벨4 이상만 접근할 수 있습니다.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,26 @@ class BagOrderModel extends Model
|
|||||||
protected $primaryKey = 'bo_idx';
|
protected $primaryKey = 'bo_idx';
|
||||||
protected $returnType = 'object';
|
protected $returnType = 'object';
|
||||||
protected $useTimestamps = false;
|
protected $useTimestamps = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 동일 발주 UUID에 대해 bo_version이 최대인 행만 (수정으로 생긴 이전 버전 행은 목록·이력에서 제외).
|
||||||
|
* DB에는 버전별 행이 그대로 남고, 조회 시에만 필터한다.
|
||||||
|
*/
|
||||||
|
public function whereLatestHead(int $lgIdx): self
|
||||||
|
{
|
||||||
|
$lg = (int) $lgIdx;
|
||||||
|
|
||||||
|
return $this->where(
|
||||||
|
"(bo_uuid, bo_version) IN (SELECT bo_uuid, MAX(bo_version) FROM {$this->table} WHERE bo_lg_idx = {$lg} GROUP BY bo_uuid)",
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
protected $allowedFields = [
|
protected $allowedFields = [
|
||||||
'bo_uuid', 'bo_version', 'bo_lg_idx', 'bo_gugun_code', 'bo_dong_code',
|
'bo_uuid', 'bo_version', 'bo_lg_idx', 'bo_gugun_code', 'bo_dong_code',
|
||||||
'bo_company_idx', 'bo_agency_idx', 'bo_fee_rate', 'bo_order_date',
|
'bo_company_idx', 'bo_agency_idx', 'bo_fee_rate', 'bo_order_date',
|
||||||
|
'bo_bag_types', 'bo_unit_prices', 'bo_qty_boxes',
|
||||||
'bo_lot_no', 'bo_hash', 'bo_status', 'bo_orderer_idx',
|
'bo_lot_no', 'bo_hash', 'bo_status', 'bo_orderer_idx',
|
||||||
'bo_regdate', 'bo_moddate',
|
'bo_regdate', 'bo_moddate',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -189,4 +189,67 @@ class MenuModel extends Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 특정 메뉴 타입(mt_idx)을 source 지자체 기준으로 모든 지자체에 재배포.
|
||||||
|
* 기존 대상 지자체의 해당 타입 메뉴는 삭제 후 source 구조로 재생성한다.
|
||||||
|
*/
|
||||||
|
public function syncTypeToAllLgs(int $mtIdx, int $sourceLg): void
|
||||||
|
{
|
||||||
|
if ($mtIdx <= 0 || $sourceLg <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$source = $this->where('mt_idx', $mtIdx)
|
||||||
|
->where('lg_idx', $sourceLg)
|
||||||
|
->orderBy('mm_dep', 'ASC')
|
||||||
|
->orderBy('mm_num', 'ASC')
|
||||||
|
->findAll();
|
||||||
|
if (empty($source)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lgRows = $this->db->table('local_government')
|
||||||
|
->select('lg_idx')
|
||||||
|
->orderBy('lg_idx', 'ASC')
|
||||||
|
->get()
|
||||||
|
->getResultArray();
|
||||||
|
|
||||||
|
foreach ($lgRows as $lgRow) {
|
||||||
|
$destLg = (int) ($lgRow['lg_idx'] ?? 0);
|
||||||
|
if ($destLg <= 0 || $destLg === $sourceLg) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->transStart();
|
||||||
|
$this->where('mt_idx', $mtIdx)
|
||||||
|
->where('lg_idx', $destLg)
|
||||||
|
->delete();
|
||||||
|
|
||||||
|
$idMap = [];
|
||||||
|
foreach ($source as $row) {
|
||||||
|
$oldId = (int) ($row->mm_idx ?? 0);
|
||||||
|
$oldP = (int) ($row->mm_pidx ?? 0);
|
||||||
|
$newPidx = 0;
|
||||||
|
if ($oldP > 0 && isset($idMap[$oldP])) {
|
||||||
|
$newPidx = (int) $idMap[$oldP];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->insert([
|
||||||
|
'mt_idx' => $mtIdx,
|
||||||
|
'lg_idx' => $destLg,
|
||||||
|
'mm_name' => (string) ($row->mm_name ?? ''),
|
||||||
|
'mm_link' => (string) ($row->mm_link ?? ''),
|
||||||
|
'mm_pidx' => $newPidx,
|
||||||
|
'mm_dep' => (int) ($row->mm_dep ?? 0),
|
||||||
|
'mm_num' => (int) ($row->mm_num ?? 0),
|
||||||
|
'mm_cnode' => (int) ($row->mm_cnode ?? 0),
|
||||||
|
'mm_level' => (string) ($row->mm_level ?? ''),
|
||||||
|
'mm_is_view' => (string) ($row->mm_is_view ?? 'Y'),
|
||||||
|
]);
|
||||||
|
$idMap[$oldId] = (int) $this->getInsertID();
|
||||||
|
}
|
||||||
|
$this->db->transComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +1,443 @@
|
|||||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||||
<span class="text-sm font-bold text-gray-700">발주 등록</span>
|
<span class="text-sm font-bold text-gray-700">발주 등록</span>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-4xl">
|
|
||||||
<form action="<?= mgmt_url('bag-orders/store') ?>" method="POST" class="space-y-4">
|
|
||||||
<?= csrf_field() ?>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<?php
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">발주일 <span class="text-red-500">*</span></label>
|
$oldBagCodes = old('item_bag_code');
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-44" name="bo_order_date" type="date" value="<?= esc(old('bo_order_date', date('Y-m-d'))) ?>" required/>
|
$oldQtyBoxes = old('item_qty_box');
|
||||||
|
$oldQtySheets = old('item_qty_sheet');
|
||||||
|
$oldBagCodes = is_array($oldBagCodes) ? $oldBagCodes : [];
|
||||||
|
$oldQtyBoxes = is_array($oldQtyBoxes) ? $oldQtyBoxes : [];
|
||||||
|
$oldQtySheets = is_array($oldQtySheets) ? $oldQtySheets : [];
|
||||||
|
$defaultOrderDate = old('bo_order_date', date('Y-m-d'));
|
||||||
|
$defaultOrderMonth = old('bo_order_month_ui', substr($defaultOrderDate, 0, 7));
|
||||||
|
|
||||||
|
$bagMeta = [];
|
||||||
|
foreach (($bagReferenceRows ?? []) as $row) {
|
||||||
|
$bagMeta[$row['code']] = [
|
||||||
|
'name' => $row['name'],
|
||||||
|
'orderPrice' => (float) $row['orderPrice'],
|
||||||
|
'boxPerPack' => (int) $row['boxPerPack'],
|
||||||
|
'packPerSheet' => (int) $row['packPerSheet'],
|
||||||
|
'totalPerBox' => max(1, (int) $row['totalPerBox']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$initialSelectedItems = [];
|
||||||
|
$maxOldCount = max(count($oldBagCodes), count($oldQtySheets), count($oldQtyBoxes));
|
||||||
|
for ($i = 0; $i < $maxOldCount; $i++) {
|
||||||
|
$code = trim((string) ($oldBagCodes[$i] ?? ''));
|
||||||
|
if ($code === '' || ! isset($bagMeta[$code])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fallbackQtyBox = (int) ($oldQtyBoxes[$i] ?? 0);
|
||||||
|
$rawQtySheet = (int) ($oldQtySheets[$i] ?? 0);
|
||||||
|
$fallbackTotalPerBox = (int) ($bagMeta[$code]['totalPerBox'] ?? 1);
|
||||||
|
if ($fallbackQtyBox <= 0 && $rawQtySheet > 0) {
|
||||||
|
$fallbackQtyBox = intdiv($rawQtySheet, max(1, $fallbackTotalPerBox));
|
||||||
|
}
|
||||||
|
$initialSelectedItems[] = [
|
||||||
|
'code' => $code,
|
||||||
|
'qtyBox' => max(0, $fallbackQtyBox),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<form action="<?= mgmt_url('bag-orders/store') ?>" method="POST" class="mt-2 space-y-2">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 bg-white p-2">
|
||||||
|
<div class="flex flex-wrap items-center gap-4 text-sm">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label for="bo_order_month_ui" class="font-bold text-gray-700">발주월</label>
|
||||||
|
<input id="bo_order_month_ui" name="bo_order_month_ui" type="month" value="<?= esc($defaultOrderMonth) ?>" class="border border-gray-300 rounded px-2 py-1" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label for="bo_order_date" class="font-bold text-gray-700">발주일 <span class="text-red-500">*</span></label>
|
||||||
|
<input id="bo_order_date" name="bo_order_date" type="date" value="<?= esc($defaultOrderDate) ?>" required class="border border-gray-300 rounded px-2 py-1" />
|
||||||
|
</div>
|
||||||
|
<p class="text-blue-600 font-bold">※ 발주수량은 박스단위로 입력해 주세요. (발주일은 미래일도 선택 가능)</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">수수료율</label>
|
<section class="xl:col-span-5 border border-gray-300 bg-white">
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32 text-right" name="bo_fee_rate" type="number" step="0.01" value="<?= esc(old('bo_fee_rate', '0')) ?>"/>
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
|
||||||
<span class="text-sm text-gray-500">%</span>
|
<div class="overflow-auto max-h-[410px]">
|
||||||
</div>
|
<table class="w-full data-table text-sm">
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">제작업체</label>
|
|
||||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="bo_company_idx">
|
|
||||||
<option value="">선택</option>
|
|
||||||
<?php foreach ($companies as $cp): ?>
|
|
||||||
<option value="<?= esc($cp->cp_idx) ?>" <?= (int) old('bo_company_idx') === (int) $cp->cp_idx ? 'selected' : '' ?>>
|
|
||||||
<?= esc($cp->cp_name) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">입고처</label>
|
|
||||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="bo_agency_idx">
|
|
||||||
<option value="">선택</option>
|
|
||||||
<?php foreach ($agencies as $ag): ?>
|
|
||||||
<option value="<?= esc($ag->sa_idx) ?>" <?= (int) old('bo_agency_idx') === (int) $ag->sa_idx ? 'selected' : '' ?>>
|
|
||||||
[<?= esc($ag->sa_kind ?? '') ?>] <?= esc($ag->sa_code ?? '') ?> — <?= esc($ag->sa_name) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4">
|
|
||||||
<label class="block text-sm font-bold text-gray-700 mb-2">발주 품목</label>
|
|
||||||
<div class="border border-gray-300 overflow-auto">
|
|
||||||
<table class="w-full data-table">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-16">순번</th>
|
<th class="w-28">발주일</th>
|
||||||
<th>봉투</th>
|
<th>제작업체</th>
|
||||||
<th class="w-32">박스수</th>
|
<th>입고처</th>
|
||||||
|
<th class="w-16">상태</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php for ($i = 0; $i < 3; $i++): ?>
|
<?php foreach (($recentOrders ?? []) as $history): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><?= $i + 1 ?></td>
|
<td class="text-center"><?= esc((string) $history->bo_order_date) ?></td>
|
||||||
<td>
|
<td class="text-left pl-2"><?= esc((string) ($companyMap[(int) $history->bo_company_idx] ?? '-')) ?></td>
|
||||||
<select class="border border-gray-300 rounded px-2 py-1 text-sm w-full" name="item_bag_code[]">
|
<td class="text-left pl-2"><?= esc((string) ($agencyMap[(int) $history->bo_agency_idx] ?? '-')) ?></td>
|
||||||
<option value="">선택</option>
|
<td class="text-center"><?= esc((string) ($statusMap[(string) $history->bo_status] ?? $history->bo_status)) ?></td>
|
||||||
<?php foreach ($bagCodes as $cd): ?>
|
|
||||||
<option value="<?= esc($cd->cd_code) ?>">
|
|
||||||
<?= esc($cd->cd_code) ?> — <?= esc($cd->cd_name) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input class="border border-gray-300 rounded px-2 py-1 text-sm w-full text-right" name="item_qty_box[]" type="number" min="0" value="0"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endfor; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($recentOrders)): ?>
|
||||||
|
<tr><td colspan="4" class="text-center text-gray-400 py-4">발주 이력이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<div class="flex gap-2 pt-2">
|
<section class="xl:col-span-7 border border-gray-300 bg-white">
|
||||||
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 Form</div>
|
||||||
<a href="<?= mgmt_url('bag-orders') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
|
<div class="p-2 space-y-2">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_fee_rate" class="w-20 font-bold text-gray-700">수수료</label>
|
||||||
|
<input id="bo_fee_rate" name="bo_fee_rate" type="number" step="0.01" value="<?= esc(old('bo_fee_rate', '0')) ?>" class="border border-gray-300 rounded px-2 py-1 w-24 text-right" />
|
||||||
|
<span>%</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_association_idx" class="w-20 font-bold text-gray-700">협회</label>
|
||||||
|
<select id="bo_association_idx" name="bo_association_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($associations ?? []) as $association): ?>
|
||||||
|
<option value="<?= esc((string) $association->cp_idx) ?>" <?= (int) old('bo_association_idx') === (int) $association->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $association->cp_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_company_idx" class="w-20 font-bold text-gray-700">제작업체</label>
|
||||||
|
<select id="bo_company_idx" name="bo_company_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($companies ?? []) as $company): ?>
|
||||||
|
<option value="<?= esc((string) $company->cp_idx) ?>" <?= (int) old('bo_company_idx') === (int) $company->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $company->cp_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_agency_idx" class="w-20 font-bold text-gray-700">입고처</label>
|
||||||
|
<select id="bo_agency_idx" name="bo_agency_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($agencies ?? []) as $agency): ?>
|
||||||
|
<option value="<?= esc((string) $agency->sa_idx) ?>" <?= (int) old('bo_agency_idx') === (int) $agency->sa_idx ? 'selected' : '' ?>>
|
||||||
|
[<?= esc((string) ($agency->sa_kind ?? '')) ?>] <?= esc((string) ($agency->sa_code ?? '')) ?> — <?= esc((string) $agency->sa_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 overflow-auto">
|
||||||
|
<table class="w-full data-table text-sm order-input-table" id="order-item-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">번호</th>
|
||||||
|
<th class="w-16">선택</th>
|
||||||
|
<th>품명</th>
|
||||||
|
<th class="w-28">수량(BOX)</th>
|
||||||
|
<th class="w-24">단가</th>
|
||||||
|
<th class="w-24">환산수량</th>
|
||||||
|
<th class="w-28">금액</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="selected-order-items-body"></tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" class="text-center">계</th>
|
||||||
|
<th class="text-right pr-2" id="sum-box-qty">0</th>
|
||||||
|
<th></th>
|
||||||
|
<th class="text-right pr-2" id="sum-sheet-qty">0</th>
|
||||||
|
<th class="text-right pr-2" id="sum-amount">0</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2 pt-1">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">발주</button>
|
||||||
|
<a href="<?= mgmt_url('bag-orders') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 등록 종류</div>
|
||||||
|
<p class="text-xs text-gray-600 px-2 py-1">아래 목록에서 봉투를 선택하면 발주 품목에 추가됩니다. (개수 제한 없음)</p>
|
||||||
|
<div class="overflow-auto">
|
||||||
|
<table class="w-full data-table text-sm order-reference-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">번호</th>
|
||||||
|
<th class="w-20">선택</th>
|
||||||
|
<th>봉투 종류</th>
|
||||||
|
<th class="w-24">발주단가</th>
|
||||||
|
<th class="w-24">Box당 팩</th>
|
||||||
|
<th class="w-24">팩당 낱장</th>
|
||||||
|
<th class="w-28">1박스 총 낱장</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($bagReferenceRows ?? []) as $idx => $row): ?>
|
||||||
|
<tr data-reference-row data-code="<?= esc((string) $row['code']) ?>" class="cursor-pointer">
|
||||||
|
<td class="text-center"><?= $idx + 1 ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="js-toggle-bag border border-gray-300 rounded px-2 py-0.5 text-xs hover:bg-gray-100" data-code="<?= esc((string) $row['code']) ?>">선택</button>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) $row['name']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((float) $row['orderPrice'], 2) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['boxPerPack']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['packPerSheet']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['totalPerBox']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($bagReferenceRows)): ?>
|
||||||
|
<tr><td colspan="7" class="text-center text-gray-400 py-4">표시할 봉투 기준 데이터가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</section>
|
||||||
</div>
|
</form>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.order-input-table tbody tr,
|
||||||
|
.order-reference-table tbody tr {
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
.order-input-table tbody td,
|
||||||
|
.order-reference-table tbody td {
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const bagMeta = <?= json_encode($bagMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const initialSelectedItems = <?= json_encode($initialSelectedItems, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const selectedBody = document.getElementById('selected-order-items-body');
|
||||||
|
const referenceRows = Array.from(document.querySelectorAll('[data-reference-row]'));
|
||||||
|
const sumBoxQtyEl = document.getElementById('sum-box-qty');
|
||||||
|
const sumSheetQtyEl = document.getElementById('sum-sheet-qty');
|
||||||
|
const sumAmountEl = document.getElementById('sum-amount');
|
||||||
|
const monthInput = document.getElementById('bo_order_month_ui');
|
||||||
|
const orderDateInput = document.getElementById('bo_order_date');
|
||||||
|
const orderForm = document.querySelector('form[action*="bag-orders/store"]');
|
||||||
|
const selectedItems = new Map();
|
||||||
|
let activeCode = null;
|
||||||
|
|
||||||
|
const formatNumber = (value) => new Intl.NumberFormat('ko-KR').format(Number.isFinite(value) ? value : 0);
|
||||||
|
const escapeHtml = (value) => String(value ?? '')
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
|
.replaceAll('"', '"')
|
||||||
|
.replaceAll("'", ''');
|
||||||
|
|
||||||
|
const syncMonthFromDate = () => {
|
||||||
|
if (!orderDateInput || !monthInput || !orderDateInput.value) return;
|
||||||
|
monthInput.value = orderDateInput.value.substring(0, 7);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncDateFromMonth = () => {
|
||||||
|
if (!orderDateInput || !monthInput || !monthInput.value) return;
|
||||||
|
const parts = monthInput.value.split('-');
|
||||||
|
if (parts.length !== 2) return;
|
||||||
|
const year = Number(parts[0]);
|
||||||
|
const month = Number(parts[1]);
|
||||||
|
if (!Number.isFinite(year) || !Number.isFinite(month)) return;
|
||||||
|
|
||||||
|
const currentDay = orderDateInput.value ? Number(orderDateInput.value.split('-')[2]) : 1;
|
||||||
|
const lastDay = new Date(year, month, 0).getDate();
|
||||||
|
const day = String(Math.min(Math.max(currentDay, 1), lastDay)).padStart(2, '0');
|
||||||
|
orderDateInput.value = `${String(year)}-${String(month).padStart(2, '0')}-${day}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateReferenceSelectionUi = () => {
|
||||||
|
referenceRows.forEach((row) => {
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
const button = row.querySelector('.js-toggle-bag');
|
||||||
|
const isSelected = selectedItems.has(code);
|
||||||
|
row.classList.toggle('bg-blue-50', isSelected);
|
||||||
|
if (button) {
|
||||||
|
button.textContent = isSelected ? '선택됨' : '선택';
|
||||||
|
button.classList.toggle('bg-blue-600', isSelected);
|
||||||
|
button.classList.toggle('text-white', isSelected);
|
||||||
|
button.classList.toggle('border-blue-600', isSelected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTotals = () => {
|
||||||
|
let sumBoxQty = 0;
|
||||||
|
let sumSheetQty = 0;
|
||||||
|
let sumAmount = 0;
|
||||||
|
|
||||||
|
selectedBody.querySelectorAll('tr[data-item-row]').forEach((row) => {
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
const qtyBox = Math.max(0, parseInt(qtyInput?.value || '0', 10));
|
||||||
|
const meta = bagMeta[code] || { orderPrice: 0, totalPerBox: 1 };
|
||||||
|
const unitPrice = Number(meta.orderPrice || 0);
|
||||||
|
const totalPerBox = Math.max(1, Number(meta.totalPerBox || 1));
|
||||||
|
const qtySheet = qtyBox * totalPerBox;
|
||||||
|
const amount = qtySheet * unitPrice;
|
||||||
|
|
||||||
|
const unitPriceEl = row.querySelector('.item-unit-price');
|
||||||
|
const qtySheetEl = row.querySelector('.item-qty-sheet');
|
||||||
|
const sheetHelpEl = row.querySelector('.item-sheet-help');
|
||||||
|
const amountEl = row.querySelector('.item-amount');
|
||||||
|
if (unitPriceEl) unitPriceEl.textContent = formatNumber(unitPrice);
|
||||||
|
if (qtySheetEl) qtySheetEl.textContent = formatNumber(qtySheet);
|
||||||
|
if (sheetHelpEl) sheetHelpEl.textContent = `낱장 ${formatNumber(qtySheet)}장`;
|
||||||
|
if (amountEl) amountEl.textContent = formatNumber(amount);
|
||||||
|
|
||||||
|
selectedItems.set(code, { qtyBox });
|
||||||
|
sumBoxQty += qtyBox;
|
||||||
|
sumSheetQty += qtySheet;
|
||||||
|
sumAmount += amount;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sumBoxQtyEl) sumBoxQtyEl.textContent = formatNumber(sumBoxQty);
|
||||||
|
if (sumSheetQtyEl) sumSheetQtyEl.textContent = formatNumber(sumSheetQty);
|
||||||
|
if (sumAmountEl) sumAmountEl.textContent = formatNumber(sumAmount);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActiveRow = (code) => {
|
||||||
|
activeCode = code || null;
|
||||||
|
selectedBody.querySelectorAll('tr[data-item-row]').forEach((row) => {
|
||||||
|
row.classList.toggle('bg-amber-50', row.dataset.code === activeCode);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSelectedRows = () => {
|
||||||
|
const codes = Object.keys(bagMeta).filter((code) => selectedItems.has(code));
|
||||||
|
if (codes.length === 0) {
|
||||||
|
selectedBody.innerHTML = '<tr><td colspan="7" class="text-center text-gray-400 py-4">아래 "발주 등록 종류"에서 봉투를 선택해 주세요.</td></tr>';
|
||||||
|
setActiveRow(null);
|
||||||
|
updateTotals();
|
||||||
|
updateReferenceSelectionUi();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedBody.innerHTML = codes.map((code, idx) => {
|
||||||
|
const meta = bagMeta[code];
|
||||||
|
const qtyBox = Math.max(0, parseInt(String(selectedItems.get(code)?.qtyBox ?? 0), 10));
|
||||||
|
const name = meta?.name || code;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr data-item-row data-code="${escapeHtml(code)}" class="cursor-pointer">
|
||||||
|
<td class="text-center">${idx + 1}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="js-remove-selected text-xs text-red-600 hover:underline" data-code="${escapeHtml(code)}">해제</button>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2">
|
||||||
|
${escapeHtml(name)}
|
||||||
|
<input type="hidden" name="item_bag_code[]" value="${escapeHtml(code)}" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input name="item_qty_box[]" type="number" min="0" step="1" value="${qtyBox}" class="item-qty-box border border-gray-300 rounded px-2 py-1 text-sm w-full text-right leading-tight" />
|
||||||
|
<p class="text-[11px] text-gray-500 mt-1 item-sheet-help">낱장 0장</p>
|
||||||
|
</td>
|
||||||
|
<td class="text-right pr-2 item-unit-price">0</td>
|
||||||
|
<td class="text-right pr-2 item-qty-sheet">0</td>
|
||||||
|
<td class="text-right pr-2 item-amount">0</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
if (!activeCode || !selectedItems.has(activeCode)) {
|
||||||
|
activeCode = codes[0];
|
||||||
|
}
|
||||||
|
setActiveRow(activeCode);
|
||||||
|
updateTotals();
|
||||||
|
updateReferenceSelectionUi();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelection = (code) => {
|
||||||
|
if (!code || !bagMeta[code]) return;
|
||||||
|
if (selectedItems.has(code)) {
|
||||||
|
selectedItems.delete(code);
|
||||||
|
if (activeCode === code) activeCode = null;
|
||||||
|
} else {
|
||||||
|
selectedItems.set(code, { qtyBox: 0 });
|
||||||
|
activeCode = code;
|
||||||
|
}
|
||||||
|
renderSelectedRows();
|
||||||
|
};
|
||||||
|
|
||||||
|
initialSelectedItems.forEach((item) => {
|
||||||
|
if (!item || !item.code || !bagMeta[item.code]) return;
|
||||||
|
selectedItems.set(item.code, { qtyBox: Math.max(0, parseInt(String(item.qtyBox ?? 0), 10)) });
|
||||||
|
activeCode = item.code;
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedBody.addEventListener('click', (event) => {
|
||||||
|
const removeButton = event.target.closest('.js-remove-selected');
|
||||||
|
if (removeButton) {
|
||||||
|
toggleSelection(removeButton.dataset.code || '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = event.target.closest('tr[data-item-row]');
|
||||||
|
if (!row) return;
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
setActiveRow(code);
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
if (qtyInput) qtyInput.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedBody.addEventListener('input', (event) => {
|
||||||
|
const qtyInput = event.target.closest('.item-qty-box');
|
||||||
|
if (!qtyInput) return;
|
||||||
|
const row = qtyInput.closest('tr[data-item-row]');
|
||||||
|
if (!row) return;
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
selectedItems.set(code, { qtyBox: Math.max(0, parseInt(qtyInput.value || '0', 10)) });
|
||||||
|
updateTotals();
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceRows.forEach((row) => {
|
||||||
|
row.addEventListener('click', (event) => {
|
||||||
|
const button = event.target.closest('.js-toggle-bag');
|
||||||
|
if (button) {
|
||||||
|
toggleSelection(button.dataset.code || '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.target.closest('td')) {
|
||||||
|
toggleSelection(row.dataset.code || '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (monthInput) monthInput.addEventListener('change', () => { syncDateFromMonth(); updateTotals(); });
|
||||||
|
if (orderDateInput) orderDateInput.addEventListener('change', syncMonthFromDate);
|
||||||
|
|
||||||
|
if (orderForm) {
|
||||||
|
orderForm.addEventListener('submit', (event) => {
|
||||||
|
const hasValidItem = Array.from(selectedBody.querySelectorAll('tr[data-item-row]')).some((row) => {
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
return Math.max(0, parseInt(qtyInput?.value || '0', 10)) > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasValidItem) {
|
||||||
|
event.preventDefault();
|
||||||
|
alert('봉투를 선택하고 수량을 1 이상 입력해 주세요.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
syncMonthFromDate();
|
||||||
|
renderSelectedRows();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|||||||
@@ -1,81 +1,200 @@
|
|||||||
<?= view('components/print_header', ['printTitle' => '발주 현황']) ?>
|
<?php
|
||||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
// 발주기간: native month 입력은 로케일에 따라 Jan 등 영문 표기될 수 있어 YYYY-MM select + 한글 라벨 사용
|
||||||
|
$bagOrderYmChoices = [];
|
||||||
|
$bagOrderYmCenterY = (int) date('Y');
|
||||||
|
for ($by = $bagOrderYmCenterY - 4; $by <= $bagOrderYmCenterY + 2; $by++) {
|
||||||
|
for ($bm = 1; $bm <= 12; $bm++) {
|
||||||
|
$bagOrderYmChoices[] = sprintf('%04d-%02d', $by, $bm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach ([(string) ($startMonth ?? ''), (string) ($endMonth ?? '')] as $ymExtra) {
|
||||||
|
if (preg_match('/^\d{4}-\d{2}$/', $ymExtra) && ! in_array($ymExtra, $bagOrderYmChoices, true)) {
|
||||||
|
$bagOrderYmChoices[] = $ymExtra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort($bagOrderYmChoices);
|
||||||
|
$bagOrderYmLabel = static function (string $ym): string {
|
||||||
|
if (preg_match('/^(\d{4})-(\d{2})$/', $ym, $m)) {
|
||||||
|
return $m[1] . '년 ' . (int) $m[2] . '월';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ym;
|
||||||
|
};
|
||||||
|
?>
|
||||||
|
<?= view('components/print_header', ['printTitle' => '봉투 발주 현황', 'printShowApproval' => false]) ?>
|
||||||
|
<section class="no-print border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||||
<span class="text-sm font-bold text-gray-700">발주 현황</span>
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<a href="<?= mgmt_url('bag-orders/export') . '?' . http_build_query(array_filter(['start_date' => $startDate ?? '', 'end_date' => $endDate ?? '', 'status' => $status ?? ''])) ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
<a href="<?= mgmt_url('bag-orders/export') . '?' . http_build_query(array_filter(['start_month' => $startMonth ?? '', 'end_month' => $endMonth ?? '', 'company_idx' => $companyIdx ?? 0, 'bag_code' => $bagCode ?? '', 'receive_type' => $receiveType ?? ''])) ?>" class="no-print border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
||||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||||
<a href="<?= mgmt_url('bag-orders/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">발주 등록</a>
|
<a href="<?= mgmt_url('bag-orders/create') ?>" class="bg-btn-search text-white px-4 py-1.5 rounded-sm flex items-center gap-1 text-sm shadow hover:opacity-90 transition border border-transparent">발주 등록</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="p-2 bg-white border-b border-gray-200">
|
|
||||||
<form method="GET" action="<?= mgmt_url('bag-orders') ?>" class="flex flex-wrap items-center gap-2">
|
<section class="no-print p-2 bg-white border-b border-gray-200">
|
||||||
<label class="text-sm text-gray-600">발주일</label>
|
<!-- GBMS 발주현황: 발주기간은 [시작] ~ [끝] 한 줄 고정, 필터 블록은 가로 나열 후 좁으면 블록 단위로만 줄바꿈 -->
|
||||||
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
<form method="GET" action="<?= mgmt_url('bag-orders') ?>" class="flex flex-wrap items-end gap-x-5 gap-y-3 w-full">
|
||||||
<label class="text-sm text-gray-600">~</label>
|
<div class="flex flex-nowrap items-center gap-2 shrink-0">
|
||||||
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
<label class="text-sm text-gray-600 whitespace-nowrap">발주 기간</label>
|
||||||
<label class="text-sm text-gray-600">상태</label>
|
<select name="start_month" class="border border-gray-300 rounded px-2 py-1 text-sm w-[11.5rem] max-w-[13rem] shrink-0">
|
||||||
<select name="status" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
<?php foreach ($bagOrderYmChoices as $ym): ?>
|
||||||
<option value="">전체</option>
|
<option value="<?= esc($ym) ?>" <?= ($startMonth ?? date('Y-m')) === $ym ? 'selected' : '' ?>><?= esc($bagOrderYmLabel($ym)) ?></option>
|
||||||
<option value="normal" <?= ($status ?? '') === 'normal' ? 'selected' : '' ?>>정상</option>
|
<?php endforeach; ?>
|
||||||
<option value="cancelled" <?= ($status ?? '') === 'cancelled' ? 'selected' : '' ?>>취소</option>
|
</select>
|
||||||
<option value="deleted" <?= ($status ?? '') === 'deleted' ? 'selected' : '' ?>>삭제</option>
|
<span class="text-sm text-gray-500 select-none">~</span>
|
||||||
</select>
|
<select name="end_month" class="border border-gray-300 rounded px-2 py-1 text-sm w-[11.5rem] max-w-[13rem] shrink-0">
|
||||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
<?php foreach ($bagOrderYmChoices as $ym): ?>
|
||||||
<a href="<?= mgmt_url('bag-orders') ?>" class="text-sm text-gray-500 hover:underline">초기화</a>
|
<option value="<?= esc($ym) ?>" <?= ($endMonth ?? date('Y-m')) === $ym ? 'selected' : '' ?>><?= esc($bagOrderYmLabel($ym)) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-nowrap items-center gap-2 shrink-0">
|
||||||
|
<label class="text-sm text-gray-600 whitespace-nowrap">제작 업체</label>
|
||||||
|
<select name="company_idx" class="border border-gray-300 rounded px-2 py-1 text-sm w-[11rem] max-w-[14rem]">
|
||||||
|
<option value="0">전 체</option>
|
||||||
|
<?php foreach (($companyOptions ?? []) as $company): ?>
|
||||||
|
<option value="<?= (int) $company->cp_idx ?>" <?= (int) ($companyIdx ?? 0) === (int) $company->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $company->cp_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-nowrap items-center gap-2 shrink-0">
|
||||||
|
<label class="text-sm text-gray-600 whitespace-nowrap">품 명</label>
|
||||||
|
<select name="bag_code" class="border border-gray-300 rounded px-2 py-1 text-sm w-[11rem] max-w-[16rem]">
|
||||||
|
<option value="">전 체</option>
|
||||||
|
<?php foreach (($bagCodeOptions ?? []) as $bag): ?>
|
||||||
|
<option value="<?= esc((string) $bag->cd_code) ?>" <?= (string) ($bagCode ?? '') === (string) $bag->cd_code ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $bag->cd_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-nowrap items-center gap-2 shrink-0">
|
||||||
|
<label class="text-sm text-gray-600 whitespace-nowrap">입고 구분</label>
|
||||||
|
<select name="receive_type" class="border border-gray-300 rounded px-2 py-1 text-sm w-[8.5rem]">
|
||||||
|
<option value="all" <?= ($receiveType ?? 'all') === 'all' ? 'selected' : '' ?>>전 체</option>
|
||||||
|
<option value="received" <?= ($receiveType ?? 'all') === 'received' ? 'selected' : '' ?>>입고완료</option>
|
||||||
|
<option value="pending" <?= ($receiveType ?? 'all') === 'pending' ? 'selected' : '' ?>>미입고</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-nowrap items-center gap-2 shrink-0 sm:ml-auto">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||||
|
<a href="<?= mgmt_url('bag-orders') ?>" class="text-sm text-gray-500 hover:underline whitespace-nowrap">초기화</a>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 overflow-auto mt-2">
|
|
||||||
<table class="w-full data-table">
|
<div class="bag-order-print-wrap border border-gray-300 overflow-auto mt-2">
|
||||||
|
<table class="bag-order-print-table w-full data-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-16">번호</th>
|
<th class="w-32">발주일자</th>
|
||||||
<th>LOT번호</th>
|
<th class="min-w-[10rem]">제작 업체</th>
|
||||||
<th>발주일</th>
|
<th class="min-w-[12rem]">품 명</th>
|
||||||
<th>제작업체</th>
|
<th class="w-28">발주 수량</th>
|
||||||
<th>입고처</th>
|
<th class="w-28">입고 수량</th>
|
||||||
<th>품목수</th>
|
<th class="w-28">미입고수량</th>
|
||||||
<th>총수량</th>
|
<th class="w-32">발주 금액</th>
|
||||||
<th>총금액</th>
|
<th class="min-w-[9rem]">입고처</th>
|
||||||
<th class="w-20">상태</th>
|
<th class="min-w-[8rem]">비 고</th>
|
||||||
<th class="w-44">작업</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="text-right">
|
<tbody class="text-right">
|
||||||
<?php foreach ($list as $row): ?>
|
<?php $printedGroup = []; ?>
|
||||||
|
<?php foreach (($rows ?? []) as $row): ?>
|
||||||
|
<?php if (! empty($row['is_subtotal'])): ?>
|
||||||
|
<tr class="bg-gray-50 font-semibold">
|
||||||
|
<td colspan="3" class="text-center"><?= esc((string) ($row['label'] ?? '소계')) ?></td>
|
||||||
|
<td><?= number_format((int) ($row['order_qty'] ?? 0)) ?></td>
|
||||||
|
<td><?= number_format((int) ($row['received_qty'] ?? 0)) ?></td>
|
||||||
|
<td><?= number_format((int) ($row['pending_qty'] ?? 0)) ?></td>
|
||||||
|
<td><?= number_format((float) ($row['amount'] ?? 0)) ?></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<?php continue; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$boIdx = (int) ($row['bo_idx'] ?? 0);
|
||||||
|
$showGroup = ! isset($printedGroup[$boIdx]);
|
||||||
|
$rowspan = (int) (($groupRows[$boIdx] ?? 1));
|
||||||
|
if ($showGroup) {
|
||||||
|
$printedGroup[$boIdx] = true;
|
||||||
|
}
|
||||||
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><?= esc($row->bo_idx) ?></td>
|
<?php if ($showGroup): ?>
|
||||||
<td class="text-center font-mono"><?= esc($row->bo_lot_no) ?></td>
|
<td class="text-center align-top" rowspan="<?= $rowspan ?>"><?= esc((string) ($row['order_date'] ?? '')) ?></td>
|
||||||
<td class="text-center"><?= esc($row->bo_order_date) ?></td>
|
<td class="text-left pl-2 align-top" rowspan="<?= $rowspan ?>"><?= esc((string) ($row['company_name'] ?? '')) ?></td>
|
||||||
<td class="text-left pl-2"><?= esc($companyMap[$row->bo_company_idx] ?? '') ?></td>
|
<?php endif; ?>
|
||||||
<td class="text-left pl-2"><?= esc($agencyMap[$row->bo_agency_idx] ?? '') ?></td>
|
<td class="text-left pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
<td><?= number_format((int) ($itemSummary[$row->bo_idx]['count'] ?? 0)) ?></td>
|
<td><?= number_format((int) ($row['order_qty'] ?? 0)) ?></td>
|
||||||
<td><?= number_format((int) ($itemSummary[$row->bo_idx]['qty'] ?? 0)) ?></td>
|
<td><?= number_format((int) ($row['received_qty'] ?? 0)) ?></td>
|
||||||
<td><?= number_format((int) ($itemSummary[$row->bo_idx]['amount'] ?? 0)) ?></td>
|
<td><?= number_format((int) ($row['pending_qty'] ?? 0)) ?></td>
|
||||||
<td class="text-center">
|
<td><?= number_format((float) ($row['amount'] ?? 0)) ?></td>
|
||||||
<?php
|
<td class="text-left pl-2"><?= esc((string) ($row['agency_name'] ?? '')) ?></td>
|
||||||
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
|
<td></td>
|
||||||
echo esc($statusMap[$row->bo_status] ?? $row->bo_status);
|
|
||||||
?>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="<?= mgmt_url('bag-orders/detail/' . (int) $row->bo_idx) ?>" class="text-blue-600 hover:underline text-sm mr-1">상세</a>
|
|
||||||
<form action="<?= mgmt_url('bag-orders/cancel/' . (int) $row->bo_idx) ?>" method="POST" class="inline" onsubmit="return confirm('취소하시겠습니까?');">
|
|
||||||
<?= csrf_field() ?>
|
|
||||||
<button type="submit" class="text-orange-600 hover:underline text-sm mr-1">취소</button>
|
|
||||||
</form>
|
|
||||||
<form action="<?= mgmt_url('bag-orders/delete/' . (int) $row->bo_idx) ?>" method="POST" class="inline" onsubmit="return confirm('삭제하시겠습니까?');">
|
|
||||||
<?= csrf_field() ?>
|
|
||||||
<button type="submit" class="text-red-600 hover:underline text-sm">삭제</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if (empty($list)): ?>
|
|
||||||
<tr><td colspan="10" class="text-center text-gray-400 py-4">등록된 발주가 없습니다.</td></tr>
|
<?php if (empty($rows ?? [])): ?>
|
||||||
|
<tr><td colspan="9" class="text-center text-gray-400 py-6">조회 조건에 해당하는 발주 내역이 없습니다.</td></tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-bold">
|
||||||
|
<td colspan="3" class="text-center">총계</td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($grandTotals['order_qty'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($grandTotals['received_qty'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($grandTotals['pending_qty'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((float) ($grandTotals['amount'] ?? 0)) ?></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<?php if (isset($pager)): ?><div class="mt-3"><?= $pager->links() ?></div><?php endif; ?>
|
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
#debug-icon,
|
||||||
|
#debug-bar,
|
||||||
|
#debug-bar-contents,
|
||||||
|
#debug-toolbar,
|
||||||
|
.debug-toolbar,
|
||||||
|
.ci-debug-toolbar,
|
||||||
|
[id^='debug-bar-'],
|
||||||
|
[id^='debug-icon'],
|
||||||
|
[class*='debug-toolbar'] {
|
||||||
|
display: none !important;
|
||||||
|
visibility: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bag-order-print-wrap {
|
||||||
|
overflow: visible !important;
|
||||||
|
border: none !important;
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bag-order-print-table {
|
||||||
|
width: 100% !important;
|
||||||
|
table-layout: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bag-order-print-table th,
|
||||||
|
.bag-order-print-table td {
|
||||||
|
white-space: nowrap !important;
|
||||||
|
word-break: keep-all !important;
|
||||||
|
overflow-wrap: normal !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
padding: 2px 3px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,83 +1,701 @@
|
|||||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
<?php
|
||||||
<span class="text-sm font-bold text-gray-700">발주 등록</span>
|
$editDefaults = is_array($editDefaults ?? null) ? $editDefaults : [];
|
||||||
|
$editMode = (bool) ($editMode ?? false);
|
||||||
|
$oldBagCodes = old('item_bag_code', $editDefaults['item_bag_code'] ?? []);
|
||||||
|
$oldQtySheets = old('item_qty_sheet', $editDefaults['item_qty_sheet'] ?? []);
|
||||||
|
$oldQtyBoxes = old('item_qty_box', $editDefaults['item_qty_box'] ?? []);
|
||||||
|
$oldBagCodes = is_array($oldBagCodes) ? $oldBagCodes : [];
|
||||||
|
$oldQtySheets = is_array($oldQtySheets) ? $oldQtySheets : [];
|
||||||
|
$oldQtyBoxes = is_array($oldQtyBoxes) ? $oldQtyBoxes : [];
|
||||||
|
$itemRowCount = max(8, count($oldBagCodes), count($oldQtySheets), count($oldQtyBoxes));
|
||||||
|
$defaultOrderDate = old('bo_order_date', $editDefaults['bo_order_date'] ?? date('Y-m-d'));
|
||||||
|
$defaultOrderMonth = old('bo_order_month_ui', substr($defaultOrderDate, 0, 7));
|
||||||
|
$defaultOrderYear = (int) substr($defaultOrderMonth, 0, 4);
|
||||||
|
$monthOptionValues = [];
|
||||||
|
for ($year = $defaultOrderYear - 2; $year <= $defaultOrderYear + 2; $year++) {
|
||||||
|
for ($month = 1; $month <= 12; $month++) {
|
||||||
|
$monthValue = sprintf('%04d-%02d', $year, $month);
|
||||||
|
$monthLabel = $year . '년 ' . $month . '월';
|
||||||
|
$monthOptionValues[] = ['value' => $monthValue, 'label' => $monthLabel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$bagMeta = [];
|
||||||
|
foreach (($bagReferenceRows ?? []) as $row) {
|
||||||
|
$bagMeta[$row['code']] = [
|
||||||
|
'name' => $row['name'],
|
||||||
|
'orderPrice' => (float) $row['orderPrice'],
|
||||||
|
'boxPerPack' => (int) $row['boxPerPack'],
|
||||||
|
'packPerSheet' => (int) $row['packPerSheet'],
|
||||||
|
'totalPerBox' => max(1, (int) $row['totalPerBox']),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$initialSelectedItems = [];
|
||||||
|
$maxOldCount = max(count($oldBagCodes), count($oldQtySheets), count($oldQtyBoxes));
|
||||||
|
for ($i = 0; $i < $maxOldCount; $i++) {
|
||||||
|
$code = trim((string) ($oldBagCodes[$i] ?? ''));
|
||||||
|
if ($code === '' || !isset($bagMeta[$code])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$fallbackQtyBox = (int) ($oldQtyBoxes[$i] ?? 0);
|
||||||
|
$rawQtySheet = (int) ($oldQtySheets[$i] ?? 0);
|
||||||
|
$fallbackTotalPerBox = (int) ($bagMeta[$code]['totalPerBox'] ?? 1);
|
||||||
|
if ($fallbackQtyBox <= 0 && $rawQtySheet > 0) {
|
||||||
|
$fallbackQtyBox = intdiv($rawQtySheet, max(1, $fallbackTotalPerBox));
|
||||||
|
}
|
||||||
|
$initialSelectedItems[] = [
|
||||||
|
'code' => $code,
|
||||||
|
'qtyBox' => max(0, $fallbackQtyBox),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
|
||||||
|
|
||||||
|
$hubReturn = (bool) ($hubReturn ?? false);
|
||||||
|
$changeMode = $editMode ? (string) ($changeMode ?? 'meta') : 'meta';
|
||||||
|
if (! in_array($changeMode, ['price', 'meta', 'delete'], true)) {
|
||||||
|
$changeMode = 'meta';
|
||||||
|
}
|
||||||
|
$orderLotNo = (string) ($orderLotNo ?? '');
|
||||||
|
$orderReturnMonth = (string) ($orderReturnMonth ?? '');
|
||||||
|
$deleteMode = $editMode && $changeMode === 'delete';
|
||||||
|
$defaultDeleteBoIdx = (int) ($editDefaults['bo_source_idx'] ?? 0);
|
||||||
|
$firstNormalBoIdx = null;
|
||||||
|
foreach (($recentOrders ?? []) as $_h) {
|
||||||
|
if ((string) ($_h->bo_status ?? '') === 'normal') {
|
||||||
|
$firstNormalBoIdx = (int) $_h->bo_idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$defaultDeleteOk = false;
|
||||||
|
foreach (($recentOrders ?? []) as $_h) {
|
||||||
|
if ((int) $_h->bo_idx === $defaultDeleteBoIdx && (string) ($_h->bo_status ?? '') === 'normal') {
|
||||||
|
$defaultDeleteOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$selectedDeleteBoIdx = $defaultDeleteOk ? $defaultDeleteBoIdx : (int) ($firstNormalBoIdx ?? 0);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<span class="text-sm font-bold text-gray-700"><?= $editMode ? '발주 변경' : '발주 등록' ?></span>
|
||||||
|
<?php if ($hubReturn && $orderReturnMonth !== ''): ?>
|
||||||
|
<a href="<?= base_url('bag/order/change?month=' . rawurlencode($orderReturnMonth) . '&hub_mode=' . rawurlencode($changeMode)) ?>" class="text-sm text-blue-600 hover:underline">발주 변경 목록</a>
|
||||||
|
<?php endif; ?>
|
||||||
</section>
|
</section>
|
||||||
<div class="border border-gray-300 p-4 mt-2 bg-white max-w-4xl">
|
|
||||||
<form action="<?= base_url('bag/order/store') ?>" method="POST" class="space-y-4">
|
|
||||||
<?= csrf_field() ?>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<?php if ($deleteMode): ?>
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">발주일 <span class="text-red-500">*</span></label>
|
<form action="<?= base_url('bag/order/delete') ?>" method="post" class="mt-2 space-y-2">
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-44" name="bo_order_date" type="date" value="<?= esc(old('bo_order_date', date('Y-m-d'))) ?>" required/>
|
<?= csrf_field() ?>
|
||||||
</div>
|
<div class="border border-gray-300 bg-white p-2 text-sm">
|
||||||
|
<span class="font-bold text-gray-700">발주월</span>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<span class="ml-2"><?= esc($defaultOrderMonth) ?></span>
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">수수료율</label>
|
<span class="text-gray-600 ml-2">왼쪽 목록에서 삭제할 발주를 선택한 뒤 「삭제 실행」을 누르세요.</span>
|
||||||
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-32 text-right" name="bo_fee_rate" type="number" step="0.01" value="<?= esc(old('bo_fee_rate', '0')) ?>"/>
|
</div>
|
||||||
<span class="text-sm text-gray-500">%</span>
|
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
|
||||||
</div>
|
<section class="xl:col-span-5 border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="overflow-auto max-h-[410px]">
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">제작업체</label>
|
<table class="w-full data-table text-sm">
|
||||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="bo_company_idx">
|
|
||||||
<option value="">선택</option>
|
|
||||||
<?php foreach ($companies as $cp): ?>
|
|
||||||
<option value="<?= esc($cp->cp_idx) ?>" <?= (int) old('bo_company_idx') === (int) $cp->cp_idx ? 'selected' : '' ?>>
|
|
||||||
<?= esc($cp->cp_name) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
|
||||||
<label class="block text-sm font-bold text-gray-700 w-28">입고처</label>
|
|
||||||
<select class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="bo_agency_idx">
|
|
||||||
<option value="">선택</option>
|
|
||||||
<?php foreach ($agencies as $ag): ?>
|
|
||||||
<option value="<?= esc($ag->sa_idx) ?>" <?= (int) old('bo_agency_idx') === (int) $ag->sa_idx ? 'selected' : '' ?>>
|
|
||||||
[<?= esc($ag->sa_kind ?? '') ?>] <?= esc($ag->sa_code ?? '') ?> — <?= esc($ag->sa_name) ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4">
|
|
||||||
<label class="block text-sm font-bold text-gray-700 mb-2">발주 품목</label>
|
|
||||||
<div class="border border-gray-300 overflow-auto">
|
|
||||||
<table class="w-full data-table">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-16">순번</th>
|
<th class="w-10">선택</th>
|
||||||
<th>봉투</th>
|
<th class="w-28">발주일</th>
|
||||||
<th class="w-32">박스수</th>
|
<th>제작업체</th>
|
||||||
|
<th>입고처</th>
|
||||||
|
<th class="w-16">상태</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php for ($i = 0; $i < 3; $i++): ?>
|
<?php $deleteRadioFirst = true; ?>
|
||||||
<tr>
|
<?php foreach (($recentOrders ?? []) as $history): ?>
|
||||||
<td class="text-center"><?= $i + 1 ?></td>
|
<?php $rowNormal = (string) ($history->bo_status ?? '') === 'normal'; ?>
|
||||||
<td>
|
<tr class="<?= $rowNormal ? '' : 'opacity-60' ?>">
|
||||||
<select class="border border-gray-300 rounded px-2 py-1 text-sm w-full" name="item_bag_code[]">
|
<td class="text-center align-middle">
|
||||||
<option value="">선택</option>
|
<?php if ($rowNormal): ?>
|
||||||
<?php foreach ($bagCodes as $cd): ?>
|
<input type="radio" name="bo_idx" value="<?= (int) $history->bo_idx ?>" class="accent-red-600" <?= (int) $history->bo_idx === $selectedDeleteBoIdx ? 'checked' : '' ?> <?= $deleteRadioFirst ? 'required' : '' ?> />
|
||||||
<option value="<?= esc($cd->cd_code) ?>">
|
<?php $deleteRadioFirst = false; ?>
|
||||||
<?= esc($cd->cd_code) ?> — <?= esc($cd->cd_name) ?>
|
<?php else: ?>
|
||||||
</option>
|
<span class="text-gray-400" title="삭제할 수 없는 상태">—</span>
|
||||||
<?php endforeach; ?>
|
<?php endif; ?>
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input class="border border-gray-300 rounded px-2 py-1 text-sm w-full text-right" name="item_qty_box[]" type="number" min="0" value="0"/>
|
|
||||||
</td>
|
</td>
|
||||||
|
<td class="text-center"><?= esc((string) $history->bo_order_date) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($companyMap[(int) $history->bo_company_idx] ?? '-')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($agencyMap[(int) $history->bo_agency_idx] ?? '-')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($statusMap[(string) $history->bo_status] ?? $history->bo_status)) ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endfor; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($recentOrders)): ?>
|
||||||
|
<tr><td colspan="5" class="text-center text-gray-400 py-4">발주 이력이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
<section class="xl:col-span-7 border border-red-200 bg-red-50 p-4">
|
||||||
|
<p class="text-sm font-bold text-red-800 mb-2">발주 삭제</p>
|
||||||
|
<p class="text-sm text-gray-700 mb-4">목록에서 선택한 발주를 삭제 처리합니다. 계속하시겠습니까?</p>
|
||||||
|
<div class="flex flex-wrap gap-2 items-center">
|
||||||
|
<button type="submit" class="bg-red-600 text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90" <?= $firstNormalBoIdx === null ? 'disabled' : '' ?>>삭제 실행</button>
|
||||||
|
<a href="<?= base_url('bag/order/change?month=' . rawurlencode($orderReturnMonth !== '' ? $orderReturnMonth : substr($defaultOrderDate, 0, 7))) ?>" class="bg-gray-200 text-gray-800 px-6 py-1.5 rounded-sm text-sm">취소</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php else: ?>
|
||||||
|
|
||||||
<div class="flex gap-2 pt-2">
|
<form action="<?= base_url('bag/order/store') ?>" method="POST" class="mt-2 space-y-2" id="bag-order-store-form">
|
||||||
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition">등록</button>
|
<?= csrf_field() ?>
|
||||||
<a href="<?= base_url('bag/purchase-inbound') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
|
<?php if ($editMode): ?>
|
||||||
|
<input type="hidden" name="bo_source_idx" value="<?= esc((string) ($editDefaults['bo_source_idx'] ?? 0)) ?>" />
|
||||||
|
<fieldset class="border border-gray-200 rounded px-3 py-2 mb-1 text-sm bg-white">
|
||||||
|
<legend class="text-xs font-bold text-gray-600 px-1">변경 구분</legend>
|
||||||
|
<div class="flex flex-wrap gap-x-4 gap-y-1">
|
||||||
|
<label class="inline-flex items-center gap-1 cursor-pointer">
|
||||||
|
<input type="radio" name="bo_change_mode" value="price" <?= $changeMode === 'price' ? 'checked' : '' ?> />
|
||||||
|
<span>발주·도매·판매 단가</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center gap-1 cursor-pointer">
|
||||||
|
<input type="radio" name="bo_change_mode" value="meta" <?= $changeMode === 'meta' ? 'checked' : '' ?> />
|
||||||
|
<span>업체·수수료·협회·발주</span>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
<p class="text-xs text-gray-500 mt-1">
|
||||||
</div>
|
발주 삭제는 <a class="text-blue-600 hover:underline" href="<?= base_url('bag/order/revise/' . (int) ($editDefaults['bo_source_idx'] ?? 0) . '?change_mode=delete') ?>">발주 삭제 화면</a>으로 이동합니다.
|
||||||
|
</p>
|
||||||
|
</fieldset>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if ($hubReturn && $orderReturnMonth !== ''): ?>
|
||||||
|
<input type="hidden" name="order_return_hub" value="1" />
|
||||||
|
<input type="hidden" name="order_return_month" value="<?= esc($orderReturnMonth) ?>" />
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 bg-white p-2">
|
||||||
|
<div class="flex flex-wrap items-center gap-4 text-sm">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label for="bo_order_month_ui" class="font-bold text-gray-700">발주월</label>
|
||||||
|
<select id="bo_order_month_ui" name="bo_order_month_ui" class="border border-gray-300 rounded px-2 py-1 w-40">
|
||||||
|
<?php foreach ($monthOptionValues as $monthOption): ?>
|
||||||
|
<option value="<?= esc($monthOption['value']) ?>" <?= $monthOption['value'] === $defaultOrderMonth ? 'selected' : '' ?>>
|
||||||
|
<?= esc($monthOption['label']) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<span id="bo_order_month_ko" class="text-sm text-gray-700"></span>
|
||||||
|
</div>
|
||||||
|
<p class="text-blue-600 font-bold">※ 발주수량을 박스단위로 떨어지게 입력해 주세요.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2">
|
||||||
|
<section class="xl:col-span-5 border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 이력</div>
|
||||||
|
<div class="overflow-auto max-h-[410px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-28">발주일</th>
|
||||||
|
<th>제작업체</th>
|
||||||
|
<th>입고처</th>
|
||||||
|
<th class="w-16">상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($recentOrders ?? []) as $history): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<a href="<?= base_url('bag/order/revise/' . (int) $history->bo_idx) ?>" class="text-blue-600 hover:underline">
|
||||||
|
<?= esc((string) $history->bo_order_date) ?>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($companyMap[(int) $history->bo_company_idx] ?? '-')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($agencyMap[(int) $history->bo_agency_idx] ?? '-')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($statusMap[(string) $history->bo_status] ?? $history->bo_status)) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($recentOrders)): ?>
|
||||||
|
<tr><td colspan="4" class="text-center text-gray-400 py-4">발주 이력이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="xl:col-span-7 border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700"><?= $editMode ? '발주 변경 수정' : '발주 Form' ?></div>
|
||||||
|
<div class="p-2 space-y-2">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_order_date" class="w-20 font-bold text-gray-700">발주일 <span class="text-red-500">*</span></label>
|
||||||
|
<input id="bo_order_date" name="bo_order_date" type="date" value="<?= esc($defaultOrderDate) ?>" required class="border border-gray-300 rounded px-2 py-1 w-full" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_association_idx" class="w-20 font-bold text-gray-700">협회</label>
|
||||||
|
<select id="bo_association_idx" name="bo_association_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($associations ?? []) as $association): ?>
|
||||||
|
<option value="<?= esc((string) $association->cp_idx) ?>" <?= (int) old('bo_association_idx', $editDefaults['bo_association_idx'] ?? 0) === (int) $association->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $association->cp_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_company_idx" class="w-20 font-bold text-gray-700">제작업체</label>
|
||||||
|
<select id="bo_company_idx" name="bo_company_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($companies ?? []) as $company): ?>
|
||||||
|
<option value="<?= esc((string) $company->cp_idx) ?>" <?= (int) old('bo_company_idx', $editDefaults['bo_company_idx'] ?? 0) === (int) $company->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) $company->cp_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<label for="bo_agency_idx" class="w-20 font-bold text-gray-700">입고처</label>
|
||||||
|
<select id="bo_agency_idx" name="bo_agency_idx" class="border border-gray-300 rounded px-2 py-1 w-full">
|
||||||
|
<option value="">선택</option>
|
||||||
|
<?php foreach (($agencies ?? []) as $agency): ?>
|
||||||
|
<option value="<?= esc((string) $agency->sa_idx) ?>" <?= (int) old('bo_agency_idx', $editDefaults['bo_agency_idx'] ?? 0) === (int) $agency->sa_idx ? 'selected' : '' ?>>
|
||||||
|
[<?= esc((string) ($agency->sa_kind ?? '')) ?>] <?= esc((string) ($agency->sa_code ?? '')) ?> — <?= esc((string) $agency->sa_name) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 overflow-auto">
|
||||||
|
<table class="w-full data-table text-sm order-input-table" id="order-item-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">번호</th>
|
||||||
|
<th class="w-16">선택</th>
|
||||||
|
<th>품명</th>
|
||||||
|
<th class="w-24">수량</th>
|
||||||
|
<th class="w-24">단가</th>
|
||||||
|
<th class="w-24">낱장환산</th>
|
||||||
|
<?php if ($editMode): ?>
|
||||||
|
<th class="w-24">선정수량</th>
|
||||||
|
<th class="w-20">LOT</th>
|
||||||
|
<?php endif; ?>
|
||||||
|
<th class="w-28">금액</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="selected-order-items-body"></tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th colspan="3" class="text-center">계</th>
|
||||||
|
<th class="text-right pr-2" id="sum-box-qty">0</th>
|
||||||
|
<th></th>
|
||||||
|
<th class="text-right pr-2" id="sum-sheet-qty">0</th>
|
||||||
|
<?php if ($editMode): ?>
|
||||||
|
<th></th>
|
||||||
|
<th></th>
|
||||||
|
<?php endif; ?>
|
||||||
|
<th class="text-right pr-2" id="sum-amount">0</th>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 bg-gray-50 p-2 text-sm">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-1">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">품목 금액 합계</span>
|
||||||
|
<strong id="sum-item-amount">0</strong>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">조달수수료</span>
|
||||||
|
<span class="flex items-center gap-1">
|
||||||
|
<input id="bo_fee_rate_summary" name="bo_fee_rate" type="number" step="0.01" value="<?= esc(old('bo_fee_rate', $editDefaults['bo_fee_rate'] ?? '0')) ?>" class="border border-gray-300 rounded px-2 py-0.5 w-20 text-right" />
|
||||||
|
<strong>%</strong>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<span class="text-gray-700">조달수수료액</span>
|
||||||
|
<strong id="sum-fee-amount">0</strong>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between border-t border-red-300 pt-1">
|
||||||
|
<span class="font-bold text-gray-800">품목 + 수수료 합계</span>
|
||||||
|
<strong id="sum-grand-amount" class="text-red-700">0</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-2 pt-1">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-6 py-1.5 rounded-sm text-sm shadow hover:opacity-90 transition"><?= $editMode ? '변경 저장' : '발주' ?></button>
|
||||||
|
<a href="<?= base_url('bag/purchase-inbound') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm hover:bg-gray-300 transition">취소</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 등록 종류</div>
|
||||||
|
<div class="overflow-auto">
|
||||||
|
<table class="w-full data-table text-sm order-reference-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">번호</th>
|
||||||
|
<th class="w-20">선택</th>
|
||||||
|
<th>봉투 종류</th>
|
||||||
|
<th class="w-24">발주단가</th>
|
||||||
|
<th class="w-24">Box당 팩</th>
|
||||||
|
<th class="w-24">팩당 낱장</th>
|
||||||
|
<th class="w-28">1박스 총 낱장</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($bagReferenceRows ?? []) as $idx => $row): ?>
|
||||||
|
<tr data-reference-row data-code="<?= esc((string) $row['code']) ?>" class="cursor-pointer">
|
||||||
|
<td class="text-center"><?= $idx + 1 ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="js-toggle-bag border border-gray-300 rounded px-2 py-0.5 text-xs hover:bg-gray-100" data-code="<?= esc((string) $row['code']) ?>">선택</button>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) $row['name']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((float) $row['orderPrice'], 2) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['boxPerPack']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['packPerSheet']) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) $row['totalPerBox']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($bagReferenceRows)): ?>
|
||||||
|
<tr><td colspan="6" class="text-center text-gray-400 py-4">표시할 봉투 기준 데이터가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (! $deleteMode): ?>
|
||||||
|
<style>
|
||||||
|
.order-input-table tbody tr,
|
||||||
|
.order-reference-table tbody tr {
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-input-table tbody td,
|
||||||
|
.order-reference-table tbody td {
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const bagMeta = <?= json_encode($bagMeta, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const initialSelectedItems = <?= json_encode($initialSelectedItems, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const selectedBody = document.getElementById('selected-order-items-body');
|
||||||
|
const referenceRows = Array.from(document.querySelectorAll('[data-reference-row]'));
|
||||||
|
const sumBoxQtyEl = document.getElementById('sum-box-qty');
|
||||||
|
const sumSheetQtyEl = document.getElementById('sum-sheet-qty');
|
||||||
|
const sumAmountEl = document.getElementById('sum-amount');
|
||||||
|
const sumItemAmountEl = document.getElementById('sum-item-amount');
|
||||||
|
const sumFeeAmountEl = document.getElementById('sum-fee-amount');
|
||||||
|
const sumGrandAmountEl = document.getElementById('sum-grand-amount');
|
||||||
|
const monthInput = document.getElementById('bo_order_month_ui');
|
||||||
|
const monthKoLabel = document.getElementById('bo_order_month_ko');
|
||||||
|
const orderDateInput = document.getElementById('bo_order_date');
|
||||||
|
const feeRateSummaryInput = document.getElementById('bo_fee_rate_summary');
|
||||||
|
const orderForm = document.querySelector('form[action*="bag/order/store"]');
|
||||||
|
const selectedItems = new Map();
|
||||||
|
let activeCode = null;
|
||||||
|
const editMode = <?= $editMode ? 'true' : 'false' ?>;
|
||||||
|
const changeMode = <?= json_encode($changeMode, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const orderLotNo = <?= json_encode($orderLotNo, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||||
|
const colEmpty = editMode ? 9 : 7;
|
||||||
|
|
||||||
|
const formatNumber = (value) => new Intl.NumberFormat('ko-KR').format(Number.isFinite(value) ? value : 0);
|
||||||
|
const escapeHtml = (value) => String(value ?? '')
|
||||||
|
.replaceAll('&', '&')
|
||||||
|
.replaceAll('<', '<')
|
||||||
|
.replaceAll('>', '>')
|
||||||
|
.replaceAll('"', '"')
|
||||||
|
.replaceAll("'", ''');
|
||||||
|
|
||||||
|
const ensureMonthOption = (monthValue) => {
|
||||||
|
if (!monthInput || !monthValue) return;
|
||||||
|
const hasOption = Array.from(monthInput.options || []).some((option) => option.value === monthValue);
|
||||||
|
if (hasOption) return;
|
||||||
|
const [year, month] = monthValue.split('-');
|
||||||
|
const label = `${Number(year)}년 ${Number(month)}월`;
|
||||||
|
const option = new Option(label, monthValue, false, false);
|
||||||
|
monthInput.add(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncMonthFromDate = () => {
|
||||||
|
if (!orderDateInput || !monthInput || !orderDateInput.value) return;
|
||||||
|
const monthValue = orderDateInput.value.substring(0, 7);
|
||||||
|
ensureMonthOption(monthValue);
|
||||||
|
monthInput.value = monthValue;
|
||||||
|
updateMonthKoLabel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncDateFromMonth = () => {
|
||||||
|
if (!orderDateInput || !monthInput || !monthInput.value) return;
|
||||||
|
const parts = monthInput.value.split('-');
|
||||||
|
if (parts.length !== 2) return;
|
||||||
|
const year = Number(parts[0]);
|
||||||
|
const month = Number(parts[1]);
|
||||||
|
if (!Number.isFinite(year) || !Number.isFinite(month)) return;
|
||||||
|
|
||||||
|
const currentDay = orderDateInput.value ? Number(orderDateInput.value.split('-')[2]) : 1;
|
||||||
|
const lastDay = new Date(year, month, 0).getDate();
|
||||||
|
const day = String(Math.min(Math.max(currentDay, 1), lastDay)).padStart(2, '0');
|
||||||
|
orderDateInput.value = `${String(year)}-${String(month).padStart(2, '0')}-${day}`;
|
||||||
|
updateMonthKoLabel();
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateMonthKoLabel = () => {
|
||||||
|
if (!monthKoLabel || !monthInput || !monthInput.value) {
|
||||||
|
if (monthKoLabel) monthKoLabel.textContent = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const [year, month] = monthInput.value.split('-');
|
||||||
|
if (!year || !month) {
|
||||||
|
monthKoLabel.textContent = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
monthKoLabel.textContent = `${Number(year)}년 ${Number(month)}월`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateReferenceSelectionUi = () => {
|
||||||
|
referenceRows.forEach((row) => {
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
const button = row.querySelector('.js-toggle-bag');
|
||||||
|
const isSelected = selectedItems.has(code);
|
||||||
|
row.classList.toggle('bg-blue-50', isSelected);
|
||||||
|
if (button) {
|
||||||
|
button.textContent = isSelected ? '선택됨' : '선택';
|
||||||
|
button.classList.toggle('bg-blue-600', isSelected);
|
||||||
|
button.classList.toggle('text-white', isSelected);
|
||||||
|
button.classList.toggle('border-blue-600', isSelected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyModeLock = () => {
|
||||||
|
if (!editMode) return;
|
||||||
|
const isPrice = changeMode === 'price';
|
||||||
|
selectedBody.querySelectorAll('.item-qty-box').forEach((el) => {
|
||||||
|
el.readOnly = isPrice;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateTotals = () => {
|
||||||
|
let sumBoxQty = 0;
|
||||||
|
let sumSheetQty = 0;
|
||||||
|
let sumAmount = 0;
|
||||||
|
|
||||||
|
selectedBody.querySelectorAll('tr[data-item-row]').forEach((row) => {
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
const qtyBox = Math.max(0, parseInt(qtyInput?.value || '0', 10));
|
||||||
|
const meta = bagMeta[code] || { orderPrice: 0, totalPerBox: 1 };
|
||||||
|
const priceInput = row.querySelector('.item-unit-price-input');
|
||||||
|
let unitPrice = Number(meta.orderPrice || 0);
|
||||||
|
if (priceInput) {
|
||||||
|
unitPrice = Math.max(0, parseFloat(priceInput.value || '0') || 0);
|
||||||
|
}
|
||||||
|
const totalPerBox = Math.max(1, Number(meta.totalPerBox || 1));
|
||||||
|
const qtySheet = qtyBox * totalPerBox;
|
||||||
|
const amount = qtySheet * unitPrice;
|
||||||
|
|
||||||
|
const unitPriceEl = row.querySelector('.item-unit-price');
|
||||||
|
const qtySheetEl = row.querySelector('.item-qty-sheet');
|
||||||
|
const selQtyEl = row.querySelector('.item-sel-qty');
|
||||||
|
const sheetHelpEl = row.querySelector('.item-sheet-help');
|
||||||
|
const amountEl = row.querySelector('.item-amount');
|
||||||
|
if (unitPriceEl) unitPriceEl.textContent = formatNumber(unitPrice);
|
||||||
|
if (qtySheetEl) qtySheetEl.textContent = formatNumber(qtySheet);
|
||||||
|
if (selQtyEl) selQtyEl.textContent = formatNumber(qtySheet);
|
||||||
|
if (sheetHelpEl) sheetHelpEl.textContent = `낱장 ${formatNumber(qtySheet)}장`;
|
||||||
|
if (amountEl) amountEl.textContent = formatNumber(amount);
|
||||||
|
|
||||||
|
selectedItems.set(code, { qtyBox });
|
||||||
|
sumBoxQty += qtyBox;
|
||||||
|
sumSheetQty += qtySheet;
|
||||||
|
sumAmount += amount;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sumBoxQtyEl) sumBoxQtyEl.textContent = formatNumber(sumBoxQty);
|
||||||
|
if (sumSheetQtyEl) sumSheetQtyEl.textContent = formatNumber(sumSheetQty);
|
||||||
|
if (sumAmountEl) sumAmountEl.textContent = formatNumber(sumAmount);
|
||||||
|
if (sumItemAmountEl) sumItemAmountEl.textContent = formatNumber(sumAmount);
|
||||||
|
|
||||||
|
const feeRateSource = feeRateSummaryInput?.value ?? '0';
|
||||||
|
const feeRate = Math.max(0, parseFloat(feeRateSource) || 0);
|
||||||
|
const feeAmount = Math.round(sumAmount * (feeRate / 100));
|
||||||
|
const grandAmount = sumAmount + feeAmount;
|
||||||
|
|
||||||
|
if (feeRateSummaryInput && feeRateSummaryInput.value !== feeRate.toString()) {
|
||||||
|
feeRateSummaryInput.value = feeRate.toString();
|
||||||
|
}
|
||||||
|
if (sumFeeAmountEl) sumFeeAmountEl.textContent = formatNumber(feeAmount);
|
||||||
|
if (sumGrandAmountEl) sumGrandAmountEl.textContent = formatNumber(grandAmount);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActiveRow = (code) => {
|
||||||
|
activeCode = code || null;
|
||||||
|
selectedBody.querySelectorAll('tr[data-item-row]').forEach((row) => {
|
||||||
|
row.classList.toggle('bg-amber-50', row.dataset.code === activeCode);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSelectedRows = () => {
|
||||||
|
const codes = Object.keys(bagMeta).filter((code) => selectedItems.has(code));
|
||||||
|
if (codes.length === 0) {
|
||||||
|
selectedBody.innerHTML = `<tr><td colspan="${colEmpty}" class="text-center text-gray-400 py-4">아래 "발주 등록 종류"에서 봉투를 선택해 주세요.</td></tr>`;
|
||||||
|
setActiveRow(null);
|
||||||
|
updateTotals();
|
||||||
|
updateReferenceSelectionUi();
|
||||||
|
applyModeLock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedBody.innerHTML = codes.map((code, idx) => {
|
||||||
|
const meta = bagMeta[code];
|
||||||
|
const qtyBox = Math.max(0, parseInt(String(selectedItems.get(code)?.qtyBox ?? 0), 10));
|
||||||
|
const name = meta?.name || code;
|
||||||
|
const up = Number(meta.orderPrice || 0);
|
||||||
|
const priceCell = editMode && changeMode === 'price'
|
||||||
|
? `<td class="text-right pr-1"><input type="number" name="item_unit_price[]" step="0.01" min="0" class="item-unit-price-input border border-gray-300 rounded px-1 py-0.5 w-full text-sm text-right" value="${up}" /></td>`
|
||||||
|
: `<td class="text-right pr-2 item-unit-price">0</td>`;
|
||||||
|
const extraCols = editMode
|
||||||
|
? `<td class="text-right pr-2 item-sel-qty">0</td><td class="text-center text-[11px] item-lot-cell leading-tight text-gray-700">생성<br /><span class="font-mono">${escapeHtml(orderLotNo || '—')}</span></td>`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr data-item-row data-code="${escapeHtml(code)}" class="cursor-pointer">
|
||||||
|
<td class="text-center">${idx + 1}</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<button type="button" class="js-remove-selected text-xs text-red-600 hover:underline" data-code="${escapeHtml(code)}">해제</button>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2">
|
||||||
|
${escapeHtml(name)}
|
||||||
|
<input type="hidden" name="item_bag_code[]" value="${escapeHtml(code)}" />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input name="item_qty_box[]" type="number" min="0" step="1" value="${qtyBox}" class="item-qty-box border border-gray-300 rounded px-2 py-1 text-sm w-full text-right leading-tight" />
|
||||||
|
<p class="text-[11px] text-gray-500 mt-1 item-sheet-help">낱장 0장</p>
|
||||||
|
</td>
|
||||||
|
${priceCell}
|
||||||
|
<td class="text-right pr-2 item-qty-sheet">0</td>
|
||||||
|
${extraCols}
|
||||||
|
<td class="text-right pr-2 item-amount">0</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
if (!activeCode || !selectedItems.has(activeCode)) {
|
||||||
|
activeCode = codes[0];
|
||||||
|
}
|
||||||
|
setActiveRow(activeCode);
|
||||||
|
updateTotals();
|
||||||
|
updateReferenceSelectionUi();
|
||||||
|
applyModeLock();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSelection = (code) => {
|
||||||
|
if (!code || !bagMeta[code]) return;
|
||||||
|
if (selectedItems.has(code)) {
|
||||||
|
selectedItems.delete(code);
|
||||||
|
if (activeCode === code) activeCode = null;
|
||||||
|
} else {
|
||||||
|
selectedItems.set(code, { qtyBox: 0 });
|
||||||
|
activeCode = code;
|
||||||
|
}
|
||||||
|
renderSelectedRows();
|
||||||
|
};
|
||||||
|
|
||||||
|
initialSelectedItems.forEach((item) => {
|
||||||
|
if (!item || !item.code || !bagMeta[item.code]) return;
|
||||||
|
selectedItems.set(item.code, { qtyBox: Math.max(0, parseInt(String(item.qtyBox ?? 0), 10)) });
|
||||||
|
activeCode = item.code;
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedBody.addEventListener('click', (event) => {
|
||||||
|
const removeButton = event.target.closest('.js-remove-selected');
|
||||||
|
if (removeButton) {
|
||||||
|
toggleSelection(removeButton.dataset.code || '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = event.target.closest('tr[data-item-row]');
|
||||||
|
if (!row) return;
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
setActiveRow(code);
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
if (qtyInput) qtyInput.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedBody.addEventListener('input', (event) => {
|
||||||
|
const qtyInput = event.target.closest('.item-qty-box');
|
||||||
|
const priceField = event.target.closest('.item-unit-price-input');
|
||||||
|
if (qtyInput) {
|
||||||
|
const row = qtyInput.closest('tr[data-item-row]');
|
||||||
|
if (!row) return;
|
||||||
|
const code = row.dataset.code || '';
|
||||||
|
selectedItems.set(code, { qtyBox: Math.max(0, parseInt(qtyInput.value || '0', 10)) });
|
||||||
|
updateTotals();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (priceField) {
|
||||||
|
updateTotals();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
referenceRows.forEach((row) => {
|
||||||
|
row.addEventListener('click', (event) => {
|
||||||
|
const button = event.target.closest('.js-toggle-bag');
|
||||||
|
if (button) {
|
||||||
|
toggleSelection(button.dataset.code || '');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.target.closest('td')) {
|
||||||
|
toggleSelection(row.dataset.code || '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (monthInput) monthInput.addEventListener('change', () => { syncDateFromMonth(); updateTotals(); });
|
||||||
|
if (orderDateInput) orderDateInput.addEventListener('change', syncMonthFromDate);
|
||||||
|
if (feeRateSummaryInput) {
|
||||||
|
feeRateSummaryInput.addEventListener('input', () => {
|
||||||
|
updateTotals();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderForm) {
|
||||||
|
orderForm.addEventListener('submit', (event) => {
|
||||||
|
const hasValidItem = Array.from(selectedBody.querySelectorAll('tr[data-item-row]')).some((row) => {
|
||||||
|
const qtyInput = row.querySelector('.item-qty-box');
|
||||||
|
return Math.max(0, parseInt(qtyInput?.value || '0', 10)) > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasValidItem) {
|
||||||
|
event.preventDefault();
|
||||||
|
alert('봉투를 선택하고 수량을 1 이상 입력해 주세요.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
syncMonthFromDate();
|
||||||
|
updateMonthKoLabel();
|
||||||
|
renderSelectedRows();
|
||||||
|
|
||||||
|
if (editMode) {
|
||||||
|
document.querySelectorAll('input[name="bo_change_mode"]').forEach((r) => {
|
||||||
|
r.addEventListener('change', () => {
|
||||||
|
const u = new URL(window.location.href);
|
||||||
|
u.searchParams.set('change_mode', r.value);
|
||||||
|
window.location.href = u.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
<?php endif; ?>
|
||||||
|
|||||||
396
app/Views/bag/inventory_inspection_select.php
Normal file
396
app/Views/bag/inventory_inspection_select.php
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
<?php
|
||||||
|
$startDate = (string) ($startDate ?? date('Y-m-01'));
|
||||||
|
$endDate = (string) ($endDate ?? date('Y-m-d'));
|
||||||
|
$workDate = (string) ($workDate ?? date('Y-m-d'));
|
||||||
|
$itemCode = (string) ($itemCode ?? '');
|
||||||
|
$viewType = (string) ($viewType ?? 'box');
|
||||||
|
$inspectionRuns = is_array($inspectionRuns ?? null) ? $inspectionRuns : [];
|
||||||
|
$popupItems = is_array($popupItems ?? null) ? $popupItems : [];
|
||||||
|
$items = is_array($items ?? null) ? $items : [];
|
||||||
|
$overviewRows = is_array($overviewRows ?? null) ? $overviewRows : [];
|
||||||
|
$selectedInspectionItemId = (int) ($selectedInspectionItemId ?? 0);
|
||||||
|
$selectedInspectionId = (int) ($selectedInspectionId ?? 0);
|
||||||
|
$boxRows = is_array($boxRows ?? null) ? $boxRows : [];
|
||||||
|
$selectedBoxCode = (string) ($selectedBoxCode ?? '');
|
||||||
|
$selectedPackCode = (string) ($selectedPackCode ?? '');
|
||||||
|
$sheetRows = is_array($sheetRows ?? null) ? $sheetRows : [];
|
||||||
|
$overviewTotalQty = 0;
|
||||||
|
foreach ($overviewRows as $row) {
|
||||||
|
$overviewTotalQty += (int) ($row['bisi_system_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$overviewTotalActual = 0;
|
||||||
|
foreach ($overviewRows as $row) {
|
||||||
|
$overviewTotalActual += (int) ($row['bisi_actual_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$overviewTotalDiff = $overviewTotalActual - $overviewTotalQty;
|
||||||
|
$packTotalQty = 0;
|
||||||
|
foreach ($boxRows as $row) {
|
||||||
|
$packTotalQty += (int) ($row['bisp_sheet_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$packTotalActual = 0;
|
||||||
|
foreach ($boxRows as $row) {
|
||||||
|
$packTotalActual += (int) ($row['bisp_actual_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$packTotalDiff = $packTotalActual - $packTotalQty;
|
||||||
|
$sheetTotalQty = 0;
|
||||||
|
foreach ($sheetRows as $row) {
|
||||||
|
$sheetTotalQty += (int) ($row['biss_system_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$sheetTotalActual = 0;
|
||||||
|
foreach ($sheetRows as $row) {
|
||||||
|
$sheetTotalActual += (int) ($row['biss_actual_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$sheetTotalDiff = $sheetTotalActual - $sheetTotalQty;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<section class="border border-gray-300 bg-white p-2">
|
||||||
|
<form method="get" class="flex flex-wrap items-end justify-between gap-2 text-sm">
|
||||||
|
<div class="flex flex-wrap items-end gap-2">
|
||||||
|
<label class="font-bold text-gray-700">실사기간</label>
|
||||||
|
<input type="date" name="start_date" value="<?= esc($startDate) ?>" class="border border-gray-300 rounded px-2 py-1">
|
||||||
|
<span>~</span>
|
||||||
|
<input type="date" name="end_date" value="<?= esc($endDate) ?>" class="border border-gray-300 rounded px-2 py-1">
|
||||||
|
|
||||||
|
<label class="font-bold text-gray-700 ml-2">실사품목</label>
|
||||||
|
<select name="item_code" class="border border-gray-300 rounded px-2 py-1 min-w-[11rem]">
|
||||||
|
<option value="">전체</option>
|
||||||
|
<?php foreach ($items as $it): ?>
|
||||||
|
<?php $code = (string) ($it['bag_code'] ?? ''); ?>
|
||||||
|
<option value="<?= esc($code) ?>" <?= $itemCode === $code ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($it['bag_name'] ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="font-bold text-gray-700 ml-2">조회구분</label>
|
||||||
|
<select name="view_type" class="border border-gray-300 rounded px-2 py-1 min-w-[7rem]">
|
||||||
|
<option value="box" <?= $viewType === 'box' ? 'selected' : '' ?>>박스별</option>
|
||||||
|
<option value="pack" <?= $viewType === 'pack' ? 'selected' : '' ?>>팩별</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-3 py-1 rounded-sm">조회</button>
|
||||||
|
<button type="button" id="open-inspection-popup" class="border border-blue-300 text-blue-700 px-3 py-1 rounded-sm hover:bg-blue-50">실사 선별</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p class="mt-1 text-xs text-blue-700">※ 해당 박스와 팩을 클릭하면 팩과 낱장이 조회됩니다.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
|
||||||
|
<div class="overflow-auto max-h-[500px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-24">실사일자</th>
|
||||||
|
<th>종류</th>
|
||||||
|
<th class="w-40">박스</th>
|
||||||
|
<th class="w-20">전산재고</th>
|
||||||
|
<th class="w-20">실사재고</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($overviewRows !== []): ?>
|
||||||
|
<?php $prevBagName = null; ?>
|
||||||
|
<?php foreach ($overviewRows as $row): ?>
|
||||||
|
<?php
|
||||||
|
$itemId = (int) ($row['bisi_idx'] ?? 0);
|
||||||
|
$bagName = (string) ($row['bisi_bag_name'] ?? '');
|
||||||
|
$showBagName = $prevBagName !== $bagName;
|
||||||
|
$prevBagName = $bagName;
|
||||||
|
$boxCode = (string) ($row['box_code'] ?? '');
|
||||||
|
$isSelected = $itemId === $selectedInspectionItemId
|
||||||
|
&& ($selectedBoxCode === '' || $selectedBoxCode === $boxCode);
|
||||||
|
$url = base_url('bag/inventory/inspection-work?' . http_build_query([
|
||||||
|
'start_date' => $startDate,
|
||||||
|
'end_date' => $endDate,
|
||||||
|
'bis_id' => $selectedInspectionId,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'view_type' => $viewType,
|
||||||
|
'sel_item_id' => $itemId,
|
||||||
|
'sel_box_code' => $boxCode,
|
||||||
|
'sel_pack_code' => '',
|
||||||
|
]));
|
||||||
|
?>
|
||||||
|
<tr class="<?= $isSelected ? 'bg-blue-100' : 'cursor-pointer hover:bg-blue-50' ?>" onclick="window.location.href='<?= esc($url, 'attr') ?>'">
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bis_work_date'] ?? '')) ?></td>
|
||||||
|
<td class="pl-2"><?= $showBagName ? esc($bagName) : '' ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['box_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) ($row['bisi_system_qty'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) ($row['bisi_actual_qty'] ?? 0)) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="5" class="text-center text-gray-400 py-4">조회 결과가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="3">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($overviewTotalQty) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($overviewTotalActual) ?></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(선택 박스 조회)</div>
|
||||||
|
<form method="post" action="<?= base_url('bag/inventory/inspection-select/save') ?>" id="inspection-save-form">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" name="bisi_idx" value="<?= esc((string) $selectedInspectionItemId) ?>">
|
||||||
|
<input type="hidden" name="bis_id" value="<?= esc((string) $selectedInspectionId) ?>">
|
||||||
|
<input type="hidden" name="start_date" value="<?= esc($startDate) ?>">
|
||||||
|
<input type="hidden" name="end_date" value="<?= esc($endDate) ?>">
|
||||||
|
<input type="hidden" name="item_code" value="<?= esc($itemCode) ?>">
|
||||||
|
<input type="hidden" name="view_type" value="<?= esc($viewType) ?>">
|
||||||
|
<input type="hidden" name="sel_item_id" value="<?= esc((string) $selectedInspectionItemId) ?>">
|
||||||
|
<input type="hidden" name="sel_box_code" value="<?= esc($selectedBoxCode) ?>">
|
||||||
|
<input type="hidden" name="sel_pack_code" value="<?= esc($selectedPackCode) ?>">
|
||||||
|
<input type="hidden" name="pack_actual_json" value="">
|
||||||
|
<div class="overflow-auto max-h-[260px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>팩코드</th>
|
||||||
|
<th class="w-16">포장량</th>
|
||||||
|
<th class="w-16">재고</th>
|
||||||
|
<th class="w-20">실사재고</th>
|
||||||
|
<th class="w-16">차이</th>
|
||||||
|
<th>낱장(시작)</th>
|
||||||
|
<th>낱장(끝)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($boxRows !== []): ?>
|
||||||
|
<?php foreach ($boxRows as $row): ?>
|
||||||
|
<?php
|
||||||
|
$code = (string) ($row['bisp_box_code'] ?? '');
|
||||||
|
$packCode = (string) ($row['bisp_pack_code'] ?? '');
|
||||||
|
$systemQty = (int) ($row['bisp_sheet_qty'] ?? 0);
|
||||||
|
$actualQty = isset($row['bisp_actual_qty']) ? (int) $row['bisp_actual_qty'] : $systemQty;
|
||||||
|
$url = base_url('bag/inventory/inspection-work?' . http_build_query([
|
||||||
|
'start_date' => $startDate,
|
||||||
|
'end_date' => $endDate,
|
||||||
|
'bis_id' => $selectedInspectionId,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'view_type' => $viewType,
|
||||||
|
'sel_item_id' => $selectedInspectionItemId,
|
||||||
|
'sel_box_code' => $code,
|
||||||
|
'sel_pack_code' => $packCode,
|
||||||
|
]));
|
||||||
|
$isSelected = $selectedBoxCode === $code && $selectedPackCode === $packCode;
|
||||||
|
?>
|
||||||
|
<tr class="<?= $isSelected ? 'bg-blue-100' : 'cursor-pointer hover:bg-blue-50' ?>" onclick="window.location.href='<?= esc($url, 'attr') ?>'">
|
||||||
|
<td class="pl-2"><?= esc($packCode) ?></td>
|
||||||
|
<td class="text-center js-pack-system"><?= number_format($systemQty) ?></td>
|
||||||
|
<td class="text-right pr-2 js-pack-stock"><?= number_format($systemQty) ?></td>
|
||||||
|
<td class="text-right pr-2">
|
||||||
|
<input type="number" min="0"
|
||||||
|
value="<?= esc((string) $actualQty) ?>"
|
||||||
|
data-pack-idx="<?= esc((string) ($row['bisp_idx'] ?? 0), 'attr') ?>"
|
||||||
|
data-original-value="<?= esc((string) $systemQty, 'attr') ?>"
|
||||||
|
data-system-qty="<?= esc((string) $systemQty, 'attr') ?>"
|
||||||
|
class="border border-gray-300 rounded px-1 py-0.5 w-20 text-right"
|
||||||
|
onclick="event.stopPropagation();">
|
||||||
|
</td>
|
||||||
|
<?php $diff = (int) ($row['bisp_diff_qty'] ?? 0); ?>
|
||||||
|
<td class="text-right pr-2 js-pack-diff <?= $diff === 0 ? '' : ($diff > 0 ? 'text-blue-700' : 'text-red-700') ?>"><?= number_format($diff) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bisp_sheet_start_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bisp_sheet_end_code'] ?? '')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="7" class="text-center text-gray-400 py-4">선택된 품목이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="2">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($packTotalQty) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($packTotalActual) ?></td>
|
||||||
|
<td class="text-right pr-2 <?= $packTotalDiff === 0 ? '' : ($packTotalDiff > 0 ? 'text-blue-700' : 'text-red-700') ?>"><?= number_format($packTotalDiff) ?></td>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="p-2 border-t border-gray-300 flex items-center justify-between">
|
||||||
|
<span class="text-xs text-gray-500">선택 품목의 팩 실사수량을 입력 후 저장하세요.</span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-3 py-1 rounded-sm text-sm">실사 저장</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(선택 낱장 코드 조회)</div>
|
||||||
|
<div class="overflow-auto max-h-[240px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">No</th>
|
||||||
|
<th>낱장</th>
|
||||||
|
<th class="w-16">전산</th>
|
||||||
|
<th class="w-16">실사</th>
|
||||||
|
<th class="w-16">차이</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($sheetRows !== []): ?>
|
||||||
|
<?php foreach ($sheetRows as $row): ?>
|
||||||
|
<?php
|
||||||
|
$sSystem = (int) ($row['biss_system_qty'] ?? 1);
|
||||||
|
$sActual = (int) ($row['biss_actual_qty'] ?? 0);
|
||||||
|
$sDiff = (int) ($row['biss_diff_qty'] ?? 0);
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['no'] ?? '')) ?></td>
|
||||||
|
<td class="pl-2"><?= esc((string) ($row['biss_sheet_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($sSystem) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($sActual) ?></td>
|
||||||
|
<td class="text-right pr-2 <?= $sDiff === 0 ? '' : ($sDiff > 0 ? 'text-blue-700' : 'text-red-700') ?>"><?= number_format($sDiff) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="5" class="text-center text-gray-400 py-4">선택된 팩/박스가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="2">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($sheetTotalQty) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($sheetTotalActual) ?></td>
|
||||||
|
<td class="text-right pr-2 <?= $sheetTotalDiff === 0 ? '' : ($sheetTotalDiff > 0 ? 'text-blue-700' : 'text-red-700') ?>"><?= number_format($sheetTotalDiff) ?></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="border border-gray-300 bg-white p-2 text-sm text-gray-600">
|
||||||
|
실사 저장 시 차이수량이 즉시 장부 재고에 반영됩니다.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="inspection-popup" class="fixed inset-0 bg-black/40 hidden items-center justify-center z-[999]">
|
||||||
|
<div class="bg-white border border-gray-400 w-[min(720px,95vw)] max-h-[90vh] overflow-auto p-4">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<h3 class="text-lg font-bold">실사 선별</h3>
|
||||||
|
<button type="button" id="close-inspection-popup" class="text-gray-600 hover:text-gray-900">닫기</button>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="<?= base_url('bag/inventory/inspection-run') ?>" id="inspection-run-form" class="space-y-3">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="font-bold text-gray-700 text-sm">작업 일자</label>
|
||||||
|
<input type="date" name="work_date" value="<?= esc($workDate) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||||
|
</div>
|
||||||
|
<p class="text-red-600 font-semibold">바코드가 없는 봉투는 실사에서 제외 됩니다.</p>
|
||||||
|
<div class="overflow-auto border border-gray-300 max-h-[55vh]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr><th>종류</th><th class="w-20">선택구분</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($popupItems as $row): ?>
|
||||||
|
<?php $hasBarcode = (bool) ($row['has_barcode'] ?? false); ?>
|
||||||
|
<tr class="<?= $hasBarcode ? '' : 'bg-gray-50 text-gray-400' ?>">
|
||||||
|
<td class="pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" name="bag_codes[]" value="<?= esc((string) ($row['bag_code'] ?? ''), 'attr') ?>" <?= $hasBarcode ? '' : 'disabled' ?>>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-5 py-1.5 rounded-sm text-sm">실행</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const popup = document.getElementById('inspection-popup');
|
||||||
|
const openBtn = document.getElementById('open-inspection-popup');
|
||||||
|
const closeBtn = document.getElementById('close-inspection-popup');
|
||||||
|
if (openBtn && popup) {
|
||||||
|
openBtn.addEventListener('click', () => {
|
||||||
|
popup.classList.remove('hidden');
|
||||||
|
popup.classList.add('flex');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (closeBtn && popup) {
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
popup.classList.add('hidden');
|
||||||
|
popup.classList.remove('flex');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const form = document.getElementById('inspection-run-form');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', (event) => {
|
||||||
|
if (!window.confirm('전산 선별 처리를 실행하시겠습니까?')) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveForm = document.getElementById('inspection-save-form');
|
||||||
|
if (saveForm) {
|
||||||
|
const formatNumber = (value) => {
|
||||||
|
const n = Number.isFinite(value) ? value : 0;
|
||||||
|
return n.toLocaleString('ko-KR');
|
||||||
|
};
|
||||||
|
const updateDiff = (input) => {
|
||||||
|
const row = input.closest('tr');
|
||||||
|
if (!row) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const system = parseInt(String(input.getAttribute('data-system-qty') ?? '0'), 10) || 0;
|
||||||
|
const actual = Math.max(0, parseInt(String(input.value ?? '0'), 10) || 0);
|
||||||
|
const diff = actual - system;
|
||||||
|
const diffCell = row.querySelector('.js-pack-diff');
|
||||||
|
if (!diffCell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diffCell.textContent = formatNumber(diff);
|
||||||
|
diffCell.classList.remove('text-blue-700', 'text-red-700');
|
||||||
|
if (diff > 0) {
|
||||||
|
diffCell.classList.add('text-blue-700');
|
||||||
|
} else if (diff < 0) {
|
||||||
|
diffCell.classList.add('text-red-700');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const qtyInputs = saveForm.querySelectorAll('input[data-pack-idx]');
|
||||||
|
qtyInputs.forEach((input) => {
|
||||||
|
input.addEventListener('input', () => updateDiff(input));
|
||||||
|
updateDiff(input);
|
||||||
|
});
|
||||||
|
|
||||||
|
saveForm.addEventListener('submit', () => {
|
||||||
|
const payload = {};
|
||||||
|
qtyInputs.forEach((input) => {
|
||||||
|
const idx = String(input.getAttribute('data-pack-idx') ?? '').trim();
|
||||||
|
if (idx === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload[idx] = Math.max(0, parseInt(String(input.value ?? '0'), 10) || 0);
|
||||||
|
});
|
||||||
|
const jsonInput = saveForm.querySelector('input[name="pack_actual_json"]');
|
||||||
|
if (jsonInput) {
|
||||||
|
jsonInput.value = JSON.stringify(payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
273
app/Views/bag/inventory_inspection_select_overview.php
Normal file
273
app/Views/bag/inventory_inspection_select_overview.php
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
<?php
|
||||||
|
$startDate = (string) ($startDate ?? date('Y-m-01'));
|
||||||
|
$endDate = (string) ($endDate ?? date('Y-m-d'));
|
||||||
|
$workDate = (string) ($workDate ?? date('Y-m-d'));
|
||||||
|
$itemCode = (string) ($itemCode ?? '');
|
||||||
|
$viewType = (string) ($viewType ?? 'box');
|
||||||
|
$inspectionRuns = is_array($inspectionRuns ?? null) ? $inspectionRuns : [];
|
||||||
|
$items = is_array($items ?? null) ? $items : [];
|
||||||
|
$popupItems = is_array($popupItems ?? null) ? $popupItems : [];
|
||||||
|
$overviewRows = is_array($overviewRows ?? null) ? $overviewRows : [];
|
||||||
|
$boxRows = is_array($boxRows ?? null) ? $boxRows : [];
|
||||||
|
$sheetRows = is_array($sheetRows ?? null) ? $sheetRows : [];
|
||||||
|
$selectedInspectionId = (int) ($selectedInspectionId ?? 0);
|
||||||
|
$selectedInspectionItemId = (int) ($selectedInspectionItemId ?? 0);
|
||||||
|
$selectedBoxCode = (string) ($selectedBoxCode ?? '');
|
||||||
|
$selectedPackCode = (string) ($selectedPackCode ?? '');
|
||||||
|
$overviewTotalQty = 0;
|
||||||
|
foreach ($overviewRows as $row) {
|
||||||
|
$overviewTotalQty += (int) ($row['bisi_system_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$packTotalQty = 0;
|
||||||
|
foreach ($boxRows as $row) {
|
||||||
|
$packTotalQty += (int) ($row['bisp_sheet_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
$sheetTotalQty = 0;
|
||||||
|
foreach ($sheetRows as $row) {
|
||||||
|
$sheetTotalQty += (int) ($row['biss_system_qty'] ?? 0);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<section class="border border-gray-300 bg-white p-2">
|
||||||
|
<form method="get" class="flex flex-wrap items-end justify-between gap-2 text-sm">
|
||||||
|
<div class="flex flex-wrap items-end gap-2">
|
||||||
|
<label class="font-bold text-gray-700">실사기간</label>
|
||||||
|
<input type="date" name="start_date" value="<?= esc($startDate) ?>" class="border border-gray-300 rounded px-2 py-1">
|
||||||
|
<span>~</span>
|
||||||
|
<input type="date" name="end_date" value="<?= esc($endDate) ?>" class="border border-gray-300 rounded px-2 py-1">
|
||||||
|
|
||||||
|
<label class="font-bold text-gray-700 ml-2">실사품목</label>
|
||||||
|
<select name="item_code" class="border border-gray-300 rounded px-2 py-1 min-w-[11rem]">
|
||||||
|
<option value="">전체</option>
|
||||||
|
<?php foreach ($items as $it): ?>
|
||||||
|
<?php $code = (string) ($it['bag_code'] ?? ''); ?>
|
||||||
|
<option value="<?= esc($code) ?>" <?= $itemCode === $code ? 'selected' : '' ?>><?= esc((string) ($it['bag_name'] ?? '')) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="font-bold text-gray-700 ml-2">조회구분</label>
|
||||||
|
<select name="view_type" class="border border-gray-300 rounded px-2 py-1 min-w-[7rem]">
|
||||||
|
<option value="box" <?= $viewType === 'box' ? 'selected' : '' ?>>박스별</option>
|
||||||
|
<option value="pack" <?= $viewType === 'pack' ? 'selected' : '' ?>>팩별</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-3 py-1 rounded-sm">조회</button>
|
||||||
|
<button type="button" id="open-inspection-popup" class="border border-blue-300 text-blue-700 px-3 py-1 rounded-sm hover:bg-blue-50">실사 선별</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<p class="mt-1 text-xs text-blue-700">※ 실사 선별 처리 결과를 조회하는 화면입니다(읽기 전용).</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="grid grid-cols-1 xl:grid-cols-2 gap-2">
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별자</div>
|
||||||
|
<div class="overflow-auto max-h-[500px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-24">실사일자</th>
|
||||||
|
<th>종류</th>
|
||||||
|
<th class="w-40">박스</th>
|
||||||
|
<th class="w-20">전산재고</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($overviewRows !== []): ?>
|
||||||
|
<?php $prevBagName = null; ?>
|
||||||
|
<?php foreach ($overviewRows as $row): ?>
|
||||||
|
<?php
|
||||||
|
$itemId = (int) ($row['bisi_idx'] ?? 0);
|
||||||
|
$bagName = (string) ($row['bisi_bag_name'] ?? '');
|
||||||
|
$showBagName = $prevBagName !== $bagName;
|
||||||
|
$prevBagName = $bagName;
|
||||||
|
$isSelected = $itemId === $selectedInspectionItemId;
|
||||||
|
$url = base_url('bag/inventory/inspection-select?' . http_build_query([
|
||||||
|
'start_date' => $startDate,
|
||||||
|
'end_date' => $endDate,
|
||||||
|
'bis_id' => $selectedInspectionId,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'view_type' => $viewType,
|
||||||
|
'sel_item_id' => $itemId,
|
||||||
|
]));
|
||||||
|
?>
|
||||||
|
<tr class="<?= $isSelected ? 'bg-blue-100' : 'cursor-pointer hover:bg-blue-50' ?>" onclick="window.location.href='<?= esc($url, 'attr') ?>'">
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bis_work_date'] ?? '')) ?></td>
|
||||||
|
<td class="pl-2"><?= $showBagName ? esc($bagName) : '' ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['box_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) ($row['bisi_system_qty'] ?? 0)) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="4" class="text-center text-gray-400 py-4">조회 결과가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="3">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($overviewTotalQty) ?></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 품목(읽기 전용)</div>
|
||||||
|
<div class="overflow-auto max-h-[260px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>팩코드</th>
|
||||||
|
<th class="w-16">포장량</th>
|
||||||
|
<th class="w-16">재고</th>
|
||||||
|
<th>낱장(시작)</th>
|
||||||
|
<th>낱장(끝)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($boxRows !== []): ?>
|
||||||
|
<?php foreach ($boxRows as $row): ?>
|
||||||
|
<?php
|
||||||
|
$code = (string) ($row['bisp_box_code'] ?? '');
|
||||||
|
$packCode = (string) ($row['bisp_pack_code'] ?? '');
|
||||||
|
$systemQty = (int) ($row['bisp_sheet_qty'] ?? 0);
|
||||||
|
$url = base_url('bag/inventory/inspection-select?' . http_build_query([
|
||||||
|
'start_date' => $startDate,
|
||||||
|
'end_date' => $endDate,
|
||||||
|
'bis_id' => $selectedInspectionId,
|
||||||
|
'item_code' => $itemCode,
|
||||||
|
'view_type' => $viewType,
|
||||||
|
'sel_item_id' => $selectedInspectionItemId,
|
||||||
|
'sel_box_code' => $code,
|
||||||
|
'sel_pack_code' => $packCode,
|
||||||
|
]));
|
||||||
|
$isSelected = $selectedBoxCode === $code && $selectedPackCode === $packCode;
|
||||||
|
?>
|
||||||
|
<tr class="<?= $isSelected ? 'bg-blue-100' : 'cursor-pointer hover:bg-blue-50' ?>" onclick="window.location.href='<?= esc($url, 'attr') ?>'">
|
||||||
|
<td class="pl-2"><?= esc($packCode) ?></td>
|
||||||
|
<td class="text-center"><?= number_format($systemQty) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($systemQty) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bisp_sheet_start_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['bisp_sheet_end_code'] ?? '')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="5" class="text-center text-gray-400 py-4">선택된 품목이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="2">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($packTotalQty) ?></td>
|
||||||
|
<td colspan="2"></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">실사 선별 내용(읽기 전용)</div>
|
||||||
|
<div class="overflow-auto max-h-[240px]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12">No</th>
|
||||||
|
<th>낱장</th>
|
||||||
|
<th class="w-16">수량</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php if ($sheetRows !== []): ?>
|
||||||
|
<?php foreach ($sheetRows as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['no'] ?? '')) ?></td>
|
||||||
|
<td class="pl-2"><?= esc((string) ($row['biss_sheet_code'] ?? '')) ?></td>
|
||||||
|
<td class="text-right pr-2"><?= number_format((int) ($row['biss_system_qty'] ?? 0)) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<tr><td colspan="3" class="text-center text-gray-400 py-4">선택된 팩/박스가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-semibold">
|
||||||
|
<td class="text-center" colspan="2">합계</td>
|
||||||
|
<td class="text-right pr-2"><?= number_format($sheetTotalQty) ?></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="inspection-popup" class="fixed inset-0 bg-black/40 hidden items-center justify-center z-[999]">
|
||||||
|
<div class="bg-white border border-gray-400 w-[min(720px,95vw)] max-h-[90vh] overflow-auto p-4">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<h3 class="text-lg font-bold">실사 선별</h3>
|
||||||
|
<button type="button" id="close-inspection-popup" class="text-gray-600 hover:text-gray-900">닫기</button>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="<?= base_url('bag/inventory/inspection-run') ?>" id="inspection-run-form" class="space-y-3">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="font-bold text-gray-700 text-sm">작업 일자</label>
|
||||||
|
<input type="date" name="work_date" value="<?= esc($workDate) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||||
|
</div>
|
||||||
|
<p class="text-red-600 font-semibold">바코드가 없는 봉투는 실사에서 제외 됩니다.</p>
|
||||||
|
<div class="overflow-auto border border-gray-300 max-h-[55vh]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr><th>종류</th><th class="w-20">선택구분</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($popupItems as $row): ?>
|
||||||
|
<?php $hasBarcode = (bool) ($row['has_barcode'] ?? false); ?>
|
||||||
|
<tr class="<?= $hasBarcode ? '' : 'bg-gray-50 text-gray-400' ?>">
|
||||||
|
<td class="pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" name="bag_codes[]" value="<?= esc((string) ($row['bag_code'] ?? ''), 'attr') ?>" <?= $hasBarcode ? '' : 'disabled' ?>>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end gap-2">
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-5 py-1.5 rounded-sm text-sm">실행</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const popup = document.getElementById('inspection-popup');
|
||||||
|
const openBtn = document.getElementById('open-inspection-popup');
|
||||||
|
const closeBtn = document.getElementById('close-inspection-popup');
|
||||||
|
if (openBtn && popup) {
|
||||||
|
openBtn.addEventListener('click', () => {
|
||||||
|
popup.classList.remove('hidden');
|
||||||
|
popup.classList.add('flex');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (closeBtn && popup) {
|
||||||
|
closeBtn.addEventListener('click', () => {
|
||||||
|
popup.classList.add('hidden');
|
||||||
|
popup.classList.remove('flex');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const form = document.getElementById('inspection-run-form');
|
||||||
|
if (form) {
|
||||||
|
form.addEventListener('submit', (event) => {
|
||||||
|
if (!window.confirm('전산 선별 처리를 실행하시겠습니까?')) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
106
app/Views/bag/order_change.php
Normal file
106
app/Views/bag/order_change.php
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?php
|
||||||
|
$month = $month ?? date('Y-m');
|
||||||
|
$hubMode = in_array(($hubMode ?? 'meta'), ['price', 'meta', 'delete'], true) ? (string) ($hubMode ?? 'meta') : 'meta';
|
||||||
|
$monthOptionValues = [];
|
||||||
|
$y = (int) substr($month, 0, 4);
|
||||||
|
for ($year = $y - 2; $year <= $y + 2; $year++) {
|
||||||
|
for ($m = 1; $m <= 12; $m++) {
|
||||||
|
$monthValue = sprintf('%04d-%02d', $year, $m);
|
||||||
|
$monthOptionValues[] = ['value' => $monthValue, 'label' => $year . '년 ' . $m . '월'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<span class="text-sm font-bold text-gray-700">발주 변경</span>
|
||||||
|
<a href="<?= base_url('bag/purchase-inbound') ?>" class="text-sm text-gray-600 hover:underline">발주 입고 관리로</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="mt-2 space-y-3">
|
||||||
|
<?php if (session()->getFlashdata('success')): ?>
|
||||||
|
<div class="border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (session()->getFlashdata('error')): ?>
|
||||||
|
<div class="border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="get" action="<?= base_url('bag/order/change') ?>" class="border border-gray-300 bg-white p-3 flex flex-wrap items-end gap-4">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<label for="hub_month" class="text-sm font-bold text-gray-700">발주월</label>
|
||||||
|
<select id="hub_month" name="month" class="border border-gray-300 rounded px-2 py-1 w-44 text-sm">
|
||||||
|
<?php foreach ($monthOptionValues as $opt): ?>
|
||||||
|
<option value="<?= esc($opt['value']) ?>" <?= $opt['value'] === $month ? 'selected' : '' ?>><?= esc($opt['label']) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<fieldset class="border border-gray-200 rounded px-3 py-2">
|
||||||
|
<legend class="text-xs font-bold text-gray-600 px-1">변경 구분</legend>
|
||||||
|
<div class="flex flex-wrap gap-x-4 gap-y-1 text-sm">
|
||||||
|
<label class="inline-flex items-center gap-1 cursor-pointer">
|
||||||
|
<input type="radio" name="hub_mode" value="price" <?= $hubMode === 'price' ? 'checked' : '' ?> />
|
||||||
|
<span>발주·도매·판매 단가</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center gap-1 cursor-pointer">
|
||||||
|
<input type="radio" name="hub_mode" value="meta" <?= $hubMode === 'meta' ? 'checked' : '' ?> />
|
||||||
|
<span>업체·수수료·협회·발주</span>
|
||||||
|
</label>
|
||||||
|
<label class="inline-flex items-center gap-1 cursor-pointer">
|
||||||
|
<input type="radio" name="hub_mode" value="delete" <?= $hubMode === 'delete' ? 'checked' : '' ?> />
|
||||||
|
<span>발주 삭제</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shadow hover:opacity-90">조회</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-2">
|
||||||
|
<section class="lg:col-span-5 border border-gray-300 bg-white">
|
||||||
|
<div class="border-b border-gray-300 bg-gray-50 px-2 py-1 text-sm font-bold text-gray-700">발주 목록 (발주일 · 제작업체)</div>
|
||||||
|
<div class="overflow-auto max-h-[min(420px,60vh)]">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-28">발주일</th>
|
||||||
|
<th>제작업체</th>
|
||||||
|
<th class="w-20">상태</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($monthOrders ?? []) as $row): ?>
|
||||||
|
<?php
|
||||||
|
$canEdit = ((string) ($row->bo_status ?? '')) === 'normal';
|
||||||
|
$href = base_url('bag/order/revise/' . (int) $row->bo_idx . '?change_mode=' . rawurlencode($hubMode));
|
||||||
|
?>
|
||||||
|
<tr class="<?= $canEdit ? '' : 'opacity-70' ?>">
|
||||||
|
<td class="text-center">
|
||||||
|
<?php if ($canEdit): ?>
|
||||||
|
<a href="<?= esc($href) ?>" class="text-blue-600 hover:underline font-medium"><?= esc((string) ($row->bo_order_date ?? '')) ?></a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span><?= esc((string) ($row->bo_order_date ?? '')) ?></span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($companyMap[(int) ($row->bo_company_idx ?? 0)] ?? '-')) ?></td>
|
||||||
|
<td class="text-center text-xs"><?= esc((string) ($statusMap[(string) ($row->bo_status ?? '')] ?? $row->bo_status)) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($monthOrders)): ?>
|
||||||
|
<tr><td colspan="3" class="text-center text-gray-400 py-6">해당 월 발주가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="lg:col-span-7 border border-dashed border-gray-300 bg-gray-50 p-4 text-sm text-gray-600">
|
||||||
|
<p class="font-bold text-gray-800 mb-2">안내</p>
|
||||||
|
<ul class="list-disc pl-5 space-y-1">
|
||||||
|
<li><strong class="text-gray-900">발주 삭제</strong>를 선택하고 <strong class="text-gray-900">조회</strong>하면 발주 삭제 화면으로 이동합니다 (해당 월에 정상 발주가 있을 때).</li>
|
||||||
|
<li>그 외 변경 구분에서는 왼쪽 목록에서 <strong class="text-gray-900">발주일</strong>을 클릭하면 선택한 <strong class="text-gray-900">변경 구분</strong>으로 발주 변경 화면이 열립니다.</li>
|
||||||
|
<li><strong class="text-gray-900">발주·도매·판매 단가</strong>: 품목별 단가를 수정해 저장합니다 (발주 변경 시에만).</li>
|
||||||
|
<li><strong class="text-gray-900">업체·수수료·협회·발주</strong>: 발주일·입고처·수량 등을 수정해 저장합니다.</li>
|
||||||
|
<li><strong class="text-gray-900">발주 삭제</strong> 화면에서 목록을 선택해 삭제 처리합니다 (복구 불가에 가깝게 동작하므로 확인 후 진행하세요).</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -58,7 +58,11 @@
|
|||||||
<section class="mt-4">
|
<section class="mt-4">
|
||||||
<div class="flex items-center justify-between mb-2 border-b pb-1">
|
<div class="flex items-center justify-between mb-2 border-b pb-1">
|
||||||
<h3 class="text-base font-bold text-gray-700">입고 현황</h3>
|
<h3 class="text-base font-bold text-gray-700">입고 현황</h3>
|
||||||
<a href="<?= base_url('bag/receiving/create') ?>" class="bg-btn-search text-white px-3 py-1.5 rounded-sm text-sm">입고 처리</a>
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/receiving/scanner') ?>" class="bg-btn-search text-white px-3 py-1.5 rounded-sm text-sm">입고 처리</a>
|
||||||
|
<a href="<?= base_url('bag/receiving/batch') ?>" class="border border-gray-300 text-gray-700 px-3 py-1.5 rounded-sm text-sm hover:bg-gray-50">일괄 입고</a>
|
||||||
|
<a href="<?= base_url('bag/receiving/status') ?>" class="border border-gray-300 text-gray-700 px-3 py-1.5 rounded-sm text-sm hover:bg-gray-50">입고 현황 리포트</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="data-table">
|
<table class="data-table">
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
|
|||||||
119
app/Views/bag/receiving_batch.php
Normal file
119
app/Views/bag/receiving_batch.php
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<span class="text-sm font-bold text-gray-700">일괄 입고</span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/receiving/scanner') ?>" class="border border-gray-300 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">스캐너 입고</a>
|
||||||
|
<a href="<?= base_url('bag/receiving/status') ?>" class="border border-gray-300 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">입고 현황</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (session()->getFlashdata('success')): ?>
|
||||||
|
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (session()->getFlashdata('error')): ?>
|
||||||
|
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<section class="p-2 bg-white border border-gray-300 mt-2">
|
||||||
|
<form method="get" action="<?= base_url('bag/receiving/batch') ?>" class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600">제작 업체</label>
|
||||||
|
<select name="company_idx" class="border border-gray-300 rounded px-2 py-1 text-sm w-56">
|
||||||
|
<option value="0">전 체</option>
|
||||||
|
<?php foreach (($companies ?? []) as $company): ?>
|
||||||
|
<option value="<?= (int) $company->cp_idx ?>" <?= (int) ($companyIdx ?? 0) === (int) $company->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($company->cp_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<form action="<?= base_url('bag/receiving/batch/store') ?>" method="post" class="mt-2 space-y-2">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" name="company_idx" value="<?= (int) ($companyIdx ?? 0) ?>" />
|
||||||
|
|
||||||
|
<section class="p-2 bg-white border border-gray-300">
|
||||||
|
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2 items-end">
|
||||||
|
<div class="xl:col-span-3 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 shrink-0 w-28">인수자 (대행소)</label>
|
||||||
|
<select name="br_receiver_ref" class="border border-gray-300 rounded px-2 py-1 text-sm w-full" required>
|
||||||
|
<?php foreach (($receiverOptions ?? []) as $opt): ?>
|
||||||
|
<option value="<?= esc((string) ($opt['ref'] ?? '')) ?>" <?= (string) ($receiverRef ?? '') === (string) ($opt['ref'] ?? '') ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($opt['label'] ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-3 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 shrink-0 w-28">인계자 (제작업체)</label>
|
||||||
|
<select name="br_sender_idx" class="border border-gray-300 rounded px-2 py-1 text-sm w-full">
|
||||||
|
<?php foreach (($senders ?? []) as $sender): ?>
|
||||||
|
<option value="<?= (int) ($sender->mg_idx ?? 0) ?>" <?= (int) ($senderIdx ?? 0) === (int) ($sender->mg_idx ?? 0) ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($sender->mg_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 w-16">입고일</label>
|
||||||
|
<input type="date" name="br_receive_date" value="<?= esc((string) old('br_receive_date', date('Y-m-d'))) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm w-full" required />
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2">
|
||||||
|
<button type="submit" class="w-full border border-blue-600 text-blue-700 px-2 py-1 rounded-sm text-sm hover:bg-blue-50">입고 처리</button>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2 text-xs text-gray-500">체크한 LOT-봉투 행의 미입고량을 전부 입고 처리합니다.</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 overflow-auto bg-white">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-12"><input type="checkbox" id="check-all" /></th>
|
||||||
|
<th>발주일자</th>
|
||||||
|
<th>봉투종류</th>
|
||||||
|
<th>발주량(매)</th>
|
||||||
|
<th>미입고량(매)</th>
|
||||||
|
<th>제작업체</th>
|
||||||
|
<th>LOT NO</th>
|
||||||
|
<th>발주NO</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($rows ?? []) as $row): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center">
|
||||||
|
<input type="checkbox" name="selected_rows[]" value="<?= esc((string) ($row['row_key'] ?? '')) ?>" />
|
||||||
|
</td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_date'] ?? '')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($row['order_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right text-blue-700 font-semibold"><?= number_format((int) ($row['pending_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['company_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-center font-mono"><?= esc((string) ($row['lot_no'] ?? '')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_no'] ?? '')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($rows ?? [])): ?>
|
||||||
|
<tr><td colspan="8" class="text-center text-gray-400 py-4">일괄 입고 가능한 미입고 행이 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/purchase-inbound') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm">취소</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const all = document.getElementById('check-all');
|
||||||
|
if (!all) return;
|
||||||
|
all.addEventListener('change', () => {
|
||||||
|
document.querySelectorAll('input[name="selected_rows[]"]').forEach((el) => {
|
||||||
|
el.checked = all.checked;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
153
app/Views/bag/receiving_scanner.php
Normal file
153
app/Views/bag/receiving_scanner.php
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<span class="text-sm font-bold text-gray-700">발주 입고(스캐너 대체 수동입력)</span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/receiving/batch') ?>" class="border border-gray-300 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">일괄 입고</a>
|
||||||
|
<a href="<?= base_url('bag/receiving/status') ?>" class="border border-gray-300 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">입고 현황</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php if (session()->getFlashdata('success')): ?>
|
||||||
|
<div class="mt-2 border border-green-300 bg-green-50 text-green-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('success')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php if (session()->getFlashdata('error')): ?>
|
||||||
|
<div class="mt-2 border border-red-300 bg-red-50 text-red-800 px-3 py-2 text-sm"><?= esc((string) session()->getFlashdata('error')) ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<section class="p-2 bg-white border border-gray-300 mt-2">
|
||||||
|
<form method="get" action="<?= base_url('bag/receiving/scanner') ?>" class="flex flex-wrap items-end gap-2">
|
||||||
|
<div class="flex flex-col min-w-[14rem] max-w-[22rem]">
|
||||||
|
<label class="text-xs text-gray-500 mb-1">제작업체</label>
|
||||||
|
<select name="company_idx" class="border border-gray-300 rounded px-2 py-1.5 text-sm bg-white">
|
||||||
|
<option value="0">제작업체 선택</option>
|
||||||
|
<?php foreach (($companies ?? []) as $company): ?>
|
||||||
|
<option value="<?= (int) ($company->cp_idx ?? 0) ?>" <?= (int) ($companyIdx ?? 0) === (int) ($company->cp_idx ?? 0) ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($company->cp_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm shrink-0">조회</button>
|
||||||
|
</form>
|
||||||
|
<?php if ((int) ($companyIdx ?? 0) <= 0): ?>
|
||||||
|
<p class="text-xs text-gray-600 mt-2">제작업체를 선택하면 해당 업체 발주 중 미입고 내역을 조회합니다.</p>
|
||||||
|
<?php elseif (empty($rows ?? [])): ?>
|
||||||
|
<p class="text-xs text-amber-700 mt-2">미입고 잔량이 있는 발주가 없습니다. 발주 등록 후 다시 확인해 주세요.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<form action="<?= base_url('bag/receiving/scanner/store') ?>" method="post" class="mt-2 space-y-2">
|
||||||
|
<?= csrf_field() ?>
|
||||||
|
<input type="hidden" name="company_idx" value="<?= (int) ($companyIdx ?? 0) ?>" />
|
||||||
|
|
||||||
|
<section class="p-2 bg-white border border-gray-300">
|
||||||
|
<div class="grid grid-cols-1 xl:grid-cols-12 gap-2 items-end">
|
||||||
|
<div class="xl:col-span-3 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 shrink-0 w-28">인수자 (대행소)</label>
|
||||||
|
<select name="br_receiver_ref" class="border border-gray-300 rounded px-2 py-1 text-sm w-full" required>
|
||||||
|
<?php foreach (($receiverOptions ?? []) as $opt): ?>
|
||||||
|
<option value="<?= esc((string) ($opt['ref'] ?? '')) ?>" <?= (string) ($receiverRef ?? '') === (string) ($opt['ref'] ?? '') ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($opt['label'] ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-3 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 shrink-0 w-28">인계자 (제작업체)</label>
|
||||||
|
<select name="br_sender_idx" class="border border-gray-300 rounded px-2 py-1 text-sm w-full">
|
||||||
|
<?php foreach (($senders ?? []) as $sender): ?>
|
||||||
|
<option value="<?= (int) ($sender->mg_idx ?? 0) ?>" <?= (int) ($senderIdx ?? 0) === (int) ($sender->mg_idx ?? 0) ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($sender->mg_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2 flex items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600 w-16">입고일</label>
|
||||||
|
<input type="date" name="br_receive_date" value="<?= esc((string) old('br_receive_date', date('Y-m-d'))) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm w-full" required />
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2">
|
||||||
|
<button type="submit" class="w-full border border-blue-600 text-blue-700 px-2 py-1 rounded-sm text-sm hover:bg-blue-50">입고 처리</button>
|
||||||
|
</div>
|
||||||
|
<div class="xl:col-span-2 text-xs text-gray-500">상단에서 제작업체를 조회한 뒤, 아래에서 입고량(매)을 입력해 저장합니다.</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 overflow-auto bg-white">
|
||||||
|
<table class="w-full data-table text-sm">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>발주일자</th>
|
||||||
|
<th>봉투종류</th>
|
||||||
|
<th>발주량(매)</th>
|
||||||
|
<th>미입고량(매)</th>
|
||||||
|
<th>입고량(매)</th>
|
||||||
|
<th>제작업체</th>
|
||||||
|
<th>LOT NO</th>
|
||||||
|
<th>발주NO</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach (($rows ?? []) as $row): ?>
|
||||||
|
<?php $k = (string) ($row['row_key'] ?? ''); ?>
|
||||||
|
<tr data-row-key="<?= esc($k) ?>" data-lot-no="<?= esc((string) ($row['lot_no'] ?? '')) ?>" data-bag-code="<?= esc((string) ($row['bag_code'] ?? '')) ?>" data-total-per-box="<?= (int) ($row['total_per_box'] ?? 1) ?>" data-pack-per-sheet="<?= (int) ($row['pack_per_sheet'] ?? 1) ?>" data-pending-original="<?= (int) ($row['pending_qty_sheet'] ?? 0) ?>">
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_date'] ?? '')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($row['order_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right pending-cell"><?= number_format((int) ($row['pending_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-right">
|
||||||
|
<input type="number" min="0" max="<?= (int) ($row['pending_qty_sheet'] ?? 0) ?>" name="receive_qty_sheet[<?= esc($k) ?>]" value="<?= esc((string) old('receive_qty_sheet.' . $k, '0')) ?>" class="w-24 border border-gray-300 rounded px-1 py-0.5 text-sm text-right receive-input" />
|
||||||
|
</td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['company_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-center font-mono"><?= esc((string) ($row['lot_no'] ?? '')) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_no'] ?? '')) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($rows ?? [])): ?>
|
||||||
|
<tr><td colspan="8" class="text-center text-gray-400 py-4"><?php
|
||||||
|
if ((int) ($companyIdx ?? 0) <= 0) {
|
||||||
|
echo '제작업체를 선택하고 조회해 주세요.';
|
||||||
|
} else {
|
||||||
|
echo '해당 제작업체의 미입고 발주 내역이 없습니다.';
|
||||||
|
}
|
||||||
|
?></td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/purchase-inbound') ?>" class="bg-gray-200 text-gray-700 px-6 py-1.5 rounded-sm text-sm">취소</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const parseIntSafe = (v) => {
|
||||||
|
const n = Number(String(v ?? '').replace(/,/g, ''));
|
||||||
|
return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshPending = (row) => {
|
||||||
|
const pendingCell = row.querySelector('.pending-cell');
|
||||||
|
const input = row.querySelector('.receive-input');
|
||||||
|
if (!pendingCell || !input) return;
|
||||||
|
const original = parseIntSafe(row.getAttribute('data-pending-original'));
|
||||||
|
const current = parseIntSafe(input.value);
|
||||||
|
const remain = Math.max(0, original - current);
|
||||||
|
pendingCell.textContent = Number(remain || 0).toLocaleString('ko-KR');
|
||||||
|
};
|
||||||
|
|
||||||
|
document.querySelectorAll('.receive-input').forEach((input) => {
|
||||||
|
input.addEventListener('input', (e) => {
|
||||||
|
const row = e.target.closest('tr');
|
||||||
|
if (!row) return;
|
||||||
|
const original = parseIntSafe(row.getAttribute('data-pending-original'));
|
||||||
|
const current = Math.min(parseIntSafe(input.value), original);
|
||||||
|
input.value = String(current);
|
||||||
|
refreshPending(row);
|
||||||
|
});
|
||||||
|
const row = input.closest('tr');
|
||||||
|
if (row) refreshPending(row);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
138
app/Views/bag/receiving_status.php
Normal file
138
app/Views/bag/receiving_status.php
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<?= view('components/print_header', ['printTitle' => '봉투 입고 현황', 'printShowApproval' => false]) ?>
|
||||||
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel no-print">
|
||||||
|
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||||
|
<span class="text-sm font-bold text-gray-700">입고 현황</span>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="<?= base_url('bag/receiving/status/export') . '?' . http_build_query(array_filter(['start_date' => $startDate ?? '', 'end_date' => $endDate ?? '', 'company_idx' => $companyIdx ?? 0, 'bag_code' => $bagCode ?? '', 'receive_type' => $receiveType ?? ''])) ?>" class="border border-btn-excel-border text-btn-excel-text px-3 py-1 rounded-sm text-sm hover:bg-green-50">엑셀저장</a>
|
||||||
|
<button onclick="window.print()" class="border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">인쇄</button>
|
||||||
|
<a href="<?= base_url('bag/receiving/scanner') ?>" class="border border-gray-300 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">입고 처리</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
||||||
|
<form method="get" action="<?= base_url('bag/receiving/status') ?>" class="flex flex-wrap items-center gap-2">
|
||||||
|
<label class="text-sm text-gray-600">입고 기간</label>
|
||||||
|
<input type="date" name="start_date" value="<?= esc((string) ($startDate ?? '')) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm" />
|
||||||
|
<span class="text-sm text-gray-500">~</span>
|
||||||
|
<input type="date" name="end_date" value="<?= esc((string) ($endDate ?? '')) ?>" class="border border-gray-300 rounded px-2 py-1 text-sm" />
|
||||||
|
|
||||||
|
<label class="text-sm text-gray-600 ml-2">제작 업체</label>
|
||||||
|
<select name="company_idx" class="border border-gray-300 rounded px-2 py-1 text-sm w-52">
|
||||||
|
<option value="0">전 체</option>
|
||||||
|
<?php foreach (($companies ?? []) as $company): ?>
|
||||||
|
<option value="<?= (int) $company->cp_idx ?>" <?= (int) ($companyIdx ?? 0) === (int) $company->cp_idx ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($company->cp_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="text-sm text-gray-600">품명</label>
|
||||||
|
<select name="bag_code" class="border border-gray-300 rounded px-2 py-1 text-sm w-44">
|
||||||
|
<option value="">전 체</option>
|
||||||
|
<?php foreach (($bagCodeOptions ?? []) as $bag): ?>
|
||||||
|
<option value="<?= esc((string) ($bag->cd_code ?? '')) ?>" <?= (string) ($bagCode ?? '') === (string) ($bag->cd_code ?? '') ? 'selected' : '' ?>>
|
||||||
|
<?= esc((string) ($bag->cd_name ?? '')) ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label class="text-sm text-gray-600">입고 구분</label>
|
||||||
|
<select name="receive_type" class="border border-gray-300 rounded px-2 py-1 text-sm w-36">
|
||||||
|
<option value="all" <?= ($receiveType ?? 'all') === 'all' ? 'selected' : '' ?>>전 체</option>
|
||||||
|
<option value="completed" <?= ($receiveType ?? 'all') === 'completed' ? 'selected' : '' ?>>완료</option>
|
||||||
|
<option value="pending" <?= ($receiveType ?? 'all') === 'pending' ? 'selected' : '' ?>>미완료</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||||
|
<a href="<?= base_url('bag/receiving/status') ?>" class="text-sm text-gray-500 hover:underline">초기화</a>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="border border-gray-300 overflow-auto mt-2 receiving-status-print-wrap">
|
||||||
|
<table class="w-full data-table text-sm receiving-status-print-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>입고일자</th>
|
||||||
|
<th>품명</th>
|
||||||
|
<th>입고수량</th>
|
||||||
|
<th>발주일자</th>
|
||||||
|
<th>발주수량</th>
|
||||||
|
<th>발주번호</th>
|
||||||
|
<th>제작업체</th>
|
||||||
|
<th>입고여부</th>
|
||||||
|
<th>입고처</th>
|
||||||
|
<th>비고</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php
|
||||||
|
$prevDate = null;
|
||||||
|
$runningSum = 0;
|
||||||
|
?>
|
||||||
|
<?php foreach (($rows ?? []) as $i => $row): ?>
|
||||||
|
<?php
|
||||||
|
$d = (string) ($row['display_date'] ?? '');
|
||||||
|
if ($prevDate !== null && $d !== $prevDate):
|
||||||
|
?>
|
||||||
|
<tr class="bg-gray-50 font-semibold">
|
||||||
|
<td colspan="2" class="text-center">소 계</td>
|
||||||
|
<td class="text-right"><?= number_format($runningSum) ?></td>
|
||||||
|
<td colspan="7"></td>
|
||||||
|
</tr>
|
||||||
|
<?php
|
||||||
|
$runningSum = 0;
|
||||||
|
endif;
|
||||||
|
$runningSum += (int) ($row['received_qty_sheet'] ?? 0);
|
||||||
|
$prevDate = $d;
|
||||||
|
?>
|
||||||
|
<tr>
|
||||||
|
<td class="text-center"><?= esc($d) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['bag_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($row['received_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_date'] ?? '')) ?></td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($row['order_qty_sheet'] ?? 0)) ?></td>
|
||||||
|
<td class="text-center"><?= esc((string) ($row['order_no'] ?? '')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['company_name'] ?? '')) ?></td>
|
||||||
|
<td class="text-center <?= ($row['receive_status_label'] ?? '') === '완료' ? 'text-red-600 font-bold' : 'text-blue-600 font-bold' ?>"><?= esc((string) ($row['receive_status_label'] ?? '')) ?></td>
|
||||||
|
<td class="text-left pl-2"><?= esc((string) ($row['agency_name'] ?? '')) ?></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php if (! empty($rows ?? [])): ?>
|
||||||
|
<tr class="bg-gray-50 font-semibold">
|
||||||
|
<td colspan="2" class="text-center">소 계</td>
|
||||||
|
<td class="text-right"><?= number_format($runningSum) ?></td>
|
||||||
|
<td colspan="7"></td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (empty($rows ?? [])): ?>
|
||||||
|
<tr><td colspan="10" class="text-center text-gray-400 py-4">조회 결과가 없습니다.</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr class="bg-gray-100 font-bold">
|
||||||
|
<td colspan="2" class="text-center">합 계</td>
|
||||||
|
<td class="text-right"><?= number_format((int) ($grandTotalReceive ?? 0)) ?></td>
|
||||||
|
<td colspan="7"></td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
.receiving-status-print-wrap {
|
||||||
|
overflow: visible !important;
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
.receiving-status-print-table th,
|
||||||
|
.receiving-status-print-table td {
|
||||||
|
white-space: nowrap !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
padding: 2px 3px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user