--[[ 编码: 名称: 预分配料箱算法 作者:HAN 日期:2024-8-5 来源:巨星料箱库 算法标识 CBG -- Compatible Bin Grid 专指WMS中与物料存储适配的料格系统 料格可以装载货品数量通过体积和料格体积或,重量和料箱的最大载重进行计算得到 函数: -- Pre_Alloc_CNTR_By_CBG( strLuaDEID, item_list, pac_cfg, pac_list, pac_detail_list ) item_list 需要入库的货品清单(注意这个清单的长度要进行控制,Lua参数传入,传出都能能太长) 返回参数: pac_list, pac_detail_list 功能: 空料箱呼出算法,货物可以适配多种类型料箱,根据入库的总体积进行计算 更改记录: V2.0 HAN 20241220 对升格进行提前处理,加了一个专门的升格链表 ------------------------------------------------------------------------------------------------------------------------- 几个主要数据类型定义 1) item_list -- 入库货品清单 { item_code, volume -- 单个体积 weight -- 单个重量 item_name, cell_type:"A/B/C/D/E" -- 货品最小适配料格类型 qty -- 计划入库数量 alloc_qty -- 已经安排料箱的货品数量 cntr_cell_list -- 已经配货的料格 { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", entry_batch_no:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} empty_cell_type -- Q 数量的空料格类型 辅助用,表示本次整体呼出的空料箱类型 Q -- 需要呼出空料格数量 } 2) supplement_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:[{"cell_no",item_code, item_name}] -- 呼出的补料料箱中的空料格 cell 属性同下面的 cell_list cell_list:{ { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", entry_batch_no:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} } } 3) out_cntr_list -- 呼出的空料箱( 和 supplement_cntr_list 是一样的) { cntr_code = "XXX", cell_type = "A/B/C/D/E", cntr_good_weight = 0, full = false, empty_cell_num = 0, empty_cell:[{"cell_no",item_code, item_name}] cell_list:{ -- 分配货品的料格 { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", entry_batch_no:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} } } 变更记录: V2.0 HAN 20241030 -- 取消给容器加锁 V3.0 HAN 20241104 -- 升格这里的处理有改进 V4.0 HAN 20241108 -- 补料呼出料箱和空料箱进行检查,相同料箱号的合并 ]] prj_base = require( "prj_base" ) wms_cntr = require( "wms_container" ) -- wms_pac_dmg 是制定料箱类型的精准预分配方法,属于 pre-alloction container 的一种,先require local wms_pac = require( "wms_pac_dmg" ) -- box_def 是料箱体积和料格,料箱类型的是一个对照表 local box_def = { { cell_type = "A", volume = 72000, box_num = 1 }, { cell_type = "B", volume = 36000, box_num = 2 }, { cell_type = "C", volume = 24000, box_num = 3 }, { cell_type = "D", volume = 18000, box_num = 4 }, { cell_type = "E", volume = 12000, box_num = 6 }, } local BOX_MAX_WEIGHT = 0 local CHECK_CAPACITY = flase -- 是否检查超重 local function item_list_is_ok( item_list ) local n for n = 1, #item_list do if ( item_list[n].ok == false ) then return false end end return true end -- 计算料格能放多少个货品, 根据料箱剩余重量计算可存储货品数量Qw, 根据料箱格剩余体积计算可存储货品数量Qv -- 取Qw,Qv最小值作为数量返回 -- container :料箱 -- container_cell :料格 {"cntr_code","cell_no","cell_type","good_volume","max_weight" } -- item: {"item_code","volume", "weight"} -- 存储货品 local function calculate_quantity( strLuaDEID, cntr_good_weight, container_cell, item ) -- 输入参数判断 if (container_cell.cell_type == '' or container_cell.cell_type == nil ) then return 1, "calculate_quantity 参数中cell_type必须有值!" end if (item.volume == nil or type(item.volume) ~= "number" or item.volume <= 0) then return 1, "calculate_quantity 参数中item.volume必须有值并且是数值类型大于0!" end if ( CHECK_CAPACITY ) then if (item.weight == nil or type(item.weight) ~= "number" or item.weight <= 0) then return 1, "calculate_quantity 参数中item.weight必须有值并且是数值类型大于0!" end end -- 通过 "常量" -- "巨星:料格最大体积-A" cell_type 获取料格最大体积 -- 通过 "常量" -- "巨星:料箱最大载重" 获取料箱最大载重量 local cntr_max_weight = BOX_MAX_WEIGHT local cell_max_volume = 0 local n for n = 1, #box_def do if ( box_def[n].cell_type == container_cell.cell_type ) then cell_max_volume = box_def[n].volume break end end if ( cell_max_volume == 0 ) then return 1, "料格类型'"..container_cell.cell_type.."'没有定义体积!" end -- V2.0 加容积率 MDF BY HAN 20250315 local far = wms_base.Get_nConst( strLuaDEID, "容积率", 1 ) if ( far == 0 or far > 1 ) then far = 1 end cell_max_volume = cell_max_volume * far -- 根据料箱格剩余体积计算可存储货品数量Qv local Qv = math.floor(( cell_max_volume - container_cell.good_volume )/item.volume) if ( Qv < 0 ) then msg = "calculate_quantity 通过体积计算数量失败! cell_max_volume = "..cell_max_volume.." container_cell_good_volume = "..container_cell.good_volume.. " 料箱号 = '"..container_cell.cntr_code.."' 料格号 = "..container_cell.cell_no lua.Warning( strLuaDEID, debug.getinfo(1), msg ) Qv = 0 end -- 根据料箱剩余重量计算可存储货品数量Qw local Qw = 0 if ( BOX_MAX_WEIGHT ) then Qw = math.floor(( cntr_max_weight - cntr_good_weight )/item.weight) if ( Qw < 0 ) then local msg = "calculate_quantity 通过重量计算数量失败! cntr_max_weight = "..cntr_max_weight.." cntr_good_weight = "..cntr_good_weight.. " 料箱号 = '"..container_cell.cntr_code.."' 料格号 = "..container_cell.cell_no lua.Warning( strLuaDEID, debug.getinfo(1), msg ) Qw = 0 end else return 0, Qv end if ( Qw > Qv ) then return 0, Qv end return 0, Qw end -- 把料格加入supplement_cntr_list (质疑20241222) local function put_cell_item_to_supplement_cntr_list( supplement_cntr_list, cntr_good_weight, cell_item ) local n local bFind = false for n = 1, #supplement_cntr_list do if ( supplement_cntr_list[n].cntr_code == cell_item.cntr_code ) then table.insert( supplement_cntr_list[n].cell_list, cell_item ) -- 容器中商品的总重量需要加上岗加入到料格里的货品重量 supplement_cntr_list[n].cntr_good_weight = supplement_cntr_list[n].cntr_good_weight + cell_item.sum_weight return end end if (bFind == false) then local supplement_cntr = {} supplement_cntr = { cntr_code = cell_item.cntr_code, cell_type = cell_item.cell_type, full = false, empty_cell = {}, cntr_good_weight = cntr_good_weight + cell_item.sum_weight, cell_list = {} } table.insert( supplement_cntr.cell_list, cell_item ) table.insert( supplement_cntr_list, supplement_cntr ) end end --[[ 计算补料料箱 item_list 需要入库的货品清单 wh_code supplement_rlue 补料规则 { enable = false/true, -- 是否启用补料 matching_attrs = {{attr = "S_ITEM_CODE",lua_attr=""},...} -- 这些属性一致的可以进行补料 matching_order = "Last_Entry_Batch/QTY_Desc/QTY_Asc" -- 可以为空 si_cntr_num = 0/1 -- 0 不限制所以可以补料的料箱优先补料, 1 -- 补一个料箱 } -- 返回需要补料的料箱料格链表 supplement_cntr_list { cntr_code:"xxx" cell_type:"A/B/C/D/E" cntr_good_weight: 100 -- 容器中货品重量 full: false/true empty_cell:[{"cell_no",item_code}] -- 呼出的补料料箱中的空料格 cell 属性同下面的 cell_list cell_list:{ { cntr_code:"", cell_no:"", item_code:"xx", item_name:"", entry_batch_no:"SN01", weight, volume, qty:10 ,sum_volume:10, sum_weigt:10} } } --]] -- 【step2】 local function get_replenishment_cntr_list( strLuaDEID, item_list, wh_code, supplement_rlue ) local nRet, strRetInfo, strCondition, strOrder local n, nCount local str_where = '' local supplement_cntr_list = {} -- 输入参数判断 if ( wh_code == nil or wh_code == '' ) then return 1, "输入参数错误, wh_code必须有值" end str_where = "S_WH_CODE = '"..wh_code.."'" -- 组织匹配料格的查询条件 if ( supplement_rlue.matching_attrs == nil or type(supplement_rlue.matching_attrs) ~= "table" ) then return 1, "输入参数错误, supplement_rlue 中matching_attrs必须有值!" end local ret_info, cell_attr local ret_attr local container_cell = {} local si_qty, qty, cntr_good_weight -- 确定匹配料格时查询顺序 strOrder = '' matching_order = lua.Get_StrAttrValue( supplement_rlue.matching_order ) if ( matching_order == "Last_Entry_Batch" ) then strOrder = "a.S_ENTRY_BATCH_NO DESC" elseif ( matching_order == "QTY_Desc" ) then strOrder = "a.F_QTY DESC" elseif ( matching_order == "QTY_Asc" ) then strOrder = "a.F_QTY Asc" end -- 查询匹配的数量 local si_cntr_num = lua.Get_NumAttrValue( supplement_rlue.si_cntr_num ) if ( si_cntr_num == 0 ) then si_cntr_num = 1000 end nCount = #item_list strTable = "TN_Container_Cell a LEFT JOIN TN_Container b ON a.S_CNTR_CODE = b.S_CODE" -- 联表 strAttrs = "a.S_CNTR_CODE, a.S_CELL_NO, a.S_ENTRY_BATCH_NO, a.F_GOOD_VOLUME, b.S_SPEC, b.F_GOOD_WEIGHT" -- 查询字段 local cntr_max_weight = BOX_MAX_WEIGHT local match_condition, str_value for n = 1, nCount do match_condition = "" for i = 1, #supplement_rlue.matching_attrs do if ( match_condition ~= '' ) then match_condition = match_condition.." AND " end if ( supplement_rlue.matching_attrs[i] == "S_ITEM_CODE" ) then str_value = item_list[n].item_code else return 1, "supplement_rlue 规则中 matching_attrs --> "..supplement_rlue.matching_attrs[i].." 没定义!" end match_condition = match_condition.." a."..supplement_rlue.matching_attrs[i].." = '"..str_value.."' " end -- 查询出仓库里同一货品最近批次号并且未满的料格, 查 Container_Cell 表 -- 注: N_EMPTY_FULL = 1 表示料格有货未满格 N_LOCK_STATE = 0 表示料格没锁 strCondition = "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_where..")) ".. " AND "..match_condition.." 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'" -- 如果要控制料箱总的载重 if ( 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 -- 只是匹配一个补料料箱,一般就是找最近入库批次的料箱 container_cell.cntr_code = ret_attr[1][1] container_cell.cell_no = ret_attr[1][2] container_cell.entry_batch_no = ret_attr[1][3] container_cell.good_volume = lua.StrToNumber( ret_attr[1][4] ) -- 料格已经存放的货品体积累计值 container_cell.cell_type = ret_attr[1][5] -- 料格类型 A/B/C/D/E cntr_good_weight = lua.StrToNumber( ret_attr[1][6] ) -- 容器当前存储货品重量 nRet, si_qty = calculate_quantity( strLuaDEID, cntr_good_weight, container_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 if ( si_qty > 0 ) then -- 补料料格里分配si_qty个货品,申请一个批次号 nRet, strSN = prj_base.Generate_Batch_No( item_list[n].item_code ) if ( nRet ~= 0 ) then return 1, strSN end 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个货品加到补料呼出的容器里 local cell_item = { cntr_code = container_cell.cntr_code, cell_type = container_cell.cell_type, cell_no = container_cell.cell_no, item_code = item_list[n].item_code, item_name = item_list[n].item_name, row = item_list[n].row, entry_batch_no = strSN, qty = si_qty, sum_volume = si_qty*item_list[n].volume, sum_weight = si_qty*item_list[n].weight, weight = item_list[n].weight, volume = item_list[n].volume } put_cell_item_to_supplement_cntr_list( supplement_cntr_list, cntr_good_weight, cell_item ) table.insert( item_list[n].cntr_cell_list, cell_item ) end else return 1, "代码未完成..." end end ::continue:: end return 0, supplement_cntr_list end -- item list 排序函数 把体积大的排前面 local function item_sort( item_a, item_b ) return item_a.volume > item_b.volume end -- 遍历补料呼出料箱,判断补料呼出料箱中是否有存在 空料格, 设置item中的 empty_cell 属性 local function set_replenishment_cntr_emptycell( strLuaDEID, supplement_cntr_list ) local n, nRet, strRetInfo, strCondition, strOrder, m local data_objects local cell 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_name = "", entry_batch_no = "", qty = 0, sum_volume = 0, sum_weigt = 0 } table.insert( supplement_cntr_list[n].empty_cell, empty_cell ) end else -- 没有空料箱格 supplement_cntr_list[n].full = true end end end -- 返回需要多少个料格,及装载的货品数量 -- 返回参数1 -- 料格数量 2 -- 可装货品数量 3 -- 错误标记非0为越界 local function calculate_box_count(box_volume, item_volume, qty ) if ( 0 == item_volume or 0 == box_volume ) then return 0,0 end local items_per_box = math.floor( box_volume / item_volume ) -- 单品就超体积,这种情况一般是数据错误 if ( items_per_box == 0 ) then return 0,0 end local num_boxes = math.floor( qty / items_per_box) return num_boxes, num_boxes*items_per_box end -- 返回需要多少个料格,及装载的货品数量 -- 返回参数1 -- 料格数量 2 -- 可装货品数量 3 -- 错误标记非0为越界 local function calculate_box_count_by_weight( cell_type_index, item_weight, qty ) if ( 0 == item_weight ) then return 0,0 end local items_per_box = math.floor( BOX_MAX_WEIGHT / item_weight ) -- 单品就超重,这种情况一般是数据错误 if ( items_per_box == 0 ) then return 0,0 end local num_boxes = math.floor( qty / items_per_box) return num_boxes*box_def[cell_type_index].box_num, items_per_box*num_boxes end -- 计算出item中的 Qa/Qb/Qc/Qd/Qe 数量 -- cell_type_index 料箱类型索引 1 -- A 2 --B -- 【step3】 local function generate_item_needemptycell_qty( strLuaDEID, item_list, cell_type_index, cntr_max_weight ) local nRet, strRetInfo, n local qty, total_volume, volumeX, pk_qty local cell_volume = box_def[cell_type_index].volume local cell_weight = cntr_max_weight/box_def[cell_type_index].box_num local cell_type = box_def[cell_type_index].cell_type local sum_Q = 0 if ( cell_volume == nil or cell_volume <= 0 ) then return 2,"料箱定义 box_def 无效或 cell_type_index 非法!" end for n = 1, #item_list do -- MDY BY HAN 20241211 如果当前进行匹配的料格类型 大于 货品的最小适配料格(A,B,C,D,E),不做判断 if ( cell_type > item_list[n].cell_type ) then goto continue end item_list[n].empty_cell_type = cell_type item_list[n].Q = 0 if (item_list[n].qty > item_list[n].alloc_qty ) then qty = item_list[n].qty - item_list[n].alloc_qty if ( lua.equation( qty, 0 ) ) then goto continue end -- *** 注意这里还留下一个问题,就是重量是否会超,可能要对 calculate_box_count 优化一下 total_volume = qty*item_list[n].volume pk_qty = 0 if ( total_volume > cell_volume ) then item_list[n].Q, pk_qty = calculate_box_count( cell_volume, item_list[n].volume, qty ) volumeX = (item_list[n].volume)*(qty-pk_qty) else volumeX = total_volume end -- 如果整除后还有余数, 判断这些体积是否增加一个料格 if ( volumeX > 0 ) then -- cell_type == E 说明不能再降格了 if (item_list[n].cell_type == cell_type or cell_type == 'E') then item_list[n].Q = item_list[n].Q + 1 else -- 2024-9-28 改进 -- 如果剩余体积 > 降格后料格体积 Q需要加1 local cell_volume = box_def[cell_type_index+1].volume if ( volumeX > cell_volume ) then item_list[n].Q = item_list[n].Q + 1 lua.DebugEx( strLuaDEID, "special attention!", item_list[n] ) end end end sum_Q = sum_Q + item_list[n].Q end ::continue:: end return 0, sum_Q end -- 补料呼出容器配货 -- 把 item_list 中的货品先匹配到 补料呼出的料箱 -- 返回批分掉的料箱料格数量 local function surplusitem_to_supplement_cntr( strLuaDEID, item_list, supplement_cntr_list, cell_type ) local nRet, strRetInfo, nCount, n, m, qty, si_qty, i local sum_Q = 0 -- 补料料箱里批分掉的Q nCount = #item_list if ( nCount == 0 ) then return 0 end for m = 1, #supplement_cntr_list do empty_cell_count = lua.GetTableCount(supplement_cntr_list[m].empty_cell) if ( supplement_cntr_list[m].full == false and supplement_cntr_list[m].cell_type == cell_type and empty_cell_count > 0 ) then -- 遍历item_list for n = 1, nCount do -- > 0 说明有cell_type类型的料箱格需求 if ( item_list[n].Q > 0 and item_list[n].cell_type == cell_type ) then -- 遍历补料料箱里的空料格 for i = 1, #empty_cell_count do if ( supplement_cntr_list[m].empty_cell[i].item_code == '' ) then -- 取一个空料格存储计算能存储多少个货品 container_cell.cntr_code = supplement_cntr_list[m].cntr_code container_cell.cell_no = supplement_cntr_list[m].empty_cell[i].cell_no container_cell.good_volume = 0 -- 是空料格 container_cell.cell_type = cell_type nRet, si_qty = calculate_quantity( strLuaDEID, supplement_cntr_list[m].cntr_good_weight, container_cell, item_list[n] ) if ( nRet ~= 0 ) then return 1, si_qty end qty = item_list[n].qty - item_list[n].alloc_qty if ( si_qty > qty ) then si_qty = qty end -- 呼出料箱里加一个料格存储 item if ( si_qty > 0 ) then -- 生成批次号 nRet, strSN = prj_base.Generate_Batch_No( item_list[n].item_code ) if ( nRet ~= 0 ) then return 1, strSN end item_list[n].alloc_qty = item_list[n].alloc_qty + si_qty if ( lua.equation( alloc_qty, qty ) ) then item_list[n].ok = true end -- 把分配掉的si_qty个货品加到补料呼出的容器里 local cell_item = { cntr_code = container_cell.cntr_code, cell_type = container_cell.cell_type, cell_no = container_cell.cell_no, item_code = item_list[n].item_code, item_name = item_list[n].item_name, row = item_list[n].row, entry_batch_no = strSN, qty = si_qty, sum_volume = si_qty*item_list[n].volume, sum_weight = si_qty*item_list[n].weight, weight = item_list[n].weight, volume = item_list[n].volume } put_cell_item_to_supplement_cntr_list( supplement_cntr_list, supplement_cntr_list[m].cntr_good_weight, cell_item ) table.insert( item_list[n].cntr_cell_list, cell_item ) -- empty_cell 里item_code 设置为有值,表示这个空料格已经有货 supplement_cntr_list[m].empty_cell[i].item_code = item_list[n].item_code supplement_cntr_list[m].empty_cell[i].item_name = item_list[n].item_name item_list[n].Q = item_list[n].Q - 1 sum_Q = sum_Q + 1 end end end end end end end return sum_Q end -- 检查 item_list 中的item是否有物料的最低适配货隔 == cell_type 并且还有量没分配料格的,如果有说明该类型的料格缺少 -- 如果有货品必须要cell_type类型料箱,返回 true, 并且返回是item_list中的货品下标 local function have_this_cell_type( item_list, cell_type ) local n for n = 1, #item_list do if ( item_list[n].ok == false and item_list[n].cell_type == cell_type ) then if ( item_list[n].qty > item_list[n].alloc_qty ) then if ( lua.equation( item_list[n].qty, item_list[n].alloc_qty ) == false ) then return true, n end end end end return false, 0 end -- 把空料格数量小的排前面 local function empty_cntr_sort( item_a, item_b ) return item_a.empty_cell_num < item_b.empty_cell_num end local function cntr_in_this_list( cntr_code, out_cntr_result ) if ( nil == out_cntr_result ) then return false end local n for n = 1, #out_cntr_result do if ( out_cntr_result[n].cntr_code == cntr_code ) then return true end end return false end --[[ *** 呼出 cell_need_num 个 cell_type 类型的空料格,需要多少个料箱 从数据库查询获取 料箱类型 = cell_type 有空料格的料箱列表,并且根据空料格数量的大小进行排序,空料格数量大的在最前面 输入参数: wh_code 仓库编码(必须有值) area_code 库区编码可以为空, aisle 指定巷道(可以为空)一般是巷道编码字符串,如 'A01','A03' 巷道是做一个字符串 out_cntr_list 呼出料箱列表 table 变量,呼出的料箱加入这个链表 cntr_subtype -- 容器子类型(用在有多种规格的带料格料箱时区分)可以为空,空不判断,一般就是一种类型的带料格料箱 cell_type 呼出料箱类型 cell_need_num 需要呼出多少个料格 query_cntr_list -- 除 out_cntr_list 外可用的料箱,用于 out_cntr_list 在分配货物是因为重量的原因不能使用需要分配新的料箱 out_cntr_result -- 已经呼出的料箱 返回3个参数,格式(nRet,x_sum_Q, strErrInfo ) nRet 0 正常 1 -- 无法匹配需求 2 -- 程序错误 x_sum_Q 呼出的料格缺少的cell_type类型的料格数量 ]] local function generate_out_cntr_list( strLuaDEID, wh_code, area_code, aisle, out_cntr_list, cntr_subtype, cell_type, cell_need_num, query_cntr_list, out_cntr_result ) local nRet, strRetInfo, strCondition, strOrder, n local str_where -- 输入参数判断 if ( wh_code == nil or wh_code == '' ) then return 1, 0, "输入参数错误, wh_code必须有值" end str_where = "S_WH_CODE = '"..wh_code.."'" if ( area_code ~= nil and area_code ~= '') then str_where = str_where.." AND S_AREA_CODE = '"..area_code.."'" end if ( aisle ~= nil and aisle ~= '') then str_where = str_where.." AND S_AISLE_CODE IN ("..aisle..")" end local cntr_max_weight = wms_base.Get_nConst( strLuaDEID, "料箱最大载重" ) local cntr_condition = " S_TYPE = 'Cell_Box' " if ( cntr_subtype ~= nil and cntr_subtype ~= '' ) then cntr_condition = cntr_condition.." AND S_SUBTYPE = '"..cntr_subtype.."' " end -- N_EMPTY_FULL < 2 料箱未满 strCondition = "N_EMPTY_CELL_NUM > N_ALLOC_CELL_NUM AND S_SPEC = '"..cell_type.."' AND N_LOCK_STATE = 0 AND C_ENABLE = 'Y' AND "..cntr_condition.." AND N_EMPTY_FULL < 2 AND N_EMPTY_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_where..")) ".. " AND F_GOOD_WEIGHT < "..cntr_max_weight strOrder = "(N_EMPTY_CELL_NUM - N_ALLOC_CELL_NUM) desc" local cntr_objs, cntr_attr nRet, cntr_objs = m3.QueryDataObject(strLuaDEID, "Container", strCondition, strOrder ) if (nRet ~= 0) then return 2, 0, cntr_objs end if ( cntr_objs == '') then return 0, cell_need_num, "没找到匹配的料箱" end local empty_cell_num, qty local empty_cntr_list = {} local query_cntr_count = 0 -- 这样的箱子不需要太多不超过100 query_cntr_list = {} -- 查询出来的符合条件的料箱 for n = 1, #cntr_objs do cntr_attr = m3.KeyValueAttrsToObjAttr(cntr_objs[n].attrs) -- 防止找到的料箱已经在呼出的料箱列表中 if ( cntr_in_this_list( cntr_attr.S_CODE, out_cntr_result ) ) then goto continue end if ( cell_need_num > 0 ) then -- 呼出料箱空料格数量 empty_cell_num = lua.StrToNumber( cntr_attr.N_EMPTY_CELL_NUM ) - lua.StrToNumber( cntr_attr.N_ALLOC_CELL_NUM ) if ( cell_need_num > empty_cell_num ) then qty = empty_cell_num else -- 特别关注: 需要找到最适配的料箱 cell_need_num = empty_cell_num 是最优的 if ( cell_need_num == empty_cell_num ) then qty = cell_need_num else -- 把当前这个料箱加到附加链表 local empty_cntr = { cntr_code = cntr_attr.S_CODE, cntr_good_weight = lua.StrToNumber( cntr_attr.F_GOOD_WEIGHT ), empty_cell_num = empty_cell_num } table.insert( empty_cntr_list, empty_cntr ) goto continue end end -- 分配成功, 把这个料箱加入呼出料箱列表 local out_cntr = { cntr_code = cntr_attr.S_CODE, cntr_good_weight = lua.StrToNumber( cntr_attr.F_GOOD_WEIGHT ), cell_type = cell_type, empty_cell = {}, full = false, cell_list = {} } table.insert( out_cntr_list, out_cntr ) cell_need_num = cell_need_num - qty else -- query_cntr_list 是候补 local out_cntr = { cntr_code = cntr_attr.S_CODE, cntr_good_weight = lua.StrToNumber( cntr_attr.F_GOOD_WEIGHT ), cell_type = cell_type, empty_cell = {}, full = false, cell_list = {} } table.insert( query_cntr_list, out_cntr ) query_cntr_count = query_cntr_count + 1 if ( query_cntr_count > 100 ) then break end end ::continue:: end -- 如果还有 cell_need_num 没分配, 从上面为了适配料格数量跳过的哪些料格 if ( cell_need_num > 0 and #empty_cntr_list > 0 ) then table.sort( empty_cntr_list, empty_cntr_sort ) -- 取第一个空料箱(这个是空料箱数最接近cell_need_num) local out_cntr = { cntr_code = empty_cntr_list[1].cntr_code, cntr_good_weight = empty_cntr_list[1].cntr_good_weight, cell_type = cell_type, empty_cell = {}, full = false, cell_list = {} } table.insert( out_cntr_list, out_cntr ) for n = 2, #empty_cntr_list do if ( query_cntr_count > 100 ) then break end local out_cntr = { cntr_code = empty_cntr_list[n].cntr_code, cntr_good_weight = empty_cntr_list[n].cntr_good_weight, cell_type = cell_type, empty_cell = {}, full = false, cell_list = {} } table.insert( query_cntr_list, out_cntr ) query_cntr_count = query_cntr_count + 1 end cell_need_num = 0 end return 0, cell_need_num end -- 设置 out_cntr_list 中的 cell_empty local function 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, empty_cell ) end end end return 0 end -- 在呼出的空料箱里分配货物, 遍历 item_list 把item_list的货品批分到 呼出的空料箱中,空料箱资源是 out_cntr_list local function distribute_to_outcntrlist( strLuaDEID, item_list, out_cntr_list, cell_type, bs_no ) local nRet, strRetInfo, nCount, n, m, qty, si_qty, i local sum_Q = 0 -- 补料料箱里批分掉的Q local empty_cell_count nCount = #item_list if ( nCount == 0 ) then return 0 end lua.DebugEx(strLuaDEID, "-->cell_type", cell_type ) -- m 是呼出的料箱列表下标 for m = 1, #out_cntr_list do empty_cell_count = lua.GetTableCount( out_cntr_list[m].empty_cell ) if ( empty_cell_count > 0 ) then -- 遍历item_list n 是货品列表下标 for n = 1, nCount do -- > 0 说明有cell_type类型的料箱格需求 if ( item_list[n].ok == false and item_list[n].Q > 0 and item_list[n].empty_cell_type == cell_type ) then lua.DebugEx(strLuaDEID, "-->item", item_list[n] ) -- 遍历呼出的空料箱里的空料格 for i = 1, empty_cell_count do if ( out_cntr_list[m].empty_cell[i].item_code == '' ) then -- 取一个空料格存储计算能存储多少个货品 local container_cell = {} container_cell.cntr_code = out_cntr_list[m].cntr_code container_cell.cell_no = out_cntr_list[m].empty_cell[i].cell_no container_cell.good_volume = 0 -- 是空料格 container_cell.cell_type = cell_type nRet, si_qty = calculate_quantity( strLuaDEID, out_cntr_list[m].cntr_good_weight, container_cell, item_list[n] ) if ( nRet ~= 0 ) then return 1, si_qty end qty = item_list[n].qty - item_list[n].alloc_qty if ( si_qty > qty ) then si_qty = qty end lua.DebugEx(strLuaDEID, "-->si_qty2", si_qty ) -- 呼出料箱里加一个料格存储 item if ( si_qty > 0 ) then -- 生成批次号 nRet, strSN = prj_base.Generate_Batch_No( item_list[n].item_code ) if ( nRet ~= 0 ) then return 1, strSN end 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个货品加到呼出的空料格里 local cell_item = { cntr_code = container_cell.cntr_code, cell_type = container_cell.cell_type, cell_no = container_cell.cell_no, item_code = item_list[n].item_code, item_name = item_list[n].item_name, row = item_list[n].row, entry_batch_no = strSN, qty = si_qty, sum_volume = si_qty*item_list[n].volume, sum_weight = si_qty*item_list[n].weight, weight = item_list[n].weight, volume = item_list[n].volume } table.insert( out_cntr_list[m].cell_list, cell_item ) -- 20241104 容器料格分配数量+1,料格设置为预分配 wms_cntr.CNTR_cell_alloc_set( strLuaDEID, container_cell.cntr_code, container_cell.cell_no, bs_no ) -- 容器中商品的总重量需要加上刚加入到料格里的货品重量 out_cntr_list[m].cntr_good_weight = out_cntr_list[m].cntr_good_weight + cell_item.sum_weight table.insert( item_list[n].cntr_cell_list, cell_item ) -- empty_cell 里item_code 设置为有值,表示这个空料格已经有货 out_cntr_list[m].empty_cell[i].item_code = item_list[n].item_code out_cntr_list[m].empty_cell[i].item_name = item_list[n].item_name item_list[n].Q = item_list[n].Q - 1 end end if ( item_list[n].Q <= 0 ) then break end end end end end end return 0 end -- 把呼出容器列表中的 empty_cell 清理一下,如果 item_code 有值锁已经分配了货品 local function 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 do if (cntr_list[n].empty_cell[m].item_code == '') then local empty_cell = { cntr_code = cntr_list[n].empty_cell[m].cntr_code, cell_no = cntr_list[n].empty_cell[m].cell_no, item_code = "", item_name = "", entry_batch_no = "", 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 = 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 -- 生成 【Pre_Alloc_Container】【Pre_Alloc_CNTR_Detail】 -- replenish = Y 表示是补料呼出料箱 station -- 站台 local function generate_pac_detail( strLuaDEID, station, cntr_code, replenish, bs_type, bs_no, cell_list, pac_list, pac_detail_list ) local nRet, strRetInfo local n local pac = m3.AllocObject( strLuaDEID, "Pre_Alloc_Container" ) pac.cntr_code = cntr_code pac.replenish = replenish pac.bs_type = bs_type pac.bs_no = bs_no pac.station = station nRet, pac = m3.CreateDataObj(strLuaDEID, pac) if (nRet ~= 0 ) then return 1, "创建【预分配容器】失败!"..pac end table.insert( pac_list, pac ) local pac_detail for n = 1, #cell_list do pac_detail = m3.AllocObject( strLuaDEID, "Pre_Alloc_CNTR_Detail" ) pac_detail.pac_no = pac.pac_no pac_detail.cntr_code = cntr_code pac_detail.station = station pac_detail.cell_no = cell_list[n].cell_no pac_detail.item_code = cell_list[n].item_code pac_detail.item_name = cell_list[n].item_name pac_detail.weight = cell_list[n].weight pac_detail.volume = cell_list[n].volume pac_detail.entry_batch_no = cell_list[n].entry_batch_no pac_detail.qty = cell_list[n].qty pac_detail.bs_type = bs_type pac_detail.bs_no = bs_no nRet, pac_detail = m3.CreateDataObj(strLuaDEID, pac_detail) if (nRet ~= 0 ) then return 1, "创建【组盘明细】失败!"..pac_detail end table.insert( pac_detail_list, pac_detail ) end return 0 end local function item_sort_by_cell_tye( item_a, item_b ) return item_a.cell_type < item_b.cell_type end -- 删除呼出容器中的empty_cell 列表中 名为 cell_no 的料格 local function 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 -- 把料箱中已经分配了item的空料格删除 local function remove_empty_cell_have_item_code( cntr ) -- 清除料箱里的空容器列表 local m = 1 while cntr.empty_cell[m] do if (cntr.empty_cell[m].item_code ~= '' ) then table.remove( cntr.empty_cell, m ) else m = m + 1 end end end -- 把一个指定的货品批分到 呼出容器列表 中, 如果 out_cntr_list 批分不完,从 query_cntr_list 里继续批分 local function distribute_item_to_out_cntr_list( strLuaDEID, item, out_cntr_list ) local nRet, strRetInfo, nCount, n, m, qty, si_qty, i local empty_cell_count if ( item.ok ) then return end for m = 1, #out_cntr_list do -- 遍历呼出的空料箱里的空料格 for i = 1, #out_cntr_list[m].empty_cell do if ( out_cntr_list[m].empty_cell[i].item_code == '' ) then -- 取一个空料格存储计算能存储多少个货品 local container_cell = {} container_cell.cntr_code = out_cntr_list[m].cntr_code container_cell.cell_no = out_cntr_list[m].empty_cell[i].cell_no container_cell.good_volume = 0 -- 是空料格 container_cell.cell_type = out_cntr_list[m].cell_type nRet, si_qty = calculate_quantity( strLuaDEID, out_cntr_list[m].cntr_good_weight, container_cell, item ) if ( nRet ~= 0 ) then return 1, si_qty end qty = item.qty - item.alloc_qty if ( si_qty > qty ) then si_qty = qty end -- 呼出料箱里加一个料格存储 item if ( si_qty > 0 ) then -- 生成批次号 nRet, strSN = prj_base.Generate_Batch_No( item.item_code ) if ( nRet ~= 0 ) then return 1, strSN end 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 = container_cell.cntr_code, cell_type = container_cell.cell_type, cell_no = container_cell.cell_no, item_code = item.item_code, item_name = item.item_name, row = item.row, entry_batch_no = strSN, qty = si_qty, sum_volume = si_qty*item.volume, sum_weight = si_qty*item.weight, weight = item.weight, volume = item.volume } table.insert( out_cntr_list[m].cell_list, cell_item ) -- 20241104 容器料格分配数量+1,料格设置为预分配 wms_cntr.CNTR_cell_alloc_set( strLuaDEID, container_cell.cntr_code, container_cell.cell_no, bs_no ) -- 容器中商品的总重量需要加上刚加入到料格里的货品重量 out_cntr_list[m].cntr_good_weight = out_cntr_list[m].cntr_good_weight + cell_item.sum_weight table.insert( item.cntr_cell_list, cell_item ) -- empty_cell 里item_code 设置为有值,表示这个空料格已经有货 out_cntr_list[m].empty_cell[i].item_code = item.item_code out_cntr_list[m].empty_cell[i].item_name = item.item_name end end if ( item.ok ) then break end end remove_empty_cell_have_item_code( out_cntr_list[m] ) if ( item.ok ) then break end end return 0 end -- 把一个料格加入呼出料箱列表 local function put_cell_item_to_out_cntr_list( out_cntr_list, cell_item ) local n 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 end -- 从已经呼出的料箱列表中分配一个空的料格 -- out_cntr_list 已经呼出的料箱列表,cell_type 匹配的料格类型, item 分配的货品, need_q 需要的料格数量 local function alloc_cell_in_out_cntr_list( strLuaDEID, out_cntr_list, cell_type, item, need_q ) local n, empty_cell_count local alloc_item = false local container_cell = {} for n = 1, #out_cntr_list do empty_cell_count = lua.GetTableCount( out_cntr_list[n].empty_cell ) alloc_item = false if ( out_cntr_list[n].full == false and out_cntr_list[n].cell_type == cell_type and empty_cell_count > 0 ) then for i = 1, empty_cell_count do if ( out_cntr_list[n].empty_cell[i].item_code == '' ) then -- 取一个空料格存储计算能存储多少个货品 container_cell.cntr_code = out_cntr_list[n].cntr_code container_cell.cell_no = out_cntr_list[n].empty_cell[i].cell_no container_cell.good_volume = 0 -- 是空料格 container_cell.cell_type = cell_type nRet, si_qty = calculate_quantity( strLuaDEID, out_cntr_list[n].cntr_good_weight, container_cell, item ) if ( nRet ~= 0 ) then return 1, si_qty end -- 需要分配料格的数量 qty,如果计算出来的数量大于需求数量,就用需求数量 qty = item.qty - item.alloc_qty if ( si_qty > qty ) then si_qty = qty end -- 呼出料箱里加一个料格存储 item if ( si_qty > 0 ) then -- 生成批次号 nRet, strSN = prj_base.Generate_Batch_No( item.item_code ) if ( nRet ~= 0 ) then return 1, strSN end 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 = container_cell.cntr_code, cell_type = container_cell.cell_type, cell_no = container_cell.cell_no, item_code = item.item_code, item_name = item.item_name, row = item.row, entry_batch_no = strSN, qty = si_qty, sum_volume = si_qty*item.volume, sum_weight = si_qty*item.weight, weight = item.weight, volume = item.volume } put_cell_item_to_out_cntr_list( out_cntr_list, cell_item ) table.insert( item.cntr_cell_list, cell_item ) -- 20241104 容器料格分配数量+1,料格设置为预分配 wms_cntr.CNTR_cell_alloc_set( strLuaDEID, container_cell.cntr_code, container_cell.cell_no, bs_no ) -- empty_cell 里item_code 设置为有值,表示这个空料格已经有货 out_cntr_list[n].empty_cell[i].item_code = item.item_code out_cntr_list[n].empty_cell[i].item_name = item.item_name need_q = need_q - 1 alloc_item = true if ( need_q == 0 ) then break end end end end end -- 删除那些已经分配货品的空料格empty_cell if ( alloc_item ) then remove_empty_cell_have_item_code( out_cntr_list[n] ) end if ( need_q == 0 ) then break end end return 0, need_q end -- 清除呼出料箱列表中没有cell_list的节点 local function remove_no_cell_list_out_cntr_list( out_cntr_list ) local n = 1 while out_cntr_list[n] do if (#out_cntr_list[n].cell_list == 0 ) then table.remove( out_cntr_list, n ) else n = n + 1 end end end --[[ item_list -- 需要预分配料箱的货品 pac_cfg -- 预分配料箱配置参数 { wh_code, area_code 仓库,库区编码 station 站台 bs_type 来源类型:入库单、入库波次 bs_no 来源单号 ctd_code (容器类型定义编码,不能为空), cntr_subtype (可以为空), box_def, check_capacty (bool-是否检测整箱载重), cntr_load_capacity(料箱最大载重) supplement = { -- 补料配置 enable = false/true, -- 是否启用补料 matching_attrs = {"S_ITEM_CODE","XX",...} -- 这些属性一致的可以进行补料 matching_order = "Last_Entry_Batch/QTY_Desc/QTY_Asc" -- 可以为空 si_cntr_num = 0/1 -- 0 不限制所以可以补料的料箱优先补料, 1 -- 补一个料箱 } -- 料格补料配置 enable -- 启用 matching_attrs 匹配字段 } 返回参数: nRet 返回值:0 成功 1 -- 料箱不足 2 -- 程序错误 返回: pac_list 呼出的料箱列表 pac_detail_list 站台入库任务列表 ]] function wms_pac.Pre_Alloc_CNTR_By_CBG( strLuaDEID, item_list, pac_cfg, pac_list, pac_detail_list ) local nRet, strRetInfo local wh_code, area_code, aisle, station, bs_type, bs_no local ctd_code, cntr_subtype local do_supplement = false -- 是否执行补料 local supplement_rlue = {} -- 补料策略 if ( pac_cfg == nil or type( pac_cfg ) ~= "table" ) then return 2, "Pre_Alloc_CNTR_By_CBG 函数输入参数错误: pac_cfg 必须有值,必须是 table 类型" end if ( pac_cfg.supplement == nil or type( pac_cfg.supplement ) ~= "table" ) then do_supplement = false else do_supplement = lua.Get_BoolAttrValue( pac_cfg.supplement.enable ) if ( do_supplement ) then supplement_rlue = pac_cfg.supplement end end aisle = lua.Get_StrAttrValue( pac_cfg.aisle ) -- 可用巷道 -- step0: 输入参数校验和初始化 wh_code = lua.Get_StrAttrValue( pac_cfg.wh_code ) if ( wh_code == '' ) then return 2, "Pre_Alloc_CNTR_By_CBG 函数输入参数错误: pac_cfg 参数中的 wh_code 必须有值!" end area_code = lua.Get_StrAttrValue( pac_cfg.area_code ) 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 ) if ( pac_cfg.box_def ~= nil ) then box_def = pac_cfg.box_def end -- 对呼出容器类型定义进行判断 ctd_code = lua.Get_StrAttrValue( pac_cfg.ctd_code ) -- 获取 ctd 定义 BOX_MAX_WEIGHT = lua.Get_NumAttrValue( pac_cfg.cntr_load_capacity ) if ( BOX_MAX_WEIGHT <= 0 ) then return 2, "系统没有定义名为'料箱最大载重'的常量! 呼出空料箱程序终止执行!" end if ( ctd_code ~= "" ) then return 2, "Pre_Alloc_CNTR_By_CBG 算法只适合 Cell_Box 类型的容器进行分配, pac_cfg中的参数 ctd_code 错误!" end cntr_subtype = lua.Get_StrAttrValue( pac_cfg.cntr_subtype ) CHECK_CAPACITY = lua.Get_BoolAttrValue( pac_cfg.check_capacty ) -- step1 计算补料呼出料箱 local supplement_cntr_list = {} -- 补料呼出的料箱 if ( do_supplement ) then nRet, supplement_cntr_list = get_replenishment_cntr_list( strLuaDEID, item_list, wh_code, supplement_rlue ) if ( nRet ~= 0 ) then return 2, supplement_cntr_list end end -- 排序把体积大的放前面 table.sort( item_list, item_sort ) local nItemCount = #item_list local count_supplement_cntr = #supplement_cntr_list lua.DebugEx( strLuaDEID, "需要呼出空料箱的货品清单 --> item_list", item_list ) lua.DebugEx( strLuaDEID, "补料呼出料箱 --> supplement_cntr_list1", supplement_cntr_list ) local cntr_max_weight = wms_base.Get_nConst( strLuaDEID, "料箱最大载重" ) -- step3 补料呼出容器配货算法 -- 首先判断一下补料呼出料箱中是否有存在 空料格, 有空料格设置 empty_cell 属性 set_replenishment_cntr_emptycell( strLuaDEID, supplement_cntr_list ) -- step4 从A类型料箱开始一直到E类型料箱呼出空料箱(大宗货品用A料框来满足入库需求) local sum_Q, cell_type_index, Qx, x_sum_Q local out_cntr_list = {} local cell_type local out_cntr_result = {} -- 呼出的料箱结果 local b_have_this_cell_type = false local item_index, n local upgrad_item_list = {} -- 需要进行升格的物料 local query_cntr_list -- cell_type_index = 1 是A类型料箱 for cell_type_index = 1, 5 do cell_type = box_def[cell_type_index].cell_type -- 获取某类型空料箱格数量 nRet, sum_Q = generate_item_needemptycell_qty( strLuaDEID, item_list, cell_type_index, cntr_max_weight ) if ( nRet ~= 0 ) then return nRet, sym_Q end lua.DebugEx( strLuaDEID, "step4.1 cell_type_index", "cell_type = "..cell_type.." sum_Q = "..sum_Q ) lua.DebugEx( strLuaDEID, "step4.2 item_list1 --> ", item_list ) if ( cell_type_index > 1 and count_supplement_cntr > 0 ) then -- 补料料箱里是否可以先分配货物 Qx = surplusitem_to_supplement_cntr( strLuaDEID, item_list, supplement_cntr_list ) lua.DebugEx( strLuaDEID, "step4.3 补料呼出料箱分配结果 --> supplement_cntr_list2", supplement_cntr_list ) sum_Q = sum_Q - Qx lua.DebugEx( strLuaDEID, "step4.4 补料呼出料箱分配结果 --> sum_Q", sum_Q ) end if ( sum_Q > 0 ) then --呼出空料箱格 生成 out_cntr_list out_cntr_list = {} query_cntr_list = {} -- step 4.2 -- 呼出数量最匹配的空料箱, 返回 out_cntr_list [{"cnter":"","good_weight":10, cell_type:"B"}] -- x_sum_Q 是不足的 cell_type 料格数量,需要进行降格 nRet, x_sum_Q, strRetInfo = generate_out_cntr_list( strLuaDEID, wh_code, area_code, aisle, out_cntr_list, cntr_subtype, cell_type, sum_Q, query_cntr_list ) lua.DebugEx( strLuaDEID, "step4.5 out_cntr_list --> ", out_cntr_list ) lua.DebugEx( strLuaDEID, "step4.6 x_sum_Q --> ", x_sum_Q ) if (nRet ~= 0 ) then return 2, "step4.2 发生错误! "..strRetInfo end -- 存在呼出的料箱列表,把货品批分到呼出的料箱格 if ( lua.IsTableEmpty( out_cntr_list ) == false ) then -- 设置 out_cntr_list 中 empty_cell set_out_cntr_empty_cell( strLuaDEID, out_cntr_list ) lua.DebugEx( strLuaDEID, "step 4.7 批分前 out_cntr_list2", out_cntr_list ) -- step 4.3 把 item_list 中的货品批分到 呼出的 out_cntr_list 中的容器中 nRet, strRetInfo = distribute_to_outcntrlist( strLuaDEID, item_list, out_cntr_list, cell_type, bs_no ) lua.DebugEx( strLuaDEID, "step 4.8 批分后 out_cntr_list3", out_cntr_list ) if ( nRet ~= 0 ) then return 2, "step4.3 发生错误! "..strRetInfo end reset_empty_cell_info( out_cntr_list ) lua.table_merge( out_cntr_result, out_cntr_list ) end lua.DebugEx( strLuaDEID, "item_list2 --> ", item_list ) if ( x_sum_Q > 0 ) then -- 如果未分配料箱的货品中还存在 cell_type 的料格 b_have_this_cell_type, item_index = have_this_cell_type( item_list, cell_type ) if ( b_have_this_cell_type ) then -- 如果不是A类型的料格需要考虑升格 if ( cell_type == 'A') then local x_qty = item_list[item_index].qty - item_list[item_index].alloc_qty return 1, "在分配货品编码 = '"..item_list[item_index].item_code.."'时, 料箱类型 = "..cell_type.."的料箱不足! 货品数量 = "..x_qty end end -- MDF 20241220 -- 说明没呼出足够的适配料箱 if ( cell_type_index == 1 ) then -- A 料格 -- 降格处理 A --> B lua.Warning( strLuaDEID, debug.getinfo(1), bs_type.."-->'"..bs_no.."'有"..x_sum_Q.."个A料箱降格!" ) else -- 升格处理 B --> A lua.Warning( strLuaDEID, debug.getinfo(1), bs_type.."-->'"..bs_no.."'有"..x_sum_Q.."个"..cell_type.."料箱升格!" ) -- 把item_list中 item.Q > 0 的加入升格通道 n = 1 while item_list[n] do if (item_list[n].Q > 0 ) then item_list[n].upgrad_cell_index = cell_type_index table.insert( upgrad_item_list, item_list[n] ) table.remove( item_list, n ) else n = n + 1 end end end end -- MDF 20241220 -- 判断 item_list 中的数量是否已经全部分配到容器,如果全部分配了料箱 break if ( item_list_is_ok( item_list ) or #item_list == 0 ) then break end end end -- MDF 20241220 *** -- step5 如果有升料格需求的货品进行升格处理 -- 如果有升料格的货品进入升格 lua.DebugEx( strLuaDEID, "STEP5 需要升格的货品列表 --> upgrad_item_list", upgrad_item_list ) lua.DebugEx( strLuaDEID, "STEP5 正常分配的货品列表 --> item_list", item_list ) local qty, total_volume, Q, cell_volume, pk_qty for n = 1, #upgrad_item_list do if ( upgrad_item_list[n].ok == false ) then cell_type_index = upgrad_item_list[n].upgrad_cell_index -- 升格循环 while ( cell_type_index > 1 ) do cell_type_index = cell_type_index - 1 -- 升一个料格 cell_type = box_def[cell_type_index].cell_type qty = upgrad_item_list[n].qty - upgrad_item_list[n].alloc_qty total_volume = qty*upgrad_item_list[n].volume cell_volume = box_def[cell_type_index].volume if ( total_volume > cell_volume ) then --Q = math.floor( total_volume/cell_volume ) Q, pk_qty = calculate_box_count( cell_volume, upgrad_item_list[n].volume, qty ) else Q = 1 end -- 从补料呼出的容器里找 nRet, Q = alloc_cell_in_out_cntr_list( strLuaDEID, supplement_cntr_list, cell_type, upgrad_item_list[n], Q ) if ( nRet ~= 0 ) then return 1, Q end if ( Q == 0 ) then break end -- 从呼出容器里找 nRet, Q = alloc_cell_in_out_cntr_list( strLuaDEID, out_cntr_result, cell_type, upgrad_item_list[n], Q ) if ( nRet ~= 0 ) then return 1, Q end if ( Q == 0 ) then break end -- 从新呼出一个料箱容器 out_cntr_list = {} query_cntr_list = {} nRet, Q, strRetInfo = generate_out_cntr_list( strLuaDEID, wh_code, area_code, aisle, out_cntr_list, cntr_subtype, cell_type, Q, query_cntr_list, out_cntr_result ) if (nRet ~= 0 ) then return 2, "step5.1 发生错误! "..strRetInfo end if ( lua.IsTableEmpty( out_cntr_list ) == false ) then -- 设置 out_cntr_list 中 empty_cell set_out_cntr_empty_cell( strLuaDEID, out_cntr_list ) -- 把 upgrad_item_list 中的货品批分到 呼出的 out_cntr_list 中的容器中 nRet, strRetInfo = distribute_item_to_out_cntr_list( strLuaDEID, upgrad_item_list[n], out_cntr_list ) if ( nRet ~= 0 ) then return 2, "step5.2 发生错误! "..strRetInfo end reset_empty_cell_info( out_cntr_list ) lua.table_merge( out_cntr_result, out_cntr_list ) end if ( Q == 0 ) then break end end end end -- MDF 20241220 *** -- step6 检查升格后货品是否有没有匹配到料箱的,如果有需要降格 local xVolume lua.DebugEx( strLuaDEID, "STEP6 需要降格的货品列表 --> upgrad_item_list", upgrad_item_list ) for n = 1, #upgrad_item_list do if ( upgrad_item_list[n].ok == false ) then -- 开始降格 cell_type_index = upgrad_item_list[n].upgrad_cell_index while ( cell_type_index < 5 ) do cell_type_index = cell_type_index + 1 -- 降一个料格 cell_type = box_def[cell_type_index].cell_type lua.DebugEx( strLuaDEID, "STEP6.1 --> cell_type", cell_type ) if ( upgrad_item_list[n].cell_type < cell_type ) then break end qty = upgrad_item_list[n].qty - upgrad_item_list[n].alloc_qty total_volume = qty*upgrad_item_list[n].volume cell_volume = box_def[cell_type_index].volume if ( total_volume > cell_volume ) then --Q = math.floor( total_volume/cell_volume ) Q, pk_qty = calculate_box_count( cell_volume, upgrad_item_list[n].volume, qty ) xVolume = upgrad_item_list[n].volume*(qty-pk_qty) if ( xVolume > 0 ) then Q = Q + 1 end else Q = 1 end -- 从补料呼出的容器里找 lua.DebugEx( strLuaDEID, "STEP6.2 --> supplement_cntr_list", supplement_cntr_list ) nRet, Q = alloc_cell_in_out_cntr_list( strLuaDEID, supplement_cntr_list, cell_type, upgrad_item_list[n], Q ) if ( nRet ~= 0 ) then return 2, Q end if ( Q == 0 ) then break end -- 从呼出容器里找 lua.DebugEx( strLuaDEID, "STEP6.3 --> out_cntr_result", out_cntr_result ) nRet, Q = alloc_cell_in_out_cntr_list( strLuaDEID, out_cntr_result, cell_type, upgrad_item_list[n], Q ) if ( nRet ~= 0 ) then return 2, Q end if ( Q == 0 ) then break end -- 从新呼出一个料箱容器 out_cntr_list = {} nRet, Q, strRetInfo = generate_out_cntr_list( strLuaDEID, wh_code, area_code, aisle, out_cntr_list, cntr_subtype, cell_type, Q, query_cntr_list, out_cntr_result ) if (nRet ~= 0 ) then return 2, "step6.1 发生错误! "..strRetInfo end lua.DebugEx( strLuaDEID,"STEP6.4 --> out_cntr_list", out_cntr_list ) if ( lua.IsTableEmpty( out_cntr_list ) == false ) then -- 设置 out_cntr_list 中 empty_cell nRet, strRetInfo = set_out_cntr_empty_cell( strLuaDEID, out_cntr_list ) -- 把 upgrad_item_list 中的货品批分到 呼出的 out_cntr_list 中的容器中 nRet, strRetInfo = distribute_item_to_out_cntr_list( strLuaDEID, upgrad_item_list[n], out_cntr_list ) if ( nRet ~= 0 ) then return 2, "step6.2 发生错误! "..strRetInfo end reset_empty_cell_info( out_cntr_list ) lua.table_merge( out_cntr_result, out_cntr_list ) end if ( Q == 0 ) then break end end end end -- step7 判断货品是否已经全部分配料箱完成,没完成报错 -- 把 item_list 和 upgrad_item_list 合并 lua.table_merge( item_list, upgrad_item_list ) lua.DebugEx( strLuaDEID, "STEP7: item_list --> ", item_list ) -- 重置 supplement_cntr_list 和 out_cntr_list 中的empty_cell 等属性, local s_cntr_have_empty_cell = reset_empty_cell_info( supplement_cntr_list ) local o_cntr_have_empty_cell = reset_empty_cell_info( out_cntr_result ) lua.DebugEx( strLuaDEID, "补料呼出 --> ", supplement_cntr_list ) lua.DebugEx( strLuaDEID, "空料箱呼出1 --> ", out_cntr_result ) -- step8 遍历item_list 对没找到呼出料箱的货品进行单个呼出 -- 有可能因为重量超的原因上面分配的料格没办法装下货品 local Q1, Q2 for n = 1, #item_list do if ( item_list[n].ok == false ) then cell_type_index = string.byte( item_list[n].cell_type ) - string.byte( "A" ) + 1 while ( cell_type_index >= 1 ) do cell_type = box_def[cell_type_index].cell_type qty = item_list[n].qty - item_list[n].alloc_qty lua.DebugEx( strLuaDEID,"STEP8.1 --> cell_type = ", cell_type.." qty = "..qty.." cell_type_index = "..cell_type_index.." item_code = "..item_list[n].item_code ) -- 根据体积算的料格数量 total_volume = qty*item_list[n].volume cell_volume = box_def[cell_type_index].volume if ( total_volume > cell_volume ) then --Q = math.floor( total_volume/cell_volume ) Q1, pk_qty = calculate_box_count( cell_volume, item_list[n].volume, qty ) xVolume = item_list[n].volume*(qty-pk_qty) if ( xVolume > 0 ) then Q1 = Q1 + 1 end else Q1 = 1 end -- 根据重量算的料格数量 Q2, pk_qty = calculate_box_count_by_weight( cell_type_index, item_list[n].weight, qty ) xVolume = item_list[n].volume*(qty-pk_qty) if ( xVolume > 0 ) then Q2 = Q2 + 1 end if ( Q2 > Q1 ) then Q = Q2 else Q = Q1 end lua.DebugEx( strLuaDEID,"STEP8.2 --> out_cntr_result1 = ", out_cntr_result ) lua.DebugEx( strLuaDEID,"STEP8.2 --> Q = ", Q ) -- 从呼出容器里找 nRet, Q = alloc_cell_in_out_cntr_list( strLuaDEID, out_cntr_result, cell_type, item_list[n], Q ) lua.DebugEx( strLuaDEID,"STEP8.3 --> out_cntr_result2 = ", out_cntr_result ) lua.DebugEx( strLuaDEID,"STEP8.3 --> Q = ", Q ) if ( nRet ~= 0 ) then return 2, Q end if ( Q == 0 ) then break end -- 从新呼出一个料箱容器 out_cntr_list = {} nRet, Q, strRetInfo = generate_out_cntr_list( strLuaDEID, wh_code, area_code, aisle, out_cntr_list, cntr_subtype, cell_type, Q, query_cntr_list, out_cntr_result ) if (nRet ~= 0 ) then return 2, "step8.1 发生错误! "..strRetInfo end lua.DebugEx( strLuaDEID,"STEP8.5 --> out_cntr_list", out_cntr_list ) if ( lua.IsTableEmpty( out_cntr_list ) == false ) then -- 设置 out_cntr_list 中 empty_cell nRet, strRetInfo = set_out_cntr_empty_cell( strLuaDEID, out_cntr_list ) lua.DebugEx( strLuaDEID,"STEP8.6 --> out_cntr_list1 = ", out_cntr_list ) -- 把 upgrad_item_list 中的货品批分到 呼出的 out_cntr_list 中的容器中 nRet, strRetInfo = distribute_item_to_out_cntr_list( strLuaDEID, item_list[n], out_cntr_list ) if ( nRet ~= 0 ) then return 2, "step8.2 发生错误! "..strRetInfo end -- 清除呼出料箱列表中没有cell_list的节点 remove_no_cell_list_out_cntr_list( out_cntr_list ) lua.DebugEx( strLuaDEID,"STEP8.7 --> out_cntr_list2 = ", out_cntr_list ) if ( #out_cntr_list > 0 ) then reset_empty_cell_info( out_cntr_list ) lua.table_merge( out_cntr_result, out_cntr_list ) end end if ( item_list[n].ok ) then break end cell_type_index = cell_type_index - 1 lua.DebugEx( strLuaDEID,"STEP8.8 --> cell_type_index = ", cell_type_index ) end end end lua.DebugEx( strLuaDEID, "空料箱呼出2 --> ", out_cntr_result ) lua.DebugEx( strLuaDEID, "货品料箱分配情况 --> ", item_list ) local msg_list = {} -- 保存无法分配料格的货品数量 for n = 1, #item_list do if ( item_list[n].ok == false ) then local qty = item_list[n].qty-item_list[n].alloc_qty local msg = "系统没有匹配到货品编码 = '"..item_list[n].item_code.."'的适配料箱进行预分配, 货品的适配料格类型 = '"..item_list[n].cell_type.."', 数量 = "..qty table.insert( msg_list, msg ) end end if ( #msg_list > 0 ) then -- 这些货品没有合适的料箱 return 1, lua.table2str( msg_list ) end -- step9 创建配盘数据对象 -- 20241108 HAN 补料呼出的料箱和空料箱呼出的如果料箱号是一样的需要合并一下,合并在补料箱列表 -- 如果呼出的有补料和空料箱的时候要检查 for n = 1, #supplement_cntr_list do for m = 1, #out_cntr_result do if (supplement_cntr_list[n].cntr_code == out_cntr_result[m].cntr_code) then for i = 1, #out_cntr_result[m].cell_list do table.insert( supplement_cntr_list[n].cell_list, out_cntr_result[m].cell_list[i]) remove_empty_cell( supplement_cntr_list[n], out_cntr_result[m].cell_list[i].cell_no ) lua.DebugEx( strLuaDEID, "合并料格到补料呼出队列 --> ", supplement_cntr_list[n].cntr_code.."-"..out_cntr_result[m].cell_list[i].cell_no ) end out_cntr_result[m].cell_list = {} end end end lua.DebugEx( strLuaDEID, "合并后补料呼出 --> ", supplement_cntr_list ) lua.DebugEx( strLuaDEID, "合并后空料箱呼出 --> ", out_cntr_result ) -- 生成 【Pre_Alloc_Container】【Pre_Alloc_CNTR_Detail】 local cell_list for n = 1, #supplement_cntr_list do cell_list = supplement_cntr_list[n].cell_list if ( #cell_list > 0 ) then nRet, strRetInfo = generate_pac_detail( strLuaDEID, station, supplement_cntr_list[n].cntr_code, 'Y', bs_type, bs_no, cell_list, pac_list, pac_detail_list ) if ( nRet ~= 0 ) then return 2, "补料 generate_pac_detail 发生错误! "..strRetInfo end end end for n = 1, #out_cntr_result do cell_list = out_cntr_result[n].cell_list if ( #cell_list > 0 ) then nRet, strRetInfo = generate_pac_detail( strLuaDEID, station, out_cntr_result[n].cntr_code, 'N', bs_type, bs_no, cell_list, pac_list, pac_detail_list ) if ( nRet ~= 0 ) then return 2, "空料箱 generate_pac_detail 发生错误! "..strRetInfo end end end -- 设置入库单业务状态为 组盘状态(入库需要的料箱已经确定) local strUpdateSql = "N_B_STATE = 1" local strCondition if ( bs_type == "Inbound_Order" ) then strCondition = "S_NO = '"..bs_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Inbound_Order", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【入库单】信息失败!"..strRetInfo end elseif ( bs_type == "Inbound_Wave" ) then strCondition = "S_WAVE_NO = '"..bs_no.."'" nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Inbound_Wave", strCondition, strUpdateSql ) if ( nRet ~= 0 ) then return 2, "更新【入库波次】信息失败!"..strRetInfo end end return 0 end return wms_pac