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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
--[[
    版本:     Version 1.0
    创建日期: 2025-5-16
    创建人:   HAN
 
    WMS-Basis-Model-Version: V15.5
 
    说明:
        涉及数据类 
        INV_Detail   库存量表
        INV_TXN_Log 库存交易日志
 
    功能:
        和库存量表相关的操作
 
 
    更改说明:
        
--]]
 
--+-----------------------------------------------------------------------------+
--| 注意: wms_inv 是比较底层的业务逻辑处理层,不能再加其它和 wms_xxx 相关的 require  |
--+-----------------------------------------------------------------------------+
wms_base = require ("wms_base")
 
local wms_inv = {_version = "0.2.1"}
 
-- 对入库出库单中的明细属性进行检查,S_ITEM_CODE,S_ITEM_STATE,S_STORER 必须有值,F_QTY 必须大于 0
-- S_ITEM_STATE,S_STORER 如果没值用缺省值进行赋值
local function item_detail_data_check( item_detial_data, default_storer, default_item_state )
    -- 数据检查
    if lua.StrIsEmpty( item_detial_data.S_ITEM_CODE ) then
        return false, "创建【库存量表】前检查没通过! S_ITEM_CODE 为空" 
    end    
    if lua.StrIsEmpty( item_detial_data.S_ITEM_STATE ) then
        if default_item_state == '' then
            return false, "数据检查没通过! S_ITEM_STATE 为空并且没设置默认货品状态常量 WMS_Default_ItemState 没定义!" 
        end
        item_detial_data.S_ITEM_STATE = default_item_state
    end
    if lua.StrIsEmpty( item_detial_data.S_STORER ) then
        if default_storer == '' then
            return false, "数据检查没通过! S_STORER 为空并且没设置默认货主常量 WMS_Default_Storer 没定义!" 
        end
        item_detial_data.S_STORER = default_storer
    end
    if lua.Get_NumAttrValue( item_detial_data.F_QTY ) <= 0 then
        return fale, "数据检查没通过! S_ITEM_CODE ='"..item_detial_data.S_ITEM_CODE.."' 的数量属性 F_QTY 不能 <= 0!" 
    end 
    return true   
end
 
--[[
    码盘结束后和货位进行绑定时库存量表的变化处理
--]]
function wms_inv.After_CntrLoc_Binding( strLuaDEID, inb_pallet, loc_code )
    local nRet, strRetInfo
 
    if (inb_pallet == nil or type(inb_pallet) ~= "table" ) then
        return 1, "After_CntrLoc_Binding 函数输入参数错误 inb_pallet 为空或不合规"
    end
    if ( loc_code == nil or loc_code == '' ) then
        return 1, "After_CntrLoc_Binding 函数输入参数错误 loc_code 不能为空!"
    end  
 
    local loc
    nRet, loc = wms_wh.GetLocInfo( loc_code )
    if ( nRet ~= 0 ) then 
        return 1, "获取货位'"..loc_code.."'信息失败! "..loc
    end
 
    -- 查询【INB_Pallet_Detail】
    local strOrder = ""
    local data_objects
    local strCondition = "S_IBP_NO = '"..inb_pallet.ibp_no.."'"
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INB_Pallet_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end
 
    local n, m, attr_count, base_attr_count
    local inv_detail_data
    local inv_txn_log_data
    local item_detial_data
    local check_is_ok
 
    -- 缺省值设置
    local default_storer = ''                    -- 默认货主
    nRet, default_storer = wms_base.Get_sConst2( "WMS_Default_Storer" )
    if ( nRet ~= 0 ) then
        default_storer = ''
    end
    local default_item_state = ''                    -- 默认货品状态
    nRet, default_item_state = wms_base.Get_sConst2( "WMS_Default_ItemState" )
    if ( nRet ~= 0 ) then
        default_item_state = ''
    end
 
    attr_count = #INB_PALLET_DETAIL_ATTRS
    base_attr_count = #INV_DETAIL_BASE_ATTRS
 
    for n = 1, #data_objects do
        item_detial_data = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
 
        check_is_ok, strRetInfo = item_detail_data_check( item_detial_data, default_storer, default_item_state )
        if not check_is_ok then
            return 1, strRetInfo 
        end
        -- 创建库存量表
        inv_detail_data = m3.AllocObject2( strLuaDEID, "INV_Detail" )
        inv_detail_data.S_WH_CODE = loc.wh_code
        inv_detail_data.S_AREA_CODE = loc.area_code
        inv_detail_data.S_LOC_CODE = loc.code
        inv_detail_data.S_CNTR_CODE = inb_pallet.cntr_code
        inv_detail_data.T_INBOUND_TIME = curTime
        for m = 1, attr_count do
            inv_detail_data[INB_PALLET_DETAIL_ATTRS[m]] = item_detial_data[INB_PALLET_DETAIL_ATTRS[m]]
        end
        nRet, inv_detail_data = m3.CreateDataObj2(strLuaDEID, inv_detail_data )
        if (nRet ~= 0 ) then 
            return 1, "创建【库存量表】失败!"..inv_detail_data 
        end    
        
        -- 创建库存交易日志
        inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
        inv_txn_log_data.S_WH_CODE = loc.wh_code
        inv_txn_log_data.S_AREA_CODE = loc.area_code
        inv_txn_log_data.S_LOC_CODE = loc.code
        inv_txn_log_data.S_CNTR_CODE = inb_pallet.cntr_code
        inv_txn_log_data.S_LOG_TYPE = "IN"
        inv_txn_log_data.C_SYMBOL = "+"
        for m = 1, base_attr_count do
            inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = item_detial_data[INV_DETAIL_BASE_ATTRS[m]]
        end
        nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
        if (nRet ~= 0 ) then 
            return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
        end 
 
    end
    return 0
