1
Jianw
2025-07-09 88e26a2a960dbbc148332772448b79b9877102d8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
 --[[
    编码: JX-API-02
    名称: 接收入库单并创建入库单及明细
    作者: kun
    入口函数:CreateInbound 
    日期: 2025-4-16
 
    功能:
        - 根据物料 area_code 对 ITEMS 进行分组,每组创建一张入库单。
        - 若中间任意校验失败,中止创建流程并返回固定结构。
        
        输入数据格式:
        {
        "S_OP_TYPE":"采购入库",
        "S_BS_NO": "20250528002",
        "SourceKey": "",
        "S_NOTE": "",
        "ITEMS": [{
            "S_ITEM_CODE":"A123",
            "F_QTY":12,
            "S_EXT_ATTR1":"",
            "D_PRD_DATE":"",
            "S_SUPPLIER_NO":"",
            "S_SUPPLIER_NAME":"",
            "S_BS_NO":"CS001",
            "N_BS_ROW_NO":1,
            "N_ITEM_STATE":"O",
            "S_EXT_ATTR2":"",
            "F_WEIGHT": 0.5,
            "S_EXT_ATTR3":"",
            "S_EXT_ATTR4":"",
            "S_EXT_ATTR5":""
            },
           {
            "S_ITEM_CODE":"A234",
            "F_QTY":12,
            "S_EXT_ATTR1":"",
            "D_PRD_DATE":"",
            "S_SUPPLIER_NO":"",
            "S_SUPPLIER_NAME":"",
            "S_BS_NO":"CS002",
            "N_BS_ROW_NO":2,
            "N_ITEM_STATE":"O",
            "S_EXT_ATTR2":"",
            "F_WEIGHT": 0.5,
            "S_EXT_ATTR3":"",
            "S_EXT_ATTR4":"",
            "S_EXT_ATTR5":""
            },
           {
            "S_ITEM_CODE":"CS001",
            "F_QTY":12,
            "S_EXT_ATTR1":"",
            "D_PRD_DATE":"",
            "S_SUPPLIER_NO":"",
            "S_SUPPLIER_NAME":"",
            "S_BS_NO":"CS003",
            "N_BS_ROW_NO":3,
            "N_ITEM_STATE":"O",
            "S_EXT_ATTR2":"",
            "F_WEIGHT": 0.5,
            "S_EXT_ATTR3":"",
            "S_EXT_ATTR4":"",
            "S_EXT_ATTR5":""
            }
        ]
        }
        
--]]
 
json = require("json")
mobox = require("OILua_JavelinExt")
m3 = require("oi_base_mobox")
wms_base = require("wms_base")
 
