HULEI преди 1 месец
родител
ревизия
625c1755fe

+ 40 - 27
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/contractApproveController.java

@@ -1610,31 +1610,35 @@ public class contractApproveController {
             if (totalReceiveAmount == null || totalReceiveAmount.compareTo(BigDecimal.ZERO) <= 0) {
                 return ResponseObject.newErrResponse("总收款金额不能为空且需大于0");
             }
+            // 强制总收款金额保留2位小数,符合财务金额规范,杜绝前端传入多位小数
+            totalReceiveAmount = totalReceiveAmount.setScale(2, BigDecimal.ROUND_HALF_UP);
 
-            // 2. 查询bindid关联的明细记录(核心修改:查询字段改为INVOICE_AMOUNT)
+            // 2. 查询bindid关联的明细记录(核心:查询字段为INVOICE_AMOUNT)
             String querySql = "SELECT ID, INVOICE_AMOUNT FROM BO_EU_DNCTT_INVOICE_PLAN_DETAIL WHERE BINDID = ?";
             List<Object> queryParams = new ArrayList<>();
             queryParams.add(bindid.trim());
             List<RowMap> detailList = DBSql.getMaps(querySql, queryParams.toArray());
 
             // 3. 预处理:统计总发票金额 + 校验数据(变量名同步改为发票金额相关)
-            Map<String, BigDecimal> idInvoiceAmountMap = new HashMap<>(); // 原idBaseAmountMap → 改为发票金额映射
-            BigDecimal totalInvoiceAmount = BigDecimal.ZERO; // 原totalBaseAmount → 改为总发票金额(比例计算基数)
+            Map<String, BigDecimal> idInvoiceAmountMap = new HashMap<>(); // 明细ID-发票金额映射
+            BigDecimal totalInvoiceAmount = BigDecimal.ZERO; // 总发票金额(比例计算基数)
             for (RowMap row : detailList) {
                 String id = row.getString("ID");
-                // 处理发票金额:从INVOICE_AMOUNT字段获取(核心修改)
+                // 处理发票金额:从INVOICE_AMOUNT字段获取
                 String invoiceAmountStr = row.getString("INVOICE_AMOUNT");
                 BigDecimal invoiceAmount = null;
                 if (invoiceAmountStr != null && !invoiceAmountStr.trim().isEmpty()) {
                     try {
                         invoiceAmount = new BigDecimal(invoiceAmountStr.trim());
+                        // 强制发票金额保留2位小数,杜绝多位小数导致的比例偏差、单条固定少0.02
+                        invoiceAmount = invoiceAmount.setScale(2, BigDecimal.ROUND_HALF_UP);
                     } catch (NumberFormatException e) {
-                        System.out.println("发票金额格式错误,ID:" + id + ",值:" + invoiceAmountStr); // 日志同步修改
+                        System.out.println("发票金额格式错误,ID:" + id + ",值:" + invoiceAmountStr);
                         continue;
                     }
                 }
                 if (invoiceAmount == null || invoiceAmount.compareTo(BigDecimal.ZERO) <= 0) {
-                    System.out.println("发票金额非法(空或<=0),ID:" + id); // 日志同步修改
+                    System.out.println("发票金额非法(空或<=0),ID:" + id);
                     continue;
                 }
                 idInvoiceAmountMap.put(id, invoiceAmount);
@@ -1646,7 +1650,7 @@ public class contractApproveController {
                 Map<String, Object> result = new HashMap<>();
                 result.put("totalMatched", detailList.size());
                 result.put("updateCount", 0);
-                result.put("message", "bindid关联明细的总发票金额为0,无法计算比例"); // 提示信息修改
+                result.put("message", "bindid关联明细的总发票金额为0,无法计算比例");
                 ResponseObject responseObject = ResponseObject.newOkResponse();
                 responseObject.setData(result);
                 return responseObject;
@@ -1657,30 +1661,36 @@ public class contractApproveController {
             connUpdate.setAutoCommit(false);
 
             int updateCount = 0;
-
-            // 5. 循环按比例分配并更新 RECEIVE_AMOUNT 字段(使用总发票金额计算比例)
-            for (String id : idInvoiceAmountMap.keySet()) {
-                // 按比例计算:使用发票金额作为基数(变量名同步修改)
+            // 转换为有序列表,保证最后一条兜底逻辑生效
+            List<String> detailIdList = new ArrayList<>(idInvoiceAmountMap.keySet());
+            int detailSize = detailIdList.size();
+            BigDecimal sumAllocatedAmount = BigDecimal.ZERO; // 前N-1条累计分配金额
+
+            // 5. 循环按比例分配并更新 RECEIVE_AMOUNT 字段(核心修复:最后一条兜底,彻底解决金额偏差)
+            for (int i = 0; i < detailSize; i++) {
+                String id = detailIdList.get(i);
                 BigDecimal invoiceAmount = idInvoiceAmountMap.get(id);
-                BigDecimal ratio = invoiceAmount.divide(totalInvoiceAmount, 6, BigDecimal.ROUND_HALF_UP);
-                BigDecimal allocatedReceiveAmount = totalReceiveAmount.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
+                BigDecimal allocatedReceiveAmount;
 
-                // 构建更新SQL(仅更新RECEIVE_AMOUNT)
-                StringBuilder updateSql = new StringBuilder("UPDATE BO_EU_DNCTT_INVOICE_PLAN_DETAIL SET ");
-                List<Object> updateParams = new ArrayList<>();
+                if (i == detailSize - 1) {
+                    // ========== 核心:最后1条兜底 ==========
+                    // 用总收款金额 - 前N-1条累计金额,补足所有差额,总额100%精准无损耗
+                    allocatedReceiveAmount = totalReceiveAmount.subtract(sumAllocatedAmount);
+                } else {
+                    // 前N-1条:比例精度拉满至12位,最小化单条偏差(从0.02→0.01)
+                    BigDecimal ratio = invoiceAmount.divide(totalInvoiceAmount, 12, BigDecimal.ROUND_HALF_UP);
+                    allocatedReceiveAmount = totalReceiveAmount.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
+                    sumAllocatedAmount = sumAllocatedAmount.add(allocatedReceiveAmount);
+                }
 
-                updateSql.append("RECEIVE_AMOUNT = ?, ");
+                // 构建更新SQL(无拼接冗余逗号,彻底解决SQL语法错误)
+                String updateSql = "UPDATE BO_EU_DNCTT_INVOICE_PLAN_DETAIL SET RECEIVE_AMOUNT = ? WHERE ID = ?";
+                List<Object> updateParams = new ArrayList<>();
                 updateParams.add(allocatedReceiveAmount);
-
-                // 移除多余逗号
-                if (updateSql.charAt(updateSql.length() - 2) == ',') {
-                    updateSql.setLength(updateSql.length() - 2);
-                }
-                updateSql.append(" WHERE ID = ?");
                 updateParams.add(id.trim());
 
                 // 执行更新
-                int affectedRows = DBSql.update(connUpdate, updateSql.toString(), updateParams.toArray());
+                int affectedRows = DBSql.update(connUpdate, updateSql, updateParams.toArray());
                 if (affectedRows > 0) {
                     updateCount++;
                 }
@@ -1689,19 +1699,21 @@ public class contractApproveController {
             // 6. 提交事务
             connUpdate.commit();
 
-            // 7. 返回结果(字段同步改为总发票金额)
+            // 7. 返回结果(字段同步改为总发票金额,返回精准分配数据
             Map<String, Object> result = new HashMap<>();
             result.put("totalMatched", detailList.size());
             result.put("updateCount", updateCount);
             result.put("totalReceiveAmount", totalReceiveAmount);
-            result.put("totalInvoiceAmount", totalInvoiceAmount); // 原totalBaseAmount → 改为totalInvoiceAmount
-            result.put("message", "按比例更新RECEIVE_AMOUNT完成,共更新" + updateCount + "条明细记录");
+            result.put("totalInvoiceAmount", totalInvoiceAmount);
+            result.put("actualAllocatedTotal", totalReceiveAmount); // 实际分配总额=总收款,绝对一致
+            result.put("message", "按比例更新RECEIVE_AMOUNT完成,共更新" + updateCount + "条明细记录,金额无差额");
 
             ResponseObject responseObject = ResponseObject.newOkResponse();
             responseObject.setData(result);
             return responseObject;
 
         } catch (SQLException e) {
+            // 事务回滚:增强容错,确保异常时数据回滚
             if (connUpdate != null && !connUpdate.isClosed()) {
                 try {
                     connUpdate.rollback();
@@ -1712,6 +1724,7 @@ public class contractApproveController {
             e.printStackTrace();
             return ResponseObject.newErrResponse("更新RECEIVE_AMOUNT失败:" + e.getMessage());
         } finally {
+            // 关闭连接:安全兜底,避免连接泄漏
             if (connUpdate != null && !connUpdate.isClosed()) {
                 DBSql.close(connUpdate);
             }

+ 80 - 51
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/controller/IVTController.java

@@ -206,11 +206,14 @@ public class IVTController extends BaseController {
      *
      *采购项接收
      */
+
+
     @Mapping("com.awspaas.user.apps.donenow_ivt.purchasereceive")
     public String purchasereceive(String ids, UserContext uc, String bindId) {
         System.out.print("idsids:" + ids);
         System.out.print("bindId:" + bindId);
 
+        // ===== 1. 订单项ID预处理 + 非空校验 =====
         if (StringUtils.isNotBlank(ids)) {
             ids = ids.trim();
             if (ids.endsWith(",")) {
@@ -224,7 +227,7 @@ public class IVTController extends BaseController {
         ids = "'" + ids.replace(",", "','") + "'";
         System.out.print("idsids:" + ids);
 
-        //采购接收查询SQL
+        // ===== 2. 采购订单项及关联数据查询 =====
         String sql = "  select op.CONTRACT_COST_ID,a.IS_SERIALIZED,o.CREATEDATE,o.FREIGHT_COST,op.WAREHOUSE_ID,op.product_id,op.id,c.id as contract_id,p.id as project_id,t.id as task_id,op.product_id," +
                 "   ifnull(c.account_id,ifnull(p.account_id,t.account_id))as account_id,b.opportunity_id," +
                 "  o.vendor_account_id,if(c.name is not null,'contract',if(p.name is not null,'project'," +
@@ -261,13 +264,14 @@ public class IVTController extends BaseController {
             return fail("请检查采购订单是否未审批!");
         }
 
-        // ========== 关键修改1:获取采购订单号 ==========
+        // ===== 3. 提取采购订单号 + 校验:单次只能处理同一个采购订单 =====
         String purchaseOrderNo = null;
         Set<String> set = new HashSet<String>();
         for (RowMap map : maps) {
-            if (StringUtils.isNotBlank(map.getString("PURCHASE_ORDER_NO")) && StringUtils.isNotBlank(map.getString("PURCHASE_ORDER_NO").trim())) {
-                set.add(map.getString("PURCHASE_ORDER_NO"));
-                purchaseOrderNo = map.getString("PURCHASE_ORDER_NO"); // 提取采购订单号
+            String poNo = map.getString("purchase_order_no");
+            if (StringUtils.isNotBlank(poNo) && StringUtils.isNotBlank(poNo.trim())) {
+                set.add(poNo);
+                purchaseOrderNo = poNo;
             }
         }
         if (set.size() > 1) {
@@ -275,97 +279,122 @@ public class IVTController extends BaseController {
             return fail("一次只能接收同一个采购订单的采购项");
         }
 
-        // ========== 关键修改2:查询是否已有采购接收主记录 ==========
+        // ===== 4. 核心修复:防重复创建【适配主表无STATUS字段】✅✅
+        // 关键改动:主表仅按采购单号判断是否存在,彻底删除STATUS查询条件,杜绝报错
         BO existingMainBO = null;
         if (StringUtils.isNotBlank(purchaseOrderNo)) {
-            // 查询条件:采购订单号匹配,可根据业务补充状态条件(比如排除已删除/已完成的记录)
-            existingMainBO = SDK.getBOAPI().query(IVTConstant.BO_EU_DNIVT_RECEIVE_MAIN)
+            long count = SDK.getBOAPI().query(IVTConstant.BO_EU_DNIVT_RECEIVE_MAIN)
                     .addQuery("PURCHASE_ORDER_NO=", purchaseOrderNo)
-                    // 可选:添加状态过滤,比如 .addQuery("STATUS!=", "已完成")
-                    .detail();
+                    .count();
+            // 只要存在同采购单号的主单,直接获取并返回,不区分状态
+            if (count > 0) {
+                existingMainBO = SDK.getBOAPI().query(IVTConstant.BO_EU_DNIVT_RECEIVE_MAIN)
+                        .addQuery("PURCHASE_ORDER_NO=", purchaseOrderNo)
+                        .detail();
+            }
         }
 
-        // ========== 关键修改3:有则返回已有记录的URL,无则创建新记录 ==========
+        // ===== 5. 存在已有单据,直接返回URL,终止创建(重复打开不报错)✅✅
         if (existingMainBO != null) {
-            // 获取已有记录的流程实例ID,生成打开URL
             String processInstanceId = existingMainBO.getBindId();
             String url = SDK.getFormAPI().getFormURL("", uc.getSessionId(), processInstanceId, "", 1, "", "", "");
             url = SDK.getPortalAPI().getPortalUrl() + "/r" + url.substring(1);
-            return success(url); // 直接返回已有记录的URL
+            return success(url);
         }
 
-        // 原创建逻辑:无已有记录时才执行
+        // ===== 6. 初始化流程实例ID(主/子表绑定)=====
         ProcessInstance createBOProcessInstance = SDK.getProcessAPI().createBOProcessInstance(IVTConstant.obj_3121af9039dc454aa776f9bdadf6b239, uc.getUID(), "");
 
+        // ===== 7. 构建采购接收子表数据(子表独有STATUS,初始待接收)✅✅
         List<BO> list = new ArrayList<BO>();
         for (RowMap orgMap : maps) {
             System.out.println("map:" + orgMap);
-            Map<String, Object> map = convertKeysToUppercase(orgMap);
-
-            //采购数量不能为空,也不能小于0
-            Object purchaseQuantity = map.get("QUANTITY");
-            System.out.println("purchaseQuantity:" + purchaseQuantity);
-            if (purchaseQuantity == null || StringUtils.isBlank(purchaseQuantity.toString()) || Double.parseDouble(purchaseQuantity.toString()) <= 0) {
+            // 原生RowMap取值,无方法歧义、零报错
+            String opId = orgMap.getString("id");
+            String productName = orgMap.getString("product_name");
+            String warehouseId = orgMap.getString("warehouse_id");
+            String accountName = orgMap.getString("account_name");
+            String contracName = orgMap.getString("contrac_project_task_name");
+            String productId = orgMap.getString("product_id");
+            String contractCostId = orgMap.getString("contract_cost_id");
+            String isSerialized = orgMap.getString("IS_SERIALIZED");
+            Object quantityObj = orgMap.get("quantity");
+            Object onHandObj = orgMap.get("on_hand");
+
+            // 过滤无效项:采购数量为空/≤0 跳过
+            if (quantityObj == null || StringUtils.isBlank(quantityObj.toString()) || Double.parseDouble(quantityObj.toString()) <= 0) {
                 continue;
             }
 
-            String unBack = "0";
+            int totalQty = Integer.parseInt(quantityObj.toString());
             BO subBo = new BO();
-            set.add(getString(map, "PURCHASE_ORDER_NO"));
-            RowMap rowMap = DBSql.getMap("select ORDER_PRODUCT_ID,sum(QUANTITY_RECEIVED) QUANTITY_RECEIVED from BO_EU_DNIVT_RECEIVE WHERE  ORDER_PRODUCT_ID=? group by ORDER_PRODUCT_ID", new Object[]{getString(map, "ID")});
-            if (null != rowMap) {
-                unBack = getUnBack(getString(map, "QUANTITY"), rowMap.getString("QUANTITY_RECEIVED"));
-                if (Integer.valueOf(unBack) <= 0) {
-                    // System.out.println("采购数量小于已接收数量:" +getString(map,"QUANTITY") + "-----" + rowMap.getString("QUANTITY_RECEIVED"));
-                    // continue;
-                }
-                subBo.set("QUANTITY_RECEIVED", rowMap.get("QUANTITY_RECEIVED"));
-                subBo.set("QUANTITY_BACKORDERED", unBack);
-            } else {
-                subBo.set("QUANTITY_RECEIVED", 0);
-                subBo.set("QUANTITY_BACKORDERED", map.get("QUANTITY"));
+            int historyReceive = 0;
+
+            // 查询子表历史累计接收数(子表有数据统计能力)
+            RowMap rowMap = DBSql.getMap(
+                    "select sum(QUANTITY_RECEIVED) QUANTITY_RECEIVED from BO_EU_DNIVT_RECEIVE_SUB WHERE ORDER_PRODUCT_ID=? group by ORDER_PRODUCT_ID",
+                    new Object[]{opId}
+            );
+            if (null != rowMap && rowMap.get("QUANTITY_RECEIVED") != null) {
+                historyReceive = Integer.parseInt(rowMap.get("QUANTITY_RECEIVED").toString());
             }
-            subBo.set("PRODUCT_NAME", map.get("PRODUCT_NAME"));
-            subBo.set("IS_SERIALIZED", map.get("IS_SERIALIZED"));
-            subBo.set("WAREHOUSE_ID", map.get("WAREHOUSE_ID"));
-            subBo.set("ACCOUNT_NAME", map.get("ACCOUNT_NAME"));
-            subBo.set("CONTRAC_PROJECT_TASK_NAME", map.get("CONTRAC_PROJECT_TASK_NAME"));
-            subBo.set("QUANTITY", map.get("QUANTITY"));
-            subBo.set("ON_HAND", map.get("ON_HAND"));
-            subBo.set("PRODUCT_ID", map.get("PRODUCT_ID"));
-            subBo.set("ORDER_PRODUCT_ID", map.get("ID"));
-            subBo.set("NOW_COUNT", 0);
-            subBo.set("CONTRACT_COST_ID", map.get("CONTRACT_COST_ID"));
+
+            // 正确计算字段初始值
+            int totalReceive = historyReceive;  // 累计接收数=历史数,初始0
+            String unBack = String.valueOf(totalQty - totalReceive); // 未收货=采购数-历史数
+
+            // ✅ 核心:子表设置STATUS,初始强制为【待接收】,手动填数后由业务端更新
             subBo.set("STATUS", "待接收");
-            subBo.set("WHOLE", DBSql.getInt("select count(1) AS CNT from BO_EU_DNCTT_CONTRACT_COST where ID=? AND IS_SERVICE_PRODUCT=1", new Object[]{map.get("CONTRACT_COST_ID")}));//判断是否整体接收
+            // ✅ 本次接收数初始0,必须手动输入保存后生效
+            subBo.set("NOW_COUNT", 0);
+
+            // 子表字段赋值(完整、无遗漏)
+            subBo.set("QUANTITY_RECEIVED", totalReceive);
+            subBo.set("QUANTITY_BACKORDERED", unBack);
+            subBo.set("PRODUCT_NAME", productName);
+            subBo.set("IS_SERIALIZED", isSerialized);
+            subBo.set("WAREHOUSE_ID", warehouseId);
+            subBo.set("ACCOUNT_NAME", accountName);
+            subBo.set("CONTRAC_PROJECT_TASK_NAME", contracName);
+            subBo.set("QUANTITY", totalQty);
+            subBo.set("ON_HAND", onHandObj);
+            subBo.set("PRODUCT_ID", productId);
+            subBo.set("ORDER_PRODUCT_ID", opId);
+            subBo.set("CONTRACT_COST_ID", contractCostId);
+
+            // 查询WHOLE字段值
+            int whole = DBSql.getInt("select count(1) AS CNT from BO_EU_DNCTT_CONTRACT_COST where ID=? AND IS_SERVICE_PRODUCT=1", new Object[]{contractCostId});
+            subBo.set("WHOLE", whole);
+
             subBo.setBindId(createBOProcessInstance.getId());
             list.add(subBo);
         }
+
+        // 无有效子表数据,返回失败
         if (list.size() == 0) {
             return fail("采购订单项都已经接收完毕");
         }
 
-        //采购接收主表添加数据
+        // ===== 8. 创建采购接收主表数据(无STATUS字段,适配表结构)✅✅
         RowMap rowMap = maps.get(0);
         BO detail = SDK.getBOAPI().query(IVTConstant.BO_EU_DNIVT_ORDER).addQuery("PURCHASE_ORDER_NO=", rowMap.getString("purchase_order_no")).detail();
         if (null == detail) {
             return fail("未找到对应的订单");
         }
 
-        //主表数据
         BO bo = new BO();
         bo.setBindId(createBOProcessInstance.getId());
-        bo.setId(UUIDGener.getUUID());
+        bo.setId(UUID.randomUUID().toString().replace("-",""));
         bo.set("COMPANY", rowMap.getString("vendor"));
         bo.set("PURCHASE_ORDER_NO", rowMap.getString("purchase_order_no"));
         bo.set("SUBMIT_TIME", rowMap.get("CREATEDATE"));
-        bo.set("VENDOR_INVOICE_NO", rowMap.get("VENDOR_INVOICE_NO"));
         bo.set("FREIGHT_COST", rowMap.get("FREIGHT_COST"));
         SDK.getBOAPI().create(IVTConstant.BO_EU_DNIVT_RECEIVE_MAIN, bo, createBOProcessInstance, uc);
 
-        //子表数据
+        // ===== 9. 创建采购接收子表数据(携带STATUS字段)=====
         SDK.getBOAPI().create(IVTConstant.BO_EU_DNIVT_RECEIVE_SUB, list, createBOProcessInstance, uc);
 
+        // ===== 10. 生成单据URL并返回 =====
         String url = SDK.getFormAPI().getFormURL("", uc.getSessionId(), createBOProcessInstance.getId(), "", 1, "", "", "");
         url = SDK.getPortalAPI().getPortalUrl() + "/r" + url.substring(1);
         return success(url);

+ 1 - 1
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/controller/ivtOrderController.java

@@ -940,7 +940,7 @@ public class ivtOrderController {
             e.printStackTrace();
             if (conn != null) {
                 try {
-                    conn.rollback();
+                    conn.rollback();    
                 } catch (SQLException ex) {
                     System.err.println("事务回滚异常: " + ex.getMessage());
                 }

+ 70 - 46
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/event/reserveFormBeforeSave.java

@@ -16,15 +16,9 @@ import java.util.List;
 
 public class reserveFormBeforeSave extends InterruptListener {
     public String getDescription() {
-        return "采购接收/取消接收保存前,校验库存";
+        return "采购接收/取消接收保存前,校验库存+自动更新接收数+自动更新状态";
     }
 
-    /**
-     *
-     * @param processExecutionContext
-     * @return
-     * @throws Exception
-     */
     @Override
     public boolean execute(ProcessExecutionContext processExecutionContext) throws Exception {
         UserContext uc = processExecutionContext.getUserContext();
@@ -32,72 +26,102 @@ public class reserveFormBeforeSave extends InterruptListener {
         String boName = processExecutionContext.getParameterOfString("$BONAME");
         String bindId = processExecutionContext.getProcessInstance().getId();
 
-        //采购接收主表保存
+        // 采购接收主表保存时执行
         if (IVTConstant.BO_EU_DNIVT_RECEIVE_MAIN.equals(boName)) {
-
+            // 查询当前单据下的所有子表数据
             List<BO> list = SDK.getBOAPI().query(IVTConstant.BO_EU_DNIVT_RECEIVE_SUB).bindId(bindId).list();
-            //序列号不能重复
             for (BO bo : list) {
-                String BEN_RECEIVE = BigDecimalUtil.isNull(bo.getString("BEN_RECEIVE"));
-                int intBenReceive = Integer.valueOf(BEN_RECEIVE).intValue();
-
-                //只能整体接收
-                if (bo.getString("WHOLE").equals("1")) {
-                    String QUANTITY_BACKORDERED = BigDecimalUtil.isNull(bo.getString("QUANTITY_BACKORDERED"));
-                    int backorderedQty = Integer.valueOf(QUANTITY_BACKORDERED).intValue();
-                    if (intBenReceive != backorderedQty && (0 - intBenReceive) != backorderedQty) {
-                        throw new BPMNError("receiveErrorCode6", bo.getString("PRODUCT_NAME") + "只能整体接收,接收数量为" + backorderedQty + "!");
-                    }
-                }
+                // ===== 1. 基础字段取值 =====
+                String benReceiveStr = BigDecimalUtil.isNull(bo.getString("BEN_RECEIVE"));
+                int intBenReceive = Integer.parseInt(benReceiveStr); // 本次接收数
+                String orderProductId = bo.getString("ORDER_PRODUCT_ID"); // 订单项ID
+                String productName = bo.getString("PRODUCT_NAME"); // 产品名称
+                int totalPurchaseQty = Integer.parseInt(bo.getString("QUANTITY")); // 采购总数量
                 String sns = bo.getString("SN");
+
+                // ===== 2. 原有校验:本次接收数为0,禁止填序列号 =====
                 if (intBenReceive == 0) {
-                    if (StringUtils.isNotBlank(sns))
+                    if (StringUtils.isNotBlank(sns)) {
                         throw new BPMNError("receiveErrorCode0", "本次接收数量为空,不需要输入序列号!");
+                    }
                     continue;
                 }
 
-                RowMap map = DBSql.getMap("select sum(QUANTITY_RECEIVED) QUANTITY_RECEIVED from BO_EU_DNIVT_RECEIVE WHERE  ORDER_PRODUCT_ID=?", new Object[]{bo.getString("ORDER_PRODUCT_ID")});
+                // ===== 3. 原有校验:整体接收产品,必须收完剩余数量 =====
+                if ("1".equals(bo.getString("WHOLE"))) {
+                    String backorderedStr = BigDecimalUtil.isNull(bo.getString("QUANTITY_BACKORDERED"));
+                    int backorderedQty = Integer.parseInt(backorderedStr);
+                    // 正向接收/反向取消,都必须等于剩余数量
+                    if (intBenReceive != backorderedQty && (0 - intBenReceive) != backorderedQty) {
+                        throw new BPMNError("receiveErrorCode6", productName + "只能整体接收,接收数量为" + backorderedQty + "!");
+                    }
+                }
+
+                // ===== 4. ✅ 核心修复1:查询【正确子表】统计历史累计接收数 =====
+                RowMap historyMap = DBSql.getMap(
+                        "select sum(QUANTITY_RECEIVED) as QUANTITY_RECEIVED from BO_EU_DNIVT_RECEIVE_SUB where ORDER_PRODUCT_ID=?",
+                        new Object[]{orderProductId}
+                );
+                int historyReceive = 0;
+                if (historyMap != null && historyMap.get("QUANTITY_RECEIVED") != null) {
+                    historyReceive = historyMap.getInt("QUANTITY_RECEIVED");
+                }
 
-                int QUANTITY_RECEIVED = map == null ? 0 : map.getInt("QUANTITY_RECEIVED");
-                if (intBenReceive + QUANTITY_RECEIVED < 0) {
+                // ===== 5. ✅ 核心校验:取消接收数 不能大于 已接收数 =====
+                if (intBenReceive + historyReceive < 0) {
                     throw new BPMNError("receiveErrorCode4", "取消接收的数量不能大于已接收的数量!");
                 }
 
-                if (intBenReceive + QUANTITY_RECEIVED > Integer.parseInt(bo.getString("QUANTITY"))) {
-                    throw new BPMNError("receiveErrorCode5", "接收数量不能大于采购数量!");
+                // ===== 6. ✅ 核心校验:累计接收数 不能大于 采购总数量 =====
+                int newTotalReceive = historyReceive + intBenReceive;
+                if (newTotalReceive > totalPurchaseQty) {
+                    throw new BPMNError("receiveErrorCode5", productName + "接收数量不能大于采购数量【" + totalPurchaseQty + "】!");
                 }
 
-                //序列号产品批次号不能重复
+                // ===== 7. ✅ 原有校验:序列号唯一性校验 =====
                 if (StringUtils.isNotBlank(sns)) {
                     for (String sn : sns.split(",")) {
-                        List<RowMap> exitsLots = DBSql.getMaps("select * from BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT WHERE FIND_IN_SET(?,SN)", new Object[]{sn});
-                        if (exitsLots.size() > 0 && intBenReceive > 0) {
-                            //已存在,不能再新增
+                        List<RowMap> existSnList = DBSql.getMaps(
+                                "select * from BO_EU_DNIVT_WAREHOUSE_PRODUCT_LOT where FIND_IN_SET(?, SN)",
+                                new Object[]{sn}
+                        );
+                        // 正向接收:序列号不能已存在
+                        if (intBenReceive > 0 && !existSnList.isEmpty()) {
                             throw new BPMNError("receiveErrorCode1", sn + "已存在此序列号,不能再新增!");
                         }
-
+                        // 反向取消:序列号必须存在+归属当前采购单
                         if (intBenReceive < 0) {
-                            if (exitsLots.size() <= 0)
-                                throw new BPMNError("receiveErrorCode2", sn + "不存在此序列号,不能再删除!");
-                            else if (exitsLots.size() > 0) {
-                                boolean sameOrderProduct = false;
-                                for (RowMap rowMap : exitsLots) {
-                                    if (rowMap.getString("ORDER_PRODUCT_ID").equals(bo.getString("ORDER_PRODUCT_ID"))) {
-                                        sameOrderProduct = true;
-                                    }
-                                }
-                                if (!sameOrderProduct) {
-                                    throw new BPMNError("receiveErrorCode3", sn + "不是此采购单接收的,不能在此采购单上取消!");
-                                }
+                            if (existSnList.isEmpty()) {
+                                throw new BPMNError("receiveErrorCode2", sn + "不存在此序列号,不能删除!");
+                            }
+                            boolean isCurrentOrder = existSnList.stream()
+                                    .anyMatch(row -> orderProductId.equals(row.getString("ORDER_PRODUCT_ID")));
+                            if (!isCurrentOrder) {
+                                throw new BPMNError("receiveErrorCode3", sn + "不是此采购单接收的,无法取消!");
                             }
                         }
                     }
                 }
+
+                // ===== 8. ✅ 核心更新1:计算并赋值 累计接收数、未收货数量 =====
+                bo.set("QUANTITY_RECEIVED", newTotalReceive); // 累计接收数(不再是0)
+                int newBackorderedQty = totalPurchaseQty - newTotalReceive;
+                bo.set("QUANTITY_BACKORDERED", Math.max(newBackorderedQty, 0)); // 未收货数量(最小为0)
+
+                // ===== 9. ✅ 核心更新2:自动更新状态(STATUS) =====
+                if (newBackorderedQty == 0) {
+                    bo.set("STATUS", "已接收"); // 未收货数量为0 → 自动变已接收
+                } else {
+                    bo.set("STATUS", "待接收"); // 还有未收 → 保持待接收
+                }
+
+                // ===== 10. ✅ 同步更新子表数据到数据库 =====
+                SDK.getBOAPI().update(IVTConstant.BO_EU_DNIVT_RECEIVE_SUB, bo);
             }
         }
 
-        //记录表单修改
+        // 记录表单修改(原有逻辑保留)
         dn.recordFormChanges.execute(processExecutionContext);
         return true;
     }
-}
+}