표 디자인 - 모든 표를 가벼운 스타일로 통일(.data-table 경량화: 작은 회색 헤더·연한 구분선·hover) - 표/패널 바깥 테두리 둥글게(rounded-lg) 일괄 적용, 표 래퍼에 패딩 카드(p-4) 통일 - 표 헤더·데이터 정렬을 전 화면 좌측 기준으로 통일 - .data-table th/td text-align:left (전역), 흩어진 center/right 정렬 정리 - 재디자인 Tailwind 표(포장단위·단가·기본코드·담당자·업체·판매대행소·무료대상자·지정판매소)도 셀 좌측화 - 기본정보관리 등 나머지 소메뉴 표를 기본 코드 관리 스타일(가벼운 표·상태 pill)로 재디자인 워크스페이스/공통 - "이 화면 설명" → 새 탭 대신 우측 드로어 팝업(현재 화면과 동시에 보기, Esc·드래그 폭조절) - 상단바 글씨 크기 조절(A−/A+), 작업 내용에 zoom 적용 - 탭 최대치 도달 시 자동 삭제 대신 안내 토스트, "모두 닫기"(업무 현황 탭은 보존) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
154 lines
8.2 KiB
PHP
154 lines
8.2 KiB
PHP
<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 rounded-lg 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 rounded-lg">
|
|
<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 rounded-lg p-4 overflow-auto bg-white">
|
|
<table class="w-full data-table text-sm">
|
|
<thead>
|
|
<tr>
|
|
<th class="text-center">발주일자</th>
|
|
<th>봉투종류</th>
|
|
<th class="text-right">발주량(매)</th>
|
|
<th class="text-right">미입고량(매)</th>
|
|
<th class="text-right">입고량(매)</th>
|
|
<th>제작업체</th>
|
|
<th class="text-center">LOT NO</th>
|
|
<th class="text-center">발주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>
|