Pārlūkot izejas kodu

库存转移方法整合。减少重复代码。

zhangyao 4 mēneši atpakaļ
vecāks
revīzija
a4a5299c81

+ 9 - 213
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/event/costPickFormAfterSave.java

@@ -7,6 +7,7 @@ import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
 import com.actionsoft.bpms.util.DBSql;
 import com.actionsoft.exception.BPMNError;
 import com.actionsoft.sdk.local.SDK;
+import com.awspaas.user.apps.donenow_ctt.service.wareHouseProductService;
 import org.apache.commons.lang3.StringUtils;
 
 import java.sql.Connection;
@@ -155,224 +156,19 @@ public class costPickFormAfterSave extends ExecuteListener {
 
             if (pickBO.getString("PICK_TYPE").equals("1"))//库存转移给我
             {
-                ProcessInstance transfeProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", processExecutionContext.getUserContext().getUID(), "库存转移");
-                BO ivtTransfer = new BO();
-                ivtTransfer.setBindId(transfeProcessIns.getId());
-                ivtTransfer.set("FROM_WAREHOUSE_ID", pickBO.getString("WAREHOUSE_ID"));
-                ivtTransfer.set("TO_WAREHOUSE_ID", WAREHOUSE_ID);
-                ivtTransfer.set("PRODUCT_ID", pickBO.getString("PRODUCT_ID"));
-                ivtTransfer.set("QUANTITY", pickBO.get("PICK_QUANTITY"));
-                ivtTransfer.set("TYPE_ID", 1481);  // 仓库间转移
-                if (pickSerials.size() > 0)
-                    ivtTransfer.set("SN", StringUtils.join(pickSerials, ","));//产品序列号
-                SDK.getBOAPI().create("BO_EU_DNIVT_TRANSFER", ivtTransfer, transfeProcessIns, processExecutionContext.getUserContext(), conn);
-
-                //库存增加
-                BO toWarehouseProductBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", pickBO.getString("PRODUCT_ID")).addQuery("WAREHOUSE_ID=", WAREHOUSE_ID).detail();
-                if (toWarehouseProductBO != null) {
-                    toWarehouseProductBO.set("QUANTITY", ObjToInt(toWarehouseProductBO.get("QUANTITY")) + ObjToInt(pickBO.get("PICK_QUANTITY")));
-                    SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toWarehouseProductBO, conn);
-                } else {
-                    ProcessInstance twpProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", processExecutionContext.getUserContext().getUID(), "库存转移");
-                    toWarehouseProductBO = new BO();
-                    toWarehouseProductBO.setBindId(twpProcessIns.getId());
-                    toWarehouseProductBO.set("WAREHOUSE_ID", WAREHOUSE_ID);
-                    toWarehouseProductBO.set("PRODUCT_ID", pickBO.getString("PRODUCT_ID"));
-                    toWarehouseProductBO.set("QUANTITY", pickBO.get("PICK_QUANTITY"));
-                    SDK.getBOAPI().create("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toWarehouseProductBO, twpProcessIns, processExecutionContext.getUserContext(), conn);
-                }
-
-                //库存减少
-                BO fromWarehouseProductBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", pickBO.getString("PRODUCT_ID")).addQuery("WAREHOUSE_ID=", pickBO.getString("WAREHOUSE_ID")).detail();
-                fromWarehouseProductBO.set("QUANTITY", ObjToInt(fromWarehouseProductBO.get("QUANTITY")) - ObjToInt(pickBO.get("PICK_QUANTITY")));
-
-                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromWarehouseProductBO, conn);
-
-                int pickQuantity = ObjToInt(pickBO.get("PICK_QUANTITY"));//拣货数量
-
-
-                //序列号减少  修改库存批次
-                List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("BINDID=", fromWarehouseProductBO.getBindId()).addQuery("QUANTITY>", 0).orderByCreated().list();
-                int cnt = 0;
-                for (BO lot : lots) {
-                    if (pickSerials.size() > 0) {
-                        //序列化产品
-                        boolean isIn = false;
-                        String lotSN = lot.getString("SN");
-                        lotSN = "," + lotSN + ",";
-                        List<String> toSN = new ArrayList<>();
-                        for (String sn : pickSerials) {
-                            if (lotSN.contains("," + sn + ",")) {
-                                isIn = true;
-                                lotSN = lotSN.replace("," + sn + ",", ",");
-                                toSN.add(sn);
-                                cnt++;
-                            }
-                            if (cnt >= pickSerials.size())
-                                break;
-                        }
-                        if (isIn) {
-                            if (StringUtils.isNotBlank(lotSN)) {
-                                lotSN = lotSN.replaceAll("^,+|,+$", "") // 移除首尾一个或多个逗号
-                                        .replaceAll(",+", ",");     // 合并中间连续逗号
-                                int count = (int) lotSN.chars().filter(c -> c == ',').count();
-                                lot.set("SN", lotSN);
-                                lot.set("QUANTITY", count + 1);
-                            } else {
-                                lot.set("SN", "");
-                                lot.set("QUANTITY", 0);
-                            }
-
-                            //更新批次上序列号
-                            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-
-                            //目标 库存批次 增加
-                            BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("LOT=", lot.get("LOT")).addQuery("ORDER_PRODUCT_ID=", lot.get("ORDER_PRODUCT_ID")).addQuery("BINDID=", toWarehouseProductBO.getBindId()).detail();
-                            if (toLot != null) {
-                                toLot.set("QUANTITY", Integer.parseInt(toLot.get("QUANTITY").toString()) + toSN.size());
-                                toLot.set("SN", toLot.get("SN") + "," + StringUtils.join(toSN, ","));
-                                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
-                            } else {
-                                //新增 目标库批次
-                                toLot = new BO();
-                                toLot.setAll(lot.asMap());
-                                toLot.setId(null);
-                                toLot.setBindId(toWarehouseProductBO.getBindId());
-                                toLot.set("SN", StringUtils.join(toSN, ","));
-                                toLot.set("QUANTITY", toSN.size());
-                                toLot.set("ORDER_PRODUCT_ID", lot.get("ORDER_PRODUCT_ID"));
-                                toLot.set("WAREHOUSE_ID", toWarehouseProductBO.get("WAREHOUSE_ID"));
-                                toLot.set("PRODUCT_ID", toWarehouseProductBO.get("PRODUCT_ID"));
-                                SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, processExecutionContext.getUserContext(), conn);
-                            }
-                        }
-                        if (cnt >= pickSerials.size())
-                            break;
-                    } else {
-                        //无序列化产品
-                        int lotQuantity = Integer.parseInt(lot.get("QUANTITY").toString());
-                        int toQuantity = 0;
-                        if (lotQuantity > pickQuantity) {
-                            lot.set("QUANTITY", lotQuantity - pickQuantity);
-                            toQuantity = pickQuantity;
-                            pickQuantity = 0;
-
-                        } else {
-                            lot.set("QUANTITY", 0);
-                            toQuantity = lotQuantity;
-                            pickQuantity -= lotQuantity;
-                        }
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-
-                        //目标 库存批次 增加
-                        BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("LOT=", lot.get("LOT")).addQuery("ORDER_PRODUCT_ID=", lot.get("ORDER_PRODUCT_ID")).addQuery("BINDID=", toWarehouseProductBO.getBindId()).detail();
-                        if (toLot != null) {
-                            toLot.set("QUANTITY", Integer.parseInt(toLot.get("QUANTITY").toString()) + toQuantity);
-                            toLot.set("SN", "");
-                            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
-                        } else {
-                            //新增 目标库批次
-                            toLot = new BO();
-                            toLot.setAll(lot.asMap());
-                            toLot.setId(null);
-                            toLot.setBindId(toWarehouseProductBO.getBindId());
-                            toLot.set("SN", "");
-                            toLot.set("QUANTITY", toQuantity);
-                            toLot.set("ORDER_PRODUCT_ID", lot.get("ORDER_PRODUCT_ID"));
-                            toLot.set("WAREHOUSE_ID", toWarehouseProductBO.get("WAREHOUSE_ID"));
-                            toLot.set("PRODUCT_ID", toWarehouseProductBO.get("PRODUCT_ID"));
-                            SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, processExecutionContext.getUserContext(), conn);
-                        }
-                        if (pickQuantity <= 0)
-                            break;
-
-                    }
-                }
+                wareHouseProductService.getInstance().transfer(processExecutionContext.getUserContext(), conn, pickBO.getString("PRODUCT_ID"), pickBO.getString("WAREHOUSE_ID"), WAREHOUSE_ID, ObjToInt(pickBO.get("PICK_QUANTITY")), pick_sns, 1481, null); // 仓库间转移
 
             } else if (pickBO.getString("PICK_TYPE").equals("2"))//库存转移给客户
             {
-
                 BO costBO = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_COST").connection(conn).addQuery("ID=", pickBO.getString("CONTRACT_COST_ID")).detail();
 
-                //移库记录
-                ProcessInstance transfeProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", processExecutionContext.getUserContext().getUID(), "库存转移");
-                BO ivtTransfer = new BO();
-                ivtTransfer.setBindId(transfeProcessIns.getId());
-                ivtTransfer.set("FROM_WAREHOUSE_ID", pickBO.getString("WAREHOUSE_ID"));
-
-                ivtTransfer.set("TO_ACCOUNT_ID", costBO.get("ACCOUNT_ID"));
-                ivtTransfer.set("TO_CONTRACT_ID", costBO.get("CONTRACT_ID"));
-
-                ivtTransfer.set("TO_PROJECT_ID", costBO.get("PROJECT_ID"));
-                ivtTransfer.set("TO_TASK_ID", costBO.get("TASK_ID"));
-
-                ivtTransfer.set("TYPE_ID", 1482);
-                ivtTransfer.set("PRODUCT_ID", pickBO.getString("PRODUCT_ID"));
-                ivtTransfer.set("QUANTITY", pickBO.get("PICK_QUANTITY"));
-                ivtTransfer.set("SN", StringUtils.join(pickSerials, ","));//产品序列号
-                SDK.getBOAPI().create("BO_EU_DNIVT_TRANSFER", ivtTransfer, transfeProcessIns, processExecutionContext.getUserContext(), conn);
-
-
-                //减少库存
-                BO fromWarehouseProductBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", pickBO.getString("PRODUCT_ID")).addQuery("WAREHOUSE_ID=", pickBO.getString("WAREHOUSE_ID")).detail();
-                fromWarehouseProductBO.set("QUANTITY", ObjToInt(fromWarehouseProductBO.get("QUANTITY")) - ObjToInt(pickBO.get("PICK_QUANTITY")));
-                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromWarehouseProductBO, conn);
-
-                int pickQuantity = ObjToInt(pickBO.get("PICK_QUANTITY"));//拣货数量
-                //减少批次
-                List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("BINDID=", fromWarehouseProductBO.getBindId()).addQuery("QUANTITY>", 0).orderByCreated().list();
-                int cnt = 0;
-                for (BO lot : lots) {
-                    if (pickSerials.size() > 0) {
-                        //序列化产品
-                        boolean isIn = false;
-                        String lotSN = lot.getString("SN");
-                        lotSN = "," + lotSN + ",";
-                        List<String> toSN = new ArrayList<>();
-                        for (String sn : pickSerials) {
-                            if (lotSN.contains("," + sn + ",")) {
-                                isIn = true;
-                                lotSN = lotSN.replace("," + sn + ",", ",");
-                                toSN.add(sn);
-                                cnt++;
-                            }
-                            if (cnt >= pickSerials.size())
-                                break;
-                        }
-                        if (isIn) {
-                            if (StringUtils.isNotBlank(lotSN)) {
-                                lotSN = lotSN.replaceAll("^,+|,+$", "") // 移除首尾一个或多个逗号
-                                        .replaceAll(",+", ",");     // 合并中间连续逗号
-                                int count = (int) lotSN.chars().filter(c -> c == ',').count();
-                                lot.set("SN", lotSN);
-                                lot.set("QUANTITY", count + 1);
-                            } else {
-                                lot.set("SN", "");
-                                lot.set("QUANTITY", 0);
-                            }
-
-                            //更新批次上序列号
-                            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-
-                        }
-                        if (cnt >= pickSerials.size())
-                            break;
-                    } else {
-                        //无序列号产品
-                        int toQuantity = ObjToInt(lot.get("QUANTITY"));
-                        if (toQuantity >= pickQuantity) {
-                            lot.set("QUANTITY", pickQuantity);
-                            pickQuantity = 0;
-                        } else {
-                            lot.set("QUANTITY", 0);
-                            pickQuantity -= toQuantity;
-                        }
-                        //更新批次上序列号
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-                        if (pickQuantity <= 0)
-                            break;
-                    }
-                }
+                Map<String, Object> ivtTransfer = new HashMap<>();
+                ivtTransfer.put("TO_ACCOUNT_ID", costBO.get("ACCOUNT_ID"));
+                ivtTransfer.put("TO_CONTRACT_ID", costBO.get("CONTRACT_ID"));
+                ivtTransfer.put("TO_PROJECT_ID", costBO.get("PROJECT_ID"));
+                ivtTransfer.put("TO_TASK_ID", costBO.get("TASK_ID"));
+
+                wareHouseProductService.getInstance().transfer(processExecutionContext.getUserContext(), conn, pickBO.getString("PRODUCT_ID"), pickBO.getString("WAREHOUSE_ID"), null, ObjToInt(pickBO.get("PICK_QUANTITY")), pick_sns, 1482, ivtTransfer); // 转移给客户
             }
 
             conn.commit();

