--[[
|
编码: JX-API-06
|
名称: 出库单以及明细新增
|
作者: kun
|
入口函数:CreateOutbound
|
日期: 2025-4-16
|
|
功能:
|
- 根据物料 area_code 对 ITEMS 进行分组,每组创建一张出库单。
|
|
输入数据格式:
|
{
|
"Name": "GetOutboundOrder",
|
"Source": "ERP",
|
"Data": {
|
"S_BS_NO": "CKD001",
|
"S_OP_TYPE": "采购退货",
|
"S_OUT_TO": "生产部",
|
"S_SHIFT_CODE": "",
|
"S_PRODUCT_TYPE": "5100",
|
"F_DIMENSION": 80,
|
"S_ITEM_DESC": "",
|
"S_SN": "",
|
"S_WO_NO": "",
|
"N_PRIORITY": 1,
|
"S_NOTE": "",
|
"ITEMS": [
|
{
|
"S_BS_NO": "11",
|
"N_BS_ROW_MO": 11,
|
"S_ITEM_CODE": "CS001",
|
"S_ITEM_STATE": "O",
|
"S_SUPPLIER_NO":"1",
|
"S_SUPPLIER_NAME":"2",
|
"F_QTY": 10,
|
"S_EXT_ATTR1":"",
|
"S_EXT_ATTR2":"2",
|
"S_EXT_ATTR3":"",
|
"S_EXT_ATTR4":"1",
|
"S_EXT_ATTR5":""
|
},
|
{
|
"S_BS_NO": "21",
|
"N_BS_ROW_MO": 21,
|
"S_ITEM_CODE": "A234",
|
"S_ITEM_STATE": "O",
|
"S_SUPPLIER_NO":"",
|
"S_SUPPLIER_NAME":"",
|
"F_QTY": 10,
|
"S_EXT_ATTR1":"2",
|
"S_EXT_ATTR2":"1",
|
"S_EXT_ATTR3":"2",
|
"S_EXT_ATTR4":"",
|
"S_EXT_ATTR5":""
|
},
|
{
|
"S_BS_NO": "31",
|
"N_BS_ROW_MO": 31,
|
"S_ITEM_CODE": "A234",
|
"S_ITEM_STATE": "O",
|
"S_SUPPLIER_NO":"",
|
"S_SUPPLIER_NAME":"",
|
"F_QTY": 10,
|
"S_EXT_ATTR1":"",
|
"S_EXT_ATTR2":"",
|
"S_EXT_ATTR3":"",
|
"S_EXT_ATTR4":"21",
|
"S_EXT_ATTR5":"1"
|
}
|
]
|
}
|
}
|
--]]
|
|
json = require("json")
|
mobox = require("OILua_JavelinExt")
|
m3 = require("oi_base_mobox")
|
wms_base = require("wms_base")
|
|
function CreateOutbound(strLuaDEID)
|
local nRet, inputData
|
local SqlCondition
|
local nRetl, strRetInfo
|
local strCondition
|
local num
|
local area_groups = {}
|
local err = {}
|
local result_code_list = {}
|
|
local seen_combinations = {}
|
local duplicate_found = false
|
local duplicate_items = {}
|
|
|
-- 获取接口传入数据
|
nRet, inputData = m3.GetSysDataJson(strLuaDEID)
|
if (nRet ~= 0) then
|
table.insert(err, "无法获取数据包!" )
|
goto err_msg_process
|
end
|
|
if not inputData or not inputData.ITEMS or #inputData.ITEMS == 0 then
|
table.insert(err, "Data 或 ITEMS 不合法!" )
|
goto err_msg_process
|
end
|
|
-- 校验出库类型
|
if (inputData.S_OP_TYPE == nil or inputData.S_OP_TYPE == "") then
|
table.insert(err, "业务来源S_OP_TYPE不能为空或不合法!" )
|
goto err_msg_process
|
end
|
|
-- 计算发货箱数量
|
if ( inputData.S_PRODUCT_TYPE ~= "" and inputData.F_DIMENSION ~= "") or (not inputData.S_PRODUCT_TYPE and not inputData.F_DIMENSION) then
|
SqlCondition = "S_PRODUCT_TYPE = '" ..inputData.S_PRODUCT_TYPE.. "' and F_DIMENSION = " .. inputData.F_DIMENSION
|
nRetl, strRetInfo = mobox.existThisData(strLuaDEID, "ShipmentBox", SqlCondition)
|
if (nRetl ~= 0) then
|
table.insert(err, "调用方法 existThisData 出错" .. inputData.S_BS_NO )
|
goto err_msg_process
|
end
|
if (strRetInfo ~= 'yes') then
|
table.insert(err, "发货箱无该类型数据,请补充!!!" .. inputData.S_BS_NO )
|
goto err_msg_process
|
end
|
nRet, strRetInfo = m3.GetDataObjByCondition(strLuaDEID, "ShipmentBox", SqlCondition)
|
if (nRet ~= 0) then
|
table.insert(err, "查询发货箱表失败" .. inputData.S_BS_NO )
|
goto err_msg_process
|
end
|
num = strRetInfo.qty
|
end
|
|
-- 校验来源单号
|
if (inputData.S_BS_NO == nil or inputData.S_BS_NO == "") then
|
table.insert(err, "业务来源S_BS_NO不能为空或不合法" )
|
goto err_msg_process
|
end
|
strCondition = " S_BS_NO = '" ..inputData.S_BS_NO.. "' "
|
nRetl, strRetInfo = mobox.existThisData(strLuaDEID, "Outbound_Order", strCondition)
|
if (nRetl ~= 0) then
|
table.insert(err, "调用方法 existThisData 出错" .. inputData.S_BS_NO )
|
goto err_msg_process
|
end
|
if (strRetInfo == 'yes') then
|
table.insert(err, "来源单号存在" .. inputData.S_BS_NO )
|
goto err_msg_process
|
end
|
|
nRet,wh_code = wms_base.Get_sConst2("默认工厂标识" )
|
if ( nRet ~= 0 ) then
|
table.insert(errcode, "系统常量:默认工厂标识" .. inputData.S_BS_NO)
|
goto err_msg_process
|
end
|
if ( wh_code == '' ) then
|
table.insert(errcode, "系统常量'默认工厂标识'必须有值!" .. inputData.S_BS_NO)
|
goto err_msg_process
|
end
|
nRet,storer = wms_base.Get_sConst2( "WMS_Default_Storer" )
|
if ( nRet ~= 0 ) then
|
table.insert(errcode, "系统常量:WMS_Default_Storer" .. inputData.S_BS_NO)
|
goto err_msg_process
|
end
|
if ( storer == '' ) then
|
table.insert(errcode, "系统常量'WMS_Default_Storer'必须有值!" .. inputData.S_BS_NO)
|
goto err_msg_process
|
end
|
|
-- 新增校验:检查重复的S_BS_NO和N_BS_ROW_MO组合
|
|
for _, item in ipairs(inputData.ITEMS) do
|
local combination_key = item.S_BS_NO .. "|" .. tostring(item.N_BS_ROW_MO)
|
|
if seen_combinations[combination_key] then
|
duplicate_found = true
|
table.insert(duplicate_items, combination_key)
|
else
|
seen_combinations[combination_key] = true
|
end
|
end
|
|
if duplicate_found then
|
table.insert(err, "存在重复的来源单号和行号组合: " .. table.concat(duplicate_items, ", "))
|
goto err_msg_process
|
end
|
|
-- 按 area_code 分组
|
for _, item in ipairs(inputData.ITEMS) do
|
if not item.S_ITEM_CODE or item.S_ITEM_CODE == "" then
|
table.insert(err, "商品编码不能为空" ..item.S_ITEM_CODE)
|
elseif not item.F_QTY or item.F_QTY == 0 then
|
table.insert(err, "商品数量不能为0" ..item.S_ITEM_CODE)
|
elseif not item.S_BS_NO or item.S_BS_NO == "" then
|
table.insert(err, "来源单号不能为空" ..item.S_ITEM_CODE)
|
elseif not item.N_BS_ROW_MO or item.N_BS_ROW_MO == "" then
|
table.insert(err, "来源行号不能为空" ..item.S_ITEM_CODE)
|
elseif (type(item.N_BS_ROW_MO) ~= "number") then
|
table.insert(err, "来源行号非数字类型" ..item.S_ITEM_CODE)
|
else
|
local strCondition = "S_BS_NO = '" ..item.S_BS_NO.. "' and N_BS_ROW_MO = '" ..item.N_BS_ROW_MO.. "'" -- N_BS_ROW_MO = '" ..item.N_BS_ROW_MO.. "' and
|
local nRetl, strRetInfo = mobox.existThisData(strLuaDEID, "Outbound_Detail", strCondition)
|
if (nRetl ~= 0) then
|
table.insert(err, "调用方法 existThisData 出错: " .. strRetInfo)
|
end
|
if (strRetInfo == 'yes') then
|
table.insert(err, "该物料的来源单行号已存在:" .. item.S_ITEM_CODE)
|
end
|
|
-- 查询物料,获取 area_code
|
local cond = "S_ITEM_CODE = '" .. item.S_ITEM_CODE .. "'"
|
local ret1, id, materialAttrs = mobox.getDataObjAttrByKeyAttr(strLuaDEID, "SKU", cond)
|
if ret1 ~= 0 or not materialAttrs then
|
table.insert(err, "获取物料失败:" .. item.S_ITEM_CODE)
|
else
|
local ret2, materialJson = mobox.objAttrsToLuaJson("SKU", materialAttrs)
|
if ret2 ~= 0 then
|
table.insert(err, "物料JSON转换失败:" .. item.S_ITEM_CODE)
|
else
|
local ok, material = pcall(json.decode, materialJson)
|
if not ok or not material or not material.udf01 then
|
table.insert(err, "物料无有效area_code:" .. item.S_ITEM_CODE)
|
else
|
item._material = material
|
local area = material.udf01
|
if not area_groups[area] then
|
area_groups[area] = {}
|
end
|
table.insert(area_groups[area], item)
|
end
|
end
|
end
|
end
|
end
|
|
::err_msg_process::
|
-- 如校验失败,返回错误结构
|
if #err > 0 then
|
local result = {
|
SourceKey = inputData.SourceKey ,
|
err_code = 1,
|
err_msg = table.concat(err, ","),
|
result = nil
|
}
|
mobox.returnValue(strLuaDEID, 1, lua.table2str(result))
|
return
|
end
|
|
-- 创建入库单(按 area_code 拆分)
|
for area_code, items in pairs(area_groups) do
|
local header = 'CK' .. os.date("%y%m%d") .. '-'
|
local ret, order_no = mobox.getSerialNumber("出库单", header, 4)
|
if ret ~= 0 then
|
table.insert(err, "申请入库单编码失败:" .. order_no)
|
goto continue
|
end
|
|
local order = m3.AllocObject(strLuaDEID, "Outbound_Order")
|
order.state = "发布" -- 单据状态
|
order.no = order_no -- 出库单号
|
order.out_type = inputData.S_OP_TYPE -- 出库类型
|
order.bs_no = inputData.S_BS_NO -- 来源单号
|
order.note = inputData.S_NOTE -- 备注
|
order.out_to = inputData.S_OUT_TO -- 出库方向
|
order.shift_code = inputData.S_SHIFT_CODE -- 班次编码
|
order.product_type = inputData.S_PRODUCT_TYPE -- 产品类型
|
order.dimension = inputData.F_DIMENSION -- 尺寸
|
order.priority = inputData.N_PRIORITY -- 优先级
|
order.sn = inputData.S_SN -- 序列号
|
order.wo_no = inputData.S_WO_NO -- 工单号
|
order.box_qty = num -- 发货箱数量
|
|
order.sour_no = inputData.SourceKey
|
order.wh_code = wh_code
|
order.area_code = area_code -- 库区编码
|
|
|
|
for _, item in ipairs(items) do
|
local material = item._material
|
local detail = m3.AllocObject(strLuaDEID, "Outbound_Detail")
|
detail.oo_no = order_no
|
detail.qty = item.F_QTY
|
detail.bs_no = item.S_BS_NO
|
detail.bs_row_no = item.N_BS_ROW_MO
|
detail.item_state = item.S_ITEM_STATE
|
detail.supplier = item.S_SUPPLIER_NO
|
detail.supplier_name = item.S_SUPPLIER_NAME
|
|
detail.item_code = material.item_code
|
detail.item_name = material.item_name
|
|
detail.sour_no = inputData.SourceKey
|
detail.storer = storer
|
|
detail.ext_attr1 = item.S_EXT_ATTR1
|
detail.ext_attr2 = item.S_EXT_ATTR2
|
detail.ext_attr3 = item.S_EXT_ATTR3
|
detail.ext_attr4 = item.S_EXT_ATTR4
|
detail.ext_attr5 = item.S_EXT_ATTR5
|
|
|
|
local ret, msg = m3.CreateDataObj(strLuaDEID, detail)
|
if ret ~= 0 then
|
lua.Stop(strLuaDEID, "创建入库单明细失败:" .. msg)
|
return
|
end
|
|
end
|
|
local ret3, msg3 = m3.CreateDataObj(strLuaDEID, order)
|
if ret3 ~= 0 then
|
lua.Stop(strLuaDEID, "创建入库单明细失败:" .. msg3)
|
return
|
end
|
|
table.insert(result_code_list, order_no)
|
|
::continue::
|
end
|
|
-- 统一格式返回结果
|
local final_result
|
if #err > 0 then
|
final_result = {
|
SourceKey = inputData.SourceKey or "",
|
err_code = 1,
|
err_msg = "出库单创建异常:" .. table.concat(err, ","),
|
result = nil
|
}
|
else
|
final_result = {
|
SourceKey = inputData.SourceKey or "",
|
err_code = 0,
|
err_msg = "出库单创建成功!",
|
result = {
|
S_NO = inputData.S_BS_NO
|
}
|
}
|
end
|
|
mobox.returnValue(strLuaDEID, 1, lua.table2str(final_result))
|
end
|