end
 
--[[
    容器和货位解绑后,调用这个函数会删除 INV_Detail 中货品内容
    输入参数:
    cntr_code -- 容器编码 
    str_note  -- 备注
--]]
function wms_inv.After_CntrLoc_UnBinding( strLuaDEID, cntr_code, str_note )
    local nRet, strRetInfo
 
    if ( cntr_code == nil or cntr_code == '' ) then
        return 1, "After_CntrLoc_UnBinding 函数输入参数错误 cntr_code 不能为空!"
    end  
 
    -- 查询【INV_Detail】
    local strOrder = ""
    local strCondition = "S_CNTR_CODE = '"..cntr_code.."'"
    local data_objects
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end
 
    local n, m, attr_count, base_attr_count
    local inv_detail_data
    local inv_txn_log_data
 
    attr_count = #INB_PALLET_DETAIL_ATTRS
    base_attr_count = #INV_DETAIL_BASE_ATTRS
 
    for n = 1, #data_objects do
        inv_detail_data = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
        
        -- 创建库存交易日志
        inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
        inv_txn_log_data.S_WH_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_AREA_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_LOC_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_CNTR_CODE = cntr_code
        inv_txn_log_data.S_LOG_TYPE = "OUT"
        inv_txn_log_data.C_SYMBOL = "-"        
        inv_txn_log_data.S_NOTE = str_note
        for m = 1, base_attr_count do
            inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
        end
        nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
        if (nRet ~= 0 ) then 
            return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
        end 
 
    end
    -- 删除 INV_Detail
    strCondition = "S_CNTR_CODE = '" .. cntr_code .."'"
    nRet, strRetInfo = mobox.dbdeleteData(strLuaDEID, "INV_Detail", strCondition)
    if (nRet ~= 0) then 
        return 2, "删除【INV_Detail】失败!"..strRetInfo
    end   
    
    return 0
end
 
