1
Jianw
9 天以前 70f29da38121b9a467841253e3268feb5df02902
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
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
--[[
    版本:     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