cl
1 天以前 ccfa611b6c2e128c0e8191e458302a85cbf8c4ee
删除取消
4个文件已修改
558 ■■■■■ 已修改文件
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java 507 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/api/service/impl/ReceiveMsgServiceImpl.java
@@ -467,126 +467,270 @@
    @Override
    @Transactional(timeout = 60, rollbackFor = Exception.class)
    public R syncCheckOrder(List<SyncOrderParams> syncOrders, Long loginUserId) {
        if (!syncOrders.isEmpty()) {
            syncOrders.forEach(syncOrder -> {
                if (StringUtils.isBlank(syncOrder.getOrderInternalCode())) {
                    throw new CoolException("单据内码不能为空!!");
                }
                // 明细 lineId(对应 platItemId)不能为空,且同一订单内不能重复
                if (syncOrder.getOrderItems() != null) {
                    Set<String> lineIds = new HashSet<>();
                    for (SyncOrdersItem item : syncOrder.getOrderItems()) {
                        if (StringUtils.isBlank(item.getPlatItemId())) {
                            throw new CoolException("明细 lineId 不能为空!!");
                        }
                        String lineId = item.getPlatItemId().trim();
                        if (!lineIds.add(lineId)) {
                            throw new CoolException("同一订单内明细 lineId 不能重复:" + lineId);
                        }
                    }
                }
                WkOrder wkOrder = new WkOrder();
                // 兼容 wkType 传数字(类型码)或中文(显示名):先按 label 反查 type,否则按原值当 type
                String wkTypeInput = syncOrder.getWkType();
                String typeCode = StringUtils.isBlank(wkTypeInput) ? null : orderWorkTypeService.getTypeByLabel(wkTypeInput);
                if (typeCode == null) {
                    typeCode = wkTypeInput;
                }
                if (StringUtils.isBlank(typeCode) || orderWorkTypeService.getLabelByType(typeCode) == null) {
                    throw new CoolException("单据:" + syncOrder.getOrderNo() + ", 业务类型不存在!!");
                }
                // 订单类型:支持数字 1/2/3、中文「出库单」/「入库单」/「调拨单」或内部码 out/in/transfer(来自字典)
                String typeInput = syncOrder.getType();
                String resolvedOrderType = orderTypeDictService.resolveType(typeInput);
                if (typeInput != null && !typeInput.trim().isEmpty() && resolvedOrderType == null) {
                    throw new CoolException("单据:" + syncOrder.getOrderNo() + ", 订单类型不存在!!");
                }
                Loc serviceOne = null;
                if (!Objects.isNull(syncOrder.getOrgLoc())) {
                    serviceOne = locService.getOne(new LambdaQueryWrapper<Loc>().eq(!Objects.isNull(syncOrder.getOrgLoc()), Loc::getCode, syncOrder.getOrgLoc()));
                }
                if (!Objects.isNull(serviceOne)) {
                    //TODO 添加调拔移库单功能
                } else {
                    // operateType=2 存在则修改、不存在则报错;operateType=1 存在则修改、不存在则新增
                    WkOrder order = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                            .eq(WkOrder::getPoCode, syncOrder.getOrderInternalCode()));
                    if (!Objects.isNull(order)) {
                        assertWkOrderNoLinkedTask(order.getId());
                        assertWkOrderExceStatusUnexecuted(order, "修改");
                        // 存在则修改(1 和 2 均走此处),组托校验在 mergeOrderWithPakin/updateOrderNoPakin 内
                        long pakinCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>()
                                .eq(WaitPakinItem::getAsnId, order.getId()).eq(WaitPakinItem::getDeleted, 0));
                        if (pakinCount > 0) {
                            mergeOrderWithPakin(order, syncOrder, resolvedOrderType, typeCode, loginUserId);
                            if (isDirectWaitPakin()) {
                                syncReceiptAreaByOrder(order.getId());
                            }
                            return;
                        }
                        updateOrderNoPakin(order, syncOrder, loginUserId);
                        if (isDirectWaitPakin()) {
                            syncReceiptAreaByOrder(order.getId());
                        }
                        return;
                    } else if (Integer.valueOf(2).equals(syncOrder.getOperateType())) {
                        // 仅 operateType=2 时要求单据必须存在
                        throw new CoolException("单据不存在,无法修改!!");
                    }
                    String rule = SerialRuleCode.SYS_ASN_ORDER;
                    if (resolvedOrderType != null && resolvedOrderType.equals(OrderType.ORDER_OUT.type)) {
                        rule = SerialRuleCode.SYS_OUT_STOCK_CODE;
                    }
                    // 有 orderNo 则直接作为 WMS 单号 code,否则按规则生成;po_code 存单据内码(orderInternalCode 已校验非空)
                    String wmsCode = StringUtils.isNotBlank(syncOrder.getOrderNo()) ? syncOrder.getOrderNo() : SerialRuleUtils.generateRuleCode(rule, null);
                    String poCodeVal = syncOrder.getOrderInternalCode();
                    wkOrder.setType(resolvedOrderType != null ? resolvedOrderType : syncOrder.getType())
                            .setWkType(typeCode)
                            .setAnfme(QuantityUtils.roundToScale(syncOrder.getAnfme()))
                            .setPoCode(poCodeVal)
                            .setWorkQty(0.0)
                            .setQty(0.0)
                            .setPoId(syncOrder.getOrderId())
                            .setCode(wmsCode)
                            .setArrTime(syncOrder.getArrTime())
                            .setStationId(syncOrder.getStationId())
                            .setId(null)
                            .setCreateTime(syncOrder.getCreateTime() != null ? syncOrder.getCreateTime() : new Date())
                            .setUpdateTime(new Date())
                            .setCreateBy(loginUserId)
                            .setUpdateBy(loginUserId);
                    if (resolvedOrderType != null && resolvedOrderType.equals(OrderType.ORDER_OUT.type)) {
                        wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val);
                    }
                    if (!asnOrderService.save(wkOrder)) {
                        throw new CoolException("单据保存失败!!");
                    }
                    syncOrder.getOrderItems().forEach(orderItem -> {
                        Map<String, Object> map = new ObjectMapper().convertValue(orderItem, Map.class);
                        map.put("orderId", wkOrder.getId());
                        map.put("poId", wkOrder.getPoId());
                        map.put("poCode", wkOrder.getPoCode());
                        map.put("order_code", wkOrder.getCode());
                        map.put("matnrCode", orderItem.getMatnr());
                        if (!asnOrderItemService.fieldsSave(map, loginUserId)) {
                            throw new CoolException("明细保存失败!!");
                        }
                    });
                    List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                            .eq(WkOrderItem::getOrderId, wkOrder.getId()));
                    Double sum = QuantityUtils.roundToScale(orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum());
                    wkOrder.setAnfme(sum);
                    if (!asnOrderService.updateById(wkOrder)) {
                        throw new CoolException("计划收货数量修改失败!!");
                    }
                }
            });
        if (syncOrders == null || syncOrders.isEmpty()) {
            return R.ok();
        }
        return R.ok();
        List<String> msgs = new ArrayList<>(syncOrders.size());
        for (SyncOrderParams syncOrder : syncOrders) {
            msgs.add(syncOneCheckOrder(syncOrder, loginUserId));
        }
        if (msgs.size() == 1) {
            return R.ok(msgs.get(0));
        }
        return R.ok(String.join(";", msgs));
    }
    /**
     * 单条单据同步(新增/修改),返回与现有接口一致的 msg 文案。
     */
    private String syncOneCheckOrder(SyncOrderParams syncOrder, Long loginUserId) {
        if (StringUtils.isBlank(syncOrder.getOrderInternalCode())) {
            throw new CoolException("单据内码不能为空!!");
        }
        if (syncOrder.getOrderItems() != null) {
            Set<String> lineIds = new HashSet<>();
            for (SyncOrdersItem item : syncOrder.getOrderItems()) {
                if (StringUtils.isBlank(item.getPlatItemId())) {
                    throw new CoolException("明细 lineId 不能为空!!");
                }
                String lineId = item.getPlatItemId().trim();
                if (!lineIds.add(lineId)) {
                    throw new CoolException("同一订单内明细 lineId 不能重复:" + lineId);
                }
            }
        }
        WkOrder wkOrder = new WkOrder();
        String wkTypeInput = syncOrder.getWkType();
        String typeCode = StringUtils.isBlank(wkTypeInput) ? null : orderWorkTypeService.getTypeByLabel(wkTypeInput);
        if (typeCode == null) {
            typeCode = wkTypeInput;
        }
        if (StringUtils.isBlank(typeCode) || orderWorkTypeService.getLabelByType(typeCode) == null) {
            throw new CoolException("单据:" + syncOrder.getOrderNo() + ", 业务类型不存在!!");
        }
        String typeInput = syncOrder.getType();
        String resolvedOrderType = orderTypeDictService.resolveType(typeInput);
        if (typeInput != null && !typeInput.trim().isEmpty() && resolvedOrderType == null) {
            throw new CoolException("单据:" + syncOrder.getOrderNo() + ", 订单类型不存在!!");
        }
        Loc serviceOne = null;
        if (!Objects.isNull(syncOrder.getOrgLoc())) {
            serviceOne = locService.getOne(new LambdaQueryWrapper<Loc>().eq(!Objects.isNull(syncOrder.getOrgLoc()), Loc::getCode, syncOrder.getOrgLoc()));
        }
        if (!Objects.isNull(serviceOne)) {
            //TODO 添加调拔移库单功能
            return "Success";
        }
        WkOrder order = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                .eq(WkOrder::getPoCode, syncOrder.getOrderInternalCode()));
        if (!Objects.isNull(order)) {
            assertWkOrderNoLinkedTask(order.getId());
            assertWkOrderExceStatusUnexecuted(order, "修改");
            long pakinCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>()
                    .eq(WaitPakinItem::getAsnId, order.getId()).eq(WaitPakinItem::getDeleted, 0));
            if (pakinCount > 0) {
                String changeSummary = computeWkOrderModifyChangeSummaryOrNull(order, syncOrder, resolvedOrderType, typeCode, true);
                if (changeSummary == null) {
                    return "修改无变化";
                }
                mergeOrderWithPakin(order, syncOrder, resolvedOrderType, typeCode, loginUserId);
                if (isDirectWaitPakin()) {
                    syncReceiptAreaByOrder(order.getId());
                }
                return "修改成功:" + changeSummary;
            }
            String changeSummary = computeWkOrderModifyChangeSummaryOrNull(order, syncOrder, resolvedOrderType, typeCode, false);
            if (changeSummary == null) {
                return "修改无变化";
            }
            updateOrderNoPakin(order, syncOrder, loginUserId);
            if (isDirectWaitPakin()) {
                syncReceiptAreaByOrder(order.getId());
            }
            return "修改成功:" + changeSummary;
        }
        if (Integer.valueOf(2).equals(syncOrder.getOperateType())) {
            throw new CoolException("单据不存在,无法修改!!");
        }
        String rule = SerialRuleCode.SYS_ASN_ORDER;
        if (resolvedOrderType != null && resolvedOrderType.equals(OrderType.ORDER_OUT.type)) {
            rule = SerialRuleCode.SYS_OUT_STOCK_CODE;
        }
        String wmsCode = StringUtils.isNotBlank(syncOrder.getOrderNo()) ? syncOrder.getOrderNo() : SerialRuleUtils.generateRuleCode(rule, null);
        String poCodeVal = syncOrder.getOrderInternalCode();
        wkOrder.setType(resolvedOrderType != null ? resolvedOrderType : syncOrder.getType())
                .setWkType(typeCode)
                .setAnfme(QuantityUtils.roundToScale(syncOrder.getAnfme()))
                .setPoCode(poCodeVal)
                .setWorkQty(0.0)
                .setQty(0.0)
                .setPoId(syncOrder.getOrderId())
                .setCode(wmsCode)
                .setArrTime(syncOrder.getArrTime())
                .setStationId(syncOrder.getStationId())
                .setId(null)
                .setCreateTime(syncOrder.getCreateTime() != null ? syncOrder.getCreateTime() : new Date())
                .setUpdateTime(new Date())
                .setCreateBy(loginUserId)
                .setUpdateBy(loginUserId);
        if (resolvedOrderType != null && resolvedOrderType.equals(OrderType.ORDER_OUT.type)) {
            wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val);
        }
        if (!asnOrderService.save(wkOrder)) {
            throw new CoolException("单据保存失败!!");
        }
        syncOrder.getOrderItems().forEach(orderItem -> {
            Map<String, Object> map = new ObjectMapper().convertValue(orderItem, Map.class);
            map.put("orderId", wkOrder.getId());
            map.put("poId", wkOrder.getPoId());
            map.put("poCode", wkOrder.getPoCode());
            map.put("order_code", wkOrder.getCode());
            map.put("matnrCode", orderItem.getMatnr());
            if (!asnOrderItemService.fieldsSave(map, loginUserId)) {
                throw new CoolException("明细保存失败!!");
            }
        });
        List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>()
                .eq(WkOrderItem::getOrderId, wkOrder.getId()));
        Double sum = QuantityUtils.roundToScale(orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum());
        wkOrder.setAnfme(sum);
        if (!asnOrderService.updateById(wkOrder)) {
            throw new CoolException("计划收货数量修改失败!!");
        }
        return "Success";
    }
    /** 与 updateOrderNoPakin / mergeOrderWithPakin 写入规则一致;无有效变更时返回 null */
    private String computeWkOrderModifyChangeSummaryOrNull(WkOrder order, SyncOrderParams syncOrder,
            String resolvedOrderType, String typeCode, boolean mergeStyleAllowEmptyItems) {
        List<String> parts = new ArrayList<>();
        if (headerDiffForSyncModify(order, syncOrder, resolvedOrderType, typeCode)) {
            parts.add("主单头信息有调整");
        }
        Map<String, SyncOrdersItem> incomingByLineId = new LinkedHashMap<>();
        if (syncOrder.getOrderItems() != null) {
            for (SyncOrdersItem it : syncOrder.getOrderItems()) {
                if (StringUtils.isBlank(it.getPlatItemId())) {
                    throw new CoolException("明细 lineId 不能为空!!");
                }
                incomingByLineId.put(it.getPlatItemId().trim(), it);
            }
        }
        if (!mergeStyleAllowEmptyItems && incomingByLineId.isEmpty()) {
            throw new CoolException("修改时明细不能为空!!");
        }
        List<WkOrderItem> existingItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId()));
        Set<String> existingLineIds = existingItems.stream()
                .map(e -> StringUtils.isNotBlank(e.getPlatItemId()) ? e.getPlatItemId().trim() : null)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        for (WkOrderItem existing : existingItems) {
            String lineId = StringUtils.isNotBlank(existing.getPlatItemId()) ? existing.getPlatItemId().trim() : null;
            if (lineId == null || !incomingByLineId.containsKey(lineId)) {
                String m = StringUtils.defaultString(existing.getMatnrCode());
                if (lineId != null) {
                    parts.add("删除明细 lineId=" + lineId + "(物料 " + m + ")");
                } else {
                    parts.add("删除明细 id=" + existing.getId() + " 无lineId(物料 " + m + ")");
                }
                continue;
            }
            SyncOrdersItem inc = incomingByLineId.get(lineId);
            Double newAnfme = QuantityUtils.roundToScale(inc.getAnfme() != null ? inc.getAnfme() : existing.getAnfme());
            Double oldAnfme = QuantityUtils.roundToScale(existing.getAnfme() != null ? existing.getAnfme() : 0.0);
            boolean anfmeDiff = QuantityUtils.compare(newAnfme, oldAnfme) != 0;
            boolean other = lineNonAnfmeFieldsDifferForModify(existing, inc, newAnfme);
            if (anfmeDiff && other) {
                parts.add("lineId=" + lineId + " 数量 " + oldAnfme + "→" + newAnfme + ",其它字段有变更");
            } else if (anfmeDiff) {
                parts.add("lineId=" + lineId + " 数量 " + oldAnfme + "→" + newAnfme);
            } else if (other) {
                parts.add("lineId=" + lineId + " 明细其它字段有变更");
            }
        }
        for (String key : incomingByLineId.keySet()) {
            if (!existingLineIds.contains(key)) {
                SyncOrdersItem inc = incomingByLineId.get(key);
                String m = StringUtils.defaultString(inc.getMatnr());
                parts.add("新增明细 lineId=" + key + "(物料 " + m + ")");
            }
        }
        if (parts.isEmpty()) {
            return null;
        }
        return String.join(";", parts);
    }
    private boolean headerDiffForSyncModify(WkOrder order, SyncOrderParams syncOrder, String resolvedOrderType, String typeCode) {
        double incAnfme = QuantityUtils.roundToScale(syncOrder.getAnfme() != null ? syncOrder.getAnfme() : 0.0);
        double dbAnfme = QuantityUtils.roundToScale(order.getAnfme() != null ? order.getAnfme() : 0.0);
        if (QuantityUtils.compare(incAnfme, dbAnfme) != 0) {
            return true;
        }
        if (resolvedOrderType != null
                && !Objects.equals(StringUtils.trimToNull(resolvedOrderType), StringUtils.trimToNull(order.getType()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(typeCode), StringUtils.trimToNull(order.getWkType()))) {
            return true;
        }
        if (syncOrder.getArrTime() != null) {
            Date db = order.getArrTime();
            if (db == null || db.getTime() != syncOrder.getArrTime().getTime()) {
                return true;
            }
        }
        if (StringUtils.isNotBlank(syncOrder.getStationId())) {
            if (!StringUtils.equals(StringUtils.trimToNull(syncOrder.getStationId()), StringUtils.trimToNull(order.getStationId()))) {
                return true;
            }
        }
        return false;
    }
    /** 与 updateOrderNoPakin 中行明细更新范围一致(不含计划数量 anfme) */
    private boolean lineNonAnfmeFieldsDifferForModify(WkOrderItem existing, SyncOrdersItem inc, Double newAnfme) {
        if (!Objects.equals(StringUtils.trimToNull(inc.getMatnr()), StringUtils.trimToNull(existing.getMatnrCode()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getMaktx()), StringUtils.trimToNull(existing.getMaktx()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getSpec()), StringUtils.trimToNull(existing.getSpec()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getModel()), StringUtils.trimToNull(existing.getModel()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getUnit()), StringUtils.trimToNull(existing.getStockUnit()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getBatch()), StringUtils.trimToNull(existing.getBatch()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getPlanNo()), StringUtils.trimToNull(existing.getPlanNo()))) {
            return true;
        }
        if (!Objects.equals(StringUtils.trimToNull(inc.getPalletId()), StringUtils.trimToNull(existing.getPalletId()))) {
            return true;
        }
        Double targetQty;
        if (inc.getQty() != null) {
            targetQty = QuantityUtils.roundToScale(inc.getQty());
        } else {
            double curQty = existing.getQty() != null ? existing.getQty() : 0.0;
            if (QuantityUtils.compare(curQty, 0.0) != 0) {
                targetQty = newAnfme;
            } else {
                targetQty = QuantityUtils.roundToScale(existing.getQty());
            }
        }
        return QuantityUtils.compare(targetQty, QuantityUtils.roundToScale(existing.getQty())) != 0;
    }
    /**
@@ -1024,60 +1168,83 @@
    @Override
    @Transactional(timeout = 60, rollbackFor = Exception.class)
    public R syncOrderDelete(List<SyncOrderParams> orders) {
        orders.forEach(order -> {
            // operateType=3:存在则判断是否可以取消;主要校验单据内码(orderInternalCode),orderNo 查到 1 条继续,查到多条则报错强调使用 orderInternalCode
            WkOrder wkOrder = null;
            if (StringUtils.isNotEmpty(order.getOrderInternalCode())) {
                wkOrder = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                        .eq(WkOrder::getPoCode, order.getOrderInternalCode()));
        if (orders == null || orders.isEmpty()) {
            return R.ok();
        }
        List<String> msgs = new ArrayList<>(orders.size());
        for (SyncOrderParams order : orders) {
            msgs.add(syncOneOrderDelete(order));
        }
        if (msgs.size() == 1) {
            return R.ok(msgs.get(0));
        }
        return R.ok(String.join(";", msgs));
    }
    /**
     * 单条取消:已删或查无单据时返回幂等成功文案;成功删除返回「取消成功」。
     */
    private String syncOneOrderDelete(SyncOrderParams order) {
        WkOrder wkOrder = null;
        if (StringUtils.isNotEmpty(order.getOrderInternalCode())) {
            wkOrder = asnOrderService.getOne(new LambdaQueryWrapper<WkOrder>()
                    .eq(WkOrder::getPoCode, order.getOrderInternalCode()));
        }
        if (wkOrder == null && StringUtils.isNotEmpty(order.getOrderNo())) {
            List<WkOrder> list = asnOrderService.list(new LambdaQueryWrapper<WkOrder>()
                    .eq(WkOrder::getCode, order.getOrderNo()));
            if (list.isEmpty()) {
                return "单据已不存在或已取消";
            }
            if (wkOrder == null && StringUtils.isNotEmpty(order.getOrderNo())) {
                List<WkOrder> list = asnOrderService.list(new LambdaQueryWrapper<WkOrder>()
                        .eq(WkOrder::getCode, order.getOrderNo()));
                if (list.isEmpty()) {
                    throw new CoolException("单据不存在,无法取消!!");
                }
                if (list.size() > 1) {
                    throw new CoolException("单号对应多条单据,请使用单据内码(orderInternalCode)唯一指定后再取消!!");
                }
                wkOrder = list.get(0);
            if (list.size() > 1) {
                throw new CoolException("单号对应多条单据,请使用单据内码(orderInternalCode)唯一指定后再取消!!");
            }
            if (wkOrder == null) {
                throw new CoolException("单据不存在,无法取消!!请提供单据内码(orderInternalCode)或单号(orderNo)。");
            wkOrder = list.get(0);
        }
        if (wkOrder == null) {
            if (StringUtils.isEmpty(order.getOrderInternalCode()) && StringUtils.isEmpty(order.getOrderNo())) {
                throw new CoolException("请提供单据内码(orderInternalCode)或单号(orderNo)。");
            }
            final WkOrder finalWkOrder = wkOrder;
            assertWkOrderNoLinkedTask(finalWkOrder.getId());
            // 已组托不可取消
            long pakinCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>()
                    .eq(WaitPakinItem::getAsnId, finalWkOrder.getId()));
            if (pakinCount > 0) {
                throw new CoolException("单据已组托,仅未组托状态可取消,请先解除组托!!");
            }
            assertWkOrderExceStatusForCancel(finalWkOrder);
            order.getOrderItems().forEach(orderItem -> {
                if (!asnOrderItemService.remove(new LambdaQueryWrapper<WkOrderItem>()
                        .eq(WkOrderItem::getMatnrCode, orderItem.getMatnr())
                        .eq(StringUtils.isNotEmpty(orderItem.getBatch()), WkOrderItem::getSplrBatch, orderItem.getBatch())
                        .eq(StringUtils.isNotEmpty(orderItem.getPlatItemId()), WkOrderItem::getPlatItemId, orderItem.getPlatItemId())
                        .eq(WkOrderItem::getOrderCode, finalWkOrder.getCode()))) {
                    throw new CoolException("单据明细删除失败!!");
                }
                List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, finalWkOrder.getId()));
                if (orderItems.isEmpty()) {
                    if (!asnOrderService.removeById(finalWkOrder.getId())) {
                        throw new CoolException("单据删除失败!!");
                    }
                } else {
                    Double sum = QuantityUtils.roundToScale(orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum());
                    if (!asnOrderService.update(new LambdaUpdateWrapper<WkOrder>()
                            .eq(WkOrder::getId, finalWkOrder.getId())
                            .set(WkOrder::getAnfme, sum))) {
                        throw new CoolException("主单数量修改失败!!");
                    }
                }
            });
        });
        return R.ok();
            return "单据已不存在或已取消";
        }
        final WkOrder finalWkOrder = wkOrder;
        assertWkOrderNoLinkedTask(finalWkOrder.getId());
        long pakinCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>()
                .eq(WaitPakinItem::getAsnId, finalWkOrder.getId()));
        if (pakinCount > 0) {
            throw new CoolException("单据已组托,仅未组托状态可取消,请先解除组托!!");
        }
        assertWkOrderExceStatusForCancel(finalWkOrder);
        long itemCnt = asnOrderItemService.count(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, finalWkOrder.getId()));
        if (itemCnt > 0 && !asnOrderItemService.remove(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, finalWkOrder.getId()))) {
            throw new CoolException("单据明细删除失败!!");
        }
        if (!asnOrderService.removeById(finalWkOrder.getId())) {
            return "单据已不存在或已取消";
        }
        return "取消成功";