--[[
    库存量货位编码从 loc_from  货位移动到 loc_to 货位
    参数:
        cntr_code -- 容器编码
        from  -- 移库起始货位
        to    -- 移库终点货位
    作用:
        改变 INV_Detail 中的仓库库位
--]]
function wms_inv.Move( strLuaDEID, cntr_code, from, to )
    local nRet, strRetInfo
    local loc_from, loc_to
 
    if ( cntr_code == nil or cntr_code == '' ) then
        return 1, "wms_inv.Move 函数输入参数错误 cntr_code 不能为空!"
    end 
    if ( from == nil or from == '' ) then
        return 1, "wms_inv.Move 函数输入参数错误 from 不能为空!"
    end  
    nRet, loc_from = wms_wh.GetLocInfo( from )
    if ( nRet ~= 0 ) then 
        return 1, "获取货位'"..from.."'信息失败! "..loc_from
    end
 
    if ( to == nil or to == '' ) then
        return 1, "wms_inv.Move 函数输入参数错误 to 不能为空!"
    end  
    nRet, loc_to = wms_wh.GetLocInfo( to )
    if ( nRet ~= 0 ) then 
        return 1, "获取货位'"..to.."'信息失败! "..loc_to
    end
 
    -- 查询【INV_Detail】
    local strCondition = "S_CNTR_CODE = '"..cntr_code.."' AND S_LOC_CODE = '"..from.."'"
    local strOrder = ""
    local data_objects
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end
 
    local n, m, base_attr_count
    local inv_detail_data
    local inv_txn_log_data
 
    base_attr_count = #INV_DETAIL_BASE_ATTRS
    for n = 1, #data_objects do
        inv_detail_data = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
        
        -- 创建库存交易日志
        -- 从from库位移出
        inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
        inv_txn_log_data.S_WH_CODE = loc_from.wh_code
        inv_txn_log_data.S_AREA_CODE = loc_from.area_code
        inv_txn_log_data.S_LOC_CODE = loc_from.code
        inv_txn_log_data.S_CNTR_CODE = cntr_code
        inv_txn_log_data.S_LOG_TYPE = "MOVE-OUT"
        inv_txn_log_data.C_SYMBOL = "-"
        for m = 1, base_attr_count do
            inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
        end
        nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
        if (nRet ~= 0 ) then return 1, "创建【库存交易日志】失败!"..inv_txn_log_data end 
 
        -- 移到to库位
        inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
        inv_txn_log_data.S_WH_CODE = loc_to.wh_code
        inv_txn_log_data.S_AREA_CODE = loc_to.area_code
        inv_txn_log_data.S_LOC_CODE = loc_to.code
        inv_txn_log_data.S_CNTR_CODE = cntr_code
        inv_txn_log_data.S_LOG_TYPE = "MOVE-IN"
        inv_txn_log_data.C_SYMBOL = "+"
        for m = 1, base_attr_count do
            inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
        end
        nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
        if (nRet ~= 0 ) then return 1, "创建【库存交易日志】失败!"..inv_txn_log_data end         
    end
 
    --更新CG_Detail
    strCondition = "S_CNTR_CODE = '"..cntr_code.."' AND S_LOC_CODE = '"..from.."'"
    local strUpdateSql = "S_WH_CODE = '"..loc_to.wh_code.."', S_AREA_CODE = '"..loc_to.area_code.."', S_LOC_CODE = '"..loc_to.code.."'"
    nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "INV_Detail", strCondition, strUpdateSql )
    if ( nRet ~= 0 ) then  
        return 2, "更新[INV_Detail]信息失败!"..strRetInfo 
    end       
    return 0
end
 