+ 3 - 134
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/event/costTransferProFromAfterSave.java

@@ -6,11 +6,10 @@ import com.actionsoft.bpms.bpmn.engine.listener.ExecuteListener;
 import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
 import com.actionsoft.bpms.util.DBSql;
 import com.actionsoft.sdk.local.SDK;
+import com.awspaas.user.apps.donenow_ctt.service.wareHouseProductService;
 import org.apache.commons.lang3.StringUtils;
 
 import java.sql.Connection;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * 库存转移修改相关库存
@@ -67,138 +66,8 @@ public class costTransferProFromAfterSave extends ExecuteListener {
             }
             String SERIAL_NUMBERS = transferProBO.getString("SERIAL_NUMBERS");
 
-            ProcessInstance transfeProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", processExecutionContext.getUserContext().getUID(), "库存转移");
-            BO ivtTransfer = new BO();
-            ivtTransfer.setBindId(transfeProcessIns.getId());
-            ivtTransfer.set("FROM_WAREHOUSE_ID", FROM_WAREHOUSE_ID);
-            ivtTransfer.set("TO_WAREHOUSE_ID", TO_WAREHOUSE_ID);
-            ivtTransfer.set("PRODUCT_ID", transferProBO.getString("PRODUCT_ID"));
-            ivtTransfer.set("QUANTITY", transferQUANTITY);
-            ivtTransfer.set("TYPE_ID", 1481);  // 仓库间转移
-            ivtTransfer.set("SN", transferProBO.getString("SERIAL_NUMBER"));//序列号
-            ivtTransfer.set("LOT", null);//批次号
-            SDK.getBOAPI().create("BO_EU_DNIVT_TRANSFER", ivtTransfer, transfeProcessIns, processExecutionContext.getUserContext(), conn);
-
-            BO toWarehouseProductBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", transferProBO.getString("PRODUCT_ID")).addQuery("WAREHOUSE_ID=", TO_WAREHOUSE_ID).detail();
-            if (toWarehouseProductBO != null) {
-                toWarehouseProductBO.set("QUANTITY", ObjToInt(toWarehouseProductBO.get("QUANTITY")) + ObjToInt(transferQUANTITY));
-                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toWarehouseProductBO, conn);
-            } else {
-                ProcessInstance twpProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", processExecutionContext.getUserContext().getUID(), "库存转移");
-                toWarehouseProductBO = new BO();
-                toWarehouseProductBO.setBindId(twpProcessIns.getId());
-                toWarehouseProductBO.set("WAREHOUSE_ID", TO_WAREHOUSE_ID);
-                toWarehouseProductBO.set("PRODUCT_ID", transferProBO.getString("PRODUCT_ID"));
-                toWarehouseProductBO.set("QUANTITY", transferQUANTITY);
-                SDK.getBOAPI().create("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toWarehouseProductBO, twpProcessIns, processExecutionContext.getUserContext(), conn);
-            }
-
             //库存转移
-            BO fromWarehouseProductBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", transferProBO.getString("PRODUCT_ID")).addQuery("WAREHOUSE_ID=", FROM_WAREHOUSE_ID).detail();
-            fromWarehouseProductBO.set("QUANTITY", ObjToInt(fromWarehouseProductBO.get("QUANTITY")) - ObjToInt(transferQUANTITY));
-            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromWarehouseProductBO, conn);
-
-
-            String[] transferSerials = SERIAL_NUMBERS.split(",");
-            int transferQuantity = transferQUANTITY;
-            //序列号减少  修改库存批次
-            List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("BINDID=", fromWarehouseProductBO.getBindId()).addQuery("QUANTITY>", 0).orderByCreated().list();
-            int cnt = 0;
-            for (BO lot : lots) {
-                if (transferSerials.length > 0) {
-                    //序列化产品
-                    boolean isIn = false;
-                    String lotSN = lot.getString("SN");
-                    lotSN = "," + lotSN + ",";
-                    List<String> toSN = new ArrayList<>();
-                    for (String sn : transferSerials) {
-                        if (lotSN.contains("," + sn + ",")) {
-                            isIn = true;
-                            lotSN = lotSN.replace("," + sn + ",", ",");
-                            toSN.add(sn);
-                            cnt++;
-                        }
-                        if (cnt >= transferSerials.length)
-                            break;
-                    }
-                    if (isIn) {
-                        if (StringUtils.isNotBlank(lotSN)) {
-                            lotSN = lotSN.replaceAll("^,+|,+$", "") // 移除首尾一个或多个逗号
-                                    .replaceAll(",+", ",");     // 合并中间连续逗号
-                            int count = (int) lotSN.chars().filter(c -> c == ',').count();
-                            lot.set("SN", lotSN);
-                            lot.set("QUANTITY", count + 1);
-                        } else {
-                            lot.set("SN", "");
-                            lot.set("QUANTITY", 0);
-                        }
-
-                        //更新批次上序列号
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-
-                        //目标 库存批次 增加
-                        BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("LOT=", lot.get("LOT")).addQuery("ORDER_PRODUCT_ID=", lot.get("ORDER_PRODUCT_ID")).addQuery("BINDID=", toWarehouseProductBO.getBindId()).detail();
-                        if (toLot != null) {
-                            toLot.set("QUANTITY", Integer.parseInt(toLot.get("QUANTITY").toString()) + toSN.size());
-                            toLot.set("SN", toLot.get("SN") + "," + StringUtils.join(toSN, ","));
-                            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
-                        } else {
-                            //新增 目标库批次
-                            toLot = new BO();
-                            toLot.setAll(lot.asMap());
-                            toLot.setId(null);
-                            toLot.setBindId(toWarehouseProductBO.getBindId());
-                            toLot.set("SN", StringUtils.join(toSN, ","));
-                            toLot.set("QUANTITY", toSN.size());
-                            toLot.set("ORDER_PRODUCT_ID", lot.get("ORDER_PRODUCT_ID"));
-                            toLot.set("WAREHOUSE_ID", toWarehouseProductBO.get("WAREHOUSE_ID"));
-                            toLot.set("PRODUCT_ID", toWarehouseProductBO.get("PRODUCT_ID"));
-                            SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, processExecutionContext.getUserContext(), conn);
-                        }
-                    }
-                    if (cnt >= transferSerials.length)
-                        break;
-                } else {
-                    //无序列化产品
-                    int lotQuantity = Integer.parseInt(lot.get("QUANTITY").toString());
-                    int toQuantity = 0;
-                    if (lotQuantity > transferQuantity) {
-                        lot.set("QUANTITY", lotQuantity - transferQuantity);
-                        toQuantity = transferQuantity;
-                        transferQuantity = 0;
-
-                    } else {
-                        lot.set("QUANTITY", 0);
-                        toQuantity = lotQuantity;
-                        transferQuantity -= lotQuantity;
-                    }
-                    SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-
-                    //目标 库存批次 增加
-                    BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("LOT=", lot.get("LOT")).addQuery("ORDER_PRODUCT_ID=", lot.get("ORDER_PRODUCT_ID")).addQuery("BINDID=", toWarehouseProductBO.getBindId()).detail();
-                    if (toLot != null) {
-                        toLot.set("QUANTITY", Integer.parseInt(toLot.get("QUANTITY").toString()) + toQuantity);
-                        toLot.set("SN", "");
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
-                    } else {
-                        //新增 目标库批次
-                        toLot = new BO();
-                        toLot.setAll(lot.asMap());
-                        toLot.setId(null);
-                        toLot.setBindId(toWarehouseProductBO.getBindId());
-                        toLot.set("SN", "");
-                        toLot.set("QUANTITY", toQuantity);
-                        toLot.set("ORDER_PRODUCT_ID", lot.get("ORDER_PRODUCT_ID"));
-                        toLot.set("WAREHOUSE_ID", toWarehouseProductBO.get("WAREHOUSE_ID"));
-                        toLot.set("PRODUCT_ID", toWarehouseProductBO.get("PRODUCT_ID"));
-                        SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, processExecutionContext.getUserContext(), conn);
-                    }
-                    if (transferQuantity <= 0)
-                        break;
-
-                }
-            }
-
+            wareHouseProductService.getInstance().transfer(processExecutionContext.getUserContext(), conn, transferProBO.getString("PRODUCT_ID"), FROM_WAREHOUSE_ID, TO_WAREHOUSE_ID, transferQUANTITY, SERIAL_NUMBERS, 1481, null); // 仓库间转移
 
             //全部转移
             if (transferQUANTITY == QUANTITY) {
@@ -219,7 +88,7 @@ public class costTransferProFromAfterSave extends ExecuteListener {
                 costProductBO.set("OLD_QUANTITY", costProductBO.get("QUANTITY"));//配送数量
                 SDK.getBOAPI().update("BO_EU_DNIVT_COST_PRODUCT", costProductBO, conn);
 
-
+                //移出的产品创建一个新的合同产品记录
                 BO toCostProductBO = new BO();
                 ProcessInstance processInstance = SDK.getProcessAPI().createBOProcessInstance("obj_6bc913271069455abbd516c556b26d31", processExecutionContext.getUserContext().getUID(), "合同产品物流管理");
                 toCostProductBO.setBindId(processInstance.getId());

+ 278 - 0
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/wareHouseProductService.java

@@ -0,0 +1,278 @@
+package com.awspaas.user.apps.donenow_ctt.service;
+
+import com.actionsoft.bpms.bo.engine.BO;
+import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
+import com.actionsoft.bpms.server.UserContext;
+import com.actionsoft.sdk.local.SDK;
+import com.actionsoft.sdk.local.api.Logger;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * 仓库产品服务类 - 处理产品库存转移等操作
+ */
+public class wareHouseProductService {
+    // 常量定义
+    private static final Logger serviceLogger = SDK.getLogAPI().getLogger(wareHouseProductService.class);
+    private static final String BO_WAREHOUSE_PRODUCT = "BO_EU_DNIVT_WAREHOUSE_PRODUCT";
+    private static final String BO_WAREHOUSE_PRODUCT_LOT = "BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT";
+
+    // 单例模式
+    private static volatile wareHouseProductService instance;
+
+    private wareHouseProductService() {
+    }
+
+    public static wareHouseProductService getInstance() {
+        if (instance == null) {
+            synchronized (wareHouseProductService.class) {
+                if (instance == null) {
+                    instance = new wareHouseProductService();
+                }
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * 核心转移方法 - 处理整个产品转移流程
+     */
+    public void transfer(UserContext uc, Connection conn, String productId, String fromWarehouseId,
+                         String toWarehouseId, int quantity, String serialNumbers, int type, Map<String, Object> params) throws SQLException {
+        // 参数校验
+        if (quantity <= 0) {
+            serviceLogger.warn("转移数量必须大于0,当前数量:" + quantity);
+            return;
+        }
+        if (fromWarehouseId.equals(toWarehouseId)) {
+            serviceLogger.warn("源仓库与目标仓库相同,无需转移:" + fromWarehouseId);
+            return;
+        }
+
+        try {
+            // 1. 更新目标仓库库存(增加)
+            BO targetWarehouseBO = null;
+            if (StringUtils.isNotBlank(toWarehouseId)) {
+                targetWarehouseBO = updateWarehouseStock(uc, conn, productId, toWarehouseId, quantity, true);
+            }
+            // 2. 更新源仓库库存(减少)
+            BO sourceWarehouseBO = updateWarehouseStock(uc, conn, productId, fromWarehouseId, quantity, false);
+
+            // 3. 处理批次和序列号转移
+            processLotAndSerialTransfer(uc, conn, sourceWarehouseBO, targetWarehouseBO, quantity, serialNumbers);
+
+            serviceLogger.info("产品转移成功,产品ID:" + productId + ",数量:" + quantity +
+                    ",从仓库:" + fromWarehouseId + "到仓库:" + toWarehouseId);
+
+
+            //移库记录
+            ProcessInstance transfeProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", uc.getUID(), "库存转移");
+            BO ivtTransfer = new BO();
+            ivtTransfer.setAll(params);
+            ivtTransfer.setBindId(transfeProcessIns.getId());
+            ivtTransfer.set("FROM_WAREHOUSE_ID", fromWarehouseId);
+            ivtTransfer.set("TO_WAREHOUSE_ID", toWarehouseId);
+            ivtTransfer.set("TYPE_ID", type);
+            ivtTransfer.set("PRODUCT_ID", productId);
+            ivtTransfer.set("QUANTITY", quantity);
+            ivtTransfer.set("SN", serialNumbers);//产品序列号
+            SDK.getBOAPI().create("BO_EU_DNIVT_TRANSFER", ivtTransfer, transfeProcessIns, uc, conn);
+
+        } catch (Exception e) {
+            serviceLogger.error("产品转移失败:" + e.getMessage(), e);
+            throw new SQLException("产品转移过程中发生错误", e);
+        }
+    }
+
+    /**
+     * 统一处理仓库库存更新(增加或减少)
+     * @param isTarget  true表示目标仓库(增加库存),false表示源仓库(减少库存)
+     */
+    private BO updateWarehouseStock(UserContext uc, Connection conn, String productId,
+                                    String warehouseId, int quantity, boolean isTarget) throws SQLException {
+        // 查询仓库中是否已有该产品记录
+        BO warehouseBO = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT)
+                .connection(conn)
+                .addQuery("PRODUCT_ID=", productId)
+                .addQuery("WAREHOUSE_ID=", warehouseId)
+                .detail();
+
+        if (warehouseBO != null) {
+            // 已有记录,更新库存
+            int currentQty = objToInt(warehouseBO.get("QUANTITY"));
+            int newQty = isTarget ? currentQty + quantity : currentQty - quantity;
+
+            // 源仓库库存不足校验
+            if (!isTarget && newQty < 0) {
+                throw new SQLException("仓库库存不足,产品ID:" + productId + ",当前库存:" + currentQty +
+                        ",需操作:" + quantity);
+            }
+
+            warehouseBO.set("QUANTITY", newQty);
+            SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT, warehouseBO, conn);
+            serviceLogger.debug((isTarget ? "更新目标仓库" : "更新源仓库") + "库存,产品ID:" + productId +
+                    ",仓库:" + warehouseId + ",原数量:" + currentQty +
+                    (isTarget ? ",新增:" : ",减少:") + quantity);
+        } else {
+            // 无记录,仅目标仓库需要创建新记录
+            if (isTarget) {
+                ProcessInstance processIns = SDK.getProcessAPI()
+                        .createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", uc.getUID(), "库存转移");
+                warehouseBO = new BO();
+                warehouseBO.setBindId(processIns.getId());
+                warehouseBO.set("WAREHOUSE_ID", warehouseId);
+                warehouseBO.set("PRODUCT_ID", productId);
+                warehouseBO.set("QUANTITY", quantity);
+                SDK.getBOAPI().create(BO_WAREHOUSE_PRODUCT, warehouseBO, processIns, uc, conn);
+                serviceLogger.debug("创建目标仓库库存记录,产品ID:" + productId + ",仓库:" + warehouseId +
+                        ",初始数量:" + quantity);
+            } else {
+                // 源仓库无记录则抛出异常
+                throw new SQLException("源仓库不存在该产品库存记录,产品ID:" + productId + ",仓库:" + warehouseId);
+            }
+        }
+        return warehouseBO;
+    }
+
+    /**
+     * 处理批次和序列号转移(合并原多个方法为一个)
+     */
+    private void processLotAndSerialTransfer(UserContext uc, Connection conn, BO sourceWarehouseBO,
+                                             BO targetWarehouseBO, int totalQuantity, String serialNumbers) throws SQLException {
+        String sourceBindId = sourceWarehouseBO.getBindId();
+        String targetBindId = targetWarehouseBO == null ? StringUtils.EMPTY : targetWarehouseBO.getBindId();
+        String productId = targetWarehouseBO.getString("PRODUCT_ID");
+        String targetWarehouseId = targetWarehouseBO.getString("WAREHOUSE_ID");
+
+        // 处理序列号
+        String[] snArray = StringUtils.isBlank(serialNumbers) ? new String[0] : serialNumbers.split(",");
+        Set<String> snSet = new HashSet<>(Arrays.asList(snArray));
+        List<BO> sourceLots = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT_LOT)
+                .addQuery("BINDID=", sourceBindId)
+                .addQuery("QUANTITY>", 0)
+                .orderByCreated()
+                .list();
+
+        // 区分序列化和非序列化产品处理
+        if (snArray.length > 0) {
+            // 处理序列化产品转移
+            int processedCount = 0;
+            int totalNeedProcess = snSet.size();
+
+            for (BO lot : sourceLots) {
+                if (processedCount >= totalNeedProcess) break;
+
+                String lotSN = lot.getString("SN");
+                if (StringUtils.isBlank(lotSN)) continue;
+
+                // 提取当前批次中需要转移的序列号
+                Set<String> currentLotSNs = new HashSet<>(Arrays.asList(lotSN.split(",")));
+                currentLotSNs.retainAll(snSet);//交集
+
+                if (!currentLotSNs.isEmpty()) {
+                    // 更新源批次
+                    Set<String> remainingSNs = new HashSet<>(Arrays.asList(lotSN.split(",")));
+                    remainingSNs.removeAll(currentLotSNs);
+                    lot.set("SN", StringUtils.join(remainingSNs, ","));
+                    lot.set("QUANTITY", remainingSNs.size());
+                    SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, lot, conn);
+
+                    // 更新或创建目标批次
+                    if (StringUtils.isNotBlank(targetBindId)) {
+                        updateOrCreateTargetLot(uc, conn, lot, targetBindId, productId, targetWarehouseId,
+                                currentLotSNs.size(), StringUtils.join(currentLotSNs, ","), true);
+                    }
+                    processedCount += currentLotSNs.size();
+                    snSet.removeAll(currentLotSNs);
+                }
+            }
+
+            if (!snSet.isEmpty()) {
+                serviceLogger.warn("部分序列号未找到对应批次,未处理SN:" + StringUtils.join(snSet, ","));
+            }
+        } else {
+            // 处理非序列化产品转移
+            int remainingQuantity = totalQuantity;
+
+            for (BO lot : sourceLots) {
+                if (remainingQuantity <= 0) break;
+
+                int lotQuantity = objToInt(lot.get("QUANTITY"));
+                if (lotQuantity <= 0) continue;
+
+                // 计算本次转移数量
+                int transferQty = Math.min(lotQuantity, remainingQuantity);
+
+                // 更新源批次
+                lot.set("QUANTITY", lotQuantity - transferQty);
+                SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, lot, conn);
+
+                // 更新或创建目标批次
+                if (StringUtils.isNotBlank(targetBindId))
+                    updateOrCreateTargetLot(uc, conn, lot, targetBindId, productId, targetWarehouseId,
+                            transferQty, "", false);
+
+                remainingQuantity -= transferQty;
+            }
+
+            if (remainingQuantity > 0) {
+                throw new SQLException("源批次库存不足,剩余未转移数量:" + remainingQuantity);
+            }
+        }
+    }
+
+    /**
+     * 工具方法:更新或创建目标批次(内部使用)
+     */
+    private void updateOrCreateTargetLot(UserContext uc, Connection conn, BO sourceLot, String targetBindId,
+                                         String productId, String targetWarehouseId,
+                                         int transferQty, String snStr, boolean isSerialized) throws SQLException {
+        BO targetLot = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT_LOT)
+                .addQuery("LOT=", sourceLot.get("LOT"))
+                .addQuery("ORDER_PRODUCT_ID=", sourceLot.get("ORDER_PRODUCT_ID"))
+                .addQuery("BINDID=", targetBindId)
+                .detail();
+
+        if (targetLot != null) {
+            int newQty = objToInt(targetLot.get("QUANTITY")) + transferQty;
+            targetLot.set("QUANTITY", newQty);
+            if (isSerialized) {
+                String currentSN = targetLot.getString("SN");
+                targetLot.set("SN", StringUtils.isBlank(currentSN) ? snStr : currentSN + "," + snStr);
+            }
+            SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, targetLot, conn);
+        } else {
+            BO newLot = new BO();
+            newLot.setAll(sourceLot.asMap());
+            newLot.setId(null);
+            newLot.setBindId(targetBindId);
+            newLot.set("WAREHOUSE_ID", targetWarehouseId);
+            newLot.set("PRODUCT_ID", productId);
+            newLot.set("QUANTITY", transferQty);
+            newLot.set("SN", snStr);
+            SDK.getBOAPI().createDataBO(BO_WAREHOUSE_PRODUCT_LOT, newLot, uc, conn);
+        }
+    }
+
+    /**
+     * 工具方法:安全的Object转int
+     */
+    private int objToInt(Object obj) {
+        if (obj == null) return 0;
+
+        try {
+            String objStr = obj.toString().trim();
+            if (objStr.isEmpty()) return 0;
+            if (objStr.contains(".")) {
+                objStr = objStr.substring(0, objStr.indexOf("."));
+            }
+            return Integer.parseInt(objStr);
+        } catch (NumberFormatException e) {
+            serviceLogger.error("对象转换为int失败,值:" + obj, e);
+            return 0;
+        }
+    }
+}

