@@ -1,6 +1,6 @@
{% extends "erp_base.html" %}
{% block head_extra %}< link rel = "stylesheet" href = "/static/cupang.css?v=20260901o " / > {% endblock %}
{% block head_extra %}< link rel = "stylesheet" href = "/static/cupang.css?v=20260901p " / > {% endblock %}
{% block content %}
< section class = "cpg" >
@@ -253,7 +253,13 @@
var pending = null ; // 대화상자 대상 {key, centerId}
function prodKey ( code ) { return "p:" + code ; }
function mixKey ( boxName , idx ) { return "m:" + boxName + "#" + idx ; }
// 혼합 상자 키는 위치가 아니라 상자마다 붙는 고유 id 로 잡는다.
// (상자를 새로 만들거나 지워도 이미 담긴 배분이 어긋나지 않게)
var mixSeq = 0 ;
function mixKeyFor ( b ) {
if ( ! b . uid ) { mixSeq += 1 ; b . uid = "b" + mixSeq ; }
return "m:" + b . uid ;
}
// 자투리 혼합 상자에 쓸 상자 종류 — 요약 카드에서 고른다(표시용).
var MIX _TYPES = [ "쿠팡상자" , "3호상자" , "6호상자" ] ;
@@ -332,7 +338,7 @@
mixOwner = { } ;
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
var k = mixKey ( m . box _name , i ) ;
var k = mixKeyFor ( b ) ;
var n = 0 ;
b . items . forEach ( function ( it ) { n += it . quantity ; } ) ;
units [ k ] = n ;
@@ -345,6 +351,7 @@
var had = Object . keys ( alloc ) . some ( function ( k ) { return alloc [ k ] . length ; } ) ;
alloc = { } ;
stamped = { } ;
pool = { } ;
allStamped = false ; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다.
var skipped = restore ? applyRestore ( restore ) : 0 ;
render ( ) ;
@@ -426,7 +433,7 @@
units : units , labels : labels , contents : contents ,
openCenters : openCenters . slice ( ) , alloc : alloc , methods : methods ,
stamped : stamped , allStamped : allStamped ,
mixType : mixType , mixOwner : mixOwner
mixType : mixType , mixOwner : mixOwner , pool : pool
} ;
}
@@ -443,6 +450,7 @@
alloc = st . alloc ; methods = st . methods ;
stamped = st . stamped ; allStamped = st . allStamped ;
mixType = st . mixType || { } ; mixOwner = st . mixOwner || { } ;
pool = st . pool || { } ;
tbody . innerHTML = "" ;
( st . items || [ ] ) . forEach ( function ( it ) { addRow ( it . product _code , it . quantity ) ; } ) ;
@@ -474,7 +482,7 @@
units : { } , labels : { } , contents : { } ,
openCenters : [ ] , alloc : { } , methods : { } ,
stamped : { } , allStamped : false ,
mixType : { } , mixOwner : { }
mixType : { } , mixOwner : { } , pool : { }
} ;
function put ( cid , key , count ) {
@@ -512,7 +520,7 @@
mx . boxes . forEach ( function ( b ) {
var idx = bucket . boxes . length ;
bucket . boxes . push ( b ) ;
var k = mixKey ( mx . box _name , idx ) ;
var k = mixKeyFor ( b ) ;
st . mixOwner [ k ] = g . center _name ;
put ( cid , k , 1 ) ;
} ) ;
@@ -530,7 +538,7 @@
m . boxes . forEach ( function ( b , i ) {
var n = 0 ;
b . items . forEach ( function ( it ) { n += it . quantity ; } ) ;
var k = mixKey ( m . box _name , i ) ;
var k = mixKeyFor ( b ) ;
st . units [ k ] = n ;
st . mixType [ k ] = MIX _TYPES [ 0 ] ;
st . labels [ k ] = st . mixType [ k ] + " 혼합 #" + ( i + 1 ) +
@@ -680,6 +688,161 @@
return n ;
}
// ── 혼합 상자 편집 ────────────────────────────────
// 상자 용량은 부피 합으로 본다: 제품 1개 = 1/입수량 상자.
// 빼낸 수량은 센터별 "미배치 자투리" 풀에 남고, 거기서 다시 담는다.
var pool = { } ; // centerId -> [{product_code, product_name, quantity}]
function upbOf ( code ) {
var hit = calc . results . filter ( function ( r ) { return r . product _code === code ; } ) [ 0 ] ;
return hit ? ( hit . units _per _box || 0 ) : 0 ;
}
function mixRef ( key ) {
var found = null ;
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b ) { if ( mixKeyFor ( b ) === key ) found = { m : m , b : b } ; } ) ;
} ) ;
return found ;
}
function mixFill ( key ) {
var vol = 0 ;
( contents [ key ] || [ ] ) . forEach ( function ( it ) {
var u = upbOf ( it . product _code ) ;
if ( u ) vol += it . quantity / u ;
} ) ;
return vol ;
}
function mixRoomFor ( key , code ) {
var u = upbOf ( code ) ;
if ( ! u ) return 0 ;
return Math . max ( 0 , Math . floor ( ( 1 - mixFill ( key ) ) * u + 1e-9 ) ) ;
}
// 내용물이 바뀐 상자를 ②(요약) 쪽 데이터와 맞춘다. 비면 상자를 없앤다.
function syncMix ( key ) {
var list = ( contents [ key ] || [ ] ) . filter ( function ( it ) { return it . quantity > 0 ; } ) ;
contents [ key ] = list ;
var ref = mixRef ( key ) ;
if ( ! list . length ) {
if ( ref ) {
var idx = ref . m . boxes . indexOf ( ref . b ) ;
if ( idx >= 0 ) ref . m . boxes . splice ( idx , 1 ) ;
if ( ! ref . m . boxes . length ) {
calc . mixes = calc . mixes . filter ( function ( m ) { return m !== ref . m ; } ) ;
}
}
Object . keys ( alloc ) . forEach ( function ( cid ) {
alloc [ cid ] = ( alloc [ cid ] || [ ] ) . filter ( function ( a ) { return a . key !== key ; } ) ;
} ) ;
delete units [ key ] ; delete labels [ key ] ; delete contents [ key ] ;
delete mixType [ key ] ; delete mixOwner [ key ] ; delete stamped [ key ] ;
return ;
}
var total = 0 ;
list . forEach ( function ( it ) { total += it . quantity ; } ) ;
units [ key ] = total ;
if ( ref ) {
ref . b . items = list . map ( function ( it ) {
return { product _code : it . product _code , product _name : it . product _name , quantity : it . quantity } ;
} ) ;
ref . b . fill _percent = Math . round ( mixFill ( key ) * 1000 ) / 10 ;
}
}
function centerOfKey ( key ) {
var hit = "" ;
Object . keys ( alloc ) . forEach ( function ( cid ) {
( alloc [ cid ] || [ ] ) . forEach ( function ( a ) { if ( a . key === key ) hit = String ( cid ) ; } ) ;
} ) ;
return hit ;
}
function mixKeysOf ( cid ) {
return ( alloc [ String ( cid ) ] || [ ] )
. filter ( function ( a ) { return a . key . indexOf ( "m:" ) === 0 ; } )
. map ( function ( a ) { return a . key ; } ) ;
}
function poolOf ( cid ) {
cid = String ( cid ) ;
if ( ! pool [ cid ] ) pool [ cid ] = [ ] ;
return pool [ cid ] ;
}
function poolAdd ( cid , code , name , qty ) {
if ( qty <= 0 ) return ;
var list = poolOf ( cid ) ;
var hit = list . filter ( function ( it ) { return it . product _code === code ; } ) [ 0 ] ;
if ( hit ) { hit . quantity += qty ; return ; }
list . push ( { product _code : code , product _name : name , quantity : qty } ) ;
}
function poolTake ( cid , code , qty ) {
var list = poolOf ( cid ) ;
var hit = list . filter ( function ( it ) { return it . product _code === code ; } ) [ 0 ] ;
if ( ! hit ) return 0 ;
var take = Math . min ( hit . quantity , qty ) ;
hit . quantity -= take ;
pool [ String ( cid ) ] = list . filter ( function ( it ) { return it . quantity > 0 ; } ) ;
return take ;
}
function poolTotal ( cid ) {
var n = 0 ;
poolOf ( cid ) . forEach ( function ( it ) { n += it . quantity ; } ) ;
return n ;
}
function mixItem ( key , code ) {
return ( contents [ key ] || [ ] ) . filter ( function ( it ) { return it . product _code === code ; } ) [ 0 ] ;
}
// 상자에 담기 — 용량만큼만 들어간다. 담은 수량을 돌려준다.
function mixPut ( key , code , name , qty ) {
var room = mixRoomFor ( key , code ) ;
var put = Math . min ( room , qty ) ;
if ( put <= 0 ) return 0 ;
var hit = mixItem ( key , code ) ;
if ( hit ) { hit . quantity += put ; }
else { ( contents [ key ] = contents [ key ] || [ ] ) . push ( { product _code : code , product _name : name , quantity : put } ) ; }
syncMix ( key ) ;
return put ;
}
// 상자에서 빼기 — 뺀 수량은 그 센터의 미배치 풀로.
function mixTake ( key , code , qty ) {
var hit = mixItem ( key , code ) ;
if ( ! hit ) return 0 ;
var take = Math . min ( hit . quantity , qty ) ;
if ( take <= 0 ) return 0 ;
var cid = centerOfKey ( key ) ;
hit . quantity -= take ;
syncMix ( key ) ;
if ( cid ) poolAdd ( cid , code , hit . product _name , take ) ;
return take ;
}
// 새 혼합 상자 (같은 센터, 비어 있는 상태로 추가)
function newMixBox ( cid , boxName ) {
var name = boxName || ( calc . mixes [ 0 ] && calc . mixes [ 0 ] . box _name ) || MIX _TYPES [ 0 ] ;
var bucket = calc . mixes . filter ( function ( m ) { return m . box _name === name ; } ) [ 0 ] ;
if ( ! bucket ) { bucket = { box _name : name , boxes : [ ] } ; calc . mixes . push ( bucket ) ; }
var b = { items : [ ] , fill _percent : 0 } ;
bucket . boxes . push ( b ) ;
var key = mixKeyFor ( b ) ;
contents [ key ] = [ ] ;
units [ key ] = 0 ;
mixType [ key ] = MIX _TYPES [ 0 ] ;
mixOwner [ key ] = centerName ( cid ) ;
labels [ key ] = mixLabel ( key , bucket . boxes . length - 1 ) ;
seq += 1 ;
( alloc [ String ( cid ) ] = alloc [ String ( cid ) ] || [ ] ) . push ( { id : seq , key : key , count : 1 } ) ;
return key ;
}
// ── ② 요약 렌더 ───────────────────────────────────
function boxSvg ( fill , label ) {
var f = Math . max ( 0 , Math . min ( 100 , fill || 0 ) ) ;
@@ -731,7 +894,7 @@
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
totalMix += 1 ;
remainMix += remainFor ( mixKey ( m . box _name , i ) ) ;
remainMix += remainFor ( mixKeyFor ( b ) ) ;
} ) ;
} ) ;
var assigned = ( totalProd + totalMix ) - ( remainProd + remainMix ) ;
@@ -783,7 +946,7 @@
var bh = "" , n = 0 ;
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
var key = mixKey ( m . box _name , i ) ;
var key = mixKeyFor ( b ) ;
var remain = remainFor ( key ) ;
if ( remain ) delete stamped [ key ] ;
var items = b . items . map ( function ( it ) {
@@ -809,18 +972,79 @@
}
}
// 혼합 상자는 안에 무엇이 몇 개 들어가는지 트리로 펼쳐 보여준다.
// 혼합 상자는 안에 무엇이 몇 개 들어가는지 트리로 펼치고, 그 자리에서
// 수량 조절(− +)과 다른 상자로 이동(⇄)을 할 수 있게 한다.
function moveOptions ( fromKey , cid , code ) {
var opts = '<option value="">이동…</option>' ;
mixKeysOf ( cid ) . forEach ( function ( k ) {
if ( k === fromKey ) return ;
var room = mixRoomFor ( k , code ) ;
opts += '<option value="' + esc ( k ) + '"' + ( room ? "" : " disabled" ) + ">" +
esc ( labels [ k ] || k ) + ( room ? " (여유 " + room + "개)" : " (가득)" ) + "</option>" ;
} ) ;
opts += '<option value="__new__">+ 새 상자로</option>' ;
opts += '<option value="__pool__">미배치로 빼기</option>' ;
return opts ;
}
function treeHtml ( entry ) {
var list = contents [ entry . key ] ;
if ( ! list || ! list . length ) return "" ;
var editable = entry . key . indexOf ( "m:" ) === 0 && entry . count === 1 ;
var cid = editable ? centerOfKey ( entry . key ) : "" ;
return '<ul class="cpg-dist-tree">' + list . map ( function ( it ) {
var qty = it . quantity * entry . count ;
if ( ! editable ) {
return '<li><span class="cpg-tree-name">' + esc ( it . product _name ) + "</span>" +
'<span class="cpg-tree-qty">' + qty + "개</span>" +
( entry . count > 1 ? '<span class="cpg-tree-per">(' + it . quantity + "개 × " + entry . count + "상자)</span>" : "" ) +
"</li>" ;
}
var canAdd = mixRoomFor ( entry . key , it . product _code ) > 0 ;
return '<li><span class="cpg-tree-name">' + esc ( it . product _name ) + "</span>" +
'<span class="cpg-tree-qty">' + ( it . quantity * entry . count ) + "개</span>" +
( entry . count > 1 ? '<span class="cpg-tree-per">(' + it . quantity + "개 × " + entry . count + "상자)</span>" : "" ) +
"</li>" ;
'<span class="cpg-mix-ctl">' +
'<button type="button" class="cpg-mix-step" data-mix-dec data-key="' + esc ( entry . key ) +
'" data-code="' + esc ( it . product _code ) + '" title="1개 빼기">− </button>' +
'<span class="cpg-tree-qty">' + qty + "개</span>" +
'<button type="button" class="cpg-mix-step" data-mix-inc data-key="' + esc ( entry . key ) +
'" data-code="' + esc ( it . product _code ) + '"' + ( canAdd ? "" : " disabled" ) +
' title="미배치에서 1개 담기">+</button>' +
'<select class="erp-select cpg-mix-move" data-key="' + esc ( entry . key ) +
'" data-code="' + esc ( it . product _code ) + '" aria-label="이동">' +
moveOptions ( entry . key , cid , it . product _code ) +
"</select>" +
"</span></li>" ;
} ) . join ( "" ) + "</ul>" ;
}
// 센터별 미배치 자투리 — 상자에서 뺀 수량이 여기 모인다.
function poolHtml ( cid ) {
var list = poolOf ( cid ) ;
if ( ! list . length ) return "" ;
var rows = list . map ( function ( it ) {
var opts = '<option value="">담을 상자…</option>' ;
mixKeysOf ( cid ) . forEach ( function ( k ) {
var room = mixRoomFor ( k , it . product _code ) ;
opts += '<option value="' + esc ( k ) + '"' + ( room ? "" : " disabled" ) + ">" +
esc ( labels [ k ] || k ) + ( room ? " (여유 " + room + "개)" : " (가득)" ) + "</option>" ;
} ) ;
opts += '<option value="__new__">+ 새 상자에</option>' ;
return '<li><span class="cpg-tree-name">' + esc ( it . product _name ) + "</span>" +
'<span class="cpg-mix-ctl">' +
'<span class="cpg-tree-qty">' + it . quantity + "개</span>" +
'<select class="erp-select cpg-pool-move" data-center="' + esc ( cid ) +
'" data-code="' + esc ( it . product _code ) + '" aria-label="담을 상자">' + opts + "</select>" +
"</span></li>" ;
} ) . join ( "" ) ;
return '<div class="cpg-pool">' +
'<div class="cpg-pool-head">미배치 자투리 <b>' + poolTotal ( cid ) + "개</b>" +
'<button type="button" class="erp-btn erp-btn-outline cpg-pool-auto" data-center="' + esc ( cid ) +
'">자동 담기</button>' +
"</div>" +
'<ul class="cpg-dist-tree">' + rows + "</ul>" +
"</div>" ;
}
// ── ③ 센터 분배 렌더 ──────────────────────────────
function renderDist ( ) {
if ( ! distList ) return ;
@@ -834,9 +1058,12 @@
var per = units [ a . key ] || 0 ;
boxes += a . count ;
pieces += a . count * per ;
items + = '<li class="cpg-dist-item" data-entry="' + a . id + '">' +
var isMix = a . key . indexOf ( "m:" ) === 0 ;
var fillTxt = isMix ? Math . round ( mixFill ( a . key ) * 100 ) + "% 채움" : "" ;
items += '<li class="cpg-dist-item' + ( isMix ? " is-mix" : "" ) + '" data-entry="' + a . id + '">' +
'<div class="cpg-dist-line">' +
'<span class="cpg-dist-name">' + esc ( labels [ a . key ] || a . key ) + "</span>" +
'<span class="cpg-dist-name">' + esc ( labels [ a . key ] || a . key ) +
( fillTxt ? ' <em class="cpg-mix-fill">' + fillTxt + "</em>" : "" ) + "</span>" +
'<input class="erp-input cpg-dist-qty" type="number" min="1" max="999" step="1" inputmode="numeric" value="' + a . count + '" />' +
'<span class="cpg-dist-unit">상자 · ' + ( a . count * per ) + "개</span>" +
'<button type="button" class="erp-btn erp-btn-danger cpg-dist-del" title="빼기" aria-label="빼기">✕</button>' +
@@ -863,6 +1090,13 @@
( rows . length
? '<ul class="cpg-dist-items">' + items + "</ul>"
: '<p class="cpg-dist-hint">여기로 상자를 끌어다 놓기</p>' ) +
poolHtml ( cid ) +
( rows . length
? '<div class="cpg-dist-foot">' +
'<button type="button" class="erp-btn erp-btn-outline cpg-mix-new" data-center="' +
esc ( cid ) + '">+ 새 혼합 상자</button>' +
"</div>"
: "" ) +
"</div>" ;
} ) ;
distList . innerHTML = html ;
@@ -1049,7 +1283,7 @@
mixType [ key ] = sel . value ;
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
var k = mixKey ( m . box _name , i ) ;
var k = mixKeyFor ( b ) ;
if ( k === key ) labels [ k ] = mixLabel ( k , i ) ;
} ) ;
} ) ;
@@ -1057,6 +1291,137 @@
msg . textContent = "상자 종류를 " + sel . value + " 로 바꿨습니다." ;
} ) ;
// ── 혼합 상자 내용물 편집 이벤트 ──────────────────
if ( distList ) {
distList . addEventListener ( "click" , function ( e ) {
var dec = e . target . closest ( "[data-mix-dec]" ) ;
if ( dec ) {
var moved = mixTake ( dec . getAttribute ( "data-key" ) , dec . getAttribute ( "data-code" ) , 1 ) ;
if ( moved ) { render ( ) ; msg . textContent = "1개를 미배치로 뺐습니다." ; }
return ;
}
var inc = e . target . closest ( "[data-mix-inc]" ) ;
if ( inc ) {
var key = inc . getAttribute ( "data-key" ) ;
var code = inc . getAttribute ( "data-code" ) ;
var cid = centerOfKey ( key ) ;
var got = poolTake ( cid , code , Math . min ( 1 , mixRoomFor ( key , code ) ) ) ;
if ( ! got ) {
msg . textContent = mixRoomFor ( key , code )
? "미배치 자투리에 남은 수량이 없습니다."
: "이 상자에 더 들어갈 자리가 없습니다." ;
return ;
}
var name = ( mixItem ( key , code ) || { } ) . product _name || code ;
mixPut ( key , code , name , got ) ;
render ( ) ;
msg . textContent = "1개를 담았습니다." ;
return ;
}
// 혼합 상자의 ✕ — 내용물을 미배치로 옮기고 상자를 없앤다.
var del = e . target . closest ( ".cpg-dist-del" ) ;
if ( del ) {
var li = del . closest ( ".cpg-dist-item" ) ;
var zone = del . closest ( "[data-drop-for]" ) ;
var dcid = zone && zone . getAttribute ( "data-drop-for" ) ;
var entry = ( alloc [ dcid ] || [ ] ) . filter ( function ( a ) {
return String ( a . id ) === li . getAttribute ( "data-entry" ) ;
} ) [ 0 ] ;
if ( entry && entry . key . indexOf ( "m:" ) === 0 ) {
e . stopImmediatePropagation ( ) ; // 뒤 핸들러가 또 지우지 않게
( contents [ entry . key ] || [ ] ) . slice ( ) . forEach ( function ( it ) {
poolAdd ( dcid , it . product _code , it . product _name , it . quantity ) ;
} ) ;
contents [ entry . key ] = [ ] ;
syncMix ( entry . key ) ;
render ( ) ;
msg . textContent = "혼합 상자를 비우고 미배치로 옮겼습니다." ;
}
return ;
}
var neu = e . target . closest ( ".cpg-mix-new" ) ;
if ( neu ) {
newMixBox ( neu . getAttribute ( "data-center" ) ) ;
render ( ) ;
msg . textContent = "빈 혼합 상자를 추가했습니다." ;
return ;
}
var auto = e . target . closest ( ".cpg-pool-auto" ) ;
if ( auto ) {
var acid = auto . getAttribute ( "data-center" ) ;
var left = 0 ;
poolOf ( acid ) . slice ( ) . forEach ( function ( it ) {
var need = it . quantity ;
mixKeysOf ( acid ) . forEach ( function ( k ) {
if ( need <= 0 ) return ;
var put = mixPut ( k , it . product _code , it . product _name , Math . min ( need , mixRoomFor ( k , it . product _code ) ) ) ;
if ( put ) { poolTake ( acid , it . product _code , put ) ; need -= put ; }
} ) ;
if ( need > 0 ) {
var nk = newMixBox ( acid ) ;
var put2 = mixPut ( nk , it . product _code , it . product _name , Math . min ( need , mixRoomFor ( nk , it . product _code ) ) ) ;
if ( put2 ) { poolTake ( acid , it . product _code , put2 ) ; need -= put2 ; }
}
left += Math . max ( need , 0 ) ;
} ) ;
render ( ) ;
msg . textContent = left ? "일부는 담지 못했습니다 (" + left + "개)" : "미배치 자투리를 모두 담았습니다." ;
}
} ) ;
distList . addEventListener ( "change" , function ( e ) {
var mv = e . target . closest ( ".cpg-mix-move" ) ;
if ( mv ) {
var fromKey = mv . getAttribute ( "data-key" ) ;
var code = mv . getAttribute ( "data-code" ) ;
var target = mv . value ;
mv . value = "" ;
if ( ! target ) return ;
var src = mixItem ( fromKey , code ) ;
if ( ! src ) return ;
var qty = src . quantity ;
var name = src . product _name ;
var cid = centerOfKey ( fromKey ) ;
if ( target === "__pool__" ) {
mixTake ( fromKey , code , qty ) ;
render ( ) ;
msg . textContent = name + " " + qty + "개를 미배치로 뺐습니다." ;
return ;
}
var toKey = target === "__new__" ? newMixBox ( cid ) : target ;
var room = mixRoomFor ( toKey , code ) ;
var move = Math . min ( qty , room ) ;
if ( move <= 0 ) { msg . textContent = "옮길 자리가 없습니다." ; render ( ) ; return ; }
mixTake ( fromKey , code , move ) ; // 일단 미배치로
poolTake ( cid , code , move ) ; // 바로 꺼내서
mixPut ( toKey , code , name , move ) ; // 대상 상자에 담기
render ( ) ;
msg . textContent = name + " " + move + "개를 " + ( labels [ toKey ] || "새 상자" ) + " 로 옮겼습니다." +
( move < qty ? " (자리가 부족해 " + ( qty - move ) + "개는 남음)" : "" ) ;
return ;
}
var pm = e . target . closest ( ".cpg-pool-move" ) ;
if ( pm ) {
var pcid = pm . getAttribute ( "data-center" ) ;
var pcode = pm . getAttribute ( "data-code" ) ;
var dest = pm . value ;
pm . value = "" ;
if ( ! dest ) return ;
var entry = poolOf ( pcid ) . filter ( function ( it ) { return it . product _code === pcode ; } ) [ 0 ] ;
if ( ! entry ) return ;
var key2 = dest === "__new__" ? newMixBox ( pcid ) : dest ;
var put = Math . min ( entry . quantity , mixRoomFor ( key2 , pcode ) ) ;
if ( put <= 0 ) { msg . textContent = "그 상자에는 자리가 없습니다." ; render ( ) ; return ; }
poolTake ( pcid , pcode , put ) ;
mixPut ( key2 , pcode , entry . product _name , put ) ;
render ( ) ;
msg . textContent = entry . product _name + " " + put + "개를 담았습니다." ;
}
} ) ;
}
if ( distList ) {
distList . addEventListener ( "input" , function ( e ) {
if ( ! e . target . classList . contains ( "cpg-dist-qty" ) ) return ;
@@ -1189,7 +1554,7 @@
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
total += 1 ;
remain += remainFor ( mixKey ( m . box _name , i ) ) ;
remain += remainFor ( mixKeyFor ( b ) ) ;
} ) ;
} ) ;
return { total : total , remain : remain } ;
@@ -1208,6 +1573,9 @@
if ( ! cids . length ) return "센터에 담긴 상자가 없습니다." ;
var missing = cids . filter ( function ( cid ) { return ! methods [ String ( cid ) ] ; } ) ;
if ( missing . length ) return "출고방식 미선택: " + missing . map ( centerName ) . join ( ", " ) ;
var leftover = 0 ;
openCenters . forEach ( function ( cid ) { leftover += poolTotal ( cid ) ; } ) ;
if ( leftover ) return "미배치 자투리 " + leftover + "개가 남았습니다." ;
return "" ;
}
@@ -1478,7 +1846,7 @@
} ) ;
calc . mixes . forEach ( function ( m ) {
m . boxes . forEach ( function ( b , i ) {
var k = mixKey ( m . box _name , i ) ;
var k = mixKeyFor ( b ) ;
labels [ k ] = mixLabel ( k , i ) ;
} ) ;
} ) ;