--[[
    根据 条件 strCondition 来扣减 INV_detail 中货品的数量
    参数:
        cntr_code    容器编码
        strCondition 查询条件
        strOrder     INV_Detail 的排序
        qty          扣减物料数量
        note         备注
    返回:
        nRet  非0 失败
        如果没有匹配到返回  0,"none"
-- ]]
function wms_inv.Reduce_INV_Detail_Qty( strLuaDEID, cntr_code, strCondition, strOrder, qty, note )
    local nRet, strRetInfo, m
 
    if ( cntr_code == nil or  cntr_code == '' ) then 
        return 1, "Reduce_INV_Detail_Qty 函数中 cntr_code 不能为空!"
    end
    if ( strCondition == nil or  strCondition == '' ) then 
        return 1, "Reduce_INV_Detail_Qty 函数中 strCondition 不能为空!"
    end
    if ( type(qty) ~= "number") then 
        return 1, "Reduce_INV_Detail_Qty 函数中 qty 必须是数值类型!"
    end
    if ( qty <= 0 ) then 
        return 1, "Reduce_INV_Detail_Qty 函数中 qty 必须大于0!"
    end
 
    if note == nil then note = '' end
 
    local data_objects
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end    
 
    local n
    local obj_attr, inv_detail_data
    local value
    local inv_txn_log_data
 
    base_attr_count = #INV_DETAIL_BASE_ATTRS
    for n = 1, #data_objects do
        inv_detail_data = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
        value = lua.Get_NumAttrValue( inv_detail.F_QTY )
 
        -- 创建 INV_Log
        inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
        inv_txn_log_data.S_WH_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_AREA_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_LOC_CODE = inv_detail_data.S_WH_CODE
        inv_txn_log_data.S_CNTR_CODE = cntr_code
        inv_txn_log_data.S_LOG_TYPE = "ADJ-OUT"
        inv_txn_log_data.C_SYMBOL = "-"
        inv_txn_log_data.S_NOTE = note
        for m = 1, base_attr_count do
            inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
        end
 
        if ( qty >= value ) then
            -- 删除 INV_Detail
            strCondition = "S_ID = '"..data_objects[n].id.."'"
            nRet, strRetInfo = mobox.deleteDataObject( strLuaDEID, "INV_Detail", strCondition )
            if ( nRet ~= 0) then return 1, "删除相关的【INV_Detail】失败!  "..strRetInfo end  
            inv_txn_log_data.F_QTY = inv_detail.F_QTY
            nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
            if (nRet ~= 0 ) then 
                return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
            end  
            if ( qty == value ) then goto done end
        else
            -- 减少 INV_Detail 中的数量
            inv_detail.F_QTY = value - qty
            nRet, strRetInfo = wms_cntr.INV_Detail_Update( strLuaDEID, inv_detail )
            if ( nRet ~= 0) then return 1, "wms_cntr.INV_Detail_Update 失败!  "..strRetInfo end  
            nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
            if (nRet ~= 0 ) then 
                return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
            end              
            goto done
        end
        qty = qty - value
    end
 
    ::done::
    return 0, ""
end
 
--[[
    根据条件加 INV_Detail 中的数量,
    参数:
        cntr_code    容器编码
        strCondition 查询条件
        strOrder     INV_Detail 的排序
        qty          扣减物料数量
        note         备注
    返回:
        nRet  非0 失败
        如果没有匹配到返回  0,"none"
-- ]]
function wms_inv.Add_INV_Detail_Qty( strLuaDEID, cntr_code, strCondition, strOrder, qty, note )
    local m, nRet, strRetInfo
 
    if ( cntr_code == nil or  cntr_code == '' ) then 
        return 1, "Add_INV_Detail_Qty 函数中 cntr_code 不能为空!"
    end
    if ( strCondition == nil or  strCondition == '' ) then 
        return 1, "Add_INV_Detail_Qty 函数中 strCondition 不能为空!"
    end
    if ( type(qty) ~= "number") then 
        return 1, "Add_INV_Detail_Qty 函数中 qty 必须是数值类型!"
    end
    if ( qty <= 0 ) then 
        return 1, "Add_INV_Detail_Qty 函数中 qty 必须大于0!"
    end
    if note == nil then note = '' end
 
    local data_objects
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end  
 
    local strUpdateCondition = "S_ID = '"..data_objects[n].id.."'"
    local strSetAttr = "F_QTY = F_QTY + "..qty
    nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "INV_Detail", strUpdateCondition, strSetAttr )
    if ( nRet ~= 0 ) then  
        return 2, "更新【容器货品明细】信息失败!"..strRetInfo 
    end 
    local inv_txn_log_data
    -- 创建库存交易日志
    local inv_detail_data = m3.KeyValueAttrsToObjAttr(data_objects[1].attrs)
 
    -- 创建 INV_Log
    inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
    inv_txn_log_data.S_WH_CODE = inv_detail_data.S_WH_CODE
    inv_txn_log_data.S_AREA_CODE = inv_detail_data.S_WH_CODE
    inv_txn_log_data.S_LOC_CODE = inv_detail_data.S_WH_CODE
    inv_txn_log_data.S_CNTR_CODE = cntr_code
    inv_txn_log_data.S_LOG_TYPE = "ADJ-IN"
    inv_txn_log_data.C_SYMBOL = "+"
    inv_txn_log_data.S_NOTE = note    
    for m = 1, base_attr_count do
        inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
    end
    nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
    if (nRet ~= 0 ) then 
        return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
    end 
 
    return 0, ""
