Files
sam-api/public/tenant/production/work_instruction.php
hskwon cc206fdbed style: Laravel Pint 코드 포맷팅 적용
- PSR-12 스타일 가이드 준수
- 302개 파일 스타일 이슈 자동 수정
- 코드 로직 변경 없음 (포맷팅만)
2025-11-06 17:45:49 +09:00

409 lines
20 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// SAM MODAL BODY (NO header/footer)
// Params: job_id, cust, order_no (POST/GET)
$jobId = $_POST['job_id'] ?? ($_GET['job_id'] ?? '');
$cust = $_POST['cust'] ?? ($_GET['cust'] ?? '');
$orderNo = $_POST['order_no'] ?? ($_GET['order_no'] ?? '');
?>
<div class="p-3">
<!-- ROW 1 : 기본정보(좌 2/3) + 우선 작업순위(우 1/3) -->
<div class="row g-3 mb-3">
<!-- 작업 기본정보 -->
<div class="col-lg-8">
<div class="border rounded p-3 h-100">
<div class="fw-bold mb-2">① 작업 기본정보</div>
<div class="row g-2">
<div class="col-6 col-md-4">
<label class="form-label mb-0 small text-muted">수주계약일</label>
<input class="form-control form-control-sm" value="자동입력" readonly>
</div>
<div class="col-6 col-md-4">
<label class="form-label mb-0 small text-muted">발주처</label>
<input class="form-control form-control-sm" value="<?= htmlspecialchars($cust) ?: '자동입력' ?>" readonly>
</div>
<div class="col-12 col-md-4">
<label class="form-label mb-0 small text-muted">현장명</label>
<input class="form-control form-control-sm" value="자동입력" readonly>
</div>
<div class="col-6 col-md-4">
<label class="form-label mb-0 small text-muted">출고요청일</label>
<input class="form-control form-control-sm" value="자동입력" readonly>
</div>
<div class="col-6 col-md-4">
<label class="form-label mb-0 small text-muted">배송방식</label>
<input class="form-control form-control-sm" value="자동입력" readonly>
</div>
<div class="col-6 col-md-4">
<label class="form-label mb-0 small text-muted">제품명</label>
<input class="form-control form-control-sm" value="자동입력" readonly>
</div>
<div class="col-12">
<label class="form-label mb-0 small text-muted">비고</label>
<input class="form-control form-control-sm" value="">
</div>
</div>
</div>
</div>
<!-- 우선 작업순위 -->
<div class="col-lg-4">
<div class="border rounded p-3 h-100">
<div class="fw-bold mb-2">우선 작업순위</div>
<div class="row g-2 align-items-center">
<div class="col-5 text-end small text-muted">현재순위</div>
<div class="col-7"><input type="number" class="form-control form-control-sm" value="0" readonly></div>
<div class="col-5 text-end small text-muted">설정순위</div>
<div class="col-7">
<select class="form-select form-select-sm" id="setPriority">
<?php for ($i = 1; $i <= 10; $i++) {
echo "<option>$i</option>";
} ?>
</select>
</div>
</div>
</div>
</div>
</div>
<!-- ROW 2 : 메인 자재(전체폭) + 일괄적용 버튼(이 박스 안) -->
<div class="border rounded p-3 mb-3">
<div class="d-flex align-items-center gap-2 mb-2">
<div class="fw-bold flex-grow-1">② 재고확인 및 자재투입</div>
<div class="d-flex gap-2">
<input type="text" class="form-control form-control-sm" id="bulkLot" placeholder="LOT 번호 입력/선택값" style="width:200px;">
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnOpenLotForBulk">LOT 선택</button>
<button type="button" class="btn btn-primary btn-sm" id="btnApplyLotMain">로트번호 일괄적용</button>
</div>
</div>
<div class="table-responsive" style="max-height:360px; overflow:auto;">
<table class="table table-sm align-middle m-0">
<thead class="table-light">
<tr>
<th style="width:42px;"><input type="checkbox" id="chkAll"></th>
<th style="width:70px;">일련</th>
<th style="width:60px;">층</th>
<th style="width:80px;">부호</th>
<th>품목명</th>
<th style="width:110px;">가로</th>
<th style="width:110px;">세로</th>
<th style="width:90px;">매수</th>
<th style="width:110px;">조인트바</th>
<th style="width:160px;">입고 LOT NO.</th>
</tr>
</thead>
<tbody id="wiRows"><!-- JS 렌더 --></tbody>
</table>
</div>
</div>
<!-- ROW 3 : 좌(내화실/부자재, 다건) / 우(작업자 배치) -->
<div class="row g-3">
<!-- ③ 부자재 (고정 목록, 추가/삭제 없음) -->
<div class="col-lg-6">
<div class="border rounded p-3 h-100">
<div class="fw-bold mb-2">③ 부자재</div>
<div class="table-responsive" style="max-height:260px; overflow:auto;">
<table class="table table-sm align-middle m-0">
<thead class="table-light">
<tr>
<th>항목명</th>
<th style="width:160px;">LOT NO.</th>
<th style="width:90px;">수량</th>
</tr>
</thead>
<tbody id="subRows"><!-- JS --></tbody>
</table>
</div>
</div>
</div>
<!-- 작업자 배치 -->
<div class="col-lg-6">
<div class="border rounded p-3 h-100">
<div class="fw-bold mb-2">④ 작업자 배치</div>
<div class="row g-2 align-items-center mb-2">
<div class="col-4 col-md-3 text-end small text-muted">부서 선택</div>
<div class="col-8 col-md-5">
<select id="deptSelect" class="form-select form-select-sm"></select>
</div>
<div class="col-12 col-md-4 mt-2 mt-md-0">
<input type="text" id="workerSearch" class="form-control form-control-sm" placeholder="작업자 검색">
</div>
</div>
<div class="row g-3">
<div class="col-md-7">
<div class="border rounded" style="height:220px; overflow:auto;">
<table class="table table-sm m-0 align-middle">
<thead class="table-light">
<tr><th style="width:42px;"></th><th>이름</th><th class="text-muted" style="width:120px;">직책</th></tr>
</thead>
<tbody id="workerList"><!-- JS --></tbody>
</table>
</div>
</div>
<div class="col-md-5">
<div class="small text-muted mb-1">선택됨</div>
<div id="chosenWrap" class="d-flex flex-wrap gap-2"></div>
</div>
</div>
</div>
</div>
</div>
<!-- 하단 버튼 -->
<div class="d-flex justify-content-end gap-2 mt-3">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">취소</button>
<button type="button" class="btn btn-primary" id="btnSaveWI">저장</button>
</div>
</div>
<!-- LOT 선택 서브 모달 (메인/부자재에서 공용) -->
<div class="modal fade" id="lotSelectModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">로트번호 선택</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="닫기"></button>
</div>
<div class="modal-body">
<div class="table-responsive border rounded">
<table class="table table-hover align-middle m-0">
<thead class="table-light">
<tr><th style="width:160px;">로트번호</th><th>자재명</th><th style="width:100px;">재고</th><th style="width:80px;">선택</th></tr>
</thead>
<tbody id="lotRows"></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<small class="text-muted">선택 즉시 대상 입력란에 반영됩니다.</small>
</div>
</div>
</div>
</div>
<style>
.table th, .table td { vertical-align: middle; }
#wiRows .lot-input { min-width: 120px; }
#chosenWrap .badge { font-size:.85rem; }
</style>
<script>
(function($){
/* ===== 샘플 데이터 ===== */
const rows = [
{no:1, floor:1, sign:'FSS-01', item:'실리카', w:696, h:3050, qty:47, joint:50, lot:'', checked:false},
{no:2, floor:1, sign:'', item:'실리카', w:700, h:3100, qty:40, joint:45, lot:'', checked:false},
{no:3, floor:2, sign:'', item:'실리카', w:650, h:3000, qty:38, joint:42, lot:'', checked:false},
];
const lots = [
{lot:'250626-01', mat:'실리카원단-WY-SC780', stock:800},
{lot:'250617-01', mat:'실리카원단-WY-SC780', stock:5069},
{lot:'250513-02', mat:'실리카원단-WY-SC780', stock:4625},
{lot:'250411-05', mat:'실리카원단-WY-SC780', stock:738},
];
const DEPTS = {
'생산1팀': [
{id:'u01', name:'김동실', role:'반장'},
{id:'u02', name:'원준서', role:'사원'},
{id:'u03', name:'유영수', role:'사원'},
{id:'u04', name:'배천석', role:'사원'},
],
'생산2팀': [
{id:'u11', name:'박서준', role:'주임'},
{id:'u12', name:'최다연', role:'사원'},
{id:'u13', name:'한민수', role:'사원'},
]
};
/* ===== 렌더: 메인 자재 ===== */
function renderMain(){
const html = rows.map((r,i)=>`
<tr data-idx="${i}">
<td><input type="checkbox" class="row-check" ${r.checked?'checked':''}></td>
<td>${r.no}</td>
<td>${r.floor}</td>
<td>${r.sign||''}</td>
<td>${r.item}</td>
<td><input type="number" class="form-control form-control-sm wi-w" value="${r.w}"></td>
<td><input type="number" class="form-control form-control-sm wi-h" value="${r.h}"></td>
<td><input type="number" class="form-control form-control-sm wi-qty" value="${r.qty}"></td>
<td><input type="number" class="form-control form-control-sm wi-joint" value="${r.joint}"></td>
<td>
<div class="input-group input-group-sm">
<input type="text" class="form-control lot-input wi-lot" value="${r.lot}" placeholder="선택">
</div>
</td>
</tr>
`).join('');
$('#wiRows').html(html);
}
/* ===== 렌더: LOT 목록 ===== */
function renderLots(){
$('#lotRows').html(
lots.map(l=>`
<tr>
<td>${l.lot}</td>
<td>${l.mat}</td>
<td>${l.stock.toLocaleString()}</td>
<td><button type="button" class="btn btn-sm btn-primary btn-choose-lot" data-lot="${l.lot}">선택</button></td>
</tr>`).join('')
);
}
/* ===== 렌더: 부자재(내화실) ===== */
// 부자재 고정 목록 (예: 제품별 프리셋)
let subItems = [
{ id: 's1', name: '내화실', lot: '', qty: 1 },
{ id: 's2', name: '실리콘(내열)', lot: '', qty: 2 }
];
function addSubRow(){ subItems.push({id:'s'+(subSeed++), name:'', lot:'', qty:1}); renderSub(); }
function renderSub(){
$('#subRows').html(
subItems.map(s => `
<tr data-sid="${s.id}">
<td><input type="text" class="form-control form-control-sm sub-name" value="${s.name}" readonly></td>
<td>
<input type="text" class="form-control form-control-sm sub-lot" value="${s.lot}" placeholder="LOT 선택">
</td>
<td><input type="number" class="form-control form-control-sm sub-qty" value="${s.qty}" min="1"></td>
</tr>
`).join('')
);
}
/* ===== 체크박스 전체/부분 ===== */
$(document).on('change', '#chkAll', function(){
const on = $(this).is(':checked'); rows.forEach(r=>r.checked=on); renderMain(); this.indeterminate=false;
});
$(document).on('change', '.row-check', function(){
const idx = +$(this).closest('tr').data('idx'); rows[idx].checked = $(this).is(':checked');
const total=rows.length, sel=rows.filter(r=>r.checked).length, all=document.getElementById('chkAll');
if(sel===0){ all.checked=false; all.indeterminate=false; }
else if(sel===total){ all.checked=true; all.indeterminate=false; }
else{ all.checked=false; all.indeterminate=true; }
});
/* ===== 값 반영 ===== */
$(document).on('input', '.wi-w, .wi-h, .wi-qty, .wi-joint, .wi-lot', function(){
const i = +$(this).closest('tr').data('idx');
rows[i].w = +$(this).closest('tr').find('.wi-w').val()||0;
rows[i].h = +$(this).closest('tr').find('.wi-h').val()||0;
rows[i].qty = +$(this).closest('tr').find('.wi-qty').val()||0;
rows[i].joint = +$(this).closest('tr').find('.wi-joint').val()||0;
rows[i].lot = $(this).closest('tr').find('.wi-lot').val().trim();
});
$(document).on('input', '.sub-name, .sub-lot, .sub-qty', function(){
const sid = $(this).closest('tr').data('sid');
const s = subItems.find(x=>x.id===sid);
s.name = $(this).closest('tr').find('.sub-name').val().trim();
s.lot = $(this).closest('tr').find('.sub-lot').val().trim();
s.qty = +($(this).closest('tr').find('.sub-qty').val())||1;
});
/* ===== LOT 모달 호출/선택 (메인/부자재 공용) ===== */
let lotTarget = {mode:null, rowIdx:null, sid:null}; // mode: 'main' | 'sub' | 'bulk'
function openLotModal(){ renderLots(); new bootstrap.Modal('#lotSelectModal').show(); }
// 부자재 LOT 입력 클릭 시 모달 오픈
$(document).on('focus click', '.sub-lot', function(){
lotTarget = { mode: 'sub', sid: $(this).closest('tr').data('sid') };
openLotModal();
});
$(document).on('click', '.btn-open-lot', function(){
lotTarget.mode = $(this).data('mode'); // main | sub
if(lotTarget.mode==='main') lotTarget.rowIdx = +$(this).closest('tr').data('idx');
if(lotTarget.mode==='sub') lotTarget.sid = $(this).closest('tr').data('sid');
openLotModal();
});
$('#btnOpenLotForBulk').on('click', function(){ lotTarget = {mode:'bulk'}; openLotModal(); });
$(document).on('click', '.btn-choose-lot', function(){
const lot = $(this).data('lot');
if(lotTarget.mode==='main' && lotTarget.rowIdx!=null){
rows[lotTarget.rowIdx].lot = lot; renderMain();
}else if(lotTarget.mode==='sub' && lotTarget.sid){
const s = subItems.find(x=>x.id===lotTarget.sid); if(s){ s.lot = lot; renderSub(); }
}else if(lotTarget.mode==='bulk'){
$('#bulkLot').val(lot);
}
const m = bootstrap.Modal.getInstance(document.getElementById('lotSelectModal')); m && m.hide();
});
// 메인 자재 일괄적용(체크된 행들만)
$('#btnApplyLotMain').on('click', function(){
const lot = $('#bulkLot').val().trim();
if(!lot) return alert('일괄 적용할 LOT 번호를 입력/선택하세요.');
const targets = rows.filter(r=>r.checked);
if(!targets.length) return alert('적용할 행(체크)을 선택하세요.');
rows.forEach(r=>{ if(r.checked) r.lot = lot; });
renderMain();
});
/* ===== 작업자 배치(부서 선택 → 체크) ===== */
let currentDept=null, chosen=new Map(); // id -> user
function fillDept(){ const $s=$('#deptSelect').empty(); Object.keys(DEPTS).forEach((k,i)=>$s.append(`<option ${i? '':'selected'}>${k}</option>`)); currentDept=$s.val(); }
function renderWorkers(){
const q=($('#workerSearch').val()||'').trim().toLowerCase();
const list=(DEPTS[currentDept]||[]).filter(u=>u.name.toLowerCase().includes(q)||u.role.toLowerCase().includes(q));
$('#workerList').html(list.map(u=>`
<tr>
<td><input type="checkbox" class="chk-worker" data-id="${u.id}" ${chosen.has(u.id)?'checked':''}></td>
<td>${u.name}</td><td class="text-muted">${u.role}</td>
</tr>`).join(''));
renderChosen();
}
function renderChosen(){
const $w=$('#chosenWrap').empty();
if(!chosen.size) return $w.append('<span class="text-muted small">선택된 작업자가 없습니다.</span>');
chosen.forEach(u=>$w.append(
`<span class="badge bg-secondary-subtle border text-body d-flex align-items-center gap-1">
${u.name}<span class="text-muted small">(${u.role})</span>
<button type="button" class="btn btn-sm btn-link p-0 ms-1 btn-pill-remove" data-id="${u.id}">×</button>
</span>`
));
}
$('#deptSelect').on('change', function(){ currentDept=this.value; renderWorkers(); });
$('#workerSearch').on('input', renderWorkers);
$(document).on('change','.chk-worker',function(){
const id=$(this).data('id'); const u=Object.values(DEPTS).flat().find(x=>x.id===id);
if($(this).is(':checked')) chosen.set(id,u); else chosen.delete(id); renderChosen();
});
$(document).on('click','.btn-pill-remove',function(){ chosen.delete($(this).data('id')); renderWorkers(); });
/* ===== 부자재 행 추가/삭제 ===== */
$('#btnAddSub').on('click', addSubRow);
$(document).on('click','.btn-del-sub',function(){
const sid=$(this).closest('tr').data('sid'); subItems=subItems.filter(x=>x.id!==sid); renderSub();
});
/* ===== 저장(모의) ===== */
$('#btnSaveWI').on('click', function(){
const payload = {
job_id: <?= json_encode($jobId) ?>,
order_no: <?= json_encode($orderNo) ?>,
priority: $('#setPriority').val(),
main_rows: rows,
sub_items: subItems,
workers: Array.from(chosen.values())
};
console.log('[WORK INSTRUCTION SAVE]', payload);
alert('저장(모의). 콘솔을 확인하세요.');
// 실제: $.post('/tenant/api/production/save_work_instruction.php', payload)
});
/* 초기 구동 */
renderMain(); renderSub(); fillDept(); renderWorkers();
})(jQuery);
</script>