+ 378 - 136
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/event/ivtTransferFormSaveAfter.java

@@ -8,180 +8,422 @@ import com.actionsoft.bpms.commons.database.RowMap;
 import com.actionsoft.bpms.util.DBSql;
 import com.actionsoft.sdk.local.SDK;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
+/**
+ * 库存转移单保存后事件处理器
+ * 功能:处理转移单保存后的库存更新逻辑,包括出库、入库及批次管理
+ */
 public class ivtTransferFormSaveAfter extends ExecuteListener {
+    // 日志记录器
+    private static final Logger logger = LoggerFactory.getLogger(ivtTransferFormSaveAfter.class);
+    // 常量定义 - 流程定义ID(库存产品新增流程)
+    private static final String PROCESS_DEF_ID = "obj_41f06b1e807647db8a69dd10d4be5764";
+    // 常量定义 - 转移类型:仓库间转移
+    private static final String TRANSFER_TYPE_WAREHOUSE = "1481";
+    // 常量定义 - 状态值:已处理
+    private static final String STATUS_PROCESSED = "1";
+
+    @Override
     public String getDescription() {
-        return "表单保存后,数据处理事件";
+        return "表单保存后,处理库存转移相关的数据更新";
     }
 
     /**
-     *转移单保存后,更新库存
-     * @param processExecutionContext
-     * @throws Exception
+     * 执行库存转移单保存后的业务逻辑
+     * 主要流程:校验状态 -> 处理出库库存 -> 处理入库库存(仓库间转移时)-> 更新转移单状态
      */
     @Override
     public void execute(ProcessExecutionContext processExecutionContext) throws Exception {
+        // 获取流程实例ID
         String processId = processExecutionContext.getProcessInstance().getId();
+        // 查询转移单主数据
         RowMap rowMap = DBSql.getMap("select * from BO_EU_DNIVT_TRANSFER where BINDID =?", new Object[]{processId});
-        if (rowMap.getString("STATUS").equals("1"))
+
+        // 若状态已为"已处理",直接返回(避免重复处理)
+        if (STATUS_PROCESSED.equals(rowMap.getString("STATUS"))) {
+            logger.info("转移单[{}]已处理,无需重复执行", processId);
             return;
+        }
 
+        // 获取SN列表(序列化产品标识)
         String sns = rowMap.getString("SN");
         Connection conn = null;
+
         try {
+            // 获取数据库连接并关闭自动提交(开启事务)
             conn = DBSql.open();
             conn.setAutoCommit(false);
 
-            String PRODUCT_ID = rowMap.getString("PRODUCT_ID");//产品id
-            String FROM_WAREHOUSE_ID = rowMap.getString("FROM_WAREHOUSE_ID");//转出仓库id
-            String TYPE_ID = rowMap.getString("TYPE_ID");//转出类型
+            // 获取关键业务参数
+            String productId = rowMap.getString("PRODUCT_ID");
+            String fromWarehouseId = rowMap.getString("FROM_WAREHOUSE_ID");
+            String typeId = rowMap.getString("TYPE_ID");
+
+            // 校验关键参数非空
+            validateRequiredParams(productId, fromWarehouseId);
 
-            if (StringUtils.isNotBlank(TYPE_ID) && !TYPE_ID.equals("0")) {
-                BO fromBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", PRODUCT_ID).addQuery("WAREHOUSE_ID=", FROM_WAREHOUSE_ID).detail();
+            // 处理非默认类型的转移(TYPE_ID不为空且不等于"0")
+            if (StringUtils.isNotBlank(typeId) && !"0".equals(typeId)) {
+                // 处理出库库存及批次
+                List<Map<String, Object>> outboundLots = processOutboundInventory(conn, productId, fromWarehouseId, rowMap, sns);
 
-                int transferQuantity = rowMap.getInt("QUANTITY");//转出数量
-                int fromQuantity = Integer.parseInt(fromBO.get("QUANTITY").toString()) - transferQuantity;
-                if (fromQuantity < 0) {
-                    //报错
-                    throw new RuntimeException("转出数量不能大于库存数量");
+                // 若为仓库间转移,处理入库库存及批次
+                if (TRANSFER_TYPE_WAREHOUSE.equals(typeId)) {
+                    processInboundInventory(conn, processExecutionContext, productId, rowMap, outboundLots);
                 }
-                fromBO.set("QUANTITY", fromQuantity);
-                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromBO, conn);
-
-                List<Map<String, Object>> fromLosts = new ArrayList<>();
-                //更新出库的批次号
-
-                String[] asArr = sns.split(",");
-                int cnt = 0;
-                List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("BINDID=", fromBO.getBindId()).addQuery("QUANTITY>0", null).orderByCreated().list();
-                for (BO lot : lots) {
-                    if (StringUtils.isNotBlank(sns)) {
-                        //序列化产品
-                        boolean isIn = false;
-                        String lotSN = lot.getString("SN");
-                        lotSN = "," + lotSN + ",";
-                        List<String> toSN = new ArrayList<>();
-                        for (String sn : asArr) {
-                            if (lotSN.contains("," + sn + ",")) {
-                                isIn = true;
-                                lotSN = lotSN.replace("," + sn + ",", ",");
-                                toSN.add(sn);
-                                cnt++;
-                            }
-                            if (cnt >= asArr.length) break;
-                        }
-                        if (isIn) {
-                            if (StringUtils.isNotBlank(lotSN)) {
-                                lotSN = lotSN.replaceAll("^,+|,+$", "") // 移除首尾一个或多个逗号
-                                        .replaceAll(",+", ",");     // 合并中间连续逗号
-                                int count = (int) lotSN.chars().filter(c -> c == ',').count();
-                                lot.set("SN", lotSN);
-                                lot.set("QUANTITY", count + 1);
-                            } else {
-                                lot.set("SN", "");
-                                lot.set("QUANTITY", 0);
-                            }
-
-                            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-                            Map<String, Object> map = lot.asMap();
-                            map.remove("ID");
-                            map.remove("BINDID");
-                            map.put("SN", StringUtils.join(toSN, ","));
-                            map.put("QUANTITY", toSN.size());
-                            fromLosts.add(map);
-                        }
-                        if (cnt >= asArr.length)
-                            break;
-                    } else {
-                        //无序列化
-                        int count = Integer.parseInt(lot.get("QUANTITY").toString());
-                        int toQuantity = 0;
-                        if (count > transferQuantity) {
-                            lot.set("QUANTITY", count - transferQuantity);
-                            toQuantity = transferQuantity;
-                            transferQuantity = 0;
-                        } else {
-                            lot.set("QUANTITY", 0);
-                            toQuantity = count;
-                            transferQuantity = transferQuantity - count;
-                        }
-
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
-                        Map<String, Object> map = lot.asMap();
-                        map.remove("ID");
-                        map.remove("BINDID");
-                        map.put("SN", "");
-                        map.put("QUANTITY", toQuantity);
-                        fromLosts.add(map);
-                        if (transferQuantity <= 0)
-                            break;
-                    }
+            }
+
+            // 更新转移单状态为"已处理"
+            DBSql.update(conn, "update BO_EU_DNIVT_TRANSFER set STATUS=? where BINDID=?",
+                    new Object[]{STATUS_PROCESSED, processId});
+
+            // 提交事务
+            conn.commit();
+            logger.info("转移单[{}]处理成功,事务已提交", processId);
+
+        } catch (Exception e) {
+            // 异常时回滚事务
+            if (conn != null) {
+                try {
+                    conn.rollback();
+                    logger.error("转移单[{}]处理失败,事务已回滚", processId, e);
+                } catch (SQLException rollbackEx) {
+                    logger.error("转移单[{}]事务回滚失败", processId, rollbackEx);
                 }
+            }
+            throw e; // 向上抛出异常,便于上层处理
+        } finally {
+            // 关闭数据库连接
+            if (conn != null) {
+                DBSql.close(conn);
+            }
+        }
+    }
 
+    /**
+     * 校验必要参数非空
+     * @param productId 产品ID
+     * @param fromWarehouseId 转出仓库ID
+     */
+    private void validateRequiredParams(String productId, String fromWarehouseId) {
+        if (StringUtils.isBlank(productId)) {
+            throw new RuntimeException("产品ID不能为空");
+        }
+        if (StringUtils.isBlank(fromWarehouseId)) {
+            throw new RuntimeException("转出仓库ID不能为空");
+        }
+    }
 
-                if (TYPE_ID.equals("1481"))//仓库间转移
-                {
-                    String TO_WAREHOUSE_ID = rowMap.getString("TO_WAREHOUSE_ID");//转入仓库id
-                    if (StringUtils.isBlank(TO_WAREHOUSE_ID)) {
-                        //报错
-                        throw new RuntimeException("转入仓库不能为空");
-                    }
-                    BO toBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT").connection(conn).addQuery("PRODUCT_ID=", PRODUCT_ID).addQuery("WAREHOUSE_ID=", TO_WAREHOUSE_ID).detail();
-
-                    if (toBO == null) {
-                        ProcessInstance processInstance = SDK.getProcessAPI().createBOProcessInstance("obj_41f06b1e807647db8a69dd10d4be5764", processExecutionContext.getUserContext().getUID(), "新增库存产品" + PRODUCT_ID + "仓库间转移" + TO_WAREHOUSE_ID);
-                        toBO = new BO();
-                        toBO.set("PRODUCT_ID", PRODUCT_ID);
-                        toBO.set("WAREHOUSE_ID", TO_WAREHOUSE_ID);
-                        toBO.set("QUANTITY", rowMap.getInt("QUANTITY"));
-                        toBO.setBindId(processInstance.getId());
-                        SDK.getBOAPI().create("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, processInstance.getId(), processExecutionContext.getUserContext().getUID(), conn);
-                    } else {
-                        toBO.set("QUANTITY", Integer.parseInt(toBO.get("QUANTITY").toString()) + rowMap.getInt("QUANTITY"));
-                        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, conn);
-                    }
+    /**
+     * 处理出库库存及批次信息
+     * @param conn 数据库连接
+     * @param productId 产品ID
+     * @param fromWarehouseId 转出仓库ID
+     * @param rowMap 转移单数据
+     * @param sns SN列表字符串
+     * @return 出库批次信息列表
+     */
+    private List<Map<String, Object>> processOutboundInventory(Connection conn, String productId,
+                                                               String fromWarehouseId, RowMap rowMap, String sns) {
+        // 查询转出仓库的产品库存信息
+        BO fromBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT")
+                .connection(conn)
+                .addQuery("PRODUCT_ID=", productId)
+                .addQuery("WAREHOUSE_ID=", fromWarehouseId)
+                .detail();
+
+        if (fromBO == null) {
+            throw new RuntimeException("转出仓库不存在该产品的库存记录");
+        }
+
+        // 计算出库后库存数量(校验库存充足性)
+        int transferQuantity = rowMap.getInt("QUANTITY");
+        int currentStock = parseQuantity(fromBO.get("QUANTITY"));
+        int afterOutboundStock = currentStock - transferQuantity;
+
+        if (afterOutboundStock < 0) {
+            throw new RuntimeException("转出数量不能大于库存数量,当前库存:" + currentStock + ",转出:" + transferQuantity);
+        }
+
+        // 更新转出仓库的产品库存
+        fromBO.set("QUANTITY", afterOutboundStock);
+        SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", fromBO, conn);
+        logger.debug("产品[{}]从仓库[{}]出库,出库数量:{},剩余库存:{}",
+                productId, fromWarehouseId, transferQuantity, afterOutboundStock);
+
+        // 处理批次信息(区分序列化和非序列化产品)
+        return processOutboundLots(conn, fromBO, transferQuantity, sns);
+    }
+
+    /**
+     * 处理出库批次信息
+     * @param conn 数据库连接
+     * @param fromBO 转出仓库产品信息
+     * @param transferQuantity 转移数量
+     * @param sns SN列表字符串
+     * @return 出库批次信息列表
+     */
+    private List<Map<String, Object>> processOutboundLots(Connection conn, BO fromBO,
+                                                          int transferQuantity, String sns) {
+        List<Map<String, Object>> outboundLots = new ArrayList<>();
+        // 查询转出仓库的有效批次(数量>0)
+        List<BO> lots = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT")
+                .addQuery("BINDID=", fromBO.getBindId())
+                .addQuery("QUANTITY>0", null)
+                .orderByCreated()
+                .list();
+
+        if (StringUtils.isNotBlank(sns)) {
+            // 处理序列化产品(有SN标识)
+            outboundLots = processSerializedOutboundLots(conn, lots, sns);
+        } else {
+            // 处理非序列化产品(无SN标识,按数量处理)
+            outboundLots = processNonSerializedOutboundLots(conn, lots, transferQuantity);
+        }
+
+        return outboundLots;
+    }
+
+    /**
+     * 处理序列化产品的出库批次
+     * @param conn 数据库连接
+     * @param lots 批次列表
+     * @param sns SN列表字符串
+     * @return 出库批次信息列表
+     */
+    private List<Map<String, Object>> processSerializedOutboundLots(Connection conn, List<BO> lots, String sns) {
+        List<Map<String, Object>> outboundLots = new ArrayList<>();
+        String[] snArray = sns.split(",");
+        Set<String> snSet = new HashSet<>(Arrays.asList(snArray)); // 使用Set提高匹配效率
+        int processedSnCount = 0; // 已处理的SN数量
+
+        for (BO lot : lots) {
+            if (processedSnCount >= snArray.length) {
+                break; // 所有SN处理完毕,退出循环
+            }
+
+            String lotSN = lot.getString("SN");
+            if (StringUtils.isBlank(lotSN)) {
+                continue; // 批次无SN,跳过
+            }
 
+            // 处理当前批次的SN
+            Set<String> currentLotSnSet = new HashSet<>(Arrays.asList(lotSN.split(",")));
+            List<String> matchedSns = new ArrayList<>(); // 本批次中匹配的SN
 
-                    //入库的批次号
-                    //更新出库的批次号
-                    if (StringUtils.isNotBlank(sns)) {
-                        for (Map<String, Object> lot : fromLosts) {
-                            BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT").addQuery("LOT=", lot.get("LOT")).addQuery("ORDER_PRODUCT_ID=", lot.get("ORDER_PRODUCT_ID")).addQuery("BINDID=", toBO.getBindId()).detail();
-                            if (toLot != null) {
-                                toLot.set("QUANTITY", Integer.parseInt(toLot.get("QUANTITY").toString()) + Integer.parseInt(lot.get("QUANTITY").toString()));
-                                if (StringUtils.isNotBlank(lot.get("SN").toString()))
-                                    toLot.set("SN", toLot.get("SN") + "," + lot.get("SN"));
-                                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
-                            } else {
-                                toLot = new BO();
-                                toLot.setAll(lot);
-                                toLot.setBindId(toBO.getBindId());
-                                toLot.set("ORDER_PRODUCT_ID", lot.get("ORDER_PRODUCT_ID"));
-                                toLot.set("WAREHOUSE_ID", toBO.get("WAREHOUSE_ID"));
-                                toLot.set("PRODUCT_ID", toBO.get("PRODUCT_ID"));
-                                SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, processExecutionContext.getUserContext(), conn);
-                            }
-                        }
+            // 筛选本批次中需要转移的SN
+            for (String sn : snArray) {
+                if (currentLotSnSet.contains(sn) && snSet.contains(sn)) {
+                    matchedSns.add(sn);
+                    snSet.remove(sn); // 标记为已处理
+                    processedSnCount++;
+                    if (processedSnCount >= snArray.length) {
+                        break;
                     }
                 }
             }
 
-            //更新状态
-            DBSql.update(conn, "update BO_EU_DNIVT_TRANSFER set STATUS=1 where BINDID=?", new Object[]{processId});
+            if (!matchedSns.isEmpty()) {
+                // 更新当前批次的SN和数量(移除已转移的SN)
+                currentLotSnSet.removeAll(matchedSns);
+                String updatedSn = StringUtils.join(currentLotSnSet, ",");
+                lot.set("SN", updatedSn);
+                lot.set("QUANTITY", currentLotSnSet.size());
+                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
 
-            conn.commit();
-        } catch (SQLException e) {
-            e.printStackTrace();
-            conn.rollback();
-        } finally {
-            if (conn != null) DBSql.close(conn);
+                // 记录出库批次信息(用于入库处理)
+                Map<String, Object> lotMap = new HashMap<>(lot.asMap());
+                lotMap.remove("ID");
+                lotMap.remove("BINDID");
+                lotMap.put("SN", StringUtils.join(matchedSns, ","));
+                lotMap.put("QUANTITY", matchedSns.size());
+                outboundLots.add(lotMap);
+            }
+        }
+
+        // 校验是否所有SN都已处理
+        if (processedSnCount < snArray.length) {
+            throw new RuntimeException("部分SN未找到对应批次,已处理:" + processedSnCount + ",总需处理:" + snArray.length);
+        }
+
+        return outboundLots;
+    }
+
+    /**
+     * 处理非序列化产品的出库批次
+     * @param conn 数据库连接
+     * @param lots 批次列表
+     * @param remainingTransferQuantity 剩余待转移数量
+     * @return 出库批次信息列表
+     */
+    private List<Map<String, Object>> processNonSerializedOutboundLots(Connection conn, List<BO> lots,
+                                                                       int remainingTransferQuantity) {
+        List<Map<String, Object>> outboundLots = new ArrayList<>();
+        int transferLeft = remainingTransferQuantity;
+
+        for (BO lot : lots) {
+            if (transferLeft <= 0) {
+                break; // 所有数量处理完毕,退出循环
+            }
+
+            int lotQuantity = parseQuantity(lot.get("QUANTITY"));
+            if (lotQuantity <= 0) {
+                continue; // 批次数量为0,跳过
+            }
+
+            // 计算本批次需要转移的数量
+            int transferQty = Math.min(lotQuantity, transferLeft);
+            int remainingQty = lotQuantity - transferQty;
+
+            // 更新当前批次的剩余数量
+            lot.set("QUANTITY", remainingQty);
+            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", lot, conn);
+
+            // 记录出库批次信息(用于入库处理)
+            Map<String, Object> lotMap = new HashMap<>(lot.asMap());
+            lotMap.remove("ID");
+            lotMap.remove("BINDID");
+            lotMap.put("SN", "");
+            lotMap.put("QUANTITY", transferQty);
+            outboundLots.add(lotMap);
+
+            // 更新剩余待转移数量
+            transferLeft -= transferQty;
+        }
+
+        // 校验是否所有数量都已处理
+        if (transferLeft > 0) {
+            throw new RuntimeException("批次库存不足,剩余未转移数量:" + transferLeft);
+        }
+
+        return outboundLots;
+    }
+
+    /**
+     * 处理入库库存及批次信息(仓库间转移时)
+     * @param conn 数据库连接
+     * @param context 流程执行上下文
+     * @param productId 产品ID
+     * @param rowMap 转移单数据
+     * @param inboundLots 待入库批次信息列表
+     */
+    private void processInboundInventory(Connection conn, ProcessExecutionContext context,
+                                         String productId, RowMap rowMap, List<Map<String, Object>> inboundLots) {
+        String toWarehouseId = rowMap.getString("TO_WAREHOUSE_ID");
+        if (StringUtils.isBlank(toWarehouseId)) {
+            throw new RuntimeException("转入仓库不能为空");
+        }
+
+        // 查询或创建转入仓库的产品库存记录
+        BO toBO = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT")
+                .connection(conn)
+                .addQuery("PRODUCT_ID=", productId)
+                .addQuery("WAREHOUSE_ID=", toWarehouseId)
+                .detail();
+
+        int transferQuantity = rowMap.getInt("QUANTITY");
+        if (toBO == null) {
+            // 转入仓库无该产品库存,创建新记录
+            ProcessInstance processInstance = SDK.getProcessAPI()
+                    .createBOProcessInstance(PROCESS_DEF_ID, context.getUserContext().getUID(),
+                            "新增库存产品[" + productId + "]仓库间转移至[" + toWarehouseId + "]");
+            toBO = new BO();
+            toBO.set("PRODUCT_ID", productId);
+            toBO.set("WAREHOUSE_ID", toWarehouseId);
+            toBO.set("QUANTITY", transferQuantity);
+            toBO.setBindId(processInstance.getId());
+            SDK.getBOAPI().create("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, processInstance.getId(),
+                    context.getUserContext().getUID(), conn);
+            logger.debug("产品[{}]在转入仓库[{}]创建新库存记录,初始数量:{}",
+                    productId, toWarehouseId, transferQuantity);
+        } else {
+            // 转入仓库已有该产品库存,更新数量
+            int currentStock = parseQuantity(toBO.get("QUANTITY"));
+            toBO.set("QUANTITY", currentStock + transferQuantity);
+            SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT", toBO, conn);
+            logger.debug("产品[{}]在转入仓库[{}]更新库存,原库存:{},新增:{},现库存:{}",
+                    productId, toWarehouseId, currentStock, transferQuantity, currentStock + transferQuantity);
+        }
+
+        // 处理入库批次信息
+        processInboundLots(conn, context, toBO, inboundLots);
+    }
+
+    /**
+     * 处理入库批次信息
+     * @param conn 数据库连接
+     * @param context 流程执行上下文
+     * @param toBO 转入仓库产品信息
+     * @param inboundLots 待入库批次信息列表
+     */
+    private void processInboundLots(Connection conn, ProcessExecutionContext context,
+                                    BO toBO, List<Map<String, Object>> inboundLots) {
+        for (Map<String, Object> lotData : inboundLots) {
+            String lotNo = (String) lotData.get("LOT");
+            String orderProductId = (String) lotData.get("ORDER_PRODUCT_ID");
+
+            // 查询转入仓库是否已有相同批次
+            BO toLot = SDK.getBOAPI().query("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT")
+                    .addQuery("LOT=", lotNo)
+                    .addQuery("ORDER_PRODUCT_ID=", orderProductId)
+                    .addQuery("BINDID=", toBO.getBindId())
+                    .detail();
+
+            if (toLot != null) {
+                // 已有相同批次,更新数量和SN
+                int currentQty = parseQuantity(toLot.get("QUANTITY"));
+                int addQty = parseQuantity(lotData.get("QUANTITY"));
+                toLot.set("QUANTITY", currentQty + addQty);
+
+                String existingSn = toLot.getString("SN");
+                String newSn = (String) lotData.get("SN");
+                if (StringUtils.isNotBlank(newSn)) {
+                    toLot.set("SN", StringUtils.isBlank(existingSn) ? newSn : existingSn + "," + newSn);
+                }
+                SDK.getBOAPI().update("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", toLot, conn);
+            } else {
+                // 无相同批次,创建新批次
+                BO newLot = new BO();
+                newLot.setAll(lotData);
+                newLot.setBindId(toBO.getBindId());
+                newLot.set("ORDER_PRODUCT_ID", lotData.get("ORDER_PRODUCT_ID"));
+                newLot.set("WAREHOUSE_ID", toBO.get("WAREHOUSE_ID"));
+                newLot.set("PRODUCT_ID", toBO.get("PRODUCT_ID"));
+                SDK.getBOAPI().createDataBO("BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT", newLot,
+                        context.getUserContext(), conn);
+            }
         }
+        logger.debug("转入仓库批次处理完成,共处理{}个批次", inboundLots.size());
+    }
 
+    /**
+     * 安全解析数量(避免空指针和类型转换异常)
+     * @param quantityObj 数量对象
+     * @return 解析后的整数数量
+     */
+    private int parseQuantity(Object quantityObj) {
+        if (quantityObj == null) {
+            return 0;
+        }
+        if (quantityObj instanceof Number) {
+            return ((Number) quantityObj).intValue();
+        }
+        try {
+            return Integer.parseInt(quantityObj.toString().trim());
+        } catch (NumberFormatException e) {
+            throw new RuntimeException("数量格式错误:" + quantityObj, e);
+        }
     }
-}
+}

+ 278 - 0
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/service/WarehouseProductService.java

@@ -0,0 +1,278 @@
+package com.awspaas.user.apps.donenow_ivt.service;
+
+import com.actionsoft.bpms.bo.engine.BO;
+import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
+import com.actionsoft.bpms.server.UserContext;
+import com.actionsoft.sdk.local.SDK;
+import com.actionsoft.sdk.local.api.Logger;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * 仓库产品服务类 - 处理产品库存转移等操作
+ */
+public class WarehouseProductService {
+    // 常量定义
+    private static final Logger serviceLogger = SDK.getLogAPI().getLogger(WarehouseProductService.class);
+    private static final String BO_WAREHOUSE_PRODUCT = "BO_EU_DNIVT_WAREHOUSE_PRODUCT";
+    private static final String BO_WAREHOUSE_PRODUCT_LOT = "BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT";
+
+    // 单例模式
+    private static volatile WarehouseProductService instance;
+
+    private WarehouseProductService() {
+    }
+
+    public static WarehouseProductService getInstance() {
+        if (instance == null) {
+            synchronized (WarehouseProductService.class) {
+                if (instance == null) {
+                    instance = new WarehouseProductService();
+                }
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * 核心转移方法 - 处理整个产品转移流程
+     */
+    public void transfer(UserContext uc, Connection conn, String productId, String fromWarehouseId,
+                         String toWarehouseId, int quantity, String serialNumbers, int type, Map<String, Object> params) throws SQLException {
+        // 参数校验
+        if (quantity <= 0) {
+            serviceLogger.warn("转移数量必须大于0,当前数量:" + quantity);
+            return;
+        }
+        if (fromWarehouseId.equals(toWarehouseId)) {
+            serviceLogger.warn("源仓库与目标仓库相同,无需转移:" + fromWarehouseId);
+            return;
+        }
+
+        try {
+            // 1. 更新目标仓库库存(增加)
+            BO targetWarehouseBO = null;
+            if (StringUtils.isNotBlank(toWarehouseId)) {
+                targetWarehouseBO = updateWarehouseStock(uc, conn, productId, toWarehouseId, quantity, true);
+            }
+            // 2. 更新源仓库库存(减少)
+            BO sourceWarehouseBO = updateWarehouseStock(uc, conn, productId, fromWarehouseId, quantity, false);
+
+            // 3. 处理批次和序列号转移
+            processLotAndSerialTransfer(uc, conn, sourceWarehouseBO, targetWarehouseBO, quantity, serialNumbers);
+
+            serviceLogger.info("产品转移成功,产品ID:" + productId + ",数量:" + quantity +
+                    ",从仓库:" + fromWarehouseId + "到仓库:" + toWarehouseId);
+
+
+            //移库记录
+            ProcessInstance transfeProcessIns = SDK.getProcessAPI().createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", uc.getUID(), "库存转移");
+            BO ivtTransfer = new BO();
+            ivtTransfer.setAll(params);
+            ivtTransfer.setBindId(transfeProcessIns.getId());
+            ivtTransfer.set("FROM_WAREHOUSE_ID", fromWarehouseId);
+            ivtTransfer.set("TO_WAREHOUSE_ID", toWarehouseId);
+            ivtTransfer.set("TYPE_ID", type);
+            ivtTransfer.set("PRODUCT_ID", productId);
+            ivtTransfer.set("QUANTITY", quantity);
+            ivtTransfer.set("SN", serialNumbers);//产品序列号
+            SDK.getBOAPI().create("BO_EU_DNIVT_TRANSFER", ivtTransfer, transfeProcessIns, uc, conn);
+
+        } catch (Exception e) {
+            serviceLogger.error("产品转移失败:" + e.getMessage(), e);
+            throw new SQLException("产品转移过程中发生错误", e);
+        }
+    }
+
+    /**
+     * 统一处理仓库库存更新(增加或减少)
+     * @param isTarget  true表示目标仓库(增加库存),false表示源仓库(减少库存)
+     */
+    private BO updateWarehouseStock(UserContext uc, Connection conn, String productId,
+                                    String warehouseId, int quantity, boolean isTarget) throws SQLException {
+        // 查询仓库中是否已有该产品记录
+        BO warehouseBO = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT)
+                .connection(conn)
+                .addQuery("PRODUCT_ID=", productId)
+                .addQuery("WAREHOUSE_ID=", warehouseId)
+                .detail();
+
+        if (warehouseBO != null) {
+            // 已有记录,更新库存
+            int currentQty = objToInt(warehouseBO.get("QUANTITY"));
+            int newQty = isTarget ? currentQty + quantity : currentQty - quantity;
+
+            // 源仓库库存不足校验
+            if (!isTarget && newQty < 0) {
+                throw new SQLException("仓库库存不足,产品ID:" + productId + ",当前库存:" + currentQty +
+                        ",需操作:" + quantity);
+            }
+
+            warehouseBO.set("QUANTITY", newQty);
+            SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT, warehouseBO, conn);
+            serviceLogger.debug((isTarget ? "更新目标仓库" : "更新源仓库") + "库存,产品ID:" + productId +
+                    ",仓库:" + warehouseId + ",原数量:" + currentQty +
+                    (isTarget ? ",新增:" : ",减少:") + quantity);
+        } else {
+            // 无记录,仅目标仓库需要创建新记录
+            if (isTarget) {
+                ProcessInstance processIns = SDK.getProcessAPI()
+                        .createBOProcessInstance("obj_e15a8fedeaeb43dc85aa258a7e1cffcf", uc.getUID(), "库存转移");
+                warehouseBO = new BO();
+                warehouseBO.setBindId(processIns.getId());
+                warehouseBO.set("WAREHOUSE_ID", warehouseId);
+                warehouseBO.set("PRODUCT_ID", productId);
+                warehouseBO.set("QUANTITY", quantity);
+                SDK.getBOAPI().create(BO_WAREHOUSE_PRODUCT, warehouseBO, processIns, uc, conn);
+                serviceLogger.debug("创建目标仓库库存记录,产品ID:" + productId + ",仓库:" + warehouseId +
+                        ",初始数量:" + quantity);
+            } else {
+                // 源仓库无记录则抛出异常
+                throw new SQLException("源仓库不存在该产品库存记录,产品ID:" + productId + ",仓库:" + warehouseId);
+            }
+        }
+        return warehouseBO;
+    }
+
+    /**
+     * 处理批次和序列号转移(合并原多个方法为一个)
+     */
+    private void processLotAndSerialTransfer(UserContext uc, Connection conn, BO sourceWarehouseBO,
+                                             BO targetWarehouseBO, int totalQuantity, String serialNumbers) throws SQLException {
+        String sourceBindId = sourceWarehouseBO.getBindId();
+        String targetBindId = targetWarehouseBO == null ? StringUtils.EMPTY : targetWarehouseBO.getBindId();
+        String productId = targetWarehouseBO.getString("PRODUCT_ID");
+        String targetWarehouseId = targetWarehouseBO.getString("WAREHOUSE_ID");
+
+        // 处理序列号
+        String[] snArray = StringUtils.isBlank(serialNumbers) ? new String[0] : serialNumbers.split(",");
+        Set<String> snSet = new HashSet<>(Arrays.asList(snArray));
+        List<BO> sourceLots = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT_LOT)
+                .addQuery("BINDID=", sourceBindId)
+                .addQuery("QUANTITY>", 0)
+                .orderByCreated()
+                .list();
+
+        // 区分序列化和非序列化产品处理
+        if (snArray.length > 0) {
+            // 处理序列化产品转移
+            int processedCount = 0;
+            int totalNeedProcess = snSet.size();
+
+            for (BO lot : sourceLots) {
+                if (processedCount >= totalNeedProcess) break;
+
+                String lotSN = lot.getString("SN");
+                if (StringUtils.isBlank(lotSN)) continue;
+
+                // 提取当前批次中需要转移的序列号
+                Set<String> currentLotSNs = new HashSet<>(Arrays.asList(lotSN.split(",")));
+                currentLotSNs.retainAll(snSet);//交集
+
+                if (!currentLotSNs.isEmpty()) {
+                    // 更新源批次
+                    Set<String> remainingSNs = new HashSet<>(Arrays.asList(lotSN.split(",")));
+                    remainingSNs.removeAll(currentLotSNs);
+                    lot.set("SN", StringUtils.join(remainingSNs, ","));
+                    lot.set("QUANTITY", remainingSNs.size());
+                    SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, lot, conn);
+
+                    // 更新或创建目标批次
+                    if (StringUtils.isNotBlank(targetBindId)) {
+                        updateOrCreateTargetLot(uc, conn, lot, targetBindId, productId, targetWarehouseId,
+                                currentLotSNs.size(), StringUtils.join(currentLotSNs, ","), true);
+                    }
+                    processedCount += currentLotSNs.size();
+                    snSet.removeAll(currentLotSNs);
+                }
+            }
+
+            if (!snSet.isEmpty()) {
+                serviceLogger.warn("部分序列号未找到对应批次,未处理SN:" + StringUtils.join(snSet, ","));
+            }
+        } else {
+            // 处理非序列化产品转移
+            int remainingQuantity = totalQuantity;
+
+            for (BO lot : sourceLots) {
+                if (remainingQuantity <= 0) break;
+
+                int lotQuantity = objToInt(lot.get("QUANTITY"));
+                if (lotQuantity <= 0) continue;
+
+                // 计算本次转移数量
+                int transferQty = Math.min(lotQuantity, remainingQuantity);
+
+                // 更新源批次
+                lot.set("QUANTITY", lotQuantity - transferQty);
+                SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, lot, conn);
+
+                // 更新或创建目标批次
+                if (StringUtils.isNotBlank(targetBindId))
+                    updateOrCreateTargetLot(uc, conn, lot, targetBindId, productId, targetWarehouseId,
+                            transferQty, "", false);
+
+                remainingQuantity -= transferQty;
+            }
+
+            if (remainingQuantity > 0) {
+                throw new SQLException("源批次库存不足,剩余未转移数量:" + remainingQuantity);
+            }
+        }
+    }
+
+    /**
+     * 工具方法:更新或创建目标批次(内部使用)
+     */
+    private void updateOrCreateTargetLot(UserContext uc, Connection conn, BO sourceLot, String targetBindId,
+                                         String productId, String targetWarehouseId,
+                                         int transferQty, String snStr, boolean isSerialized) throws SQLException {
+        BO targetLot = SDK.getBOAPI().query(BO_WAREHOUSE_PRODUCT_LOT)
+                .addQuery("LOT=", sourceLot.get("LOT"))
+                .addQuery("ORDER_PRODUCT_ID=", sourceLot.get("ORDER_PRODUCT_ID"))
+                .addQuery("BINDID=", targetBindId)
+                .detail();
+
+        if (targetLot != null) {
+            int newQty = objToInt(targetLot.get("QUANTITY")) + transferQty;
+            targetLot.set("QUANTITY", newQty);
+            if (isSerialized) {
+                String currentSN = targetLot.getString("SN");
+                targetLot.set("SN", StringUtils.isBlank(currentSN) ? snStr : currentSN + "," + snStr);
+            }
+            SDK.getBOAPI().update(BO_WAREHOUSE_PRODUCT_LOT, targetLot, conn);
+        } else {
+            BO newLot = new BO();
+            newLot.setAll(sourceLot.asMap());
+            newLot.setId(null);
+            newLot.setBindId(targetBindId);
+            newLot.set("WAREHOUSE_ID", targetWarehouseId);
+            newLot.set("PRODUCT_ID", productId);
+            newLot.set("QUANTITY", transferQty);
+            newLot.set("SN", snStr);
+            SDK.getBOAPI().createDataBO(BO_WAREHOUSE_PRODUCT_LOT, newLot, uc, conn);
+        }
+    }
+
+    /**
+     * 工具方法:安全的Object转int
+     */
+    private int objToInt(Object obj) {
+        if (obj == null) return 0;
+
+        try {
+            String objStr = obj.toString().trim();
+            if (objStr.isEmpty()) return 0;
+            if (objStr.contains(".")) {
+                objStr = objStr.substring(0, objStr.indexOf("."));
+            }
+            return Integer.parseInt(objStr);
+        } catch (NumberFormatException e) {
+            serviceLogger.error("对象转换为int失败,值:" + obj, e);
+            return 0;
+        }
+    }
+}