end
 
--[[
    把预分配容器明细加入 INV_Detail  适用料箱带料格的预分配料箱入库
    pac_no -- 预分配容器流水号
    loc_code -- 当前容器位置
]]
function wms_inv.Add_INV_Detail_By_PAC_Detail( strLuaDEID, cntr_code, pac_no, loc_code )
    local nRet, strRetInfo, n
 
    if ( pac_no == nil or pac_no == '' ) then
        return 1, "wms_inv.Add_INV_Detail_By_PAC_Detail 函数中 pac_no 必须有值!"
    end
    if ( cntr_code == nil or cntr_code == '' ) then
        return 1, "wms_inv.Add_INV_Detail_By_PAC_Detail 函数中 cntr_code 必须有值!"
    end    
    local loc
    nRet, loc = wms_wh.GetLocInfo( loc_code )
    if ( nRet ~= 0 ) then 
        return 1, "获取货位'"..loc_code.."'信息失败! "..loc
    end
    local forced_fill_cell = {}         -- 强制置满的料格
    local curTime = os.date("%Y-%m-%d %H:%M:%S")
      
    -- 查询【Pre_Alloc_CNTR_Detail】
    local strOrder = ""
    local strCondition = "S_PAC_NO = '"..pac_no.."'"
    local data_objects
    nRet, data_objects = m3.QueryDataObject(strLuaDEID, "Pre_Alloc_CNTR_Detail", strCondition, strOrder )
    if ( nRet ~= 0 ) then return 2, "QueryDataObject失败!"..data_objects end
    if ( data_objects == '') then return 0 end
 
    local n, m, attr_count, base_attr_count
    local inv_detail_data, inv_detail
    local inv_txn_log_data
    local item_detial_data
    local check_is_ok
 
    attr_count = #INB_PALLET_DETAIL_ATTRS
    base_attr_count = #INV_DETAIL_BASE_ATTRS
 
    -- 缺省值设置
    local default_storer = ''                    -- 默认货主
    nRet, default_storer = wms_base.Get_sConst2( "WMS_Default_Storer" )
    if ( nRet ~= 0 ) then
        default_storer = ''
    end
    local default_item_state = ''                    -- 默认货品状态
    nRet, default_item_state = wms_base.Get_sConst2( "WMS_Default_ItemState" )
    if ( nRet ~= 0 ) then
        default_item_state = ''
    end
 
    for n = 1, #data_objects do
        item_detial_data = m3.KeyValueAttrsToObjAttr(data_objects[n].attrs)
        if ( item_detial_data.C_FORCED_FILL == 'Y' ) then
            table.insert( forced_fill_cell, item_detial_data.S_CELL_NO )
        end   
        check_is_ok, strRetInfo = item_detail_data_check( item_detial_data, default_storer, default_item_state )
        if  not check_is_ok then
            return 1, strRetInfo 
        end
 
        if lua.Get_NumAttrValue( item_detial_data.F_ACT_QTY ) > 0 then
            -- 创建库存量表
            inv_detail_data = m3.AllocObject2( strLuaDEID, "INV_Detail" )
            inv_detail_data.S_WH_CODE = loc.wh_code
            inv_detail_data.S_AREA_CODE = loc.area_code
            inv_detail_data.S_LOC_CODE = loc.code
            inv_detail_data.S_CNTR_CODE = cntr_code
            inv_detail_data.T_INBOUND_TIME = curTime
            for m = 1, attr_count do
                inv_detail_data[INB_PALLET_DETAIL_ATTRS[m]] = item_detial_data[INB_PALLET_DETAIL_ATTRS[m]]
            end
            inv_detail_data.F_QTY = item_detial_data.F_ACT_QTY
 
            nRet, inv_detail_data = m3.CreateDataObj2(strLuaDEID, inv_detail_data )
            if (nRet ~= 0 ) then 
                return 1, "创建【库存量表】失败!"..inv_detail_data 
            end    
            
            -- 创建库存交易日志
            inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
            inv_txn_log_data.S_WH_CODE = loc.wh_code
            inv_txn_log_data.S_AREA_CODE = loc.area_code
            inv_txn_log_data.S_LOC_CODE = loc.code
            inv_txn_log_data.S_CNTR_CODE = cntr_code
            inv_txn_log_data.S_LOG_TYPE = "IN"
            inv_txn_log_data.C_SYMBOL = "+"
            for m = 1, base_attr_count do
                inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = item_detial_data[INV_DETAIL_BASE_ATTRS[m]]
            end
            nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
            if (nRet ~= 0 ) then 
                return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
            end 
        end
    end
 
    local nCount = #forced_fill_cell
    if ( nCount == 0 ) then return 0 end
 
    -- 设置料格强制置满属性
    local str_cell_no = lua.strArray2string( forced_fill_cell )
    if  ( str_cell_no == '' ) then return 0 end
    local strCondition, srtSetAttr
    strCondition = " S_CELL_NO IN ("..str_cell_no..") AND S_CNTR_CODE = '"..cntr_code.."'"
    srtSetAttr = "C_FORCED_FILL = 'Y', N_EMPTY_FULL = 2"
    nRet, strRetInfo = mobox.updateDataAttrByCondition( strLuaDEID, "Container_Cell", strCondition, srtSetAttr )
    if ( nRet ~= 0 ) then
        lua.Warning( strLuaDEID, debug.getinfo(1), "更新【容器箱格】信息失败! condition = "..strCondition )  
        return 2, "更新【容器箱格】信息失败!"..strRetInfo 
    end 
 
    return 0   
