--[[ 版本: Version 1.0 创建日期: 2025-1-26 创建人: HAN 功能: 和出库相关的操作,如配盘 -- Shipping_Order_CheckState 检查【发货单明细】 配盘是否全部完成,如果全部完成 N_B_STATE = 1 ** Creat_Distribution 创建配盘及配盘明细(适合手工配盘) -- Check_Distribution_WholeOut 检测配盘是否为整出 -- Get_Outbound_Detail_list 根据输入的出库单编码获取 出库单明细列表,oo_no_set 是一个出库单编码数组,如["OO010","OO012"] ** Split_Distribution_CNTR_Detail 波次合并后产生的 配盘明细 批分到每个出库单明细 ** Distribution_Procedure 根据货品列表系统自动生成配盘和配盘明细,条件是根据货品编码 item_code 确定货品, 根据批次号顺序配盘,批次号小的优先出 -- Creat_Distribution_list 创建配盘及配盘明细数据对象 -- Sum_outbound_detail 把出库单明细中的 item 加入出库明细汇总链表 -- Distribution_CNTR_Detail_PostProcess 配盘明细的后处理程序(分拣出库后对数据的处理) ** 标记的是主要函数 更改说明: V2.0 HAN 20241122 Distribution_Procedure 增加全属性覆盖,条件判断改进 --]] wms_cntr = require("wms_container") wms_wh = require("wms_wh") local wms_out = { _version = "0.1.1" } -- 检查 发货单明细 配盘是否全部完成,如果全部完成 N_B_STATE = 1 function wms_out.Shipping_Order_CheckState (strLuaDEID, shipping_no) local n, nRet, strRetInfo -- 获取【发货单明细】 local strCondition = "S_SHIPPING_NO = '" .. shipping_no .. "'" local strOrder = "" local data_objs local distribution_finish = true nRet, data_objs = m3.QueryDataObject(strLuaDEID, "Shipping_Detail", strCondition, strOrder) if (nRet ~= 0) then return 1, "获取【Shipping_Detail】信息失败! " .. data_objs end local obj_attrs local qty, acc_d_qty for n = 1, #data_objs do obj_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) qty = lua.StrToNumber(obj_attrs.F_QTY) acc_d_qty = lua.StrToNumber(obj_attrs.F_ACC_D_QTY) if (qty > acc_d_qty) then distribution_finish = false break end end if (distribution_finish) then local strUpdateSql = "N_B_STATE = 1" strCondition = "S_NO = '" .. shipping_no .. "'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Shipping_Order", strCondition, strUpdateSql) if (nRet ~= 0) then return 1, "更新【发货单】信息失败!" .. strRetInfo end end return 0 end -- 检测配盘是否为整出, 判断的依据是 CG_Detail中的 qty - 配盘明细中的qty 后的值全部为 0 -- distribution_id 【配盘】对象标识 -- nRet = 0 程序执行无错误 否则 > 0. strRetInfo = "yes" 表示是整拖出 function wms_out.Check_Distribution_WholeOut(strLuaDEID, distribution_cntr) local nRet, strRetInfo, n, i if (distribution_id == nil or type(distribution_cntr) ~= 'table') then return 1, "wms_out.Check_Distribution_WholeOut 函数中 distribution 不能为空,必须为table!" end -- 获取【配盘明细】 local data_objects local strCondition = "S_DC_NO = '" .. distribution_cntr.dc_no .. "'" nRet, data_objects = m3.QueryDataObject(strLuaDEID, "Distribution_CNTR_Detail", strCondition, "S_ITEM_CODE") if (nRet ~= 0) then return 2, "QueryDataObject失败!" .. data_objects end if (data_objects == '') then return 0, "no" end local dc_detail = {} for n = 1, #data_objects do dc_detail[n] = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs) end -- 获取【INV_Detail】 local strCondition = "S_CNTR_CODE = '" .. distribution_cntr.cntr_code .. "'" nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, "S_ITEM_CODE") if (nRet ~= 0) then return 2, "QueryDataObject失败!" .. data_objects end if (data_objects == '') then return 0, "no" end local obj_attrs if (#data_objects > #dc_detail) then -- 如果容器中的货品条数 > 配盘明细 肯定不是整托出 return 0, "no" end -- 遍历 容器货品明细 判断:inv_detail.qty - dc_detail.qty = 0 local inv_detail_qty, dc_detail_qty local count = #dc_detail for n = 1, #data_objects do obj_attrs = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs) inv_detail_qty = lua.StrToNumber(obj_attrs.F_QTY) -- 从【Distribution_CNTR_Detail】找到 cg_detial.S_ID 相同的 配盘明细 for i = 1, count do if (dc_detail[i].G_INV_DETAIL_ID == data_objects[n].id) then dc_detail_qty = lua.StrToNumber(dc_detail[i].F_QTY) if (inv_detail_qty > dc_detail_qty) then -- 如果容器货品中的数量大于本次配盘出库数量,说明出不完,不是整托出 return 0, "no" end break end end end return 0, "yes" end --[[ 创建配盘及配盘明细, 用于手工配盘 输入参数 distribution_info -- 配盘信息,是一个table,格式如下 { bs_type = "Outbound_Ordre", -- 来源类型(出库单/出库波次) bs_no = "XX-001", -- 来源单号 factory = "81" -- 工厂标识 bs_row_no = bs_row_no, -- 来源单行号 cntr_code = "", -- 容器号 loc_code = "", -- 容器来源货位信息 inv_detail_id = "xxxx", -- INV_Detail 中记录标识 S_ID item_code = "", -- 物料编码 batch_no = "", -- 批次号 serial_no = "", -- 系列号 qty = 1, -- 配盘数量 loc_code = loc_code, -- 容器所在货位 exit_area_code = area_code, -- 出库口库区编码 exit_loc_code = "" urgent_outbound = false -- 是否紧急出库 } --]] -- 返回值 nRet = 0 表示成功配盘 1 -- 表示业务流程不许可 2 -- 程序错误 function wms_out.Creat_Distribution(strLuaDEID, distribution_info) local nRet, strRetInfo, strErr -- step1 判断一下是否已经存在配盘 -- 查找 容器编码 = cntr_code 并且 状态=0,1,2 的配盘容器 local distribution_cntr -- 输入参数检查,一些关键的属性必须有值 if (distribution_info.cntr_code == nil or distribution_info.cntr_code == '') then return 1, "输入的配盘信息中必须有 cntr_code !" end if (distribution_info.bs_type == nil or distribution_info.bs_type == '') then return 1, "输入的配盘信息中必须有 bs_type !" end if (distribution_info.bs_no == nil or distribution_info.bs_no == '') then return 1, "输入的配盘信息中必须有 bs_no !" end if (distribution_info.inv_detail_id == nil or distribution_info.inv_detail_id == '') then return 1, "输入的配盘信息中必须有 inv_detail_id !" end if (distribution_info.loc_code == nil or distribution_info.loc_code == '') then return 1, "输入的配盘信息中必须有 loc_code !" end local cntr_code = distribution_info.cntr_code local loc nRet, loc = wms_wh.GetLocInfo(distribution_info.loc_code) if (nRet ~= 0) then return 1, '获取货位信息失败! ' .. distribution_info.loc_code end local strCondition = "S_CNTR_CODE = '" .. distribution_info.cntr_code .. "' AND N_B_STATE >= 0 AND N_B_STATE <= 3" nRet, distribution_cntr = m3.GetDataObjByCondition(strLuaDEID, "Distribution_CNTR", strCondition) -- nRet = 0 表示配盘已经存在 local b_have_sanme_inv_detail = false local distribution_detail = {} if (nRet == 0) then if (distribution_cntr.b_state ~= 0) then return 1, "容器号 = '" .. distribution_info.cntr_code .. "' 的容器已经配盘完成不能继续配盘!" end -- 进一步判断配盘明细里是否已经存在 该 INV_Detail ID相同的配货信息(发货单一样),如果有数量累计即可,不需要另外创建 strCondition = "S_DC_NO = '" .. distribution_cntr.dc_no .. "' AND G_INV_DETAIL_ID = '" .. distribution_info.inv_detail_id .. "'" strCondition = strCondition .. " AND S_BS_TYPE = '" .. distribution_info.bs_type .. "' AND S_BS_NO = '" .. distribution_info.bs_no .. "'" nRet, distribution_detail = m3.GetDataObjByCondition(strLuaDEID, "Distribution_CNTR_Detail", strCondition) if (nRet == 0) then b_have_sanme_inv_detail = true elseif (nRet > 1) then return 1, "在检查[配盘明细]是否在时失败! " .. distribution_detail end -- 如果有紧急配盘,需要把配盘业务状态设置为 = 1(配盘完成) if (distribution_info.urgent_outbound) then -- 检查一下是否为整出 nRet, strRetInfo = wms_out.Check_Distribution_WholeOut(strLuaDEID, distribution_cntr) if (nRet ~= 0) then return nRet, strRetInfo end strCondition = "S_ID = '" .. distribution_cntr.id .. "'" local strSetAttr = "N_B_STATE = 1, S_B_STATE = '配盘完成' " if (strRetInfo == "yes") then -- 说明是整个容器的货品都出库 strSetAttr = strSetAttr .. ", C_WHOLE_OUT = 'Y'" end nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Distribution_CNTR", strCondition, strSetAttr) if (nRet ~= 0) then return 1, "updateDataAttrByCondition(Distribution_CNTR)失败" .. strRetInfo end end elseif (nRet == 1) then -- step2 如果配盘不存在 创建配盘 distribution_cntr = m3.AllocObject(strLuaDEID, "Distribution_CNTR") distribution_cntr.factory = distribution_info.factory distribution_cntr.bs_type = distribution_info.bs_type distribution_cntr.bs_no = distribution_info.bs_no distribution_cntr.cntr_code = cntr_code distribution_cntr.wh_code = loc.wh_code distribution_cntr.area_code = loc.area_code distribution_cntr.loc_code = loc.code distribution_cntr.exit_area_code = distribution_info.exit_area_code distribution_cntr.exit_loc_code = distribution_info.exit_loc_code -- 如果有紧急配盘,需要把配盘业务状态设置为 = 1(配盘完成) if (distribution_info.urgent_outbound) then -- 检查一下是否为整出 nRet, strRetInfo = wms_out.Check_Distribution_WholeOut(strLuaDEID, distribution_cntr) if (nRet ~= 0) then return nRet, strRetInfo end distribution_cntr.b_state = 1 distribution_cntr.b_state_name = "配盘完成" -- 这地方要改成从字典获取 if (strRetInfo == "yes") then distribution_cntr.whole_out = 'Y' end end nRet, distribution_cntr = m3.CreateDataObj(strLuaDEID, distribution_cntr) if (nRet ~= 0) then return 1, '创建【配盘】对象失败!' .. distribution_cntr end else return 1, "GetDataObjByCondition失败! " .. distribution_cntr end local dc_no = distribution_cntr.dc_no -- 获取配盘号 -- step3 创建【配盘明细/Distribution_CNTR_Detail】 -- 获取 INV_Detail 信息 if (b_have_sanme_inv_detail) then -- 【配盘明细】中已经存在相同的 INV_Detail, 更新数量 local strSetSQL = "F_QTY = F_QTY + " .. distribution_info.qty strCondition = "S_DC_NO = '" .. distribution_cntr.dc_no .. "' AND G_INV_DETAIL_ID = '" .. distribution_info.inv_detail_id .. "'" strCondition = strCondition .. " AND S_BS_TYPE = '" .. distribution_info.bs_type .. "' AND S_BS_NO = '" .. distribution_info.bs_no .. "'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Distribution_CNTR_Detail", strCondition, strSetSQL) if (nRet ~= 0) then return 1, "设置【配盘明细】 数量失败!" .. strRetInfo end else local inv_detail nRet, inv_detail = m3.GetDataObject(strLuaDEID, "INV_Detail", distribution_info.inv_detail_id) if (nRet ~= 0) then return 1, '获取【容器货品明细】对象失败!' .. inv_detail end distribution_detail = m3.AllocObject(strLuaDEID, "Distribution_CNTR_Detail") distribution_detail.dc_no = dc_no distribution_detail.cntr_code = inv_detail.cntr_code distribution_detail.item_code = inv_detail.item_code distribution_detail.item_name = inv_detail.item_name distribution_detail.item_spec = inv_detail.item_spec distribution_detail.cell_no = inv_detail.cell_no distribution_detail.item_state = inv_detail.item_state distribution_detail.item_state_name = inv_detail.item_state_name distribution_detail.batch_no = inv_detail.batch_no distribution_detail.serial_no = inv_detail.serial_no distribution_detail.end_user = inv_detail.end_user distribution_detail.owner = inv_detail.owner distribution_detail.supplier = inv_detail.supplier distribution_detail.uom = inv_detail.uom distribution_detail.qty = distribution_info.qty distribution_detail.bs_type = distribution_info.bs_type distribution_detail.bs_no = distribution_info.bs_no distribution_detail.bs_row_no = distribution_info.bs_row_no distribution_detail.inv_detail_id = distribution_info.inv_detail_id distribution_detail.wh_code = loc.wh_code distribution_detail.area_code = loc.area_code nRet, distribution_detail = m3.CreateDataObj(strLuaDEID, distribution_detail) if (nRet ~= 0) then return 1, '创建【配盘明细】对象失败!' .. distribution_detail end end -- 仓库/库区量表+分配量 local item = {} -- 量表变化参数 item[1] = { N_ROW_NO = 1, S_ITEM_CODE = distribution_detail.item_code, S_ITEM_NAME = distribution_detail.item_name, F_QTY = distribution_info.qty } -- + 仓库/库区分配量 nRet, strRetInfo = wms.wms_IPA_Start(loc.wh_code, loc.area_code, lua.table2str(item)) if (nRet ~= 0 or strRetInfo == '') then return 1, 'wms_IPA_Start 失败!' .. strRetInfo end local success local pre_alloc_storage success, pre_alloc_storage = pcall(json.decode, strRetInfo) if (success == false) then return 1, "wms_IPA_Start 返回非法的JSON格式!" .. pre_alloc_storage end local trans_id = pre_alloc_storage.trans_id -- +分配量 事务标识 -- 如果没有trans_id说明+分配量失败 if (trans_id == nil or trans_id == '') then return 1, "系统在增加仓库/库区分配量时失败! 可能是库存量不足!" end -- INV_Detail 加分配量 strCondition = "S_ID = '" .. distribution_info.inv_detail_id .. "'" nRet, strRetInfo = mobox.incDataObjAccQty(strLuaDEID, "INV_Detail", strCondition, "F_QTY", "F_ALLOC_QTY", distribution_info.qty) if (nRet ~= 0 or strRetInfo ~= '') then wms.wms_IPA_Abort(trans_id) return 1, '在增加【容器货品明细】中的分配量时失败!' .. strRetInfo end -- 业务来源这里加累计配盘数量 if (distribution_info.bs_type == "Shipping_Order") then -- 发货单 strCondition = "S_SHIPPING_NO = '" .. distribution_info.bs_no .. "' AND N_ROW_NO = " .. distribution_info.bs_row_no nRet, strRetInfo = mobox.incDataObjAccQty(strLuaDEID, "Shipping_Detail", strCondition, "F_QTY", "F_ACC_D_QTY", distribution_info.qty) if (nRet ~= 0 or strRetInfo ~= '') then wms.wms_IPA_Abort(trans_id) return 1, '在增加【发货单明细】中的累计配货数量时失败!' .. strRetInfo end nRet, strRetInfo = wms_out.Shipping_Order_CheckState(strLuaDEID, distribution_info.bs_no) if (nRet ~= 0) then wms.wms_IPA_Abort(trans_id) return 1, strRetInfo end else -- 出库单 end wms.wms_IPA_Commit(trans_id) return 0 end --[[ 根据输入的出库单编码获取 出库单明细列表,oo_no_set 是一个出库单编码数组,如["OO010","OO012"] 返回值: 1 -- nRet 0 正确 2 --outbound_detail_list 出库单明细列表 table ]] function wms_out.Get_Outbound_Detail_list(strLuaDEID, oo_no_set) local nRet, strRetInfo local where, nCount, n local outbound_detail_list = {} nCount = #oo_no_set if (nCount == 0) then return 1, "出库单编码不能为空!" end if (nCount == 1) then where = " S_OO_NO = '" .. oo_no_set[1] .. "'" else where = " S_OO_NO IN (" for n = 1, nCount do where = where .. "'" .. oo_no_set[n] .. "'," end where = lua.trim_laster_char(where) .. ")" end local strOrder = "S_ITEM_CODE" nRet, strRetInfo = mobox.queryDataObjAttr(strLuaDEID, "Outbound_Detail", where, strOrder) if (nRet ~= 0) then return 1, "获取【发货单明细】失败! " .. strRetInfo end -- 如果没有满足条件的出库单明细就直接返回 if (strRetInfo == '') then return 0, outbound_detail_list end local retObjs = json.decode(strRetInfo) local outbound_detail for n = 1, #retObjs do nRet, outbound_detail = m3.ObjAttrStrToLuaObj("Outbound_Detail", lua.table2str(retObjs[n].attrs)) table.insert(outbound_detail_list, outbound_detail) end return 0, outbound_detail_list end --[[ 把配盘明细中的货品 批分到每个出库单明细, 前提是这个配盘是针对 出库波次单 进行的出库分拣操作 输入参数: d_cntr_detail_list [配盘明细] 返回值: 1 -- nRet 0 正确 2 --outbound_detail_list 出库单明细列表 table ]] function wms_out.Split_Distribution_CNTR_Detail(strLuaDEID, wave_no, wave_cls_id, d_cntr_detail_list, outbound_detail_list) local new_d_cntr_detail_list = {} for n = 1, #d_cntr_detail_list do local cd_item_code = d_cntr_detail_list[n].S_ITEM_CODE local cd_item_state = d_cntr_detail_list[n].S_ITEM_STATE local cd_storer = d_cntr_detail_list[n].S_STORER local cd_qty = tonumber(d_cntr_detail_list[n].F_QTY) for m = 1, #outbound_detail_list do local out_item_code = outbound_detail_list[m].item_code local out_item_state = outbound_detail_list[m].item_state local out_storer = outbound_detail_list[m].storer local out_qty = tonumber(outbound_detail_list[m].qty) local acc_d_qty = tonumber(outbound_detail_list[m].acc_d_qty) if (cd_item_code == out_item_code and cd_item_state == out_item_state and cd_storer == out_storer and out_qty > acc_d_qty) then local d_cntr_detail = { item_code = d_cntr_detail_list[n].S_ITEM_CODE, item_name = d_cntr_detail_list[n].S_ITEM_NAME, cntr_code = d_cntr_detail_list[n].S_CNTR_CODE, batch_no = d_cntr_detail_list[n].S_BATCH_NO, cell_no = d_cntr_detail_list[n].S_CELL_NO, station = d_cntr_detail_list[n].S_STATION_NO, serial_no = d_cntr_detail_list[n].S_SERIAL_NO, item_spec = d_cntr_detail_list[n].S_ITEM_SPEC, end_user = d_cntr_detail_list[n].S_END_USER, owner = d_cntr_detail_list[n].S_OWNER, uom = d_cntr_detail_list[n].S_UOM, volume = d_cntr_detail_list[n].F_VOLUME, weight = d_cntr_detail_list[n].F_WEIGHT, wh_code = d_cntr_detail_list[n].S_WH_CODE, area_code = d_cntr_detail_list[n].S_AREA_CODE, loc_code = d_cntr_detail_list[n].S_LOC_CODE, inv_detail_id = d_cntr_detail_list[n].G_INV_DETAIL_ID, pick_box_code = "", qty = 0 } local need_qty = out_qty - acc_d_qty -- 出库单明细需求量 local split_qty -- 本次批分数量 if (cd_qty > need_qty) then split_qty = need_qty else split_qty = cd_qty end cd_qty = cd_qty - split_qty -- 配盘明细剩余量 acc_d_qty = acc_d_qty + split_qty -- 出库单明细累计配货量 d_cntr_detail.qty = split_qty d_cntr_detail.bs_type = "Outbound_Order" d_cntr_detail.bs_no = outbound_detail_list[m].oo_no d_cntr_detail.bs_row_no = outbound_detail_list[m].row_no d_cntr_detail.wave_no = wave_no d_cntr_detail.wave_cls_id = wave_cls_id table.insert(new_d_cntr_detail_list, d_cntr_detail) if (cd_qty == 0) then break end end end end return new_d_cntr_detail_list end --[[ 出库分拣标准配货算法 **** 说明: 根据 parameter 的业务来源类型和来源编码获取需要出库的货品清单 item_lits 遍历 item_list 从库存中找出匹配的库存明细进行预分配,生成 d_cntr_detail_list 配盘明细(出库任务) 如果有缺件 保存 在shortage_list 注意:item_list中的 item(SKU) 参与查询条件的属性-- S_PICKING_RULE, S_MATCH_RULE 中定义的规则进行 parameter = { wh_code, area_code 出库货品仓库-库区 , station -- 分拣站台, 可以为空 exit_loc -- 出库出口货位,货位对象{ area_code, code } aisle -- 可用巷道 bs_type --(Outbound_Order/Outbound_Wave) bs_no -- 波次号/出库单号 factory -- 工厂标识 cntr_out_op_def -- 容器出库作业定义 cntr_back_op_def -- 容器回库作业定义 } 返回: d_cntr_list 配盘/Distribution_CNTR d_cntr_detail_list 配盘明细/Distribution_CNTR_Detail shortage_list -- 缺件清单 更改记录: ]] function wms_out.Distribution_Procedure(strLuaDEID, parameter, d_cntr_list, d_cntr_detail_list, shortage_list) local nRet, strRetInfo local strCondition local outbound_order local data_objs local storer_data local str_loc_where = "" local default_match_rule = "" local default_picking_rule = "" -- 输入参数判断 if (parameter == nil or parameter == "") then return 1, "输入参数错误, wh_code必须有值" end if (parameter.wh_code == nil or parameter.wh_code == "") then return 1, "输入参数错误, parameter.wh_code必须有值" end if (parameter.bs_type == nil or parameter.bs_type == "") then return 1, "输入参数错误, parameter.bs_type 必须有值" end if (parameter.bs_no == nil or parameter.bs_no == "") then return 1, "输入参数错误, parameter.bs_no 必须有值" end -- 获取入库货品清单item_list if parameter.bs_type == "Outbound_Wave" then -- 获取波次明细 nRet, data_objs = m3.QueryDataObject(strLuaDEID, "OW_Detail", "S_WAVE_NO = '" .. parameter.bs_no .. "'", "N_ROW_NO") if (nRet ~= 0) then return 2, "QueryDataObject失败!" .. data_objs end elseif parameter.bs_type == "Outbound_Order" then -- 获取出库单明细 nRet, data_objs = m3.QueryDataObject(strLuaDEID, "Outbound_Detail", "S_OO_NO = '" .. parameter.bs_no .. "'", "N_ROW_NO") if (nRet ~= 0) then return 2, "QueryDataObject失败!" .. data_objs end -- 获取出库单的拣货规则,匹配规则 nRet, outbound_order = m3.GetDataObjByCondition(strLuaDEID, "Outbound_Order", "S_NO = '" .. parameter.bs_no .. "'") if (nRet ~= 0) then return 2, "获取出库单信息失败!" .. outbound_order end local order_match_rule = outbound_order.match_rule or "" local order_picking_rule = outbound_order.picking_rule or "" local storer = outbound_order.storer or "" if (storer == "") then return 2, "出库单'" .. parameter.bs_no .. "'没定义货主!" end nRet, storer_data = m3.GetDataFromCache("Storer", storer) if (nRet ~= 0) then return 2, storer_data end if order_match_rule ~= "" then default_match_rule = order_match_rule else default_match_rule = storer_data.S_MATCH_RULE or "" end if order_picking_rule ~= "" then default_picking_rule = order_picking_rule else default_picking_rule = storer_data.S_PICKING_RULE or "" end else return 1, "输入参数错误,parameter.bs_type不合规!" end if (data_objs == "") then return 1, "编号号'" .. parameter.bs_no .. "'的'" .. parameter.bs_type .. "'为空!" end local OUT_DETAIL_ATTR_COUNT = #OUTBOUND_DETAIL_BASE_ATTRS local UDF_ATTRS_COUNT = #UDF_ATTRS local DC_DETAIL_ATTRS_COUNT = #DC_DETAIL_ATTRS local item_list = {} for n = 1, #data_objs do local detail_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) local item = {} for m = 1, OUT_DETAIL_ATTR_COUNT do item[OUTBOUND_DETAIL_BASE_ATTRS[m]] = detail_attrs[OUTBOUND_DETAIL_BASE_ATTRS[m]] end for m = 1, UDF_ATTRS_COUNT do item[UDF_ATTRS[m]] = detail_attrs[UDF_ATTRS[m]] end item.qty = lua.Get_NumAttrValue(detail_attrs.F_QTY) - lua.Get_NumAttrValue(detail_attrs.F_ACC_D_QTY) item.alloc_qty = 0 table.insert(item_list, item) end -- 拣货策略和匹配策略确定,如果 Detail 里有定义就根据Detail里的定义,没有就从 Order 上找,Order上没有就从 货主这里找 -- 从 INV_Detail 中分配数量 local attr_set = {} local strTable = "TN_INV_Detail a LEFT JOIN TN_Container b ON a.S_CNTR_CODE = b.S_CODE" local strAttrs = 'a.S_ID, a.S_WH_CODE, a.S_AREA_CODE, a.S_LOC_CODE,' local INV_DETAIL_BASE_ATTRS_COUNT = #INV_DETAIL_BASE_ATTRS for m = 1, INV_DETAIL_BASE_ATTRS_COUNT do strAttrs = strAttrs .. "a." .. INV_DETAIL_BASE_ATTRS[m] .. "," table.insert(attr_set, INV_DETAIL_BASE_ATTRS[m]) end for m = 1, UDF_ATTRS_COUNT do strAttrs = strAttrs .. "a." .. UDF_ATTRS[m] .. "," table.insert(attr_set, UDF_ATTRS[m]) end strAttrs = lua.trim_laster_char(strAttrs) str_loc_where = "S_WH_CODE = '" .. parameter.wh_code .. "'" if (parameter.area_code ~= nil and parameter.area_code ~= '') then str_loc_where = str_loc_where .. " AND S_AREA_CODE = '" .. parameter.area_code .. "'" end local match_rule, picking_rule, inv_condition, inv_order local index, qty, need_qty, d_qty local strUpdateSql local have_alloc = false -- 遍历待出库货品清单 for n, item in ipairs(item_list) do inv_order = "" -- step2.1 组织查询条件 -- 获取匹配策略 match_rule = item.S_MATCH_RULE or "" if match_rule == "" then match_rule = default_match_rule end -- 拣货规则 picking_rule = item.S_PICKING_RULE or "" if picking_rule == "" then picking_rule = default_picking_rule end inv_condition = wms_base.Get_INV_Detail_MatchSql(item, match_rule) inv_order = wms_base.Get_INV_Detail_QueryOrder(picking_rule) -- b.C_ENABLE 容器没禁用 a.F_QTY_VALID 可用量大于0 strCondition = inv_condition .. " AND b.C_ENABLE = 'Y' AND a.F_QTY_VALID > 0 " .. "AND a.S_CNTR_CODE IN (select S_CNTR_CODE from TN_Loc_Container where S_LOC_CODE IN (select S_CODE from TN_Location where " .. str_loc_where .. ")) " lua.Debug(strLuaDEID, debug.getinfo(1), "配盘查询内容 -->", strAttrs) lua.Debug(strLuaDEID, debug.getinfo(1), "配盘查询联表 -->", strTable) lua.Debug(strLuaDEID, debug.getinfo(1), "最终配盘条件 -->", strCondition) -- step2.2 多表联查获取容器货品信息 nRet, strRetInfo = mobox.queryMultiTable(strLuaDEID, strAttrs, strTable, 1000, strCondition, inv_order) if (nRet ~= 0) then return 2, "查询【容器货品明细】信息失败! " .. strRetInfo end if (strRetInfo ~= "") then local inv_detail_set = json.decode(strRetInfo) -- step2.3 根据出库数量遍历2.2获取的容器货品信息进行数量批分 -- item.qty --> 需要出库的数量 item.alloc_qty --> 已经配货数量 for _, inv_detail in ipairs(inv_detail_set) do -- step2.3.1 配货数量已经到达出库数量 if (lua.equation(item.qty, item.alloc_qty)) then break end -- 获取查询获取的 INV_Detail 中的属性 local inv_detail_data = {} inv_detail_data.S_ID = inv_detail[1] inv_detail_data.S_WH_CODE = inv_detail[2] inv_detail_data.S_AREA_CODE = inv_detail[3] inv_detail_data.S_LOC_CODE = inv_detail[4] index = 5 for m = 1, INV_DETAIL_BASE_ATTRS_COUNT do inv_detail_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail[index] index = index + 1 end for m = 1, UDF_ATTRS_COUNT do inv_detail_data[UDF_ATTRS[m]] = inv_detail[index] index = index + 1 end -- 容器中可用于配货的数量 qty = lua.StrToNumber(inv_detail_data.F_QTY_VALID) need_qty = item.qty - item.alloc_qty -- 把容器加入 配货容器 清单 -- step2.3.3 检查一下容器是否已经在 d_cntr_list, 如果不存在要把 容器加入 d_cntr_lis local cntr_code = inv_detail_data.S_CNTR_CODE local bFind = false for i = 1, #d_cntr_list do if (d_cntr_list[i].S_CNTR_CODE == cntr_code) then bFind = true break end end if (bFind == false) then -- 初始化【配盘】容器信息 local distribution_cntr = m3.AllocObject2(strLuaDEID, "Distribution_CNTR") distribution_cntr.S_FACTORY = parameter.factory distribution_cntr.S_BS_TYPE = parameter.bs_type distribution_cntr.S_BS_NO = parameter.bs_no distribution_cntr.S_OUT_OP_NAME = parameter.cntr_out_op_def or "" distribution_cntr.S_BACK_OP_NAME = parameter.cntr_back_op_def or "" distribution_cntr.S_CNTR_CODE = cntr_code distribution_cntr.N_B_STATE = 1 distribution_cntr.S_WH_CODE = parameter.wh_code distribution_cntr.S_AREA_CODE = parameter.area_code distribution_cntr.S_LOC_CODE = inv_detail_data.S_LOC_CODE distribution_cntr.S_BS_TYPE = parameter.bs_type distribution_cntr.S_BS_NO = parameter.bs_no distribution_cntr.S_EXIT_AREA_CODE = "" distribution_cntr.S_EXIT_LOC_CODE = "" distribution_cntr.S_STATION_NO = parameter.station distribution_cntr.S_DC_NO = "" if (parameter.exit_loc ~= nil) then distribution_cntr.S_EXIT_AREA_CODE = parameter.exit_loc.area_code distribution_cntr.S_EXIT_LOC_CODE = parameter.exit_loc.code end table.insert(d_cntr_list, distribution_cntr) end -- step2.3.4 批分容器配盘数量 if (need_qty > qty) then d_qty = qty else d_qty = need_qty end if (d_qty > 0) then have_alloc = true item.alloc_qty = item.alloc_qty + d_qty -- step2.3.5 生成 配盘明细并且加入配盘明细清单 local d_cntr_detail = m3.AllocObject2(strLuaDEID, "Distribution_CNTR_Detail") d_cntr_detail.S_BS_TYPE = parameter.bs_type d_cntr_detail.S_BS_NO = parameter.bs_no d_cntr_detail.N_BS_ROW_NO = n d_cntr_detail.G_INV_DETAIL_ID = inv_detail_data.S_ID d_cntr_detail.S_PICK_BOX_CODE = "" d_cntr_detail.S_STATION_NO = parameter.station for m = 1, DC_DETAIL_ATTRS_COUNT do d_cntr_detail[DC_DETAIL_ATTRS[m]] = inv_detail_data[DC_DETAIL_ATTRS[m]] end d_cntr_detail.F_QTY = d_qty table.insert(d_cntr_detail_list, d_cntr_detail) -- INV_Detail 的 + F_ALLOC_QTY,并且创建库存锁定日志 nRet, strRetInfo = wms_inv.INV_Detail_Add_AllocQty(strLuaDEID, inv_detail_data, d_qty, parameter.bs_type, parameter.bs_no) if nRet ~= 0 then return 2, "wms_inv.INV_Detail_Add_AllocQty 失败!" .. strRetInfo end -- 入库单明细/入库波次明细 货品的累计配货数量 + d_qty strCondition = "AND S_ITEM_CODE = '" .. item.S_ITEM_CODE .. "' AND S_ITEM_STATE = '" .. item.S_ITEM_STATE .. "' AND S_STORER = '" .. item.S_STORER .. "'" if parameter.bs_type == "Outbound_Wave" then strCondition = "S_WAVE_NO = '" .. parameter.bs_no .. "'" .. strCondition strUpdateSql = "F_ACC_D_QTY = F_ACC_D_QTY + " .. d_qty nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "OW_Detail", strCondition, strUpdateSql) if (nRet ~= 0) then return 2, "更新【波次明细】信息失败!" .. strRetInfo end elseif parameter.bs_type == "Outbound_Order" then strCondition = "S_OO_NO = '" .. parameter.bs_no .. "'" .. strCondition strUpdateSql = "F_ACC_D_QTY = F_ACC_D_QTY + " .. d_qty nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Outbound_Detail", strCondition, strUpdateSql) if (nRet ~= 0) then return 2, "更新【入库单明细】信息失败!" .. strRetInfo end end end end end end -- step3 检查一下是否有未分配完成的货品,如果有生成缺件清单 local alloc_is_ok = true for _, item in ipairs(item_list) do if item.qty > item.alloc_qty then -- 加入缺件清单 alloc_is_ok = false local shortage = { storer = item.S_STORER, item_code = item.S_ITEM_CODE, item_state = item.S_ITEM_STATE, item_name = item.S_ITEM_NAME, qty = item.qty - item.alloc_qty } table.insert(shortage_list, shortage) end end -- 如果出库单明细中已经全部分配完成 if alloc_is_ok then -- 更新出库单、出库波次的状态为 -- 配货完成 if parameter.bs_type == "Outbound_Wave" then strCondition = "S_WAVE_NO = '" .. parameter.bs_no .. "'" strUpdateSql = "N_B_STATE = " .. OUTBOUND_ORDER_STATE.AllocOK .. ",C_PRE_PICKING_OK = 'Y'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Outbound_Wave", strCondition, strUpdateSql) if (nRet ~= 0) then return 2, "更新【出库波次】信息失败!" .. strRetInfo end elseif parameter.bs_type == "Outbound_Order" then strCondition = "S_NO = '" .. parameter.bs_no .. "'" strUpdateSql = "N_B_STATE = " .. OUTBOUND_ORDER_STATE.AllocOK .. ",C_PRE_PICKING_OK = 'Y'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Outbound_Order", strCondition, strUpdateSql) if (nRet ~= 0) then return 2, "更新【出库单】信息失败!" .. strRetInfo end end end return 0 end --[[ 创建配盘及配盘明细数据对象 参数: d_cntr_list 【配盘】链表 [{cntr_code, bs_type, bs_no, wh_code, area_code, exit_area_code, exit_loc_code }] d_cntr_detail_list【配盘明细】链表 [{item_code, item_name, batch_no, serial_no, cntr_code, cell_no, item_state, uom, qty, bs_type, bs_no, wave_cls_id, wave_no, inv_detail_id }] 返回值 nRet = 0 表示创建成功 ]] function wms_out.Creat_Distribution_list(strLuaDEID, wave_no, d_cntr_list, d_cntr_detail_list) local nRet -- 创建配盘单并获取单号 for n = 1, #d_cntr_list do local distribution_cntr = m3.AllocObject(strLuaDEID, "Distribution_CNTR") distribution_cntr.cntr_code = d_cntr_list[n].S_CNTR_CODE distribution_cntr.b_state = d_cntr_list[n].N_B_STATE distribution_cntr.wh_code = d_cntr_list[n].S_WH_CODE distribution_cntr.area_code = d_cntr_list[n].S_AREA_CODE distribution_cntr.loc_code = d_cntr_list[n].S_LOC_CODE distribution_cntr.bs_type = d_cntr_list[n].S_BS_TYPE distribution_cntr.bs_no = d_cntr_list[n].S_BS_NO distribution_cntr.station = d_cntr_list[n].S_STATION_NO distribution_cntr.exit_area_code = d_cntr_list[n].S_EXIT_AREA_CODE distribution_cntr.exit_loc_code = d_cntr_list[n].S_EXIT_LOC_CODE distribution_cntr.out_op_name = '货品出库' distribution_cntr.back_op_name = '料箱入库' lua.Debug(strLuaDEID, debug.getinfo(1), "配盘创建前 -->", distribution_cntr) nRet, distribution_cntr = m3.CreateDataObj(strLuaDEID, distribution_cntr) if (nRet ~= 0) then return 1, "创建【配盘】对象失败!" .. distribution_cntr end local dc_no = distribution_cntr.dc_no d_cntr_list[n].S_DC_NO = dc_no for m = 1, #d_cntr_detail_list do local dc_detail = d_cntr_detail_list[m] if (dc_detail.cntr_code == distribution_cntr.cntr_code) then local distribution_detail = m3.AllocObject(strLuaDEID, "Distribution_CNTR_Detail") distribution_detail.dc_no = dc_no distribution_detail.wave_no = wave_no distribution_detail.wh_code = d_cntr_list[n].S_WH_CODE distribution_detail.area_code = d_cntr_list[n].S_AREA_CODE distribution_detail.loc_code = distribution_cntr.loc_code distribution_detail.cntr_code = dc_detail.cntr_code distribution_detail.item_code = dc_detail.item_code distribution_detail.item_name = dc_detail.item_name distribution_detail.item_spec = dc_detail.item_spec distribution_detail.cell_no = dc_detail.cell_no distribution_detail.item_state = dc_detail.item_state distribution_detail.item_state_name = dc_detail.item_state_name distribution_detail.batch_no = dc_detail.batch_no distribution_detail.serial_no = dc_detail.serial_no distribution_detail.end_user = dc_detail.end_user distribution_detail.owner = dc_detail.owner distribution_detail.supplier = dc_detail.supplier distribution_detail.uom = dc_detail.uom distribution_detail.qty = dc_detail.qty distribution_detail.bs_type = dc_detail.bs_type distribution_detail.bs_no = dc_detail.bs_no distribution_detail.bs_row_no = dc_detail.bs_row_no distribution_detail.inv_detail_id = dc_detail.inv_detail_id distribution_detail.wh_code = dc_detail.wh_code distribution_detail.area_code = dc_detail.area_code distribution_detail.pick_box_code = dc_detail.pick_box_code distribution_detail.station = d_cntr_list[n].S_STATION_NO lua.Debug(strLuaDEID, debug.getinfo(1), "配盘明细创建前 -->", distribution_detail) nRet, distribution_detail = m3.CreateDataObj(strLuaDEID, distribution_detail) if (nRet ~= 0) then return 1, "创建【配盘明细】对象失败!" .. distribution_detail end end end end return 0 end -- 把出库单明细中的 item 加入出库明细汇总链表 sum_outbound_detail, 如果 货品编码相同 数量相加 -- strOONo 出库单号 -- sum_outbound_detail 出库单波次明细 [{item_name,item_code,qty,weight,volume,cell_type}] -- 返回:nRet = 0 outbound_detail_info -- 出库单明细汇总信息 -- outbound_detail_info { total_qty, total_weight, total_volume } function wms_out.Sum_outbound_detail(strLuaDEID, strOONo, sum_outbound_detail) local nRet, strRetInfo local strCondition local data_objs local outbound_detail_info = { total_qty = 0, total_weight = 0, total_volume = 0 } strCondition = "S_OO_NO = '" .. strOONo .. "'" nRet, data_objs = m3.QueryDataObject(strLuaDEID, "Outbound_Detail", strCondition, "N_ROW_NO") if (nRet ~= 0) then return 2, "QueryDataObject失败!" .. data_objs end if (data_objs == '') then return 0, outbound_detail_info end local bFind for n = 1, #data_objs do local detail_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) local item_code = lua.Get_StrAttrValue(detail_attrs.S_ITEM_CODE) local item_state = lua.Get_StrAttrValue(detail_attrs.S_ITEM_STATE) local storer = lua.Get_StrAttrValue(detail_attrs.S_STORER) local qty = lua.StrToNumber(detail_attrs.F_QTY) local weight = lua.StrToNumber(detail_attrs.F_WEIGHT) local volume = lua.StrToNumber(detail_attrs.F_VOLUME) if (item_code ~= "" and qty > 0) then outbound_detail_info.total_qty = outbound_detail_info.total_qty + qty outbound_detail_info.total_weight = outbound_detail_info.total_weight + qty * weight outbound_detail_info.total_volume = outbound_detail_info.total_volume + qty * volume bFind = false for m = 1, #sum_outbound_detail do if (sum_outbound_detail[m].item_code == item_code and sum_outbound_detail[m].item_state == item_state and sum_outbound_detail[m].storer == storer) then sum_outbound_detail[m].qty = sum_outbound_detail[m].qty + qty bFind = true break end end if (bFind == false) then local out_item = { item_code = item_code, item_name = lua.Get_StrAttrValue(detail_attrs.S_ITEM_NAME), item_state = item_state, storer = storer, qty = qty, alloc_qty = 0, weight = weight, volume = volume, cell_type = lua.Get_StrAttrValue(detail_attrs.S_CELL_TYPE) } table.insert(sum_outbound_detail, out_item) end end end return 0, outbound_detail_info end --[[ 分拣出库后,根据配盘明细中的分拣结果 影响 量表,INV_Detail [配盘明细 /Distribution_CNTR_Detail] 的后处理程序 --减仓库/库区量表的分配量 --减仓库/库区量表的存储量 --生成【OnOff_Shelves】上下架记录 --减 INV_Detail 中的QTY个ALLOC_QTY --更新 来源单号 中的累计出库数量 --更新容器料格中的强制置满标记 C_FORCED_FILL = N ]] function wms_out.Distribution_CNTR_Detail_PostProcess(strLuaDEID, wh_code, area_code, loc_code, dc_no) local nRet, strRetInfo if (dc_no == nil or dc_no == '') then return 1, "wms_out.Distribution_CNTR_Detail_PostProcess 函数中 dc_no 必须有值!" end local chg_target = '' -- 量表变化对象 if (wh_code == '' or wh_code == nil) then return 1, "wms_out.Distribution_CNTR_Detail_PostProcess 函数中仓库编码必须有值!" end if (area_code == nil) then area_code = '' end if (loc_code == nil) then loc_code = '' end -- 获取 Pre_Alloc_CNTR_Detail local strOrder = '' local strCondition = "S_DC_NO = '" .. dc_no .. "'" nRet, strRetInfo = mobox.queryDataObjAttr(strLuaDEID, "Distribution_CNTR_Detail", strCondition, strOrder) if (nRet ~= 0) then return 1, "获取【配盘明细】失败! " .. strRetInfo end if (strRetInfo == '') then lua.Warning(strLuaDEID, debug.getinfo(1), "配盘号'" .. dc_no .. "'的明细为空!") return 1, "配盘号'" .. dc_no .. "'的明细为空!" end local retObjs = json.decode(strRetInfo) local n local alloc_qty_change = {} -- 分配量变化 local dc_detail = {} local onoff_shelves = {} -- 上下架记录 local days = os.date("%Y%m%d") local cancel_qty -- 取消数量 local strSetAttr -- 获取存储量变化数据 并且创建 上下架记录 for n = 1, #retObjs do nRet, dc_detail = m3.ObjAttrStrToLuaObj("Distribution_CNTR_Detail", lua.table2str(retObjs[n].attrs)) if (nRet ~= 0) then return 1, "m3.ObjAttrStrToLuaObj(Pre_Alloc_CNTR_Detail) 失败! " .. dc_detail end -- 更新容器料格中的强制置满标记 C_FORCED_FILL = N strCondition = "S_CNTR_CODE = '" .. dc_detail.cntr_code .. "' AND S_CELL_NO = '" .. dc_detail.cell_no .. "'" strSetAttr = "C_FORCED_FILL = 'N'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Container_Cell", strCondition, strSetAttr) if (nRet ~= 0) then return 1, "设置【Container_Cell】属性失败!" .. strRetInfo end -- 强制完成产生的取消数量 qty/计划检出数量 acc_p_qty/实际检出数量 cancel_qty = dc_detail.qty - dc_detail.acc_p_qty if (dc_detail.acc_p_qty > 0) then -- 生成分配量变化输入参数 local alloc_qty_info = { item_code = dc_detail.item_code, item_name = dc_detail.item_name, qty = dc_detail.qty, wh_code = wh_code, area_code = area_code } table.insert(alloc_qty_change, alloc_qty_info) -- 生成上架记录【OnOff_Shelves】 onoff_shelves = m3.AllocObject(strLuaDEID, "OnOff_Shelves") onoff_shelves.action = "-" onoff_shelves.d_action = days onoff_shelves.wh_code = wh_code onoff_shelves.area_code = area_code onoff_shelves.loc_code = loc_code onoff_shelves.item_code = dc_detail.item_code onoff_shelves.item_name = dc_detail.item_name onoff_shelves.batch_no = dc_detail.batch_no onoff_shelves.serial_no = dc_detail.serial_no onoff_shelves.item_spec = dc_detail.item_spec onoff_shelves.item_state_name = dc_detail.item_state_name onoff_shelves.item_state = dc_detail.item_state onoff_shelves.end_user = dc_detail.end_user onoff_shelves.owner = dc_detail.owner onoff_shelves.supplier = dc_detail.supplier onoff_shelves.supplier_name = dc_detail.supplier_name onoff_shelves.cntr_code = dc_detail.cntr_code onoff_shelves.cell_no = dc_detail.cell_no onoff_shelves.qty = dc_detail.acc_p_qty onoff_shelves.uom = dc_detail.uom onoff_shelves.bs_no = dc_detail.bs_no onoff_shelves.bs_type = dc_detail.bs_type nRet, onoff_shelves = m3.CreateDataObj(strLuaDEID, onoff_shelves) if (nRet ~= 0) then return 1, 'mobox 创建【上下架记录】对象失败!' .. onoff_shelves end end -- 减库存量表 if (dc_detail.inv_detail_id ~= '') then -- 配盘明细没有合并 nRet, strRetInfo = wms_inv.INV_Detail_Out(strLuaDEID, dc_detail.inv_detail_id, dc_detail.acc_p_qty, dc_detail.qty, dc_detail.bs_type, dc_detail.bs_no) if (nRet ~= 0) then return 1, "wms_inv.INV_Detail_Out 失败! " .. strRetInfo end else -- 配盘明细是合并 INV_Detail 的因此需要把计划出库数量,实际出库数量批分到 INV_Detail 上 strOrder = "S_BATCH_NO" strCondition = "S_CNTR_CODE = '" .. dc_detail.cntr_code .. "' AND S_CELL_NO = '" .. dc_detail.cell_no .. "'" nRet, strRetInfo = wms_inv.INV_Detail_SplitOut(strLuaDEID, strCondition, strOrder, dc_detail.acc_p_qty, dc_detail.qty, dc_detail.bs_type, dc_detail.bs_no) if (nRet ~= 0) then return 1, "wms_inv.INV_Detail_SplitOut 失败! " .. strRetInfo end end -- 如果来源类型 = Outbound_Order 更新出库单的累计出库数量 if (dc_detail.bs_type == "Outbound_Order") then if (dc_detail.bs_no ~= nil and dc_detail.bs_no ~= '') then strCondition = "S_OO_NO = '" .. dc_detail.bs_no .. "' AND S_ITEM_CODE = '" .. dc_detail.item_code .. "'" strSetAttr = "F_ACC_O_QTY = F_ACC_O_QTY +" .. dc_detail.acc_p_qty if (lua.equation(0, cancel_qty) == false) then strSetAttr = strSetAttr .. ", F_ACC_C_QTY = F_ACC_C_QTY + " .. cancel_qty end nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Outbound_Detail", strCondition, strSetAttr) if (nRet ~= 0) then return 1, "设置【Outbound_Detail】累计出库数量失败!" .. strRetInfo end end end if (dc_detail.wave_cls_id == 'Outbound_Wave') then if (dc_detail.wave_no ~= nil and dc_detail.wave_no ~= '') then strCondition = "S_WAVE_NO = '" .. dc_detail.wave_no .. "' AND S_ITEM_CODE = '" .. dc_detail.item_code .. "'" strSetAttr = "F_ACC_O_QTY = F_ACC_O_QTY +" .. dc_detail.acc_p_qty if (lua.equation(0, cancel_qty) == false) then strSetAttr = strSetAttr .. ", F_ACC_C_QTY = F_ACC_C_QTY + " .. cancel_qty end nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "OW_Detail", strCondition, strSetAttr) if (nRet ~= 0) then return 1, "设置【OW_Detail】累计出库数量失败!" .. strRetInfo end end end end return 0 end --[[ 根据物料/货品号创建指定出库作业(可能会有多个容器) SOO -- Specify Outbound Operation 输入参数: so_no -- 指定出库指令号 item_code -- 物料货品编码 to_loc_code -- 出库口货位 op_def_name -- 作业类型 source_sys -- 来源系统 --]] function wms_out.Create_SOO_ByMaterial(strLuaDEID, station, so_no, area_code, item_code, to_loc_code, op_def_name, source_sys) local nRet, strRetInfo, n -- step1:输入参数合法性检查 if (so_no == nil or so_no == '') then return 1, "so_no 必须有值!" end if (area_code == nil or area_code == '') then return 1, "area_code 必须有值!" end if (source_sys == nil) then source_sys = "" end if (item_code == nil or item_code == '') then return 1, "item_code 必须有值!" end if (to_loc_code == nil or to_loc_code == '') then return 1, "loc_code 必须有值!" end if (station == nil or station == '') then return 1, "station 必须有值!" end -- 判断目标货位是否正确 local to_loc nRet, to_loc = wms_wh.GetLocInfo(to_loc_code) if (nRet ~= 0) then return 1, '获取货位信息失败! ' .. to_loc end -- 通过货品找出货品所在容器 local str_good_condition local success, queryInfo, dataSet local nPage, nPageCount local cntr_code str_good_condition = "S_ITEM_CODE = '" .. item_code .. "' AND 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 S_AREA_CODE = '" .. area_code .. "'))" -- 查询有该货品的容器编号 -- 获取货品所在容器(考虑到有比较极端情况容器数量大于1000因此采用 queryDataObjAttr2 ) nRet, strRetInfo = mobox.queryDataObjAttr2(strLuaDEID, "INV_Detail", str_good_condition, strOrder, 100, "S_CNTR_CODE") if (nRet ~= 0) then return 1, "queryDataObjAttr2: " .. strRetInfo end if (strRetInfo == '') then return 0 end lua.Debug(strLuaDEID, debug.getinfo(1), "货品所在容器 -- >", strRetInfo) success, queryInfo = pcall(json.decode, strRetInfo) if (success == false) then return 2, "queryDataObjAttr2 返回结果啊非法的JSON格式!" end queryID = queryInfo.queryID nPageCount = queryInfo.pageCount nPage = 1 dataSet = queryInfo.dataSet -- 查询出来的数据集 local count = 0 while (nPage <= nPageCount) do for i = 1, #dataSet do cntr_code = dataSet[i].attrs[1].value nRet, strRetInfo = create_so_cntr_operation(strLuaDEID, station, so_no, cntr_code, to_loc, item_code, source_sys) if (nRet ~= 0) then wms.wms_AbortCntrLockTrans(so_no) return 1, "create_so_cntr_operation! " .. strRetInfo end count = count + 1 end nPage = nPage + 1 if (nPage <= nPageCount) then -- 取下一页 nRet, strRetInfo = mobox.queryDataObjAttr2(queryID, nPage) if (nRet ~= 0) then wms.wms_AbortCntrLockTrans(so_no) return 2, "queryDataObjAttr2失败! nPage=" .. nPage .. " " .. strRetInfo end queryInfo = json.decode(strRetInfo) dataSet = queryInfo.dataSet end end -- 设置 Specify_Outbound 状态为执行 N_B_STATE = 2 执行中 strUpdateSql = "N_B_STATE = 2, N_CNTR_TOTAL = " .. count strCondition = "S_SO_NO = '" .. so_no .. "'" nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "Specify_Outbound", strCondition, strUpdateSql) if (nRet ~= 0) then wms.wms_AbortCntrLockTrans(so_no) return 2, "更新【Specify_Outbound】信息失败!" .. strRetInfo end -- create_so_cntr_operation 有锁容器操作 wms.wms_CommitCntrLockTrans(so_no) return 0 end -- 根据体积进行排序,体积大的放前面 local function dc_detail_sort_by_volume(t1, t2) return t1.volume * t1.qty > t2.volume * t2.qty end local function picking_box_sort_by_volume(t1, t2) return t1.box_volume > t2.box_volume end --[[ 把配盘明细批分到拣料箱里 输入参数: d_cntr_detail_list ( 某个出库单相关的配盘明细 ) pick_box_code 拣料箱编码,如果有多个拣料箱用 ; 分割 new_d_cntr_detail_list 批分结果 注意: 如果出库的是出库波次,下面这个双份没考虑根据出库单进行批分拣料箱 --]] function wms_out.Split_CD_Detial_ByPickingBox(strLuaDEID, d_cntr_detail_list, picking_box_code, new_d_cntr_detail_list) local seg = lua.split(picking_box_code, ",") -- 初始化拣料箱 local picking_box_list = {} local box_volume = wms_base.Get_nConst(strLuaDEID, "拣料箱体积") local pick_box_num = #seg for n = 1, pick_box_num do local picking_box = { picking_box_code = seg[n], box_volume = box_volume, } table.insert(picking_box_list, picking_box) end -- 批分到不同的拣料箱 local find, volume, qty, need_split_qty for n = 1, #d_cntr_detail_list do find = false volume = d_cntr_detail_list[n].qty * d_cntr_detail_list[n].volume for m = 1, pick_box_num do if (volume <= picking_box_list[m].box_volume) then d_cntr_detail_list[n].pick_box_code = picking_box_list[m].picking_box_code picking_box_list[m].box_volume = picking_box_list[m].box_volume - volume find = true break end end if (find) then table.insert(new_d_cntr_detail_list, d_cntr_detail_list[n]) else -- 需要批分,把一条【配盘明细】根据拣料箱 need_split_qty = d_cntr_detail_list[n].qty -- 需要批分的数量 for m = 1, pick_box_num do if (picking_box_list[m].box_volume > 0) then qty = math.floor(picking_box_list[m].box_volume / d_cntr_detail_list[n].volume) if (need_split_qty < qty) then qty = need_split_qty end need_split_qty = need_split_qty - qty local d_cntr_detail = { item_code = d_cntr_detail_list[n].item_code, item_name = d_cntr_detail_list[n].item_name, cntr_code = d_cntr_detail_list[n].cntr_code, batch_no = d_cntr_detail_list[n].batch_no, cell_no = d_cntr_detail_list[n].cell_no, station = d_cntr_detail_list[n].station, serial_no = d_cntr_detail_list[n].serial_no, item_spec = d_cntr_detail_list[n].item_spec, end_user = d_cntr_detail_list[n].end_user, owner = d_cntr_detail_list[n].owner, uom = d_cntr_detail_list[n].uom, volume = d_cntr_detail_list[n].volume, weight = d_cntr_detail_list[n].weight, wh_code = d_cntr_detail_list[n].wh_code, area_code = d_cntr_detail_list[n].area_code, loc_code = d_cntr_detail_list[n].loc_code, cg_detail_id = d_cntr_detail_list[n].cg_detail_id, bs_type = d_cntr_detail_list[n].bs_type, bs_no = d_cntr_detail_list[n].bs_no, bs_row_no = d_cntr_detail_list[n].bs_row_no, wave_no = d_cntr_detail_list[n].wave_no, wave_cls_id = d_cntr_detail_list[n].wave_cls_id, pick_box_code = picking_box_list[m].picking_box_code, qty = qty } picking_box_list[m].box_volume = picking_box_list[m].box_volume - qty * d_cntr_detail_list[n].volume table.insert(new_d_cntr_detail_list, d_cntr_detail) end if (need_split_qty == 0) then break end end -- 如果还有 need_split_qty 说明无法分配拣货箱,这是有问题的需要报警 if (need_split_qty > 0) then return 1, "货品'" .. d_cntr_detail_list[n].item_code .. "'因为体积的原因无法分配拣料箱!" end end end return 0 end --[[ 根据配盘明细创建【拣料箱】/【拣料箱明细】 输入参数: dc_detail_id 配盘明细标识 pc_code 拣料箱编码 qty -- 拣料数量 返回 nRet 0 成功 非0 失败 pc_detail_data -- Picking_CNTR_Detail 数据对象(数据库表属性模式) --]] function wms_out.Creat_Picking_CNTR_Detail(strLuaDEID, dc_detail_id, qty, pc_code, pc_cell_no) local nRet, strRetInfo local pc_detail_data = {} if dc_detail_id == nil or dc_detail_id == '' then return 1, "wms_out.Creat_Picking_CNTR_Detail 输入参数中 dc_detail_id 必须有值!" end if pc_code == nil or pc_code == '' then return 1, "wms_out.Creat_Picking_CNTR_Detail 输入参数中 pc_code 必须有值!" end if qty == nil or qty <= 0 then return 1, "wms_out.Creat_Picking_CNTR_Detail 输入参数中 qty 不合规!" end if pc_cell_no == nil then pc_cell_no = '' end -- 获取配盘明细对象 local dc_detail_data dc_detail_id = lua.trim_guid_str(dc_detail_id) nRet, dc_detail_data = m3.GetDataObject2(strLuaDEID, "Distribution_CNTR_Detail", dc_detail_id) if (nRet ~= 0) then lua.Stop(strLuaDEID, dc_detail_data) return end -- 首先检查一下是否存在没封箱的【拣料箱】,如果不存在需要先创建 local picking_cntr local strCondition = "N_B_STATE = " .. PICKING_CNTR_STATE.WaitPicking .. " AND S_CNTR_CODE = '" .. pc_code .. "'" nRet, picking_cntr = m3.GetDataObjByCondition(strLuaDEID, "Picking_CNTR", strCondition) if (nRet == 1) then -- 不存在需要新增【拣料箱】 local loc_code nRet, loc_code = wms_cntr.Get_Container_Loc(strLuaDEID, pc_code) if nRet ~= 0 then return 1, "wms_cntr.Get_Container_Loc 失败!" .. loc_code end picking_cntr = m3.AllocObject(strLuaDEID, "Picking_CNTR") picking_cntr.cntr_code = pc_code picking_cntr.bs_type = dc_detail_data.S_BS_TYPE picking_cntr.bs_no = dc_detail_data.S_BS_NO picking_cntr.station = dc_detail_data.S_STATION_NO picking_cntr.loc_code = loc_code nRet, picking_cntr = m3.CreateDataObj(strLuaDEID, picking_cntr) if (nRet ~= 0) then return 2, "创建【拣料箱】失败!" .. picking_cntr end elseif nRet ~= 0 then return 2, picking_cntr end pc_detail_data = m3.AllocObject2(strLuaDEID, "Picking_CNTR_Detail") pc_detail_data.S_PC_NO = picking_cntr.pc_no for m = 1, #DC_DETAIL_ATTRS do pc_detail_data[DC_DETAIL_ATTRS[m]] = dc_detail_data[DC_DETAIL_ATTRS[m]] end pc_detail_data.S_CNTR_CODE = pc_code pc_detail_data.S_CELL_NO = pc_cell_no pc_detail_data.F_QTY = qty pc_detail_data.G_DC_DETAIL_ID = dc_detail_id nRet, pc_detail_data = m3.CreateDataObj2(strLuaDEID, pc_detail_data) if (nRet ~= 0) then return 2, "创建【拣料箱明细】失败!" .. pc_detail_data end return 0, pc_detail_data end return wms_out