//            order.getOrderItems().forEach(orderItem -> {
//                if (!asnOrderItemService.remove(new LambdaQueryWrapper<WkOrderItem>()
//                        .eq(WkOrderItem::getMatnrCode, orderItem.getMatnr())
//                        .eq(StringUtils.isNotEmpty(orderItem.getBatch()), WkOrderItem::getSplrBatch, orderItem.getBatch())
//                        .eq(StringUtils.isNotEmpty(orderItem.getPlatItemId()), WkOrderItem::getPlatItemId, orderItem.getPlatItemId())
//                        .eq(WkOrderItem::getOrderCode, finalWkOrder.getCode()))) {
//                    throw new CoolException("单据明细删除失败!!");
//                }
//                List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, finalWkOrder.getId()));
//                if (orderItems.isEmpty()) {
//                    if (!asnOrderService.removeById(finalWkOrder.getId())) {
//                        throw new CoolException("单据删除失败!!");
//                    }
//                } else {
//                    Double sum = QuantityUtils.roundToScale(orderItems.stream().mapToDouble(WkOrderItem::getAnfme).sum());
//                    if (!asnOrderService.update(new LambdaUpdateWrapper<WkOrder>()
//                            .eq(WkOrder::getId, finalWkOrder.getId())
//                            .set(WkOrder::getAnfme, sum))) {
//                        throw new CoolException("主单数量修改失败!!");
//                    }
//                }
//            });
    }
    /**
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/AsnOrderServiceImpl.java
@@ -382,6 +382,10 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R removeOrders(List<Long> ids) {
        long existOrderCnt = this.count(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getId, ids));
        if (existOrderCnt == 0) {
            return R.ok("单据已不存在或已删除");
        }
        // 已组托不可删除,需先解除组托
        long palletizedCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>()
                .in(WaitPakinItem::getAsnId, ids).eq(WaitPakinItem::getDeleted, 0));
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -265,7 +265,7 @@
        }
        WkOrder order = this.getById(id);
        if (Objects.isNull(order)) {
            throw new CoolException("单据不存在!!");
            return R.ok("单据已不存在或已取消");
        }
        if (!order.getExceStatus().equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)) {
            throw new CoolException("当前单据状态为" + AsnExceStatus.getExceStatus(order.getExceStatus()) + ", 不可执行取消操作!!");
@@ -295,10 +295,10 @@
        }
        if (!this.remove(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getId, id))) {
            throw new CoolException("主单删除失败!!");
            return R.ok("单据已不存在或已取消");
        }
        outStockItemService.remove(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, id));
        return R.ok("操作成功");
        return R.ok("取消成功");
    }
    /**
@@ -889,11 +889,13 @@
    @Transactional(rollbackFor = Exception.class)
    public R cancelOutOrderByItems(List<WkOrderItem> orderItems) {
        Map<Long, List<WkOrderItem>> listMap = orderItems.stream().collect(Collectors.groupingBy(WkOrderItem::getOrderId));
        boolean anyProcessed = false;
        for (Long key : listMap.keySet()) {
            WkOrder order = this.getById(key);
            if (Objects.isNull(order)) {
                throw new CoolException("单据不存在!!");
                continue;
            }
            anyProcessed = true;
            List<WkOrderItem> items = listMap.get(key);
            if (!items.isEmpty()) {
                for (WkOrderItem orderItem : items) {
@@ -916,14 +918,11 @@
            }
            if (!this.remove(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getId, key))) {
                throw new CoolException("主单删除失败!!");
                continue;
            }
            if (!outStockItemService.remove(new LambdaQueryWrapper<WkOrderItem>()
                    .eq(WkOrderItem::getOrderId, key))) {
                throw new CoolException("单据明细删除失败!!");
            }
            outStockItemService.remove(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, key));
        }
        return R.ok("操作成功");
        return R.ok(anyProcessed ? "取消成功" : "单据已不存在或已取消");
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/TaskServiceImpl.java
@@ -945,14 +945,24 @@
        List<Integer> list = Arrays.asList(TaskType.TASK_TYPE_IN.type, TaskType.TASK_TYPE_OUT.type, TaskType.TASK_TYPE_PICK_AGAIN_OUT.type,
                TaskType.TASK_TYPE_CHECK_OUT.type, TaskType.TASK_TYPE_EMPITY_IN.type, TaskType.TASK_TYPE_LOC_MOVE.type,
                TaskType.TASK_TYPE_EMPITY_OUT.type, TaskType.TASK_TYPE_MERGE_OUT.type);
        List<Task> allTasks = this.list(new LambdaQueryWrapper<Task>()
                .in(Task::getTaskType, list)
                .in(Task::getId, (Object[]) ids));
        if (allTasks.isEmpty()) {
        List<Task> tasksById = this.list(new LambdaQueryWrapper<Task>().in(Task::getId, (Object[]) ids));
        if (tasksById.isEmpty()) {
            throw new CoolException("任务不存在!!");
        }
        // 拣料出库/盘点出库进入再入库阶段后禁止取消(任务类型已由 103/107 变为再入库)
        for (Task t : tasksById) {
            if (TaskType.TASK_TYPE_PICK_IN.type.equals(t.getTaskType())
                    || TaskType.TASK_TYPE_CHECK_IN.type.equals(t.getTaskType())) {
                throw new CoolException("拣料/盘点出库已进入再入库阶段,禁止取消!!");
            }
        }
        List<Task> allTasks = tasksById.stream()
                .filter(t -> list.contains(t.getTaskType()))
                .collect(Collectors.toList());
        if (allTasks.isEmpty()) {
            throw new CoolException("当前任务类型不支持取消!!");
        }
        // 收集需要取消的RCS任务编号和批次编号(不限制状态,只要已下发到RCS就需要取消)
        List<String> rcsTaskCodes = new ArrayList<>();
        String batchNo = null;
@@ -1029,6 +1039,10 @@
                log.info("RCS取消任务响应状态码:{}", exchange.getStatusCode());
                log.info("RCS取消任务响应体:{}", exchange.getBody());
                if (!exchange.getStatusCode().is2xxSuccessful()) {
                    throw new CoolException("RCS取消任务失败:HTTP " + exchange.getStatusCode().value());
                }
                if (Objects.isNull(exchange.getBody())) {
                    log.error("RCS取消任务失败:响应体为空");
                    throw new CoolException("RCS取消任务失败:响应体为空");
@@ -1058,7 +1072,7 @@
            }
        }
        
        // 可取消状态:原 1/101(不含 199);拣料/盘点出库 RCS 执行中(<198);拣料/盘点再入库(53/57)不支持取消
        // 可取消状态:原 1/101(不含 199);拣料/盘点出库 RCS 执行中(<198);再入库(53/57)在方法入口已禁止取消
        List<Task> tasks = this.list(new LambdaQueryWrapper<Task>()
                .in(Task::getTaskType, list)
                .in(Task::getId, (Object[]) ids)