end
 
--[[
        库存量加预分配量
        输入参数:
            inv_detail_data  --- 需要进行分配量增加的 INV_Detial
            qty -- 分配量
            bs_type -- 增加分配量的业务类型 bs_no 为业务单号
--]]
function wms_inv.INV_Detail_Add_AllocQty( strLuaDEID, inv_detail_data, qty, bs_type, bs_no )
    local nRet, strRetInfo
    local strCondition
    local inv_detail_id = inv_detail_data.S_ID or ''
 
    if inv_detail_id == '' then
        return 1, "wms_inv.INV_Detail_Add_AllocQty 函数失败! inv_detail_data 中S_ID为空!"
    end
    if qty <= 0 or qty == nil then
        return 1, "wms_inv.INV_Detail_Add_AllocQty 函数失败! qty 必须大于 0!"
    end
 
    strCondition = "S_ID = '"..inv_detail_data.S_ID.."'"
    local strSetAttr = "F_ALLOC_QTY = F_ALLOC_QTY+"..qty
    nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "INV_Detail", strCondition, strSetAttr )
    if (nRet ~= 0) then 
        return 2, "设置【INV_Detail】数量分配量信息失败!"..strRetInfo
    end
 
    -- 创建库存锁定日志
    local m
    local inv_lock_log_data = m3.AllocObject2( strLuaDEID, "INV_Lock_Log" )
 
    for m = 1, #INV_LOG_BASE_ATTRS do
        inv_lock_log_data[INV_LOG_BASE_ATTRS[m]] = inv_detail_data[INV_LOG_BASE_ATTRS[m]]
    end
    inv_lock_log_data.S_LOG_TYPE = "allocate"          -- 订单分配库存量
    inv_lock_log_data.F_QTY = qty
    inv_lock_log_data.S_BS_TYPE = bs_type
    inv_lock_log_data.S_BS_NO = bs_no
    inv_lock_log_data.G_INV_DETAIL_ID = inv_detail_data.S_ID
 
    nRet, inv_lock_log_data = m3.CreateDataObj2(strLuaDEID, inv_lock_log_data )
    if (nRet ~= 0 ) then 
        return 1, "创建【库存锁定日志】失败!"..inv_lock_log_data 
    end   
    return 0  
