--[[ 版本: 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 -- 获取【CG_Detail】 local strCondition = "S_CNTR_CODE = '"..distribution_cntr.cntr_code.."'" nRet, data_objects = m3.QueryDataObject(strLuaDEID, "CG_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 -- 遍历 容器货品明细 判断:cg_detail.qty - dc_detail.qty = 0 local cg_detail_qty, dc_detail_qty local count = #dc_detail for n = 1, #data_objects do obj_attrs = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs) cg_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_CG_DETAIL_ID == data_objects[n].id) then dc_detail_qty = lua.StrToNumber( dc_detail[i].F_QTY ) if ( cg_detail_qty > dc_detail_qty ) then -- 如果容器货品中的数量大于本次配盘出库数量,说明出不完,不是整托出 return 0, "no" end break end end end return 0, "yes" end --[[ 创建配盘及配盘明细, 用于手工配盘 输入参数 distribution_info -- 配盘信息,是一个table,格式如下 { bs_type = "Shipping_Ordre", -- 来源类型(发货单/出库单) bs_no = "shipping_no", -- 来源单号 bs_row_no = bs_row_no, -- 来源单行号 cntr_code = "", -- 容器号 loc_code = "", -- 容器来源货位信息 cg_detail_id = "xxxx", -- CG_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.cg_detail_id == nil or distribution_info.cg_detail_id == '' ) then return 1, "输入的配盘信息中必须有 cg_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_cg_detail = false local distribution_detail = {} if (nRet == 0) then if ( distribution_cntr.b_state ~= 0 ) then return 1, "容器号 = '"..distribution_info.cntr_code.."' 的容器已经配盘完成不能继续配盘!" end -- 进一步判断配盘明细里是否已经存在 该 CG_Detail ID相同的配货信息(发货单一样),如果有数量累计即可,不需要另外创建 strCondition = "S_DC_NO = '"..distribution_cntr.dc_no.."' AND G_CG_DETAIL_ID = '"..distribution_info.cg_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_cg_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.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】 -- 获取 CG_Detail 信息 if ( b_have_sanme_cg_detail ) then -- 【配盘明细】中已经存在相同的 CG_Detail, 更新数量 local strSetSQL = "F_QTY = F_QTY + ".. distribution_info.qty strCondition = "S_DC_NO = '"..distribution_cntr.dc_no.."' AND G_CG_DETAIL_ID = '"..distribution_info.cg_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 cg_detail nRet, cg_detail = m3.GetDataObject( strLuaDEID, "CG_Detail", distribution_info.cg_detail_id ) if ( nRet ~= 0 ) then return 1, '获取【容器货品明细】对象失败!'..cg_detail end distribution_detail = m3.AllocObject(strLuaDEID, "Distribution_CNTR_Detail") distribution_detail.dc_no = dc_no distribution_detail.cntr_code = cg_detail.cntr_code distribution_detail.item_code = cg_detail.item_code distribution_detail.item_name = cg_detail.item_name distribution_detail.item_spec = cg_detail.item_spec distribution_detail.cell_no = cg_detail.cell_no distribution_detail.item_state = cg_detail.item_state distribution_detail.item_state_name = cg_detail.item_state_name distribution_detail.batch_no = cg_detail.batch_no distribution_detail.serial_no = cg_detail.serial_no distribution_detail.end_user = cg_detail.end_user distribution_detail.owner = cg_detail.owner distribution_detail.supplier = cg_detail.supplier distribution_detail.uom = cg_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.cg_detail_id = distribution_info.cg_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 -- CG_Detail 加分配量 strCondition = "S_ID = '"..distribution_info.cg_detail_id.."'" nRet, strRetInfo = mobox.incDataObjAccQty ( strLuaDEID, "CG_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 nRet, strRetInfo local n, m, nCount local new_d_cntr_detail_list = {} local qty nCount = #outbound_detail_list for n = 1, #d_cntr_detail_list do qty = d_cntr_detail_list[n].qty for m = 1, nCount do if ( d_cntr_detail_list[n].item_code == outbound_detail_list[m].item_code and outbound_detail_list[m].qty > outbound_detail_list[m].acc_d_qty ) then 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, pick_box_code = "", qty = 0 } -- need_qty 出库单明细 需要批分的货品数量 -- split_qty 计算出可批分出去的货品数量 need_qty = outbound_detail_list[m].qty - outbound_detail_list[m].acc_d_qty if ( qty > need_qty ) then split_qty = need_qty else split_qty = qty end outbound_detail_list[m].acc_d_qty = outbound_detail_list[m].acc_d_qty + split_qty qty = 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 ( qty == 0 ) then break end end end end return new_d_cntr_detail_list end local function generate_cg_detial_match_sql( item ) local item_sql = "a.S_ITEM_CODE = '"..item.item_code.."'" if (item.batch_no ~= nil and item.batch_no ~= '') then item_sql = item_sql.." AND a.S_BATCH_NO = '"..item.batch_no.."'" end if (item.serial_no ~= nil and item.serial_no ~= '') then item_sql = item_sql.." AND a.S_SERIAL_NO = '"..item.serial_no.."'" end if (item.supplier ~= nil and item.supplier ~= '') then item_sql = item_sql.." AND a.S_SUPPLIER_NO = '"..item.supplier.."'" end if (item.owner ~= nil and item.owner ~= '') then item_sql = item_sql.." AND a.S_OWNER = '"..item.owner.."'" end if (item.item_state ~= nil and type(item.item_state) == "number") then item_sql = item_sql.." AND a.N_ITEM_STATE = "..item.item_state end if (item.end_user ~= nil and item.end_user ~= '') then item_sql = item_sql.." AND a.S_END_USER = '"..item.end_user.."'" end if (item.erp_wh_code ~= nil and item.erp_wh_code ~= '') then item_sql = item_sql.." AND a.S_ERP_WH_CODE = '"..item.erp_wh_code.."'" end if (item.ext_attr1 ~= nil and item.ext_attr1 ~= '') then item_sql = item_sql.." AND a.S_EXT_ATTR1 = '"..item.ext_attr1.."'" end if (item.ext_attr2 ~= nil and item.ext_attr2 ~= '') then item_sql = item_sql.." AND a.S_EXT_ATTR2 = '"..item.ext_attr2.."'" end if (item.ext_attr3 ~= nil and item.ext_attr3 ~= '') then item_sql = item_sql.." AND a.S_EXT_ATTR3 = '"..item.ext_attr3.."'" end if (item.ext_attr4 ~= nil and item.ext_attr4 ~= '') then item_sql = item_sql.." AND a.S_EXT_ATTR4 = '"..item.ext_attr4.."'" end if (item.ext_attr5 ~= nil and item.ext_attr5 ~= '') then item_sql = item_sql.." AND a.S_EXT_ATTR5 = '"..item.ext_attr5.."'" end return item_sql end --[[ 起源:巨星料箱配货算法, 可作为标准配货算法 说明: 根据货品编码 item_code 确定货品,根据批次号顺序配盘,批次号小的优先出 输入参数: item_list 需要出库的货品清单,其中item中参与查询条件的属性 {item_code, batch_no,serial_no,supplier,owner,item_state,end_user, erp_wh_code, ext_attr1,..ext_attr5} parameter = { wh_code, area_code 出库货品仓库-库区 , station -- 分拣站台, 可以为空 exit_loc -- 出库出口货位,货位对象{ area_code, code } bs_type, bs_no 业务来源 order_by 匹配CG_Detail时的次序 } match_method: 可以不输入默认为0,0表示会根据 item_list 中出现的 批次号,系列号等进行匹配, 1 -- 表示只对 item_code 进行匹配 返回: d_cntr_list 配盘/Distribution_CNTR d_cntr_detail_list 配盘明细/Distribution_CNTR_Detail 更改记录: V2.0 HAN 20241023 -- 取消查询时对容器锁的要求(不对容器锁进行查询) -- 取消容器加锁(改成作业启动的时候加锁) V3.0 HAN 20241027 加分件站台位置 station V4.0 HAN 20241122 输入参数做了整合 把 wh_code, area_code, station, exit_loc, bs_type, bs_no 整合到 table 参数 parameter { wh_code, area_code, station, exit_loc, bs_type, bs_no, order_by } 其中 order_by 是CG_Detail表中的属性顺序 ]] function wms_out.Distribution_Procedure( strLuaDEID, item_list, parameter, d_cntr_list, d_cntr_detail_list, match_method ) local nRet, strRetInfo, strCondition local n, nCount local str_where = '' local strOrder = '' -- step1:输入参数判断,并且组织仓库查询条件 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 str_where = "S_WH_CODE = '"..parameter.wh_code.."'" if ( parameter.area_code ~= nil and parameter.area_code ~= '') then str_where = str_where.." AND S_AREA_CODE = '"..parameter.area_code.."'" end if ( parameter.exit_loc == nil or parameter.exit_loc == '' ) then return 1, "输入参数错误, parameter.exit_loc 必须有值" end if ( match_method == nil or match_method == '' ) then match_method = 0 end strTable = "TN_CG_Detail 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_BATCH_NO, a.F_QTY, a.F_ALLOC_QTY, a.S_ID, b.S_POSITION,".. "a.S_SERIAL_NO, a.N_ITEM_STATE, a.S_END_USER, a.S_OWNER, a.S_SUPPLIER_NO,a.S_ERP_WH_CODE,".. "a.S_EXT_ATTR1,a.S_EXT_ATTR2,a.S_EXT_ATTR3,a.S_EXT_ATTR4,a.S_EXT_ATTR5" if ( parameter.order_by ~= nil and parameter.order_by ~= '') then strOrder = "a."..parameter.order_by end local qty, alloc_qty, need_qty local bFind local item_condition, cg_detail_id, cell_no, cntr_loc_code local batch_no, serial_no, item_state, end_user, owner, supplier, erp_wh_code local ext_attr1, ext_attr2, ext_attr3, ext_attr4, ext_attr5 -- step2: 遍历待出库货品清单 for n = 1, #item_list do -- step2.1 组织查询条件 -- CG_Detail中的货品物料编码一样,容器可以使用 -- V2.0 改进查询条件 if ( match_method == 1 ) then item_condition = "a.S_ITEM_CODE = '"..item_list[n].item_code.."'" else item_condition = generate_cg_detial_match_sql( item_list[n] ) end -- MDF BY WHB @20250110 在配盘时取消对容器锁的限制 strCondition = item_condition.." AND b.C_ENABLE = 'Y' AND a.F_QTY > a.F_ALLOC_QTY ".. "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_where..")) " lua.Debug( strLuaDEID, debug.getinfo(1), "strCondition", strCondition ) -- step2.2 多表联查获取容器货品信息 nRet, strRetInfo = mobox.queryMultiTable(strLuaDEID, strAttrs, strTable, 1000, strCondition, strOrder ) if ( nRet ~= 0 ) then return 2, "查询【容器货品明细】信息失败! " .. strRetInfo end if ( strRetInfo == '' ) then lua.Debug( strLuaDEID, debug.getinfo(1), "查询条件 = ", strCondition ) return 1, "货品编码 = '"..item_list[n].item_code.."' 没找到库存! 错误编码:5001" end cg_detail_set = json.decode(strRetInfo) -- step2.3 根据出库数量遍历2.2获取的容器货品信息进行数量批分 -- item_list[n].qty --> 需要出库的数量 item_list[n].alloc_qty --> 已经配货数量 for m = 1, #cg_detail_set do -- step2.3.1 配盘数量已经到达出库数量 if ( lua.equation( item_list[n].qty, item_list[n].alloc_qty ) ) then break end -- step2.3.2 获取容器货品记录里的一些基本属性 cntr_code = cg_detail_set[m][1] cell_no = cg_detail_set[m][2] batch_no = cg_detail_set[m][3] -- 容器中货品数量 qty = lua.StrToNumber( cg_detail_set[m][4] ) -- 容器中已经被分配的数量 alloc_qty = lua.StrToNumber( cg_detail_set[m][5] ) cg_detail_id = cg_detail_set[m][6] cntr_loc_code = cg_detail_set[m][7] -- 容器所在货位 -- serial_no = cg_detail_set[m][8] item_state = lua.StrToNumber( cg_detail_set[m][9] ) end_user = cg_detail_set[m][10] owner = cg_detail_set[m][11] supplier = cg_detail_set[m][12] erp_wh_code = cg_detail_set[m][13] ext_attr1 = cg_detail_set[m][14] ext_attr2 = cg_detail_set[m][15] ext_attr3 = cg_detail_set[m][16] ext_attr4 = cg_detail_set[m][17] ext_attr5 = cg_detail_set[m][18] -- 可用于配货的数量 qty = qty - alloc_qty -- 还有多少货品没配 need_qty = item_list[n].qty - item_list[n].alloc_qty -- 把容器加入 配货容器 清单 -- 初始化【配盘】容器信息 local distribution_cntr = { cntr_code = cntr_code, b_state = 1, wh_code = parameter.wh_code, area_code = parameter.area_code, loc_code = cntr_loc_code, bs_type = parameter.bs_type, bs_no = parameter.bs_no, exit_area_code = parameter.exit_loc.area_code, exit_loc_code = parameter.exit_loc.code, station = parameter.station, dc_no = "" -- 配盘号 } -- step2.3.3 检查一下容器是否已经在 d_cntr_list, 如果不存在要把 容器加入 d_cntr_list bFind = false for i = 1, #d_cntr_list do if ( d_cntr_list[i].cntr_code == distribution_cntr.cntr_code ) then bFind = true break end end if ( bFind == false ) then 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 break end item_list[n].alloc_qty = item_list[n].alloc_qty + d_qty -- step2.3.5 生成 配盘明细并且加入配盘明细清单 local d_cntr_detail = { cntr_code = cntr_code, cell_no = cell_no, batch_no = batch_no, item_code = item_list[n].item_code, item_name = item_list[n].item_name, serial_no = serial_no, item_state = item_state, end_user = end_user, owner = owner, supplier = supplier, erp_wh_code = erp_wh_code, ext_attr1 = ext_attr1, ext_attr2 = ext_attr2, ext_attr3 = ext_attr3, ext_attr4 = ext_attr4, ext_attr5 = ext_attr5, volume = item_list[n].volume, weight = item_list[n].weight, bs_type = parameter.bs_type, bs_no = parameter.bs_no, bs_row_no = n, -- 货品清单中的顺序号 wave_cls_id = "", wave_no = "", qty = d_qty, station = parameter.station, cg_detail_id = cg_detail_id, pick_box_code = "" -- 拣料箱编码 } table.insert( d_cntr_detail_list, d_cntr_detail ) 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, cg_detail_id }] 返回值 nRet = 0 表示创建成功 ]] function wms_out.Creat_Distribution_list( strLuaDEID, d_cntr_list, d_cntr_detail_list ) local nRet, strRetInfo, n local distribution_cntr local nCount = #d_cntr_list for n = 1, nCount do distribution_cntr = m3.AllocObject(strLuaDEID, "Distribution_CNTR") distribution_cntr.cntr_code = d_cntr_list[n].cntr_code distribution_cntr.b_state = d_cntr_list[n].b_state distribution_cntr.wh_code = d_cntr_list[n].wh_code distribution_cntr.area_code = d_cntr_list[n].area_code distribution_cntr.loc_code = d_cntr_list[n].loc_code distribution_cntr.bs_type = d_cntr_list[n].bs_type distribution_cntr.bs_no = d_cntr_list[n].bs_no distribution_cntr.station = d_cntr_list[n].station distribution_cntr.exit_area_code = d_cntr_list[n].exit_area_code distribution_cntr.exit_loc_code = d_cntr_list[n].exit_loc_code nRet, distribution_cntr = m3.CreateDataObj( strLuaDEID, distribution_cntr ) if ( nRet ~= 0 ) then return 1, '创建【配盘】对象失败!'..distribution_cntr end d_cntr_list[n].dc_no = distribution_cntr.dc_no end local dc_no, wh_code, area_code, loc_code local strCondition local distribution_detail -- step3 创建【配盘明细/Distribution_CNTR_Detail】 -- 获取 CG_Detail 信息 for m = 1, #d_cntr_detail_list do -- 通过容器编码获取 配盘号 dc_no = "" for n = 1, nCount do if ( d_cntr_list[n].cntr_code == d_cntr_detail_list[m].cntr_code ) then dc_no = d_cntr_list[n].dc_no wh_code = d_cntr_list[n].wh_code area_code = d_cntr_list[n].area_code loc_code = d_cntr_list[n].loc_code break end end if ( dc_no == '' ) then return 1, "容器'"..d_cntr_detail_list[m].cntr_code.."' 无法定位到【配盘】!" end distribution_detail = m3.AllocObject(strLuaDEID, "Distribution_CNTR_Detail") distribution_detail.dc_no = dc_no distribution_detail.cntr_code = d_cntr_detail_list[m].cntr_code distribution_detail.item_code = lua.Get_StrAttrValue( d_cntr_detail_list[m].item_code ) distribution_detail.item_name = lua.Get_StrAttrValue( d_cntr_detail_list[m].item_name ) distribution_detail.item_spec = lua.Get_StrAttrValue( d_cntr_detail_list[m].item_spec ) distribution_detail.cell_no = lua.Get_StrAttrValue( d_cntr_detail_list[m].cell_no ) distribution_detail.item_state = lua.Get_NumAttrValue( d_cntr_detail_list[m].item_state ) distribution_detail.batch_no = lua.Get_StrAttrValue( d_cntr_detail_list[m].batch_no ) distribution_detail.serial_no = lua.Get_StrAttrValue( d_cntr_detail_list[m].serial_no ) distribution_detail.end_user = lua.Get_StrAttrValue( d_cntr_detail_list[m].end_user ) distribution_detail.owner = lua.Get_StrAttrValue( d_cntr_detail_list[m].owner ) distribution_detail.supplier = lua.Get_StrAttrValue( d_cntr_detail_list[m].supplier ) distribution_detail.erp_wh_code = lua.Get_StrAttrValue( d_cntr_detail_list[m].erp_wh_code ) distribution_detail.ext_attr1 = lua.Get_StrAttrValue( d_cntr_detail_list[m].ext_attr1 ) distribution_detail.ext_attr2 = lua.Get_StrAttrValue( d_cntr_detail_list[m].ext_attr2 ) distribution_detail.ext_attr3 = lua.Get_StrAttrValue( d_cntr_detail_list[m].ext_attr3 ) distribution_detail.ext_attr4 = lua.Get_StrAttrValue( d_cntr_detail_list[m].ext_attr4 ) distribution_detail.ext_attr5 = lua.Get_StrAttrValue( d_cntr_detail_list[m].ext_attr5 ) distribution_detail.uom = lua.Get_StrAttrValue( d_cntr_detail_list[m].uom ) distribution_detail.qty = lua.Get_NumAttrValue( d_cntr_detail_list[m].qty ) distribution_detail.bs_type = d_cntr_detail_list[m].bs_type distribution_detail.bs_no = d_cntr_detail_list[m].bs_no distribution_detail.bs_row_no = d_cntr_detail_list[m].bs_row_no distribution_detail.cg_detail_id = d_cntr_detail_list[m].cg_detail_id distribution_detail.wave_no = d_cntr_detail_list[m].wave_no distribution_detail.wave_cls_id = d_cntr_detail_list[m].wave_cls_id distribution_detail.wh_code = wh_code distribution_detail.area_code = area_code distribution_detail.loc_code = loc_code distribution_detail.station = d_cntr_detail_list[m].station distribution_detail.pick_box_code = d_cntr_detail_list[m].pick_box_code nRet, distribution_detail = m3.CreateDataObj( strLuaDEID, distribution_detail ) if ( nRet ~= 0 ) then return 1, '创建【配盘明细】对象失败!'..distribution_detail 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 n, item_code, qty, weight, volume local detail_attrs local bFind for n = 1, #data_objs do detail_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) item_code = lua.Get_StrAttrValue( detail_attrs.S_ITEM_CODE ) qty = lua.StrToNumber( detail_attrs.F_QTY ) weight = lua.StrToNumber( detail_attrs.F_WEIGHT ) 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) then bFind = true sum_outbound_detail[m].qty = sum_outbound_detail[m].qty + qty break end end if ( bFind == false ) then local out_item = { item_code = item_code, item_name = lua.Get_StrAttrValue( detail_attrs.S_ITEM_NAME ), 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 --[[ 分拣出库后,根据配盘明细中的分拣结果 影响 量表,CG_Detail [配盘明细 /Distribution_CNTR_Detail] 的后处理程序 --减仓库/库区量表的分配量 --减仓库/库区量表的存储量 --生成【OnOff_Shelves】上下架记录 --减 CG_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 inventory_change = {} -- 存储量变化 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 inventory_info = { item_code = dc_detail.item_code, item_name = dc_detail.item_name, qty = dc_detail.acc_p_qty, wh_code = wh_code, area_code = area_code } table.insert( inventory_change, inventory_info ) -- 生成分配量变化输入参数 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 -- 减 CG_Detail 中的QTY if ( dc_detail.cg_detail_id ~= '' ) then -- 配盘明细没有合并 nRet, strRetInfo = wms_cntr.Reduc_CG_Detail_Qty_AlloccQty( strLuaDEID, dc_detail.cg_detail_id, dc_detail.acc_p_qty, dc_detail.qty ) if ( nRet ~= 0 ) then return 1, "wms_cntr.Reduc_CG_Detail_Qty_AlloccQty 失败! "..strRetInfo end else -- 配盘明细是合并CG_Detail的因此需要把计划出库数量,实际出库数量批分到 CG_Detail 上 strOrder = "S_BATCH_NO" strCondition = "S_CNTR_CODE = '"..dc_detail.cntr_code.."' AND S_CELL_NO = '"..dc_detail.cell_no.."'" nRet, strRetInfo = wms_cntr.Batch_Reduc_CG_Detail_Qty_AlloccQty( strLuaDEID, strCondition, strOrder, dc_detail.acc_p_qty, dc_detail.qty ) if ( nRet ~= 0 ) then return 1, "wms_cntr.Batch_Reduc_CG_Detail_Qty_AlloccQty 失败! "..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 -- 存储量处理 if ( #inventory_change > 0 ) then local str_inventory_change = lua.table2str( inventory_change ) if ( wh_code ~= '' and wh_code ~= nil ) then -- 8 减仓库存储量 nRet, strRetInfo = wms.wms_AddWHInventoryChange(strLuaDEID, 8, str_inventory_change ) if ( nRet ~= 0 ) then return 1, "wms_AddWHInventoryChange 失败! "..strRetInfo end end if ( area_code ~= '' and area_code ~= nil ) then -- 9 减库区存储量 nRet, strRetInfo = wms.wms_AddWHInventoryChange(strLuaDEID, 9, str_inventory_change ) if ( nRet ~= 0 ) then return 1, "wms_AddWHInventoryChange 失败! "..strRetInfo end end end -- 分配量处理 if ( #alloc_qty_change > 0 ) then local str_alloc_qty_change = lua.table2str( alloc_qty_change ) if ( wh_code ~= '' and wh_code ~= nil ) then -- 10 减仓库分配量 nRet, strRetInfo = wms.wms_AddWHInventoryChange(strLuaDEID, 10, str_alloc_qty_change ) if ( nRet ~= 0 ) then return 1, "wms_AddWHInventoryChange 失败! "..strRetInfo end end if ( area_code ~= '' and area_code ~= nil ) then -- 11 减库区分配量 nRet, strRetInfo = wms.wms_AddWHInventoryChange(strLuaDEID, 11, str_alloc_qty_change ) if ( nRet ~= 0 ) then return 1, "wms_AddWHInventoryChange 失败! "..strRetInfo end end end return 0 end local function create_so_cntr_detail( strLuaDEID, station, cntr_code, so_no, soc_no, item_code ) local nRet, strRetInfo local obj_attrs, data_objs local strCondition = "S_CNTR_CODE = '"..cntr_code.."'" local so_cntr_detail if ( item_code ~= nil and item_code ~= '' ) then strCondition = strCondition.." AND S_ITEM_CODE = '"..item_code.."'" end nRet, data_objs = m3.QueryDataObject( strLuaDEID, "CG_Detail", strCondition ) if ( nRet ~= 0 ) then return 1, "获取【CG_Detail】失败! "..data_objs end if ( data_objs == '' ) then return 0 end for n = 1, #data_objs do obj_attrs = m3.KeyValueAttrsToObjAttr(data_objs[n].attrs) so_cntr_detail = m3.AllocObject(strLuaDEID,"SO_CNTR_Detail") so_cntr_detail.soc_no = soc_no so_cntr_detail.station = station so_cntr_detail.so_no = so_no so_cntr_detail.cntr_code = cntr_code so_cntr_detail.qty = lua.Get_NumAttrValue( obj_attrs.F_QTY ) so_cntr_detail.cell_no = obj_attrs.S_CELL_NO so_cntr_detail.item_code = obj_attrs.S_ITEM_CODE so_cntr_detail.item_name = obj_attrs.S_ITEM_NAME so_cntr_detail.item_state = lua.StrToNumber( obj_attrs.N_ITEM_STATE ) so_cntr_detail.batch_no = obj_attrs.S_BATCH_NO so_cntr_detail.serial_no = obj_attrs.S_SERIAL_NO so_cntr_detail.erp_wh_code = obj_attrs.S_ERP_WH_CODE so_cntr_detail.end_user = obj_attrs.S_END_USER so_cntr_detail.owner = obj_attrs.S_OWNER so_cntr_detail.supplier = obj_attrs.S_SUPPLIER_NO so_cntr_detail.uom = obj_attrs.S_UOM so_cntr_detail.ext_attr1 = obj_attrs.S_EXT_ATTR1 so_cntr_detail.ext_attr2 = obj_attrs.S_EXT_ATTR2 so_cntr_detail.ext_attr3 = obj_attrs.S_EXT_ATTR3 so_cntr_detail.ext_attr4 = obj_attrs.S_EXT_ATTR4 so_cntr_detail.ext_attr5 = obj_attrs.S_EXT_ATTR5 nRet, so_cntr_detail = m3.CreateDataObj( strLuaDEID, so_cntr_detail ) if ( nRet ~= 0 ) then return 1, 'mobox 创建【指定出库容器货品明细】对象失败!'..so_cntr_detail end end return 0 end -- 根据指(so)定出库货品,创建指定出库容器和 指定出库容器明细 local function create_so_cntr_operation( strLuaDEID, station, so_no, cntr_code, to_loc, item_code, source_sys ) local nRet, strRetInfo if ( source_sys == nil ) then source_sys = "" end -- 获取容器所在的货位 local from_loc_code = wms_wh.GetLocCodeByCNTR( strLuaDEID, cntr_code ) if ( from_loc_code == '' ) then return 1, "容器'"..cntr_code.."'无法获取货位!" end local from_loc nRet, from_loc = wms_wh.GetLocInfo( from_loc_code ) if ( nRet ~= 0 ) then return 1, '获取货位信息失败! '..from_loc end -- 创建 指定出库容器 local so_cntr = m3.AllocObject(strLuaDEID,"Specify_Outbound_CNTR") so_cntr.so_no = so_no so_cntr.cntr_code = cntr_code so_cntr.wh_code = from_loc.wh_code so_cntr.area_code = from_loc.area_code so_cntr.loc_code = from_loc.code so_cntr.to_loc_code = to_loc.code so_cntr.station = station nRet, so_cntr = m3.CreateDataObj( strLuaDEID, so_cntr ) if (nRet ~= 0) then return 1, "创建【盘点单】失败!"..so_cntr end nRet, strRetInfo = create_so_cntr_detail( strLuaDEID, station, cntr_code, so_no, so_cntr.soc_no, item_code ) if ( nRet ~= 0 ) then return nRet, strRetInfo end local operation = m3.AllocObject(strLuaDEID,"Operation") operation.source_sys = source_sys operation.start_wh_code = from_loc.wh_code operation.start_area_code = from_loc.area_code operation.start_loc_code = from_loc.code operation.end_wh_code = to_loc.wh_code operation.end_area_code = to_loc.area_code operation.end_loc_code = to_loc.code operation.lock_cntr = "N" -- 作业启动时不需要锁容器 operation.cntr_code = cntr_code operation.op_type = wms_base.Get_nConst( strLuaDEID, "作业类型-出库" ) operation.op_def_name = "指定出库" operation.bs_type = "Specify_Outbound" operation.bs_no = so_no nRet, operation = m3.CreateDataObj( strLuaDEID, operation ) if ( nRet ~= 0 ) then return 2, '创建【作业】失败!'..operation end -- 容器加出库锁/2 -- 加内存同时加数据库 nRet, strRetInfo = wms_cntr.Lock( strLuaDEID, cntr_code, 2, so_no ) if ( nRet ~= 0 ) then return 2, "给容器'"..cntr_code.."'加出库锁失败!" end return 0 end --[[ 根据容器号创建一个指定出库作业 SOO -- Specify Outbound Operation 输入参数: so_no -- 指定出库指令号 cntr_code -- 容器编码 to_loc_code -- 出库口货位 op_def_name -- 作业类型 source_sys -- 来源系统 --]] function wms_out.Create_SOO_ByContainer( strLuaDEID, station, so_no, cntr_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 ( cntr_code == nil or cntr_code == '') then return 1, "cntr 必须有值!" 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 if ( source_sys == nil ) then source_sys = "" end -- 判断目标货位是否正确 local to_loc nRet, to_loc = wms_wh.GetLocInfo( to_loc_code ) if ( nRet ~= 0 ) then return 1, '获取货位信息失败! '..to_loc end -- 创建指定出库容器+容器明细+作业 nRet, strRetInfo = create_so_cntr_operation( strLuaDEID, station, so_no, cntr_code, to_loc, "", source_sys ) if ( nRet ~= 0 ) then wms.wms_AbortCntrLockTrans( so_no ) return nRet, strRetInfo end -- 设置 Specify_Outbound 状态为执行 N_B_STATE = 2 执行中 strUpdateSql = "N_B_STATE = 2, N_CNTR_TOTAL = 1" 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 --[[ 根据物料/货品号创建指定出库作业(可能会有多个容器) 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, "CG_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 return wms_out