`;
}
function pageShell(s,body,commands=''){
return `
${esc(s.module)} > ${esc(s.group)}
${esc(s.title)}
${esc(s.template)}
${commands?`
${commands}
`:''}
${body}
`;
}
function setBackgroundInert(value){for(const el of [$('.global-header'),$('.shell-body')]){if(!el)continue;value?el.setAttribute('inert',''):el.removeAttribute('inert');}}
function openDrawerContent(title,html){
drawerRestoreFocus=document.activeElement instanceof HTMLElement?document.activeElement:null;drawerTitle.textContent=title;drawerBody.innerHTML=html;
drawer.classList.add('open');drawer.setAttribute('aria-hidden','false');drawerBackdrop.hidden=false;setBackgroundInert(true);setTimeout(()=>$('#drawerClose').focus(),0);
}
function applyIntent(intent){
if(intent==='order-stock'){state.orderQuickFilter='stock';state.selectedOrders.clear();state.orderDataState='ready';return openScreen('OMS-ORD-001');}
if(intent==='order-errors'){state.orderQuickFilter='error';state.selectedOrders.clear();state.orderDataState='ready';return openScreen('OMS-ORD-001');}
if(intent==='reconcile-mismatch'){state.mismatchOnly=true;return openScreen('COMMON-REC-001');}
if(intent==='import-progress'){state.importStep=state.importJobStatus==='completed'?6:Math.max(1,state.importStep);return openScreen('OMS-ORD-003');}
}
function showUtilityPanel(s,kind){
if(kind==='help'){openDrawerContent(`${s.title} 도움말`,`
화면 도움말 · ${esc(s.id)}
${esc(s.title)}
이 화면은 ${esc(s.group)} 업무를 표준 ${esc(s.template)} 문법으로 처리합니다.
기본 사용법
조회 조건 또는 업무 대상을 확인합니다.
필요한 행이나 값을 선택합니다.
Command Bar의 업무 동사를 실행합니다.
단축키
F2
Lookup
F3
조회
F8
저장
Esc
현재 Context 닫기
화면의 Disabled 상태는 UX 표현이며 최종 권한과 업무 규칙은 서버에서 재검증해야 합니다.
`);return;}
if(kind==='ai'){openDrawerContent(`${s.title} AI 도우미`,`
현재 화면 · ${esc(s.id)}
확인할 항목
빠른 질문
AI는 설명·제안만 수행합니다. 실제 변경은 구조화된 Proposal과 사용자 확인 후 Domain Command로 실행해야 합니다.
`);$$('[data-ai-intent]',drawerBody).forEach(button=>button.onclick=()=>{const intent=button.dataset.aiIntent;closeDrawer();setTimeout(()=>applyIntent(intent),0);});return;}
openDrawerContent(`${s.title} 제안`,``);$('#suggestionForm').onsubmit=e=>{e.preventDefault();const value=$('#suggestionText').value.trim();if(!value){$('#suggestionText').focus();return;}closeDrawer();toast('화면 의견을 접수했습니다. 업무 민감데이터는 포함하지 않았습니다.');};setTimeout(()=>$('#suggestionText')?.focus(),0);
}
function closeSystemPanel(restoreFocus=true){
if(systemPanel.hidden)return;systemPanel.hidden=true;$('#jobsButton').setAttribute('aria-expanded','false');$('#noticeButton').setAttribute('aria-expanded','false');
const restore=systemPanelRestoreFocus;systemPanelRestoreFocus=null;if(restoreFocus)restore?.focus();
}
function showSystemPanel(kind){
const trigger=kind==='jobs'?$('#jobsButton'):$('#noticeButton');
if(!systemPanel.hidden&&trigger.getAttribute('aria-expanded')==='true'){closeSystemPanel();return;}
closeDrawer();systemPanelRestoreFocus=document.activeElement instanceof HTMLElement?document.activeElement:null;systemPanelTitle.textContent=kind==='jobs'?'작업 센터':'알림 센터';
$('#jobsButton').setAttribute('aria-expanded',String(kind==='jobs'));$('#noticeButton').setAttribute('aria-expanded',String(kind!=='jobs'));
if(kind==='jobs'){const progress=state.importJobStatus==='running'?`${state.importProgress}%`:'완료';systemPanelBody.innerHTML=`
백그라운드 작업
진행 중 / 최근 작업
송장 재처리대기 · 자동 재시도대기
`;}else{systemPanelBody.innerHTML=`
업무 알림
확인 필요 4건
`;}
systemPanel.hidden=false;$$('[data-system-intent]',systemPanelBody).forEach(button=>button.onclick=()=>{const intent=button.dataset.systemIntent;closeSystemPanel(false);setTimeout(()=>applyIntent(intent),0);});setTimeout(()=>systemPanel.focus(),0);
}
function bindPageTools(s){
const fav=$('#pageFavorite');if(fav)fav.onclick=()=>toggleFavorite(s.id);
const help=$('#pageHelp');if(help)help.onclick=()=>showUtilityPanel(s,'help');
const ai=$('#pageAi');if(ai)ai.onclick=()=>showUtilityPanel(s,'ai');
const suggestion=$('#pageSuggestion');if(suggestion)suggestion.onclick=()=>showUtilityPanel(s,'suggestion');
}
function grid(cols,rows,{caption='업무 데이터',className=''}={}){
return `
${esc(caption)}
${cols.map(c=>`
${esc(c)}
`).join('')}
${rows}
`;
}
function formField(label,value,{required=false,name='',readonly=false}={}){
return ``;
}
function lookupField(label,code,name,{field='',entity=field}={}){
return `
`;
}
function checkboxField(label,checked,{field=''}={}){return ``;}
function lookupItems(){
const list=lookupData[state.lookupEntity]||[];const q=(lookupSearchInput.value||'').trim().toLowerCase();
return list.filter(item=>!q||`${item.code} ${item.name} ${item.secondary}`.toLowerCase().includes(q));
}
function renderLookupResults(){
const list=lookupItems();if(state.lookupIndex>=list.length)state.lookupIndex=Math.max(0,list.length-1);
lookupResults.innerHTML=list.length?list.map((item,index)=>``).join(''):'
조회된 항목이 없습니다.
';
$('#lookupStatus').textContent=`${list.length}건 · ↑↓ 이동 · Enter 선택 · Esc 닫기`;
$$('.lookup-result',lookupResults).forEach((button,index)=>{button.onmousemove=()=>{state.lookupIndex=index;renderLookupResults();};button.onclick=()=>{state.lookupIndex=index;selectLookup();};});
$('#lookupSelectButton').disabled=!list.length;
}
function openLookup(root){
if(!root)return;state.lookupEntity=root.dataset.lookup||'';state.lookupField=root.dataset.lookupField||'';state.lookupIndex=0;lookupRestoreFocus=document.activeElement instanceof HTMLElement?document.activeElement:null;
const label=root.closest('.form-field')?.querySelector('label')?.textContent?.trim()||'업무';$('#lookupTitle').textContent=`${label} 검색`;$('#lookupSubtitle').textContent='F2 · 코드/명칭 조회';lookupSearchInput.value='';renderLookupResults();
if(!lookupDialog.open)lookupDialog.showModal();setTimeout(()=>lookupSearchInput.focus(),0);
}
function focusAfterLookup(root){
const focusables=$$('input:not([disabled]),select:not([disabled]),button:not([disabled]),textarea:not([disabled])',workspace);
const current=root?.querySelector('button.lookup-trigger');const index=focusables.indexOf(current);(focusables[index+1]||lookupRestoreFocus)?.focus();
}
function applyLookupToActive(field,item){
if(state.activeId==='ERP-MST-ITEM-001'&&field==='warehouse'&&state.masterDraft){state.masterDraft.warehouseCode=item.code;state.masterDraft.warehouse=item.name;}
if(state.activeId==='OMS-ORD-002'){
if(field==='customer'){state.transactionHeader.customerCode=item.code;state.transactionHeader.customerName=item.name;}
if(field==='warehouse'){state.transactionHeader.warehouseCode=item.code;state.transactionHeader.warehouseName=item.name;}
}
}
function selectLookup(){
const list=lookupItems();const item=list[state.lookupIndex];if(!item)return;const root=$(`.lookup[data-lookup-field="${CSS.escape(state.lookupField)}"]`,workspace);
if(root){const code=$(`[data-field="${CSS.escape(state.lookupField)}Code"]`,root);const name=$(`[data-field="${CSS.escape(state.lookupField)}Name"]`,root);if(code)code.value=item.code;if(name)name.value=item.name;applyLookupToActive(state.lookupField,item);markDirty(state.activeId,true);}
lookupDialog.close();announce(`${item.code} ${item.name}을 선택했습니다.`);toast(`${item.name}을 선택했습니다.`);setTimeout(()=>focusAfterLookup(root),0);
}
function bindLookups(){
$$('.lookup',workspace).forEach(root=>{const trigger=$('.lookup-trigger',root);if(trigger)trigger.onclick=()=>openLookup(root);$$('input',root).forEach(input=>input.onkeydown=e=>{if(e.key==='F2'){e.preventDefault();openLookup(root);}});});
}
function dataState(kind,title,detail,retryId=''){
const action=retryId?btn('다시 조회','primary',retryId):'';
return `${esc(title)}${esc(detail)}${action}`;
}
function homeAttentionItems(){
return [
{source:'실패 작업',title:'송장 발급 실패 3건',detail:'오류 주문 필터로 바로 이동',id:'OMS-ORD-001',critical:true,intent:'order-errors'},
{source:'예외',title:'재고부족 주문 7건',detail:'재고부족 필터를 적용해 이동',id:'OMS-ORD-001',critical:true,intent:'order-stock'},
{source:'중요 알림',title:'대사 불일치 43건',detail:'불일치만 표시해 이동',id:'COMMON-REC-001',critical:false,intent:'reconcile-mismatch'},
{source:'진행 작업',title:state.importJobStatus==='running'?`Excel 주문 반영 ${state.importProgress}%`:state.importJobStatus==='completed'?'Excel 주문 반영 완료':'Excel 주문 업로드 대기',detail:'작업상태 보기',id:'OMS-ORD-003',critical:false,intent:'import-progress'},
{source:'미저장',title:'주문등록 수정 중',detail:'저장하지 않은 변경사항 이어서 편집',id:'OMS-ORD-002',critical:false,intent:''},
{source:'알림',title:'주소 확인 필요 4건',detail:'배송지 오류 주문만 확인',id:'OMS-ORD-001',critical:false,intent:'order-errors'},
{source:'알림',title:'SKU 매칭 실패 2건',detail:'상품 매핑 오류 주문만 확인',id:'OMS-ORD-001',critical:false,intent:'order-errors'},
{source:'진행 작업',title:'피킹 작업 1건 지연',detail:'현재 물류작업 Queue에서 확인',id:'WMS-WORK-001',critical:false,intent:''},
{source:'알림',title:'품목 바코드 확인 1건',detail:'품목 기준정보에서 확인',id:'ERP-MST-ITEM-001',critical:false,intent:''},
{source:'진행 작업',title:'재고 실사 작업 확인',detail:'물류 작업 Queue에서 확인',id:'WMS-WORK-001',critical:false,intent:''},
...Array.from({length:18},(_,index)=>({source:index%3===0?'진행 작업':'알림',title:`추가 확인 업무 ${index+1}`,detail:index%3===0?'작업 센터에서 전체 진행상태 확인':'알림 센터에서 전체 알림 확인',id:index%3===0?'WMS-WORK-001':'OMS-ORD-001',critical:false,intent:''})),
];
}
function attention(source,title,detail,id,critical=false,intent=''){return ``;}
function continueItem(source,title,detail,id,type){return ``;}
function homeMenuRow(item){return ``;}
function renderHome(){
const allScreens=screens.filter(x=>x.id!=='HOME');const visible=allScreens.filter(x=>state.module==='ALL'||x.module===state.module);const grouped=groupScreens(visible);
const moduleKeys=['OMS','ERP','WMS','COMMON'].filter(module=>allScreens.some(item=>item.module===module));
const moduleRail=``;
const dirtyTab=state.tabs.find(t=>t.dirty);const resumedTab=state.tabs.find(t=>t.resumed&&!t.dirty);const regularOpen=state.tabs.find(t=>!t.dirty&&!t.resumed);
const allAttention=homeAttentionItems();const attentionCap=24;const visibleAttention=state.homeAttentionExpanded?allAttention.slice(0,attentionCap):allAttention.slice(0,8);const attentionOverflow=Math.max(allAttention.length-visibleAttention.length,0);const attentionToggleLabel=state.homeAttentionExpanded?'상위 8개만 보기':allAttention.length<=attentionCap?`전체 ${allAttention.length}개 보기`:`우선 ${attentionCap}개 보기`;const attentionCenters=state.homeAttentionExpanded&&attentionOverflow?``:'';
workspace.innerHTML=`
KBX 업무 시작
홈
검색하고, 이어서 처리하고, 예외만 확인하는 한국형 업무 시작점${state.workspaceDirtyDiscarded?` · 이전 미저장 ${state.workspaceDirtyDiscarded}개는 안전상 복원 제외`:''}
${moduleRail}
확인 필요
정상 8,224건은 자동 처리 · 사용자는 예외와 실패만 확인
전체 ${allAttention.length}개 · ${visibleAttention.length}개 표시${attentionCenters}
${continueItem('미저장',dirtyTab?.title||'주문등록',dirtyTab?`${screen(dirtyTab.id).module} · 저장하지 않은 변경`:'OMS · 주문 · ORD 임시작성',dirtyTab?.id||'OMS-ORD-002','dirty')}${resumedTab?continueItem('복원',resumedTab.title,`${screen(resumedTab.id).module} · 이전 세션의 업무 위치`,resumedTab.id,'open'):continueItem('열림',regularOpen?.title||'품목관리',regularOpen?`${screen(regularOpen.id).module} · 열린 업무`:'ERP · 기준정보',regularOpen?.id||'ERP-MST-ITEM-001','open')}${continueItem('즐겨찾기','재고현황','ERP · 재고','ERP-INV-001','favorite')}${continueItem('최근','오늘 물류작업','WMS · 작업관리','WMS-WORK-001','recent')}
`);
}
function closeDrawer(){drawer.classList.remove('open');drawer.setAttribute('aria-hidden','true');drawerBackdrop.hidden=true;setBackgroundInert(false);const restore=drawerRestoreFocus;drawerRestoreFocus=null;restore?.focus();}
function masterDraftFrom(item){return item?{...item}:{id:'',name:'',group:'',spec:'',unit:'EA',warehouse:'',warehouseCode:'',barcode:'',lot:false,expiry:false,status:'사용'};}
function loadMasterDraft(id){const item=masterItems.find(x=>x.id===id);state.selectedMasterId=item?.id||'';state.masterMode='edit';state.masterDraft=masterDraftFrom(item||masterItems[0]);state.masterErrors={};}
function validateMasterDraft(){
const draft=state.masterDraft||masterDraftFrom();const errors={};
if(!draft.id.trim())errors.id='품목코드를 입력하세요.';
if(!draft.name.trim())errors.name='품목명을 입력하세요.';
if(!draft.warehouseCode.trim())errors.warehouse='기본창고를 선택하세요.';
if(state.masterMode==='new'&&masterItems.some(item=>item.id===draft.id.trim()))errors.id='이미 사용 중인 품목코드입니다.';
state.masterErrors=errors;return Object.keys(errors).length===0;
}
function masterValidationSummary(){const entries=Object.entries(state.masterErrors);return entries.length?`
저장할 수 없습니다. ${entries.length}개 항목을 확인하세요.${entries.map(([field,message])=>``).join('')}
`:'';}
function performMasterTarget(target){
if(target==='__NEW__'){state.selectedMasterId='';state.masterMode='new';state.masterDraft=masterDraftFrom();state.masterErrors={};renderT02(screen('ERP-MST-ITEM-001'));setTimeout(()=>$('[data-field="code"]',workspace)?.focus(),0);return;}
if(target==='__COPY__'){const source=state.masterDraft||masterItems.find(x=>x.id===state.selectedMasterId)||masterItems[0];state.selectedMasterId='';state.masterMode='new';state.masterDraft={...masterDraftFrom(source),id:'',barcode:'',status:'사용'};state.masterErrors={};renderT02(screen('ERP-MST-ITEM-001'));setTimeout(()=>$('[data-field="code"]',workspace)?.focus(),0);return;}
loadMasterDraft(target);renderT02(screen('ERP-MST-ITEM-001'));
}
function requestMasterTarget(target){const tab=state.tabs.find(x=>x.id==='ERP-MST-ITEM-001');if(tab?.dirty){state.pendingMasterId=target;state.pendingUnsavedMode='master-select';$('#unsavedMessage').textContent='현재 품목에 저장하지 않은 변경사항이 있습니다. 저장하거나 변경을 버린 뒤 다른 품목으로 전환하세요.';if(!unsavedDialog.open)unsavedDialog.showModal();return;}performMasterTarget(target);}
function saveMaster(){
if(!validateMasterDraft()){renderT02(screen('ERP-MST-ITEM-001'));setTimeout(()=>$('[data-master-error]',workspace)?.focus(),0);return false;}
const draft={...state.masterDraft,id:state.masterDraft.id.trim(),name:state.masterDraft.name.trim(),warehouseCode:state.masterDraft.warehouseCode.trim()};
if(state.masterMode==='new'){masterItems.push(draft);state.selectedMasterId=draft.id;state.masterMode='edit';}
else{const current=masterItems.find(x=>x.id===state.selectedMasterId);if(current)Object.assign(current,draft);}
state.masterDraft={...draft};state.masterErrors={};markDirty('ERP-MST-ITEM-001',false);renderT02(screen('ERP-MST-ITEM-001'));toast('품목을 저장했습니다.');return true;
}
function renderT02(s){
if(!state.masterDraft)loadMasterDraft(state.selectedMasterId);
const draft=state.masterDraft;
const q=state.masterQuery.trim().toLowerCase();const visible=masterItems.filter(x=>!q||`${x.id} ${x.name} ${x.group}`.toLowerCase().includes(q));
const rows=visible.map(x=>`
`;
workspace.innerHTML=pageShell(s,body,commands);bindPageTools(s);bindT02();
}
function bindT02(){
bindLookups();
$$('.master-select',workspace).forEach(button=>button.onclick=()=>requestMasterTarget(button.dataset.item));
$$('[data-field]',workspace).forEach(input=>{if(input.readOnly)return;const event=input.type==='checkbox'?'change':'input';input.addEventListener(event,()=>{if(!state.masterDraft)return;const key=input.dataset.field;if(key==='warehouseName')return;if(key==='code')state.masterDraft.id=input.value;else if(key==='warehouseCode'){state.masterDraft.warehouseCode=input.value;state.masterDraft.warehouse='';}else state.masterDraft[key]=input.type==='checkbox'?input.checked:input.value;state.masterErrors={};markDirty('ERP-MST-ITEM-001',true);});});
$$('[data-master-error]',workspace).forEach(button=>button.onclick=()=>{const field=button.dataset.masterError;const target=field==='warehouse'?$('.lookup-trigger',workspace):$(`[data-field="${CSS.escape(field==='id'?'code':field)}"]`,workspace);target?.focus();});
const runSearch=()=>{state.masterQuery=$('#masterQueryInput')?.value||'';renderT02(screen('ERP-MST-ITEM-001'));};
$('#masterSearchForm').onsubmit=e=>{e.preventDefault();runSearch();};$('#masterSearch').onclick=runSearch;$('#masterSearchTop').onclick=runSearch;
$('#masterSave').onclick=saveMaster;$('#masterNew').onclick=()=>requestMasterTarget('__NEW__');$('#masterCopy').onclick=()=>requestMasterTarget('__COPY__');
$('#masterSuspend').onclick=()=>{state.masterDraft.status=state.masterDraft.status==='사용'?'중지':'사용';markDirty('ERP-MST-ITEM-001',true);renderT02(screen('ERP-MST-ITEM-001'));};
$('#auditButton').onclick=()=>showUtilityPanel(screen('ERP-MST-ITEM-001'),'help');
}
function transactionTotals(){const valid=state.transactionLines.filter(line=>line.code);return {items:valid.length,qty:valid.reduce((sum,line)=>sum+Number(line.qty||0),0),amount:valid.reduce((sum,line)=>sum+Number(line.qty||0)*Number(line.price||0),0)};}
function validateTransaction(){
const h=state.transactionHeader;const errors=[];
if(!h.orderDate.trim())errors.push({field:'orderDate',message:'주문일을 입력하세요.'});
if(!h.customerCode.trim())errors.push({field:'customerCode',message:'거래처를 선택하세요.'});
if(!h.warehouseCode.trim())errors.push({field:'warehouseCode',message:'출고창고를 선택하세요.'});
if(!h.receiver.trim())errors.push({field:'receiver',message:'수취인을 입력하세요.'});
if(!h.address.trim())errors.push({field:'address',message:'주소를 입력하세요.'});
state.transactionLines.forEach((line,index)=>{if(!line.code)return;if(Number(line.qty)<=0)errors.push({row:index,field:'qty',message:`${index+1}행 수량은 0보다 커야 합니다.`});if(Number(line.price)<0)errors.push({row:index,field:'price',message:`${index+1}행 단가는 0 이상이어야 합니다.`});});
if(!state.transactionLines.some(line=>line.code))errors.push({field:'lines',message:'품목을 1개 이상 입력하세요.'});
state.transactionErrors=errors;return errors.length===0;
}
function transactionValidationSummary(){return state.transactionErrors.length?`
저장할 수 없습니다. ${state.transactionErrors.length}개 항목을 확인하세요.${state.transactionErrors.map((error,index)=>``).join('')}
`:'';}
function resetTransaction(){state.transactionStatus='작성';state.transactionErrors=[];state.transactionHeader={orderDate:'2026-08-11',channel:'직접주문',customerCode:'',customerName:'',owner:'홍길동',warehouseCode:'WH01',warehouseName:'서울물류센터',receiver:'',phone:'',postal:'',address:''};state.transactionLines=[{code:'',name:'',spec:'',available:0,qty:1,price:0,remark:''}];markDirty('OMS-ORD-002',true);renderT03(screen('OMS-ORD-002'));setTimeout(()=>$('[data-field="customerCode"]',workspace)?.focus(),0);}
function renderT03(s){
const h=state.transactionHeader;const totals=transactionTotals();
const lineRows=state.transactionLines.map((line,rowIndex)=>`
${['작성','확정','할당','피킹','검수','출고'].map(status=>`${status}`).join('')}현재: ${esc(state.transactionStatus)} · ${state.transactionStatus==='작성'?'다음: 저장/확정':'확정 이후 주요 값은 서버 업무 규칙으로 제한'}
`;
workspace.innerHTML=pageShell(s,body,commands);bindPageTools(s);bindT03();
}
function bindT03(){
bindLookups();
$$('[data-field]',workspace).forEach(input=>{if(input.readOnly)return;input.addEventListener('input',()=>{const key=input.dataset.field;if(key==='customerCode'){state.transactionHeader.customerCode=input.value;state.transactionHeader.customerName='';}else if(key==='warehouseCode'){state.transactionHeader.warehouseCode=input.value;state.transactionHeader.warehouseName='';}else if(key in state.transactionHeader)state.transactionHeader[key]=input.value;state.transactionErrors=[];markDirty('OMS-ORD-002',true);});});
const editors=$$('.cell-editor',workspace);editors.forEach((editor,index)=>{editor.oninput=()=>{const line=state.transactionLines[Number(editor.dataset.lineRow)];const field=editor.dataset.lineField;let value=editor.value;if(['qty','price'].includes(field))value=Number(String(value).replaceAll(',',''))||0;line[field]=value;if(field==='code')line.name=fastName(String(value).trim());state.transactionErrors=[];markDirty('OMS-ORD-002',true);const totals=transactionTotals();$('#transactionItemCount').textContent=`${totals.items}종`;$('#transactionQty').textContent=totals.qty.toLocaleString('ko-KR');$('#transactionAmount').textContent=`₩${totals.amount.toLocaleString('ko-KR')}`;};editor.onkeydown=e=>{if(e.key==='Enter'){e.preventDefault();editors[Math.min(index+1,editors.length-1)]?.focus();}};});
$$('[data-transaction-error]',workspace).forEach(button=>button.onclick=()=>{const error=state.transactionErrors[Number(button.dataset.transactionError)];if(!error)return;if(Number.isInteger(error.row))$(`[data-line-row="${error.row}"][data-line-field="${CSS.escape(error.field)}"]`,workspace)?.focus();else if(error.field==='customerCode')$('.lookup[data-lookup-field="customer"] .lookup-trigger',workspace)?.focus();else if(error.field==='warehouseCode')$('.lookup[data-lookup-field="warehouse"] .lookup-trigger',workspace)?.focus();else $(`[data-field="${CSS.escape(error.field)}"]`,workspace)?.focus();});
$('#transactionAddLine').onclick=()=>{state.transactionLines.push({code:'',name:'',spec:'',available:0,qty:1,price:0,remark:''});markDirty('OMS-ORD-002',true);renderT03(screen('OMS-ORD-002'));setTimeout(()=>$$('.cell-editor',workspace).at(-4)?.focus(),0);};
$('#saveTransaction').onclick=()=>{if(!validateTransaction()){renderT03(screen('OMS-ORD-002'));return;}markDirty('OMS-ORD-002',false);toast('주문을 저장했습니다. 서버 최종 검증을 통과한 것으로 가정한 FE Reference 상태입니다.');};
$('#confirmTransaction').onclick=()=>{const tab=state.tabs.find(x=>x.id==='OMS-ORD-002');if(tab?.dirty){toast('저장하지 않은 변경이 있습니다. 먼저 저장하세요.','error');return;}if(!validateTransaction()){renderT03(screen('OMS-ORD-002'));return;}state.transactionStatus='확정';renderT03(screen('OMS-ORD-002'));toast('주문 상태를 확정으로 전환했습니다. 실제 제품에서는 Domain이 최종 검증합니다.');};
$('#transactionNew').onclick=()=>{const tab=state.tabs.find(x=>x.id==='OMS-ORD-002');if(tab?.dirty){toast('현재 주문의 미저장 변경을 먼저 저장하거나 버리세요.','error');return;}resetTransaction();};
$('#transactionCopy').onclick=()=>{if(state.tabs.find(x=>x.id==='OMS-ORD-002')?.dirty){toast('미저장 변경을 먼저 처리하세요.','error');return;}state.transactionStatus='작성';state.transactionErrors=[];state.transactionHeader={...state.transactionHeader,receiver:'',phone:'',postal:'',address:''};state.transactionLines=state.transactionLines.map(line=>({...line}));markDirty('OMS-ORD-002',true);renderT03(screen('OMS-ORD-002'));toast('주문을 복사했습니다. 수취/배송정보를 새 주문에 맞게 입력하세요.');};
}
function fastName(code){return ({ABC001:'운동화',ABC002:'러닝화',ABC003:'양말',ABC004:'셔츠'})[code]||'';}
function fastErrorCount(){return state.fastRows.reduce((sum,row)=>sum+(row.code&&(!row.price||Number(String(row.price).replaceAll(',',''))<=0)?1:0),0);}
function renderT04(s){
const rows=state.fastRows.map((row,index)=>`
`:'';
const search=``;
let queueContent='';if(state.queueDataState==='loading')queueContent=dataState('loading','작업 Queue 조회 중...','현재 필터와 검색조건을 유지하고 최신 작업 상태를 조회합니다.');else if(!visible.length)queueContent=dataState('empty','처리할 작업이 없습니다.','검색조건 또는 예외 필터를 초기화해 보세요.','queueClearSearch');else queueContent=`
지금 처리할 업무표시 ${visible.length}건선택 ${selectedCount}건지연 ${visible.filter(row=>row.status==='지연').length}건
`:'';
const search=``;
let resultContent='';if(state.reconcileDataState==='loading')resultContent=dataState('loading','대사 결과 조회 중...','Expected/Actual/Difference Projection을 다시 조회하고 있습니다.');else if(!visible.length)resultContent=dataState('empty','조건에 맞는 대사 건이 없습니다.','불일치 필터 또는 검색어를 초기화해 보세요.','reconcileReset');else resultContent=`${selectedCount?`
`;
workspace.innerHTML=pageShell(s,body,commands);bindPageTools(s);bindT07();
}
function bindT07(){
const mismatch=$('#mismatchOnly');if(mismatch)mismatch.onchange=e=>{state.mismatchOnly=e.target.checked;state.selectedReconcileRows.clear();state.reconcileDataState='ready';renderT07(screen('COMMON-REC-001'));};const form=$('#reconcileSearch');if(form)form.onsubmit=e=>{e.preventDefault();executeReconcileSearch();};const reconcileInline=$('#reconcileSearchInline');if(reconcileInline)reconcileInline.onclick=executeReconcileSearch;$('#reconcileSearchButton').onclick=executeReconcileSearch;
const reset=$('#reconcileReset');if(reset)reset.onclick=()=>{state.mismatchOnly=false;state.reconcileSearch={date:'2026-08-11',keyword:''};state.selectedReconcileRows.clear();state.reconcileDataState='ready';renderT07(screen('COMMON-REC-001'));};
$$('[data-reconcile-row]',workspace).forEach(box=>box.onchange=()=>{box.checked?state.selectedReconcileRows.add(box.dataset.reconcileRow):state.selectedReconcileRows.delete(box.dataset.reconcileRow);renderT07(screen('COMMON-REC-001'));});
const all=$('#reconcileSelectAll');if(all)all.onchange=()=>{reconcileVisibleRows().forEach(row=>all.checked?state.selectedReconcileRows.add(row.no):state.selectedReconcileRows.delete(row.no));renderT07(screen('COMMON-REC-001'));};
const rerun=()=>{if(!state.selectedReconcileRows.size)return;const count=state.selectedReconcileRows.size;for(const row of reconcileRows){if(state.selectedReconcileRows.has(row.no)){row.actual=row.expected;row.diff=0;row.reason='재처리 완료';row.status='해결';}}state.reconcileActionResult=`${count}건을 재처리하여 현재 Reference 데이터 기준 차이를 0으로 맞췄습니다.`;state.selectedReconcileRows.clear();renderT07(screen('COMMON-REC-001'));};
for(const id of ['reconcileRun','reconcileRunTop']){const button=$('#'+id);if(button)button.onclick=rerun;}const reason=$('#reconcileReason');if(reason)reason.onclick=()=>{state.reconcileActionResult=`${state.selectedReconcileRows.size}건의 원인 Projection을 다시 조회했습니다.`;renderT07(screen('COMMON-REC-001'));};const note=$('#reconcileReasonNote');if(note)note.onclick=()=>{state.reconcileActionResult=`${state.selectedReconcileRows.size}건에 처리사유가 필요합니다. 실제 제품에서는 사유 입력 Drawer를 사용합니다.`;renderT07(screen('COMMON-REC-001'));};const clear=$('#reconcileResultClear');if(clear)clear.onclick=()=>{state.reconcileActionResult='';renderT07(screen('COMMON-REC-001'));};
}
function applyImportFile(file){
if(!file)return false;const name=String(file.name||'').trim();const size=Number(file.size||0);const validExt=/\.xlsx?$/i.test(name);const max=20*1024*1024;
if(!validExt){toast('Excel .xlsx 또는 .xls 파일을 선택하세요.','error');return false}if(size>max){toast('파일 크기는 20MB 이하여야 합니다.','error');return false}
state.importFileName=name;state.importFileSize=size;state.importFileType=file.type||'';state.importJobStatus='idle';state.importProgress=0;state.importProcessed=0;renderT08(screen('OMS-ORD-003'));announce(`${name} 파일을 선택했습니다.`);return true;
}
function renderT08(s){
const step=state.importStep;const steps=['파일','매핑','검증','미리보기','반영','결과'];
let content='';
if(step===1)content=`
${state.importFileName?esc(state.importFileName):'Excel 파일을 선택하세요'}
${state.importFileName?`${state.importFileSize?`${(state.importFileSize/1024/1024).toFixed(2)}MB · `:''}파일 선택 완료 · 다음 단계에서 필드 매핑을 확인합니다.`:'파일 선택은 필수 접근 경로이며 Drag & Drop은 편의 기능입니다. .xlsx/.xls · 최대 20MB'}
${btn(state.importFileName?'파일 다시 선택':'파일 선택','primary','fileSelect')} ${btn('업로드 양식 다운로드')}
`;
if(step===2)content=`
Excel ColumnSystem Field매핑 근거
${[['주문번호','주문번호','정확 일치'],['상품코드','품목코드','Alias 일치'],['수량','주문수량','Alias 일치'],['구매자','주문자명','Alias 일치'],['업체코드','거래처코드','사용자 확인 필요']].map(row=>`