end
 
local function create_inv_detail_allocate_out_log( strLuaDEID, inv_detail_data, qty, alloc_qty, bs_type, bs_no )
    local nRet, m
 
    -- 创建库存交易日志
     local inv_txn_log_data
    inv_txn_log_data = m3.AllocObject2( strLuaDEID, "INV_TXN_Log" )
    inv_txn_log_data.S_WH_CODE = inv_detail_data.S_WH_CODE
    inv_txn_log_data.S_AREA_CODE = inv_detail_data.S_AREA_CODE
    inv_txn_log_data.S_LOC_CODE = inv_detail_data.S_LOC_CODE
    inv_txn_log_data.S_LOG_TYPE = "OUT"
    inv_txn_log_data.C_SYMBOL = "-"
 
    for m = 1, #INV_DETAIL_BASE_ATTRS do
        inv_txn_log_data[INV_DETAIL_BASE_ATTRS[m]] = inv_detail_data[INV_DETAIL_BASE_ATTRS[m]]
    end
    inv_txn_log_data.F_QTY = qty
    nRet, inv_txn_log_data = m3.CreateDataObj2(strLuaDEID, inv_txn_log_data )
    if (nRet ~= 0 ) then 
        return 1, "创建【库存交易日志】失败!"..inv_txn_log_data 
    end 
 
    -- 创建库存锁定日志
    local inv_lock_log_data = m3.AllocObject2( strLuaDEID, "INV_Lock_Log" )
 
    for m = 1, #INV_LOG_BASE_ATTRS do
        inv_lock_log_data[INV_LOG_BASE_ATTRS[m]] = inv_detail_data[INV_LOG_BASE_ATTRS[m]]
    end
    inv_lock_log_data.S_LOG_TYPE = "allocate_release"          -- 订单分配库存量释放
    inv_lock_log_data.F_QTY = alloc_qty
    inv_lock_log_data.S_BS_TYPE = bs_type
    inv_lock_log_data.S_BS_NO = bs_no
    inv_lock_log_data.G_INV_DETAIL_ID = inv_detail_data.id
    inv_lock_log_data.F_QTY = alloc_qty
    nRet, inv_lock_log_data = m3.CreateDataObj2(strLuaDEID, inv_lock_log_data )
    if (nRet ~= 0 ) then 
        return 1, "创建【库存锁定日志】失败!"..inv_lock_log_data 
    end  
    return 0    
end
 
--[[
    INV_Detail 中分配的数量出库
    -- qty 是实际出库数量 alloc_qty 是分配量
--]]
function wms_inv.INV_Detail_Out( strLuaDEID, inv_detail_id, qty, alloc_qty, bs_type, bs_no )
    local nRet, strRetInfo, strCondition
 
    if ( inv_detail_id == '' or inv_detail_id == nil ) then
        return 2, "wms_inv.Allocate_Qty_Out 函数中 参数 inv_detail_id 不能为空!"
    end
    local inv_detail_data
    nRet, inv_detail_data = m3.GetDataObject2( strLuaDEID, "INV_Detail", inv_detail_id ) 
    if ( nRet ~= 0 ) then 
        return 2, inv_detail_data 
    end 
 
    strCondition = "S_ID = '"..inv_detail_id.."'"
    local strSetAttr = "F_QTY = F_QTY-"..qty..", F_ALLOC_QTY = F_ALLOC_QTY-"..alloc_qty
    nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "INV_Detail", strCondition, strSetAttr )
    if (nRet ~= 0) then return 2, "设置【INV_Detail】数量分配量信息失败!"..strRetInfo end
 
    nRet, strRetInfo = create_inv_detail_allocate_out_log( strLuaDEID, inv_detail_data, qty, alloc_qty, bs_type, bs_no )
    if nRet ~= 0 then
        return 2, strRetInfo
    end
    return 0