function CreateInbound(strLuaDEID)
    local nRet, inputData
    local err = {}
    local result_code_list = {}
    local errcode = {}
    local err_msg
    local inbound_data
    local strCondition
    local nRetl,strRetInfo
    local nRet,wh_code 
    m3.PrintLuaDEInfo(strLuaDEID)
 
    nRet, inputData = m3.GetSysDataJson(strLuaDEID)
    if (nRet ~= 0) then
        table.insert(errcode, "无法获取数据包!")
        goto continue
    end
 
    inbound_data = inputData.ITEMS
    if not inbound_data or #inbound_data == 0 then
        table.insert(errcode, "Data 或 ITEMS 不合法!")
        goto continue
    end
 
    if (inputData.S_OP_TYPE == nil or inputData.S_OP_TYPE == "") then
        table.insert(errcode, "业务来源S_OP_TYPE不能为空或不合法!")
        goto continue
    end
 
    if (inputData.S_BS_NO == nil or inputData.S_BS_NO == "") then
        table.insert(errcode, "业务来源S_BS_NO不能为空或不合法!")
        goto continue
    end
 
    strCondition = " S_BS_NO = '" .. inputData.S_BS_NO .. "' "
    nRetl, strRetInfo = mobox.existThisData(strLuaDEID, "Inbound_Order", strCondition)
    if (nRetl ~= 0) then
        table.insert(errcode, "调用方法 existThisData 出错" .. inputData.S_BS_NO)
        goto continue
    end
    if (strRetInfo == 'yes') then
        table.insert(errcode, "来源单号存在" .. inputData.S_BS_NO)
        goto continue
    end
    nRet,wh_code = wms_base.Get_sConst2( "默认工厂标识" )
    if ( nRet ~= 0 ) then
        table.insert(errcode, "系统常量:默认工厂标识" .. inputData.S_BS_NO)
        goto continue
    end
    if ( wh_code == '' ) then
        table.insert(errcode, "系统常量'默认工厂标识'必须有值!" .. inputData.S_BS_NO)
        goto continue
    end
    lua.Debug(strLuaDEID, debug.getinfo(1), " wh_code--->", wh_code)
    nRet,storer = wms_base.Get_sConst2( "WMS_Default_Storer" )
    if ( nRet ~= 0 ) then
        table.insert(errcode, "系统常量:WMS_Default_Storer" .. inputData.S_BS_NO)
        goto continue
    end
    if ( storer == '' ) then
        table.insert(errcode, "系统常量'WMS_Default_Storer'必须有值!" .. inputData.S_BS_NO)
        goto continue
    end
    lua.Debug(strLuaDEID, debug.getinfo(1), " wh_code--->", wh_code)
    ::continue::
 
    if #errcode > 0 then
        local result = {
            SourceKey = inputData.SourceKey or "",
            err_code = 1,
            err_msg = "入库单校验失败:" .. table.concat(errcode, ","),
            result = nil
        }
        mobox.returnValue(strLuaDEID, 1, lua.table2str(result))
        return
    end
 
    -- 分组入库单
    -- 1. 获取物料的 area_code
    -- 2. 根据 area_code 分组
    -- 3. 创建入库单
    local area_groups = {}
    for _, item in ipairs(inbound_data) 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_NO or item.N_BS_ROW_NO == "" then
            table.insert(err, "来源行号不能为空" .. item.S_ITEM_CODE)
        elseif (type(item.N_BS_ROW_NO) ~= "number") then
            table.insert(err, "来源行号非数字类型" .. item.S_ITEM_CODE)
        else
            -- 查询物料
            -- 物料编码不能为空
            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
 
    if #err > 0 then
        local result = {
            SourceKey = inputData.SourceKey or "",
            err_code = 1,
            err_msg = table.concat(err, ","),
            result = nil
        }
        mobox.returnValue(strLuaDEID, 1, lua.table2str(result))
        return
    end
 
    -- 辅助函数:根据多个字段生成唯一合并键
    local function get_merge_key(item)
        return table.concat({
            item._material.item_code or "",
            item.S_SUPPLIER_NAME or "",
            item.S_SUPPLIER_NO or "",
            tostring(item.N_ITEM_STATE or ""),
            tostring(item.D_PRD_DATE or ""),
            tostring(item.F_WEIGHT or 0),
            item.S_EXT_ATTR2 or "",  -- 原产地
            item.S_EXT_ATTR1 or "",
            item.S_EXT_ATTR3 or "",
            item.S_EXT_ATTR4 or "",
            item.S_EXT_ATTR5 or ""
        }, "|")
    end
    
    for area_code, items in pairs(area_groups) do 
        local header = 'RK' .. os.date("%y%m%d") .. '-'
        local ret, order_no = mobox.getSerialNumber("入库单", header, 4)
        if ret ~= 0 then
            table.insert(err, "申请入库单编码失败:" .. order_no)
            goto done
        end
        -- 创建 ERP 入库单
        local erp_order = m3.AllocObject(strLuaDEID, "ERP_Inbound_Order")
        erp_order.state = "发布"
        erp_order.no = order_no
        erp_order.op_type = inputData.S_OP_TYPE
        erp_order.bs_no = inputData.S_BS_NO
        erp_order.note = inputData.S_NOTE
        erp_order.wh_code = wh_code
        erp_order.area_code = area_code
        erp_order.sour_no = inputData.SourceKey or ""  -- 来源单号
    
        -- 创建 ERP 入库单明细
        local erp_map = {}
    
        for _, item in ipairs(items) do
            local material = item._material
            local erp_detail = m3.AllocObject(strLuaDEID, "ERP_Inbound_Detail")
            erp_detail.io_no = order_no                                                 -- 入库单号
            erp_detail.qty = item.F_QTY                                                 -- 数量    
            erp_detail.bs_no = item.S_BS_NO                                             -- 来源单号 
            erp_detail.bs_row_no = item.N_BS_ROW_NO                                     -- 来源行号
            erp_detail.item_state = item.N_ITEM_STATE                                   -- 物料状态
            erp_detail.prd_date = item.D_PRD_DATE                                       -- 生产日期
    
            erp_detail.item_code = material.item_code                                   -- 物料编码
            erp_detail.item_name = material.item_name                                   -- 物料名称 
            erp_detail.s_material = material.udf02                                      -- 物料材质
    
            erp_detail.supplier = item.S_SUPPLIER_NO                                    -- 供应商编码
            erp_detail.supplier_name = item.S_SUPPLIER_NAME                             -- 供应商名称
            erp_detail.weight = item.F_WEIGHT or 0                                      -- 重量
            erp_detail.ext_attr1 = item.S_EXT_ATTR1                                     -- 扩展属性1
            erp_detail.ext_attr2 = item.S_EXT_ATTR2                                     -- 原产地
            erp_detail.ext_attr3 = item.S_EXT_ATTR3                                     -- 扩展属性3
            erp_detail.ext_attr4 = item.S_EXT_ATTR4                                     -- 扩展属性4
            erp_detail.ext_attr5 = item.S_EXT_ATTR5                                     -- 扩展属性5
            erp_detail.sour_no = inputData.SourceKey or ""                             -- 来源单号
    
            local ret, msg = m3.CreateDataObj(strLuaDEID, erp_detail)
            if ret ~= 0 then
                lua.Stop(strLuaDEID, "创建入库单明细失败:" .. msg)
                return
            end
    
            -- 合并 ERP 明细,使用复合键
            local key = get_merge_key(item)
            if not erp_map[key] then
                erp_map[key] = {
                    qty = item.F_QTY,                                       -- 数量
                    io_no = order_no,                                       -- 入库单号
                    supplier = item.S_SUPPLIER_NO,                          -- 供应商编码
                    supplier_name = item.S_SUPPLIER_NAME,                   -- 供应商名称
                    item_state = item.N_ITEM_STATE,                         -- 物料状态
                    prd_date = item.D_PRD_DATE,                             -- 生产日期
                    weight = item.F_WEIGHT or 0,                            -- 重量
                    item_name = material.item_name,                         -- 物料名称
                    item_code = material.item_code,                         -- 物料编码
                    s_material = material.udf02,                            -- 物料材质
                    ext_attr1 = item.S_EXT_ATTR1,                           -- 扩展属性1
                    ext_attr2 = item.S_EXT_ATTR2,                           -- 扩展属性2 (原产地)
                    ext_attr3 = item.S_EXT_ATTR3,                           -- 扩展属性3
                    ext_attr4 = item.S_EXT_ATTR4,                           -- 扩展属性4
                    ext_attr5 = item.S_EXT_ATTR5,                           -- 扩展属性5
                }
            else
                erp_map[key].qty = erp_map[key].qty + item.F_QTY
            end
        end
    
        local ret3, msg3 = m3.CreateDataObj(strLuaDEID, erp_order)
        if ret3 ~= 0 then
            lua.Stop(strLuaDEID, "创建入库单失败:" .. msg3)
            return
        end
    
        -- 创建入库单
        local order = m3.AllocObject(strLuaDEID, "Inbound_Order")
        order.state = "发布"
        order.no = order_no
        order.bs_no = inputData.S_BS_NO
        order.op_type = inputData.S_OP_TYPE
        order.note = inputData.S_NOTE
        order.area_code = area_code
        order.wh_code = wh_code
        order.sour_no = inputData.SourceKey or ""  -- 来源单号
    
        local ret4, msg4 = m3.CreateDataObj(strLuaDEID, order)
        if ret4 ~= 0 then
            lua.Stop(strLuaDEID, "创建ERP入库单失败:" .. msg4)
            return
        end
    
        -- 创建合并后的明细
        for _, v in pairs(erp_map) do
            local detail = m3.AllocObject(strLuaDEID, "Inbound_Detail")
            detail.storer = storer
            detail.io_no = v.io_no
            detail.item_code = v.item_code
            detail.item_name = v.item_name
            detail.qty = v.qty
            detail.item_state = v.item_state
            detail.prd_date = v.prd_date
            detail.supplier = v.supplier
            detail.supplier_name = v.supplier_name
            detail.weight = v.weight
            detail.ext_attr1 = v.ext_attr1
            detail.ext_attr2 = v.ext_attr2
            detail.ext_attr3 = v.ext_attr3
            detail.ext_attr4 = v.ext_attr4
            detail.ext_attr5 = v.ext_attr5
            detail.s_material = v.s_material
            detail.sour_no = inputData.SourceKey or ""  -- 来源单号
    
            local ret5, msg5 = m3.CreateDataObj(strLuaDEID, detail)
            if ret5 ~= 0 then
                lua.Stop(strLuaDEID, "创建ERP入库明细失败:" .. msg5)
                return
            end
        end
    
        ::done::
    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