|
|
@@ -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);
|
|
|
+ }
|
|
|
}
|
|
|
-}
|
|
|
+}
|