end
 
-- 根据输入的 strCondition 扣减 INV_Detail 中某数量和分配量, 会有多条 INV_Detail 需要批分
-- qty 是真实际出库数量 alloc_qty 是分配量
function wms_inv.INV_Detail_SplitOut( strLuaDEID, strCondition, strOrder, qty, alloc_qty, bs_type, bs_no )
    local nRet, strRetInfo
 
    if ( strCondition == '' or strCondition == nil ) then
        return 2, "函数 wms_inv.INV_Detail_SplitOut 里strCondition 不能为空!"
    end
    if ( strOrder == nil ) then strOrder = '' end
 
    local inv_detail_objs
    nRet, inv_detail_objs = m3.QueryDataObject(strLuaDEID, "INV_Detail", strCondition, strOrder )
    if (nRet ~= 0) then 
        return 2, "查询CG_Detail失败! "..inv_detail_objs 
    end
    if ( inv_detail_objs == '' ) then return 0 end
 
    local n, inv_detail_data
    local strSetAttr, inv_detail_qty, inv_detail_alloc_qty
    local strSetQtyAttr, strSetAllocQtyAttr
    local Q, AQ
 
    for n = 1, #inv_detail_objs do
        inv_detail_data = m3.KeyValueAttrsToObjAttr(inv_detail_objs[n].attrs)  
        inv_detail_data.id = lua.trim_guid_str( inv_detail_objs[n].id )
        inv_detail_qty = lua.Get_NumAttrValue( inv_detail_data.F_QTY )
        inv_detail_alloc_qty = lua.Get_NumAttrValue( inv_detail_data.F_ALLOC_QTY )
 
        strSetAttr = ''
        Q = 0
        AQ = 0
        if ( qty > 0 ) then
            if ( inv_detail_qty < qty ) then
                strSetQtyAttr = "F_QTY = F_QTY-"..inv_detail_qty
                qty = qty - inv_detail_qty
                Q = inv_detail_qty
            else
                strSetQtyAttr = "F_QTY = F_QTY-"..qty
                Q = qty
                qty = 0
            end   
            strSetAttr = strSetQtyAttr   
        end     
 
        if ( alloc_qty > 0 ) then
            if ( inv_detail_alloc_qty < alloc_qty ) then
                strSetAllocQtyAttr = "F_ALLOC_QTY = F_ALLOC_QTY-"..inv_detail_alloc_qty
                AQ = inv_detail_alloc_qty
                alloc_qty = alloc_qty - inv_detail_alloc_qty
            else
                strSetAllocQtyAttr = "F_ALLOC_QTY = F_ALLOC_QTY-"..alloc_qty
                AQ = alloc_qty
                alloc_qty = 0
            end  
            if ( strSetAttr ~= '' ) then strSetAttr = strSetAttr..","  end 
            strSetAttr = strSetAttr..strSetAllocQtyAttr        
        end
        
        if ( strSetAttr == '' ) then break end
 
        strCondition = "S_ID = '"..inv_detail_data.id.."'"
        nRet, strRetInfo = mobox.updateDataAttrByCondition(strLuaDEID, "INV_Detail", strCondition, strSetAttr )
        if (nRet ~= 0) then 
            return 2, "设置【INV_Detail】数量分配量信息失败!"..strRetInfo 
        end
        -- 创建日志
        nRet, strRetInfo = create_inv_detail_allocate_out_log( strLuaDEID, inv_detail_data, Q, AQ, bs_type, bs_no )
        if nRet ~= 0 then
            return 2, strRetInfo
        end
    end
    return 0
end
 
return wms_inv