--[[ 版本: Version 1.0 创建日期: 2025-4-20 创建人: HAN WMS-Basis-Model-Version: V15.5 功能: 在处理入库作业时,将入库单明细中的货品预先分配到具体容器料格的过程在WMS系统里叫 预分配容器 一般用在料箱库,空料箱有多种规格,根据货品中的设置呼出适配的料格 主要函数: -- wms_pac.Pre_Alloc_Cntr_DMG supplement_cntr_list 和 out_cntr_list 的数据结构是一样的,如下 { cntr_code:"xxx" cell_type:"A/B/C/D/E" cntr_good_weight: 100 -- 容器中货品重量 full: false/true, empty_cell_num: 0, empty_cell_list:[{"cell_no",item_code, item_state, storer, item_name}] -- 呼出的补料料箱中的空料格 cell 属性同下面的 cell_list cell_list:{ { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", wms_bn:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} } } --]] wms_cntr = require ("wms_container") wms_wh = require ("wms_wh") local wms_pac = {_version = "0.1.1"} local BOX_MAX_WEIGHT = 0 local CHECK_CAPACITY = flase -- 是否检查超重 -- 需要查询的物料货品属性,一般用在 明细表 中如入库单明细,出库单明细 local DETAIL_ATTRS = { "S_STORER", "S_ITEM_CODE", "S_ITEM_NAME", "S_ITEM_STATE", "S_ITEM_SPEC","S_BATCH_NO","S_SERIAL_NO", "S_WMS_BN", "D_PRD_DATE", "D_EXP_DATE", "S_OWNER", "S_SUPPLIER_NO", "N_ROW_NO","F_QTY", "S_UDF01", "S_UDF02", "S_UDF03", "S_UDF04", "S_UDF05", "S_UDF06", "S_UDF07", "S_UDF08", "S_UDF09", "S_UDF10", "S_UDF11", "S_UDF12", "S_UDF13", "S_UDF14", "S_UDF15", "S_UDF16", "S_UDF17", "S_UDF18", "S_UDF19", "S_UDF20" } local SKU_ATTRS = { "F_WEIGHT","F_VOLUME","S_CTD_CODE", "S_CELL_TYPE", "S_AVL_SPEC", "S_ABCTYPE", "N_LOADING_LIMIT", "F_LOAD_CAPACITY","S_COUNT_METHOD","S_SKU_GRID_PARM" } -- 设置 out_cntr_list 中的 cell_empty -- out_cntr_list = {{ cntr_code,cntr_good_weight,cell_type,empty_cell_list = {},,full = false,cell_list = {},...} function wms_pac.set_out_cntr_empty_cell( strLuaDEID, out_cntr_list ) local nRet, strRetInfo, strCondition, strOrder, n, m, empty_cell_count local cntr_cell_objs, cell_attr for n = 1, #out_cntr_list do strCondition = "S_CNTR_CODE = '"..out_cntr_list[n].cntr_code.."' AND N_EMPTY_FULL = 0" strOrder = "S_CELL_NO" nRet, cntr_cell_objs = m3.QueryDataObject(strLuaDEID, "Container_Cell", strCondition, strOrder ) if (nRet ~= 0) then return 1, cntr_cell_objs end if ( cntr_cell_objs ~= '') then for m = 1, #cntr_cell_objs do cell_attr = m3.KeyValueAttrsToObjAttr(cntr_cell_objs[m].attrs) local empty_cell = { cntr_code = out_cntr_list[n].cntr_code, cell_no = cell_attr.S_CELL_NO, item_code = "", item_name = "", entry_batch_no = "", qty = 0, sum_volume = 0, sum_weigt = 0 } table.insert( out_cntr_list[n].empty_cell_list, empty_cell ) end end end return 0 end --[[ 获取入库单,入库波次中需要入库的物料信息,并且根据容器类型进行区分 输入参数: paramter -- 预分配料箱配置参数 { wh_code, area_code 仓库,库区编码 station 站台 bs_type 来源类型:入库单、入库波次 bs_no 来源单号 } 返回:ctd_list = { {ctd_code="CTD-003",item_list={..}}, ... } --]] function wms_pac.Get_ItemList_GroupBy_CntrType( strLuaDEID, paramter ) local nRet, strRetInfo, n, isOk -- step1: 输入参数合法性检查 isOk, strRetInfo = wms_base.PreAllocCntr_CFG_Check( paramter ) if not isOk then return 1, strRetInfo end -- step2: 获取入库货品明细 item_lis, 这个货品列表要根据容器类型进行区分,因此保存在 ctd_list -- { {ctd_code="", item_lits = {} },... } -- 确定要联表查询的表和条件 local strTable, strCondition if ( paramter.bs_type == "Inbound_Order" ) then strTable = "TN_Inbound_Detail a LEFT JOIN TN_SKU b ON a.S_ITEM_CODE = b.S_ITEM_CODE" strCondition = "a.S_IO_NO = '"..paramter.bs_no.."'" else strTable = "TN_IW_Detail a LEFT JOIN TN_SKU b ON a.S_ITEM_CODE = b.S_ITEM_CODE" strCondition = "a.S_WAVE_NO = '"..paramter.bs_no.."'" end -- 要查询的属性 local detail_attrs_count = #DETAIL_ATTRS local sku_attrs_count = #SKU_ATTRS local strAttrs = "" for n = 1, detail_attrs_count do strAttrs = strAttrs.."a."..DETAIL_ATTRS[n].."," end for n = 1, sku_attrs_count do strAttrs = strAttrs.."b."..SKU_ATTRS[n].."," end strAttrs = lua.trim_laster_char( strAttrs ) local strOrder = "a.N_ROW_NO" -- 注意最多只能 2000 条明细, 如果有超过 2000 的记录概要分页查询函数,一般的入库单不会有这么多记录 nRet, strRetInfo = mobox.queryMultiTable(strLuaDEID, strAttrs, strTable, 2000, strCondition, strOrder ) if (nRet ~= 0) then return 2,"QueryDataObject失败!"..strRetInfo end if ( strRetInfo == '' ) then return 1, paramter.bs_type.."'"..paramter.bs_no.."'明细为空!" end local ret_attr = json.decode(strRetInfo) local m local ctd_list = {} local find, success for n = 1, #ret_attr do local item = {} for m = 1, detail_attrs_count do item[DETAIL_ATTRS[m]] = ret_attr[n][m] end for m = 1, sku_attrs_count do item[SKU_ATTRS[m]] = ret_attr[n][m+detail_attrs_count] end item.qty = lua.Get_NumAttrValue( item.F_QTY ) item.volume = lua.Get_NumAttrValue( item.F_VOLUME ) item.weight = lua.Get_NumAttrValue( item.F_WEIGHT ) item.row = lua.Get_NumAttrValue( item.N_ROW_NO ) item.alloc_qty = 0 item.ok = false item.cntr_cell_list = {} -- 预分配的料格列表 item.sku_grid_parm = '' if not lua.StrIsEmpty( item.S_SKU_GRID_PARM ) then success, item.sku_grid_parm = pcall( json.decode, item.S_SKU_GRID_PARM ) if ( success == false ) then return 1, "SKU 编码 = '"..sku.S_ITEM_CODE.."' 的数据对象中 S_SKU_GRID_PARM 不符合json规范!" end end -- 获取SKU的容器定义类型 S_CTD_CODE ctd_code = item.S_CTD_CODE or '' if ( ctd_code == '' ) then return 1, "编码 = '"..item.S_ITEM_CODE.."' 的SKU没有定义容器类型!" end find = false if ( ctd_list ~= nil ) then for i = 1, #ctd_list do if ( ctd_list[i].ctd_code == ctd_code ) then table.insert( ctd_list[i].item_list, item ) find = true break end end end if not find then local ctd_item = { ctd_code = ctd_code, item_list = {item} } table.insert( ctd_list, ctd_item ) end end return 0, ctd_list end -- 检测需要入库预分配的货品清单是否已经全部有了预分配 function wms_pac.item_list_is_all_ok( item_list ) for _, item in ipairs( item_list ) do if not item.ok then return false end end return true end -- 把匹配到的料格 cell_item 加入 呼出料箱容器清单 out_cntr_list -- cntr_mixing_rule 为补料容器的混箱规则 function wms_pac.put_cell_item_to_out_cntr_list( out_cntr_list, cntr_good_weight, cell_item, cntr_mixing_rule ) local n local bFind = false if cntr_mixing_rule == nil then cntr_mixing_rule = {} end for n = 1, #out_cntr_list do if ( out_cntr_list[n].cntr_code == cell_item.cntr_code ) then table.insert( out_cntr_list[n].cell_list, cell_item ) -- 容器中商品的总重量需要加上岗加入到料格里的货品重量 out_cntr_list[n].cntr_good_weight = out_cntr_list[n].cntr_good_weight + cell_item.sum_weight return end end if (bFind == false) then local out_cntr = {} out_cntr = { cntr_code = cell_item.cntr_code, cell_type = cell_item.cell_type, full = false, cntr_mixing_rule = cntr_mixing_rule, -- 和混箱策略相关的属性值 empty_cell_list = {}, cntr_good_weight = cntr_good_weight + cell_item.sum_weight, cell_list = {} } table.insert( out_cntr.cell_list, cell_item ) table.insert( out_cntr_list, out_cntr ) end end -- 遍历补料呼出料箱,判断补料呼出料箱中是否有存在 空料格, 设置item中的 empty_cell 属性 -- 返回是否可以用补料箱进行预分配 function wms_pac.set_replenishment_cntr_emptycell( strLuaDEID, supplement_cntr_list ) local n, nRet, strRetInfo, strCondition, strOrder, m local data_objects local cell local have_empty_cell = false strOrder = "S_CELL_NO" for n = 1, #supplement_cntr_list do -- 空料格 strCondition = "S_CNTR_CODE = '"..supplement_cntr_list[n].cntr_code.."' AND N_EMPTY_FULL = 0" nRet, data_objects = m3.QueryDataObject(strLuaDEID, "Container_Cell", strCondition, strOrder ) if (nRet ~= 0) then return 2, "QueryDataObject失败!"..data_objects end if ( data_objects ~= '') then for m = 1, #data_objects do cell = m3.KeyValueAttrsToObjAttr(data_objects[m].attrs) local empty_cell = { cntr_code = supplement_cntr_list[n].cntr_code, cell_no = cell.S_CELL_NO, item_code = "", item_state = "", storer = "", item_name = "", wms_bn = "", qty = 0, sum_volume = 0, sum_weigt = 0 } table.insert( supplement_cntr_list[n].empty_cell_list, empty_cell ) have_empty_cell = true end else -- 没有空料箱格 supplement_cntr_list[n].full = true end end return have_empty_cell end -- 检查 sku 是否符合混箱规则 function wms_pac.sku_complies_with_mixing_rule ( ctd, sku, cntr_mixing_rule ) local sku_value, cntr_mixing_rule_value if lua.isTableEmpty( cntr_mixing_rule ) then return 1, "wms_pac.sku_complies_with_mixing_rule 函数中 cntr_mixing_rule 不合规!" end for _, attr in ipairs( ctd.mixing_attrs ) do sku_value = sku[attr] if sku_value == nil then return 1, "在判断是否符合混箱规则时,发现 SKU 缺少属性'"..attr.."'" end cntr_mixing_rule_value = cntr_mixing_rule[attr] if cntr_mixing_rule_value == nil then return 1, "在判断是否符合混箱规则时,发现 容器扩展属性中 缺少属性'"..attr.."'" end if sku_value ~= cntr_mixing_rule_value then return 0, false end end return 0, true end -- 把呼出容器列表中的 empty_cell 清理一下,如果 item_code 有值表示已经分配了货品 function wms_pac.reset_empty_cell_info( cntr_list ) local empty_cell_list local n, m local have_empty_cell = false -- 说明cntr_list是否有空料箱 for n = 1, #cntr_list do empty_cell_list = {} for m = 1, #cntr_list[n].empty_cell_list do if (cntr_list[n].empty_cell_list[m].item_code == '') then local empty_cell = { cntr_code = cntr_list[n].empty_cell_list[m].cntr_code, cell_no = cntr_list[n].empty_cell_list[m].cell_no, item_code = "", item_name = "", wms_bn = "", weight = 0, volume = 0, qty = 0, sum_volume = 0, sum_weigt = 0 } table.insert( empty_cell_list, empty_cell ) end end cntr_list[n].empty_cell_list = empty_cell_list if ( 0 == #empty_cell_list ) then cntr_list[n].full = true else have_empty_cell = true end end return have_empty_cell end --[[ 计算在容器 out_cntr 里预分配 sku 的数量,注意这个时候的 out_cntr 已经是在呼出容器的队列,比如已经在补料呼出队列,呼出空料箱队列 ctd -- 容器类型定义 sku -- 入库商品 out_cntr = { cntr_code:"xxx" cell_type:"A/B/C/D/E" cntr_good_weight: 100 -- 容器中货品重量 full: false/true, empty_cell_num: 0, empty_cell_list:{{"cell_no",item_code, item_state, storer, item_name}} -- 空料格 cell_list:{ { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", wms_bn:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} } } bs_no -- 入库波次号/入库单号 --]] function wms_pac.pre_alloc_sku_to_out_cntr( strLuaDEID, pac_cfg, sku, out_cntr ) local nRet, Q, strRetInfo local match_ok = false local sku_count_method = lua.Get_StrAttrValue( sku.S_COUNT_METHOD ) local qty -- 需要分配料箱格的货品数量 if ( sku_count_method == '' ) then return 1, "SKU '"..sku.S_ITEM_CODE.."' 没有定义计数方法 S_COUNT_METHOD" end --补料料箱不满,且料格规格和SKU设定的料格规格相等 if not out_cntr.full and out_cntr.cell_type == sku.S_CELL_TYPE then if ( pac_cfg.ctd.have_mixing_rule ) then nRet, match_ok = wms_pac.sku_complies_with_mixing_rule ( pac_cfg.ctd, sku, out_cntr.cntr_mixing_rule ) if ( nRet ~= 0 ) then return 1, "wms_pac.sku_complies_with_mixing_rule 时出错! "..match_ok end else match_ok = true end end Q = 0 if match_ok then qty = sku.qty - sku.alloc_qty local cntr_cell -- SKU 找到适配的空料箱格 for _, empty_cell in ipairs( out_cntr.empty_cell_list ) do if ( empty_cell.item_code == '' ) then cntr_cell = { qty = 0, good_volume = 0, good_weight = 0, cntr_code = empty_cell.cntr_code, cell_no = empty_cell.cell_no, cell_type = out_cntr.cell_type, } nRet, Q = wms_cntr.Get_CntrCell_Goods_Qty( pac_cfg.ctd, out_cntr.cntr_good_weight, cntr_cell, sku ) if nRet ~= 0 then return 1, Q end if ( Q > 0 ) then if Q > qty then Q = qty end sku.alloc_qty = sku.alloc_qty + Q qty = qty - Q if ( lua.equation( sku.alloc_qty, sku.qty ) ) then sku.ok = true -- 表示已经全部分配了料箱 end -- 把分配掉的si_qty个货品加到补料呼出的容器里 local cell_item = { cntr_code = out_cntr.cntr_code, cell_type = out_cntr.cell_type, cell_no = empty_cell.cell_no, item_code = sku.S_ITEM_CODE, item_name = sku.S_ITEM_NAME, wms_bn = "", qty = Q, sum_volume = Q*sku.volume, sum_weight = Q*sku.weight, weight = sku.weight, volume = sku.volume, sku = sku } empty_cell.item_code = sku.S_ITEM_CODE -- 说明该空料格已经有分配,需要后面的程序删除 table.insert( out_cntr.cell_list, cell_item ) out_cntr.cntr_good_weight = out_cntr.cntr_good_weight + cell_item.sum_weight table.insert( sku.cntr_cell_list, cell_item ) nRet, strRetInfo = wms_cntr.CNTR_cell_alloc_set( strLuaDEID, cntr_cell.cntr_code, cntr_cell.cell_no, pac_cfg.bs_no ) if nRet ~= 0 then return 1, strRetInfo end if ( sku.ok ) then break end end end end end return 0 end -- 删除呼出容器中的empty_cell 列表中 名为 cell_no 的料格 function wms_pac.remove_empty_cell( cntr, cell_no ) local n for n = 1, #cntr.empty_cell do if (cntr.empty_cell[n].cell_no == cell_no ) then table.remove( cntr.empty_cell, n ) return end end end -- SKU 根据 cell_type + Q (需要呼出料格数量) 进行排序 Q 大的前面 local function sku_sort( sku_a, sku_b ) -- A 排 B 前面 if sku_a.S_CELL_TYPE > sku_b.S_CELL_TYPE then return false elseif sku_a.S_CELL_TYPE == sku_b.S_CELL_TYPE then return sku_a.Q > sku_b.Q end return true end --[[ 合并可混箱的相同类型料格数量, same_mixing_rule_item_list --> {{ cell_type ,need_cell_num , mixing_rule={} item_codes = {"X1","X2"} },...} out_cell_item --> { cell_type ,need_cell_num , mixing_rule={}, item_code } --]] local function add_in_same_mixing_rule_item_list( ctd, same_mixing_rule_item_list, out_cell_item ) local find = false for _, cell_item in ipairs( same_mixing_rule_item_list ) do if cell_item.cell_type == out_cell_item.cell_type then if not lua.isTableEmpty( out_cell_item.mixing_rule ) then -- 判断是否相同混箱规则 nRet, find = wms_pac.sku_complies_with_mixing_rule ( ctd, out_cell_item.mixing_rule, cell_item.mixing_rule ) if ( nRet ~= 0 ) then return 1, find end if find then cell_item.need_cell_num = cell_item.need_cell_num + out_cell_item.need_cell_num table.insert( cell_item.sku_list, out_cell_item.sku ) return 0 end end end end if not find then local out_cntr = { cell_type = out_cell_item.cell_type, need_cell_num = out_cell_item.need_cell_num, mixing_rule = out_cell_item.mixing_rule, sku_list = {} } table.insert( out_cntr.sku_list, out_cell_item.sku ) table.insert( same_mixing_rule_item_list, out_cntr ) end return 0 end --[[ 把货品分配到 呼出的料箱格列表 out_cntr_list cntr_list/准备呼出的料箱 = {{ cntr_code,cntr_good_weight,cntr_mixing_rule = {} cell_type,empty_cell_list = {},,full = false,cell_list = {},...} cntr_mixing_rule -- 料箱混料规则 item -- 入库单明细 item_list 中的一个元素 out_cntr_list -- 分配好的料格加入这个列表 注意:这里的 out_cntr_list 都是符合混箱规则的,空料格都可以分配 --]] local function alloc_cntr( strLuaDEID, pac_cfg, cntr_list, item, out_cntr_list ) local nRet, strRetInfo if pac_cfg == nil then return 1, "alloc_cntr 函数中输入参数 pac_cfg 不能为 nil" end if item == nil then return 1, "alloc_cntr 函数中输入参数 item 不能为 nil" end if cntr_list == nil then return 1, "alloc_cntr 函数中输入参数 cntr_list 不能为 nil" end -- 如果货品已经分配完成不需要执行该函数 if item.ok then return 0 end local cntr_cell = {} local si_qty, qty for _, cntr in ipairs( cntr_list ) do -- 遍历呼出料箱中的空料格列表 for _, empty_cell in ipairs( cntr.empty_cell_list ) do -- item_code 为空说明这个料格还没有被其它货品分配 if empty_cell.item_code == '' then -- 料格初始化 cntr_cell = { cntr_code = cntr.cntr_code, cell_no = empty_cell.cell_no, cell_type = cntr.cell_type, good_volume = 0, -- 料格已经存放的货品体积累计值 good_weight = 0, qty = 0, wms_bn = "" } cntr_good_weight = cntr.cntr_good_weight -- 计算一下料格能分配多少个货品 si_qty nRet, si_qty = wms_cntr.Get_CntrCell_Goods_Qty( pac_cfg.ctd, cntr_good_weight, cntr_cell, item ) if ( nRet ~= 0 ) then return 1, si_qty end -- 如果计算出来的可存储数量大于 item.qty qty = item.qty - item.alloc_qty if ( si_qty > qty ) then si_qty = qty end -- si_qty 补料数量 if ( si_qty > 0 ) then item.alloc_qty = item.alloc_qty + si_qty if ( lua.equation( item.alloc_qty, item.qty) ) then item.ok = true -- 表示已经全部分配了料箱 end -- 把分配掉的si_qty个货品加到补料呼出的容器里 local cell_item = { cntr_code = cntr_cell.cntr_code, cell_type = cntr_cell.cell_type, cell_no = cntr_cell.cell_no, item_code = item.S_ITEM_CODE, item_name = item.S_ITEM_NAME, wms_bn = "", qty = si_qty, sum_volume = si_qty*item.volume, sum_weight = si_qty*item.weight, weight = item.weight, volume = item.volume, sku = item } table.insert( item.cntr_cell_list, cell_item ) table.insert( cntr.cell_list, cell_item ) cntr.cntr_good_weight = cntr.cntr_good_weight + cell_item.sum_weight empty_cell.item_code = cell_item.item_code if lua.isTableEmpty( cntr.cntr_mixing_rule ) then local cntr_mixing_rule = {} for _, attr in ipairs( pac_cfg.ctd.mixing_attrs ) do cntr_mixing_rule[attr] = lua.Get_StrAttrValue( item[attr]) end cntr.cntr_mixing_rule = cntr_mixing_rule end -- 更新容器对象里 N_ALLOC_CELL_NUM 数量 nRet, strRetInfo = wms_cntr.CNTR_cell_alloc_set( strLuaDEID, cntr_cell.cntr_code, cntr_cell.cell_no, pac_cfg.bs_no ) if nRet ~= 0 then return 1, strRetInfo end if item.ok then goto reste_cntr_list end end end end end ::reste_cntr_list:: -- 设置一下 cntr_list 中的空料格 local find wms_pac.reset_empty_cell_info( cntr_list ) for _, cntr in ipairs( cntr_list ) do if #cntr.cell_list > 0 then find = false for _, out_cntr in ipairs( out_cntr_list ) do if cntr.cntr_code == out_cntr.cntr_code then find = true break end end if not find then table.insert( out_cntr_list, cntr ) end end end return 0 end --[[ 把对应的货品分配到呼出的空料箱 same_cell_sum/相同料格汇总信息 = { cell_type ,need_cell_num , mixing_rule={} sku_list = {item_code,item_state,storer} } cntr_list/呼出的料箱编码 ={{ cntr_code,cntr_good_weight,cell_type,empty_cell = {},,full = false,cell_list = {},...} item_list/入库单明细 out_cntr_list --]] local function alloc_cntr_by_cell_same_sum( strLuaDEID, pac_cfg, cell_same_sum, cntr_list, item_list, out_cntr_list ) local nRet, strRetInfo for _, sku in ipairs( cell_same_sum.sku_list ) do for _, item in ipairs( item_list ) do -- 定位到 入库单明细 这里的item if item.S_ITEM_CODE == sku.item_code and item.S_ITEM_STATE == sku.item_state and item.S_STORER == sku.storer then nRet, strRetInfo = alloc_cntr( strLuaDEID, pac_cfg, cntr_list, item, out_cntr_list ) if ( nRet ~= 0 ) then return 1, strRetInfo end break end end end return 0 end --[[ 根据货品的料格类型呼出符合混箱规则的空料格 ctd -- 容器类型定义 str_loc_where -- 查询货位的条件 item -- 入库明细中的货品 out_cntr_list -- 呼出料箱格清单 --]] local function empty_cell( strLuaDEID, pac_cfg, str_loc_where, item, out_cntr_list ) local nRet, strRetInfo local mixing_condition = '' local strTable = "TN_Container_Cell a LEFT JOIN TN_Container b ON a.S_CNTR_CODE = b.S_CODE " -- 联表 -- 如果有混箱规则,需要把容器中规则定义的属性取值 if ( pac_cfg.ctd.have_mixing_rule ) then strTable = strTable.." LEFT JOIN TN_Container_Ext c ON a.S_CNTR_CODE = c.S_CNTR_CODE" -- 获取 SKU 的混箱属性 for _, attr in ipairs( pac_cfg.ctd.mixing_attrs ) do mixing_condition = mixing_condition.." AND c."..attr.." = '"..lua.Get_StrAttrValue( item[attr] ).."' " end end local cell_type = item.S_CELL_TYPE local strAttrs = "a.S_CNTR_CODE, a.S_CELL_NO, b.F_GOOD_WEIGHT" -- 查询字段 local strCondition = " b.S_CTD_CODE = '"..pac_cfg.ctd.ctd_code.."' AND b.S_SPEC = '"..cell_type.."' ".. " AND a.S_CNTR_CODE IN (select S_CNTR_CODE from TN_Loc_Container with (NOLOCK) where S_LOC_CODE IN (select S_CODE from TN_Location with (NOLOCK) where "..str_loc_where..")) ".. " AND a.N_EMPTY_FULL = 0 AND b.N_LOCK_STATE = 0 AND b.C_ENABLE = 'Y' AND b.N_EMPTY_FULL < 2 AND b.C_FORCED_FILL = 'N'".. " AND a.S_STATE <> 'Abnormal'"..mixing_condition -- 如果要控制料箱总的载重 if ( pac_cfg.ctd.check_capacity ) then strCondition = strCondition.." AND b.F_GOOD_WEIGHT < "..pac_cfg.ctd.load_capacity end -- nRet, strRetInfo = mobox.queryMultiTable(strLuaDEID, strAttrs, strTable, 1000, strCondition ) if ( nRet ~= 0 ) then return 2, "查询【容器料格】信息失败! " .. strRetInfo end if ( strRetInfo == '' ) then return 0 end local cntr_cell_attr_set = json.decode(strRetInfo) for _, cntr_cell_attr in ipairs( cntr_cell_attr_set ) do -- cntr_cell 用于计算数量用 local cntr_cell = { cntr_code = cntr_cell_attr[1], cell_no = cntr_cell_attr[2], cell_type = cell_type, wms_bn = "", good_volume = 0, good_weight = 0, qty = 0 } cntr_good_weight = lua.Get_NumAttrValue( cntr_cell_attr[3] ) -- 计算一下料格能分配多少个货品 si_qty nRet, si_qty = wms_cntr.Get_CntrCell_Goods_Qty( pac_cfg.ctd, cntr_good_weight, cntr_cell, item ) if ( nRet ~= 0 ) then return 1, si_qty end -- 如果计算出来的可存储数量大于 item.qty qty = item.qty - item.alloc_qty if ( si_qty > qty ) then si_qty = qty end -- si_qty 补料数量 if ( si_qty > 0 ) then item.alloc_qty = item.alloc_qty + si_qty if ( lua.equation( item.alloc_qty, item.qty) ) then item.ok = true -- 表示已经全部分配了料箱 end -- 把分配掉的si_qty个货品加到补料呼出的容器里 weight = lua.Get_NumAttrValue( item.F_WEIGHT ) volume = lua.Get_NumAttrValue( item.F_VOLUME ) local cell_item = { cntr_code = cntr_cell.cntr_code, cell_type = cntr_cell.cell_type, cell_no = cntr_cell.cell_no, item_code = item.S_ITEM_CODE, item_name = item.S_ITEM_NAME, wms_bn = "", qty = si_qty, sum_volume = si_qty*volume, sum_weight = si_qty*weight, weight = weight, volume = volume, sku = item } wms_pac.put_cell_item_to_out_cntr_list( out_cntr_list, cntr_good_weight, cell_item, item.mixing_rule ) table.insert( item.cntr_cell_list, cell_item ) nRet, strRetInfo = wms_cntr.CNTR_cell_alloc_set( strLuaDEID, cntr_cell.cntr_code, cntr_cell.cell_no, pac_cfg.bs_no ) if nRet ~= 0 then return 1, strRetInfo end if item.ok then return 0 end end end return 0 end -- 计算 SKU 需要多少个空料格 Q local function set_sku_Q_value( ctd, item_list ) local nRet, strRetInfo local cntr_cell = { cntr_code = "", cell_no = "", wms_bn = "", good_volume = 0, cell_type = "", good_weight = 0, qty = 0 } local cell_max_qty -- 料格数量 local x_qty, qty for _, sku in ipairs( item_list ) do if not sku.ok then -- 计算 SKU 需要多少个空料格 -- 获取一个料格能分配多少给 SKU qty = sku.qty - sku.alloc_qty cntr_cell.cell_type = sku.S_CELL_TYPE nRet, cell_max_qty = wms_cntr.Get_CntrCell_Goods_Qty( ctd, 0, cntr_cell, sku ) if nRet ~= 0 then return nRet, cell_max_qty end if cell_max_qty <= 0 then return 1, "SKU'"..sku.S_ITEM_CODE.."'在计算料格转载最大数量是失败,返回数量 <= 0" end Q = math.floor( qty/cell_max_qty) -- 余量 x_qty = qty - Q*cell_max_qty if ( lua.equation( 0, x_qty ) == false ) then Q = Q + 1 end sku.Q = Q else sku.Q = 0 end end return 0 end local function get_same_mixing_rule_cntr_list( ctd, item_list ) local nRet, strRetInfo local same_mixing_rule_cntr_list = {} for _, sku in ipairs( item_list ) do -- 合并相同 cell_type 及 混箱规则的SKU这些货品可以一起呼叫料箱 if not sku.ok then local out_cell_item = { cell_type = sku.S_CELL_TYPE, need_cell_num = sku.Q, mixing_rule = sku.mixing_rule, sku = { item_code = sku.S_ITEM_CODE, item_state = sku.S_ITEM_STATE, storer = sku.S_STORER } } nRet, strRetInfo = add_in_same_mixing_rule_item_list( ctd, same_mixing_rule_cntr_list, out_cell_item ) if nRet ~= 0 then return 1, strRetInfo end end end return 0, same_mixing_rule_cntr_list end -- 从容器表查出 num 个空料箱(所有料格都空)分配 -- cell_same_sum 相同混箱规则需要的料格定义, num -- 需要的料箱数量, str_loc_where -- 查货位的条件 -- out_cntr_list -- 呼出料箱料格结果 local function query_empty_cntr_to_alloc( strLuaDEID, pac_cfg, item_list, cell_same_sum, num, str_loc_where, out_cntr_list ) local n, nRet, data_objs, strRetInfo, data_attrs local cntr_list = {} -- 注: N_EMPTY_FULL = 0 表示空箱 N_LOCK_STATE = 0 表示料格没锁 N_ALLOC_CELL_NUM = 0 表示没预分配 -- **** N_ALLOC_CELL_NUM 这里需要再思考一下 local strCondition = "S_CTD_CODE = '"..pac_cfg.ctd.ctd_code.."' AND S_SPEC = '"..cell_same_sum.cell_type.."' AND N_EMPTY_FULL = 0 AND N_LOCK_STATE = 0 AND N_ALLOC_CELL_NUM = 0 AND ".. "S_CODE IN (select S_CNTR_CODE from TN_Loc_Container with (NOLOCK) where S_LOC_CODE IN (select S_CODE from TN_Location with (NOLOCK) where "..str_loc_where..")) " nRet, data_objs = m3.QueryDataObject3( strLuaDEID, "Container", strCondition, "",num) if (nRet ~= 0) then return 2, "查询容器失败! "..data_objs end if ( data_objs ~= '' ) then for n = 1, #data_objs do data_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) local out_cntr = { cntr_code = data_attrs.S_CODE, cntr_good_weight = 0, cell_type = cell_same_sum.cell_type, empty_cell_list = {}, full = false, cell_list = {} } table.insert( cntr_list, out_cntr ) end nRet, strRetInfo = wms_pac.set_out_cntr_empty_cell( strLuaDEID, cntr_list ) if ( nRet ~= 0 ) then return 1, strRetInfo end -- 把对应的货品分配到呼出的空料箱,把料箱加入呼出列表 out_cntr_list nRet, strRetInfo = alloc_cntr_by_cell_same_sum( strLuaDEID, pac_cfg, cell_same_sum, cntr_list, item_list, out_cntr_list ) if ( nRet ~= 0 ) then return 1, strRetInfo end end return 0 end --[[ step2.0 获取空料格 --]] function wms_pac.get_out_cntr_list_dmg( strLuaDEID, pac_cfg, item_list, out_cntr_list ) local nRet, strRetInfo, strCondition, strOrder local n, nCount local str_loc_where = '' -- step1 输入参数判断 if ( pac_cfg.wh_code == nil or pac_cfg.wh_code == '' ) then return 1, "输入参数错误, wh_code 必须有值" end -- 这里要加一下巷道限制 ****** str_loc_where = "S_WH_CODE = '"..pac_cfg.wh_code.."'" local ctd_code = pac_cfg.ctd.ctd_code if ( ctd_code == nil or ctd_code == '' ) then return 1, "输入参数错误, pac_cfg 中 ctd.ctd_code 必须有值" end -- step2 数据准备 -- 统计入库 sku 需要多少个料格 nRet, strRetInfo = set_sku_Q_value( pac_cfg.ctd, item_list ) if nRet ~= 0 then return 1, strRetInfo end -- SKU 根据 cell_type + Q (需要呼出料格数量) 进行排序 Q 大的前面 table.sort( item_list, sku_sort ) -- 遍历需要入库的货品清单,把相同入库条件的货品需要呼出的料箱数量合并,确定是否整个料箱都是空的料箱先呼出 -- 先呼出整个料箱都是空的料箱,目的是减少出入库料箱数量 local same_mixing_rule_cntr_list nRet, same_mixing_rule_cntr_list = get_same_mixing_rule_cntr_list( pac_cfg.ctd, item_list ) if ( nRet ~= 0 ) then return 1, same_mixing_rule_cntr_list end -- step3 -- 先把可以进相同混箱规则料箱的货品 SUM 在一起,呼出全空的料箱(每个料格都是空),尽量减少搬运次数 local cell_def, data_attrs, data_objs, cell_same_sum local cntr_list for _, cell_same_sum in ipairs( same_mixing_rule_cntr_list ) do nRet, cell_def = wms_cntr.Get_CTD_GridDef( pac_cfg.ctd, cell_same_sum.cell_type ) if nRet ~= 0 then return 1, cell_def end -- 如果需要的料格数量小于 一个空料箱的料格 不执行下面的呼出 if cell_same_sum.need_cell_num >= cell_def.box_num then num = math.floor( cell_same_sum.need_cell_num/cell_def.box_num ) -- 从容器表找到合适的料箱(数量=num)分配给 item_list nRet, strRetInfo = query_empty_cntr_to_alloc( strLuaDEID, pac_cfg, item_list, cell_same_sum, num, str_loc_where, out_cntr_list ) if nRet ~= 0 then return 1, strRetInfo end end end local pac_is_ok = wms_pac.item_list_is_all_ok( item_list ) -- step 4 查找有相同混箱规则的且未满有货的料格 if not pac_is_ok then for _, item in ipairs( item_list ) do if not item.ok then -- 根据货品的适配料格类型呼出空料格 for _, cntr in ipairs( out_cntr_list ) do nRet, strRetInfo = wms_pac.pre_alloc_sku_to_out_cntr( strLuaDEID, pac_cfg, item, cntr ) if ( nRet ~= 0 ) then return 1, strRetInfo end if item.ok then break end end end end end pac_is_ok = wms_pac.item_list_is_all_ok( item_list ) -- step 5 查找全空料箱装载剩余没分配料格的货品 if not pac_is_ok then -- 进一步设置一下货品需要的料格数 Q nRet, strRetInfo = set_sku_Q_value( pac_cfg.ctd, item_list ) if nRet ~= 0 then return 1, strRetInfo end -- SKU 根据 cell_type + Q (需要呼出料格数量) 进行排序 Q 大的前面 table.sort( item_list, sku_sort ) nRet, same_mixing_rule_cntr_list = get_same_mixing_rule_cntr_list( pac_cfg.ctd, item_list ) if ( nRet ~= 0 ) then return 1, same_mixing_rule_cntr_list end for _, cell_same_sum in ipairs( same_mixing_rule_cntr_list ) do nRet, cell_def = wms_cntr.Get_CTD_GridDef( pac_cfg.ctd, cell_same_sum.cell_type ) if nRet ~= 0 then return 1, cell_def end num = math.floor( cell_same_sum.need_cell_num/cell_def.box_num ) if ( cell_same_sum.need_cell_num-num*cell_def.box_num ) > 0 or num == 0 then num = num + 1 end -- 从容器表找到合适的料箱(数量=num)分配给 item_list nRet, strRetInfo = query_empty_cntr_to_alloc( strLuaDEID, pac_cfg, item_list, cell_same_sum, num, str_loc_where, out_cntr_list ) if nRet ~= 0 then return 1, strRetInfo end end end return 0 end --[[ step1.1 获取补料料格 --]] function wms_pac.get_replenishment_cntr_list( strLuaDEID, pac_cfg, item_list ) local nRet, strRetInfo, strCondition, strOrder local n, nCount local str_loc_where = '' local supplement_cntr_list = {} -- 输入参数判断 if ( pac_cfg.wh_code == nil or pac_cfg.wh_code == '' ) then return 1, "输入参数错误, wh_code必须有值" end str_loc_where = "S_WH_CODE = '"..pac_cfg.wh_code.."'" local ctd_code = pac_cfg.ctd.ctd_code if ( ctd_code == nil or ctd_code == '' ) then return 1, "输入参数错误, pac_cfg 中 ctd.ctd_code 必须有值" end -- 组织匹配料格的查询条件 local si_match_attrs = pac_cfg.ctd.si_match_attrs or {} if ( type(si_match_attrs) ~= "table" ) then return 1, "输入参数错误, pac_cfg.ctd.si_match_attrs 必须是 table 类型!" end -- 匹配属性要加上 S_ITEM_CODE, S_STRORER, S_ITEM_STATE table.insert( si_match_attrs, "S_STORER" ) table.insert( si_match_attrs, "S_ITEM_CODE" ) table.insert( si_match_attrs, "S_ITEM_STATE" ) local ret_attr local cntr_cell = {} local si_qty, qty, cntr_good_weight -- 确定匹配料格时查询顺序 strOrder = '' local si_match_order = lua.Get_StrAttrValue( pac_cfg.ctd.si_match_order ) if ( si_match_order == "Last_Entry_Batch" ) then strOrder = "a.S_WMS_BN DESC" elseif ( si_match_order == "QTY_Desc" ) then strOrder = "a.F_QTY DESC" elseif ( si_match_order == "QTY_Asc" ) then strOrder = "a.F_QTY Asc" end -- 查询匹配的数量 local si_cntr_num = lua.Get_NumAttrValue( pac_cfg.ctd.si_cntr_num ) if ( si_cntr_num == 0 ) then si_cntr_num = 1000 end strTable = "TN_Container_Cell a LEFT JOIN TN_Container b ON a.S_CNTR_CODE = b.S_CODE " -- 联表 -- 如果有混箱规则,需要把容器中规则定义的属性取值 if ( pac_cfg.ctd.have_mixing_rule ) then strTable = strTable.." LEFT JOIN TN_Container_Ext c ON a.S_CNTR_CODE = c.S_CNTR_CODE" end strAttrs = "a.S_CNTR_CODE, a.S_CELL_NO, a.S_WMS_BN, a.F_GOOD_VOLUME, b.S_SPEC, b.F_GOOD_WEIGHT, a.F_GOOD_WEIGHT, a.F_QTY" -- 查询字段 local ext_attr_index = 8 if ( pac_cfg.ctd.have_mixing_rule ) then for _, attr in ipairs( pac_cfg.ctd.mixing_attrs ) do strAttrs = strAttrs..",c."..attr end end local cntr_max_weight = pac_cfg.ctd.load_capacity local match_condition, str_value, weight, volume local si_match_attrs_count = #si_match_attrs local cntr_mixing_rule for n = 1, #item_list do match_condition = "" for i = 1, si_match_attrs_count do str_value = item_list[n][si_match_attrs[i]] if ( str_value == nil ) then return 1, "容器类型定义'"..pac_cfg.ctd.ctd_code.."' matching_attrs --> "..si_match_attrs[i].." 没有在 item_list 中定义!" end match_condition = match_condition.." AND a."..si_match_attrs[i].." = '"..str_value.."' " end -- 查询出仓库里同一货品最近批次号并且未满的料格, 查 Container_Cell 表 -- 注: N_EMPTY_FULL = 1 表示料格有货未满格 N_LOCK_STATE = 0 表示料格没锁 -- a.S_STATE <> 'Abnormal' 料格有异常 strCondition = "b.S_CTD_CODE = '"..ctd_code.."' AND a.S_CNTR_CODE IN (select S_CNTR_CODE from TN_Loc_Container with (NOLOCK) where S_LOC_CODE IN (select S_CODE from TN_Location with (NOLOCK) where "..str_loc_where..")) ".. " AND a.C_FORCED_FILL = 'N' AND a.N_EMPTY_FULL = 1 AND b.N_LOCK_STATE = 0 AND b.C_ENABLE = 'Y' AND b.N_EMPTY_FULL < 2 AND b.C_FORCED_FILL = 'N'".. " AND a.S_STATE <> 'Abnormal'"..match_condition -- 如果要控制料箱总的载重 if ( pac_cfg.ctd.check_capacity ) then strCondition = strCondition.." AND b.F_GOOD_WEIGHT < "..cntr_max_weight end -- nRet, strRetInfo = mobox.queryMultiTable(strLuaDEID, strAttrs, strTable, si_cntr_num, strCondition ) if ( nRet ~= 0 ) then return 2, "查询【容器料格】信息失败! " .. strRetInfo end if ( strRetInfo ~= '' ) then ret_attr = json.decode(strRetInfo) if ( si_cntr_num == 1 ) then -- 只是匹配一个补料料箱,一般就是找最近入库批次的料箱 cntr_cell.cntr_code = ret_attr[1][1] cntr_cell.cell_no = ret_attr[1][2] cntr_cell.wms_bn = ret_attr[1][3] cntr_cell.good_volume = lua.StrToNumber( ret_attr[1][4] ) -- 料格已经存放的货品体积累计值 cntr_cell.cell_type = ret_attr[1][5] -- 料格类型 A/B/C/D/E cntr_good_weight = lua.StrToNumber( ret_attr[1][6] ) -- 容器当前存储货品重量 cntr_cell.good_weight = lua.StrToNumber( ret_attr[1][7] ) cntr_cell.qty = lua.StrToNumber( ret_attr[1][8] ) -- 获取料箱混箱规则属性 cntr_mixing_rule = {} for i, attr in ipairs( pac_cfg.ctd.mixing_attrs ) do cntr_mixing_rule[attr] = lua.Get_StrAttrValue( ret_attr[1][ext_attr_index+i]) end -- 计算一下料格能分配多少个货品 si_qty nRet, si_qty = wms_cntr.Get_CntrCell_Goods_Qty( pac_cfg.ctd, cntr_good_weight, cntr_cell, item_list[n] ) if ( nRet ~= 0 ) then return 1, si_qty end -- 如果计算出来的可存储数量大于 item.qty qty = item_list[n].qty - item_list[n].alloc_qty if ( si_qty > qty ) then si_qty = qty end -- si_qty 补料数量 if ( si_qty > 0 ) then item_list[n].alloc_qty = item_list[n].alloc_qty + si_qty if ( lua.equation( item_list[n].alloc_qty, item_list[n].qty) ) then item_list[n].ok = true -- 表示已经全部分配了料箱 end -- 把分配掉的si_qty个货品加到补料呼出的容器里 weight = lua.Get_NumAttrValue( item_list[n].F_WEIGHT ) volume = lua.Get_NumAttrValue( item_list[n].F_VOLUME ) local cell_item = { cntr_code = cntr_cell.cntr_code, cell_type = cntr_cell.cell_type, cell_no = cntr_cell.cell_no, item_code = item_list[n].S_ITEM_CODE, item_name = item_list[n].S_ITEM_NAME, wms_bn = "", qty = si_qty, sum_volume = si_qty*volume, sum_weight = si_qty*weight, weight = weight, volume = volume, sku = item_list[n] } wms_pac.put_cell_item_to_out_cntr_list( supplement_cntr_list, cntr_good_weight, cell_item, cntr_mixing_rule ) table.insert( item_list[n].cntr_cell_list, cell_item ) end else return 1, "代码未完成..." end end ::continue:: end return 0, supplement_cntr_list end --[[ 料箱料格类型根据 SKU 中的 S_CELL_TYPE 获取 输入参数: pac_cfg = { wh_code, area_code 仓库,库区编码 station 站台 bs_type 来源类型:入库单、入库波次 bs_no 来源单号 aisle -- 可用巷道 'A01',"A02",... 字符串 cntr_out_op_def = "料箱出库", --空料箱出库的作业定义 cntr_back_op_def = "货品入库" --料箱回库的主业定义 -- 容器类型定义 ctd = { check_capacty = false/true, ---是否检测整箱载重), load_capacity = 50, --料箱最大载重 si_enable = false/true, -- 是否启用补料 si_match_attrs = {"S_ITEM_CODE","XX",...} -- 这些属性一致的可以进行补料 si_match_order = "Last_Entry_Batch/QTY_Desc/QTY_Asc" -- 可以为空 si_cntr_num = 0/1 -- 0 不限制所以可以补料的料箱优先补料, 1 -- 补一个料箱 grid_box_def = { { cell_type = "A",volume = 72000, box_num = 1,load_capacity=12},...} have_mixing_rule = false/true true 表示有混箱规则 mixing_attrs = {"A","B"} -- 这些属性一样的可以放一个料箱 } } item_list -- 需要预分配料箱的货品列表(注意这里入库的货品的容器类型都一样) pac_list, pac_detail_list 为返回值 --]] function wms_pac.Pre_Alloc_Cntr_DMG( strLuaDEID, pac_cfg, item_list ) local nRet, strRetInfo, n local wh_code, station, bs_type, bs_no local ctd_code local sku_count = #item_list local supplement_cntr_list = {} local out_cntr_list = {} -- step0: 输入参数校验,及初始化 if ( sku_count == 0 ) then return 0 end if ( pac_cfg == nil or type( pac_cfg ) ~= "table" ) then return 2, "Pre_Alloc_Cntr_DMG 函数输入参数错误: pac_cfg 必须有值,必须是 table 类型" end wh_code = lua.Get_StrAttrValue( pac_cfg.wh_code ) if ( wh_code == '' ) then return 2, "Pre_Alloc_Cntr_DMG 函数输入参数错误: pac_cfg 参数中的 wh_code 必须有值!" end station = lua.Get_StrAttrValue( pac_cfg.station ) bs_type = lua.Get_StrAttrValue( pac_cfg.bs_type ) bs_no = lua.Get_StrAttrValue( pac_cfg.bs_no ) ctd_code = lua.Get_StrAttrValue( pac_cfg.ctd.ctd_code ) if ( ctd_code == "" ) then return 2, "Pre_Alloc_Cntr_DMG 算法只适合 Cell_Box 类型的容器进行分配, pac_cfg中的参数 ctd_code 错误!" end -- step1 计算补料呼出料箱 -- 是否启用补料呼出 local have_si_empty_cell = false -- 补料呼出料箱中是否还有空料格 local pac_is_ok = false -- 预分配操作已经完成 if ( pac_cfg.ctd.si_enable ) then -- step1.1 容器定义中允许补料箱 nRet, supplement_cntr_list = wms_pac.get_replenishment_cntr_list( strLuaDEID, pac_cfg, item_list ) if ( nRet ~= 0 ) then return 2, supplement_cntr_list end -- step1.2 设置补料料箱中的空料格列表 have_si_empty_cell = wms_pac.set_replenishment_cntr_emptycell( strLuaDEID, supplement_cntr_list ) -- step1.3 检查补料箱中是否和合适料格做预分配 if not wms_pac.item_list_is_all_ok( item_list ) and have_si_empty_cell then for _, sku in ipairs( item_list ) do if ( sku.qty > sku.alloc_qty ) then for _, si_cntr in ipairs( supplement_cntr_list ) do nRet, strRetInfo = wms_pac.pre_alloc_sku_to_out_cntr( strLuaDEID, pac_cfg, sku, si_cntr ) if ( nRet ~= 0 ) then return 1, strRetInfo end if sku.ok then break end end end end -- 删除 empty_cell_list 中已经分配出去的料格,返回 supplement_cntr_list 是否还有空料格 have_si_empty_cell = wms_pac.reset_empty_cell_info( supplement_cntr_list ) end -- 判断是否已经完成预分配 pac_is_ok = wms_pac.item_list_is_all_ok( item_list ) end -- step2 遍历item_list计算适配空料箱 if not pac_is_ok then -- 继续呼出料箱 nRet, strRetInfo = wms_pac.get_out_cntr_list_dmg( strLuaDEID, pac_cfg, item_list, out_cntr_list ) if ( nRet ~= 0 ) then return 2, strRetInfo end end -- step3 检查是否所以货品都已经预分配了料箱, local msg_list = {} -- 保存无法分配料格的货品数量 for _, sku in ipairs( item_list ) do if ( sku.ok == false ) then local qty = sku.qty-sku.alloc_qty local msg = "系统没有匹配到货品编码 = '"..sku.S_ITEM_CODE.."'的适配料箱进行预分配, 货品的适配料格类型 = '"..sku.S_CELL_TYPE.."', 数量 = "..qty table.insert( msg_list, msg ) end end if ( #msg_list > 0 ) then -- 这些货品没有合适的料箱 return 1, lua.table2str( msg_list ) end return 0, supplement_cntr_list, out_cntr_list end return wms_pac