--[[
|
版本: 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
|