1
heshaofeng
2026-03-16 569b9900086f06fbd986f69534c8d5af54c4d55b
1
已修改9个文件
478 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/views/inbound/Dt_AllocateOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/inbound/allocateinboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/allocateoutboundOrder.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outPicking.vue 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/CalcOut/OutboundCompleteRequestDTO.cs 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundService.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/Dt_AllocateOrder.vue
@@ -209,7 +209,7 @@
          title: "回传MES状态",
          field: "returnToMESStatus",
          type: "select",
          dataKey: "returnToMESStatus",
          dataKey: "outboundReturnToMESStatus",
          data: [],
        },
        { title: "物料编号", field: "materielCode", type: "like" },
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/inbound/allocateinboundOrder.vue
@@ -94,7 +94,7 @@
          title: "回传MES状态",
          field: "returnToMESStatus",
          type: "select",
          dataKey: "returnToMESStatus",
          dataKey: "outboundReturnToMESStatus",
          data: [],
        },
        { title: "物料编号", field: "materielCode", type: "like" },
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/allocateoutboundOrder.vue
@@ -103,7 +103,7 @@
          title: "回传MES状态",
          field: "returnToMESStatus",
          type: "select",
          dataKey: "returnToMESStatus",
          dataKey: "outboundReturnToMESStatus",
          data: [],
        },
        { title: "物料编号", field: "materielCode", type: "like" },
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -214,6 +214,19 @@
                            <el-table-column prop="createDate" label="拣选时间" width="160" />
                            <el-table-column prop="originalBarcode" label="原物料码" width="160" />
                            <el-table-column prop="newBarcode" label="新物料码" width="160" />
                            <el-table-column label="操作" width="100" align="center">
                        <template #default="scope">
                            <el-button
                                type="danger"
                                size="small"
                                icon="el-icon-refresh-left"
                                @click="handleReversePicking(scope.row)"
                                :disabled="reverseLoading"
                            >
                                æ’¤é”€
                            </el-button>
                        </template>
                    </el-table-column>
                        </el-table>
                        <div class="table-footer">
@@ -376,7 +389,8 @@
            loadingInstance: null,
            // éŸ³é¢‘实例(缓存,避免重复创建)
            successAudio: null,
            errorAudio: null
            errorAudio: null,
            reverseLoading: false
        }
    },
    computed: {
@@ -412,7 +426,6 @@
            }
        },
        // ============== æ–°å¢žï¼šæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·ï¼ˆæ ¸å¿ƒåŠŸèƒ½ï¼‰ ==============
        async getOrderNoByPallet(palletCode) {
            if (!palletCode) {
                this.$message.warning('托盘号不能为空');
@@ -457,7 +470,7 @@
                this.hideFullScreenLoading();
            }
        },
        // ============== æ–°å¢žç»“束 ==============
        // æ’­æ”¾æˆåŠŸéŸ³é¢‘
        playSuccessAudio() {
@@ -485,14 +498,7 @@
        initPage() {
            // ä»Žè·¯ç”±å‚数获取订单号
            this.orderNo = this.$route.query.orderNo || ''
            // ============== å¾®è°ƒï¼šæ³¨é‡Šå¼ºåˆ¶è¿”回逻辑,兼容托盘号查订单号 ==============
            // if (!this.orderNo) {
            //     this.$message.error('订单号不能为空')
            //     this.$router.back()
            //     return
            // }
            // ============== å¾®è°ƒç»“束 ==============
            // åŠ è½½è®¢å•ä¿¡æ¯
            this.loadOrderInfo()
@@ -694,6 +700,55 @@
            });
        },
        async handleReversePicking(row) {
            // 1. äºŒæ¬¡ç¡®è®¤
            try {
                await this.$confirm(
                    '确定要撤销这条拣选记录吗?撤销后库存将恢复,出库单状态也会同步更新',
                    '撤销确认',
                    {
                        type: 'warning',
                        confirmButtonText: '确认撤销',
                        cancelButtonText: '取消',
                        closeOnClickModal: false
                    }
                );
                // 2. æ‰§è¡Œæ’¤é”€
                this.reverseLoading = true;
                this.showFullScreenLoading();
                // 3. è°ƒç”¨æ’¤é”€æŽ¥å£ï¼ˆé€‚配你后端的撤销接口地址)
                const response = await this.http.post('/api/Outbound/ReversePickingWithBarcode', {
                    Barcode: row.originalBarcode || row.newBarcode, // ä¼˜å…ˆå–原条码,无则取新条码
                    OrderNo: this.orderNo,
                    PalletCode: row.palletCode || this.scanForm.palletCode,
                    Operator: this.getUserName(),
                    OrderType: this.orderInfo?.orderType || 0 // è®¢å•类型,无则传0
                });
                // 4. å¤„理结果
                if (response.status) {
                    this.$message.success('撤销拣选成功');
                    this.playSuccessAudio(); // æ’­æ”¾æˆåŠŸéŸ³é¢‘
                    // åˆ·æ–°æ•°æ®åˆ—表
                    await this.loadPalletData(false);
                } else {
                    this.$message.error(response.message || '撤销拣选失败');
                    this.playErrorAudio();
                }
            } catch (error) {
                // å–消确认不报错
                if (error !== 'cancel') {
                    this.$message.error('撤销拣选失败:' + (error.message || '网络异常'));
                    this.playErrorAudio();
                }
            } finally {
                this.reverseLoading = false;
                this.hideFullScreenLoading();
            }
        },
        handleMaterialScan() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/CalcOut/OutboundCompleteRequestDTO.cs
@@ -76,4 +76,70 @@
        public string barCode { get; set; }
    }
    /// <summary>
    /// æ’¤é”€æ‹£é€‰è¯·æ±‚DTO
    /// </summary>
    public class ReversePickingRequestDTO
    {
        /// <summary>
        /// è¦æ’¤é”€çš„æ‹£é€‰æ¡ç ï¼ˆå¿…填)
        /// </summary>
        [Required(ErrorMessage = "条码不能为空")]
        [MaxLength(50, ErrorMessage = "条码长度不能超过50个字符")]
        public string Barcode { get; set; }
        /// <summary>
        /// å‡ºåº“单号(必填)
        /// </summary>
        [Required(ErrorMessage = "出库单号不能为空")]
        [MaxLength(50, ErrorMessage = "出库单号长度不能超过50个字符")]
        public string OrderNo { get; set; }
        /// <summary>
        /// æ‰˜ç›˜å·ï¼ˆå¿…填)
        /// </summary>
        [Required(ErrorMessage = "托盘号不能为空")]
        [MaxLength(50, ErrorMessage = "托盘号长度不能超过50个字符")]
        public string PalletCode { get; set; }
        /// <summary>
        /// æ“ä½œè€…(必填)
        /// </summary>
        [Required(ErrorMessage = "操作者不能为空")]
        [MaxLength(50, ErrorMessage = "操作者长度不能超过50个字符")]
        public string Operator { get; set; }
        /// <summary>
        /// è®¢å•类型(可选,智仓调拨专用)
        /// </summary>
        [Range(0, int.MaxValue, ErrorMessage = "订单类型必须为非负整数")]
        public int OrderType { get; set; }
    }
    /// <summary>
    /// æ’¤é”€æ‹£é€‰å“åº”DTO
    /// </summary>
    public class ReversePickingResponseDTO
    {
        /// <summary>
        /// æ˜¯å¦æˆåŠŸ
        /// </summary>
        public bool Success { get; set; }
        /// <summary>
        /// æç¤ºä¿¡æ¯
        /// </summary>
        public string Message { get; set; }
        /// <summary>
        /// æ’¤é”€çš„æ¡ç 
        /// </summary>
        public string Barcode { get; set; }
        /// <summary>
        /// æ¢å¤çš„库存数量
        /// </summary>
        public decimal RestoredQuantity { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundService.cs
@@ -69,6 +69,8 @@
        /// <returns></returns>
        WebResponseContent RecheckPicking(RecheckPickingDTO pickingDTO);
        WebResponseContent ReversePicking(ReversePickingRequestDTO reversePickingRequestDTO);
        WebResponseContent PrintFromData(string barcode);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -1970,6 +1970,7 @@
                OperateType = "出库完成",
                InsertTime = DateTime.Now,
                StockId = stockDetail.StockId,
                Barcode = stockDetail.Barcode,
                MaterielCode = stockDetail.MaterielCode,
                MaterielName = stockDetail.MaterielName,
                OrderNo = stockDetail.OrderNo,
@@ -2444,6 +2445,300 @@
        #endregion
        #region æ’¤é”€æ‹£é€‰
        /// <summary>
        /// æ’¤é”€æ‹£é€‰æ¡ç ï¼ˆåå‘回滚出库拣选操作)
        /// </summary>
        /// <param name="request">撤销拣选请求(至少包含条码、订单号、托盘号)</param>
        /// <returns>撤销响应</returns>
        public WebResponseContent ReversePicking(ReversePickingRequestDTO request)
        {
            WebResponseContent content = WebResponseContent.Instance;
            ReversePickingResponseDTO response = new ReversePickingResponseDTO();
            try
            {
                if (string.IsNullOrWhiteSpace(request.Barcode) || string.IsNullOrWhiteSpace(request.OrderNo) || string.IsNullOrWhiteSpace(request.PalletCode))
                {
                    response.Success = false;
                    response.Message = "条码、订单号、托盘号不能为空";
                    return WebResponseContent.Instance.Error(response.Message);
                }
                Dt_StockInfo stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == request.PalletCode);
                if (stockInfo == null)
                {
                    response.Success = false;
                    response.Message = $"托盘号 {request.PalletCode} å¯¹åº”的库存不存在";
                    return WebResponseContent.Instance.Error(response.Message);
                }
                Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(o => o.OrderNo == request.OrderNo);
                if (outboundOrder == null)
                {
                    response.Success = false;
                    response.Message = $"出库单 {request.OrderNo} ä¸å­˜åœ¨";
                    return WebResponseContent.Instance.Error(response.Message);
                }
                Dt_StockInfoDetail_Hty historyDetail = _stockDetailHistoryRepository.QueryFirst(x =>
                    x.Barcode == request.Barcode &&
                    (x.OperateType == "出库完成" || x.OperateType == "拆包-原始记录"));
                if (historyDetail == null)
                {
                    response.Success = false;
                    response.Message = $"条码 {request.Barcode} å·²æ‹†åŒ…,无法撤销";
                    return WebResponseContent.Instance.Error(response.Message);
                }
                Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
                    x.OrderNo == request.OrderNo &&
                    x.StockId == stockInfo.Id &&
                    x.MaterielCode == historyDetail.MaterielCode &&
                    x.PalletCode == stockInfo.PalletCode);
                _unitOfWorkManage.BeginTran();
                try
                {
                    if(lockInfo == null)
                    {
                        return WebResponseContent.Instance.Error("该托盘已全部拣选完,不允许撤销");
                    }
                    bool isUnpack = historyDetail.OperateType == "拆包-原始记录";
                    if (isUnpack)
                    {
                        return WebResponseContent.Instance.Error("该条码已拆包,不允许撤销");
                    }
                    else
                    {
                        ReverseFullOutboundOperation(historyDetail, stockInfo, request);
                    }
                    RollbackOutboundOrderDetails(historyDetail.MaterielCode, request.OrderNo, historyDetail.StockQuantity, stockInfo.Id);
                    if (lockInfo != null)
                    {
                        lockInfo.SortedQuantity = Math.Max(0, (decimal)(lockInfo.SortedQuantity - historyDetail.StockQuantity));
                        if (lockInfo.SortedQuantity == 0)
                        {
                            _outboundLockInfoRepository.UpdateData(lockInfo);
                        }
                        else
                        {
                            _outboundLockInfoRepository.UpdateData(lockInfo);
                        }
                    }
                    _stockDetailHistoryRepository.DeleteData(historyDetail);
                    DeleteStockChangeRecord(request.Barcode, request.OrderNo);
                    if (!CheckOutboundOrderCompleted(request.OrderNo))
                    {
                        UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.出库中.ObjToInt());
                    }
                    _unitOfWorkManage.CommitTran();
                    response.Success = true;
                    response.Message = $"条码 {request.Barcode} æ’¤é”€æ‹£é€‰æˆåŠŸ";
                    response.Barcode = request.Barcode;
                    response.RestoredQuantity = historyDetail.StockQuantity;
                    content = WebResponseContent.Instance.OK(data: response);
                }
                catch (Exception ex)
                {
                    _unitOfWorkManage.RollbackTran();
                    response.Success = false;
                    response.Message = $"撤销拣选失败:{ex.Message}";
                    content = WebResponseContent.Instance.Error(response.Message);
                }
            }
            catch (Exception ex)
            {
                response.Success = false;
                response.Message = $"处理撤销拣选失败:{ex.Message}";
                content = WebResponseContent.Instance.Error(response.Message);
            }
            return content;
        }
        /// <summary>
        /// æ’¤é”€æ‹†åŒ…出库操作(反向恢复拆包前状态)
        /// </summary>
        private void ReverseUnpackOperation(Dt_StockInfoDetail_Hty historyDetail, Dt_StockInfo stockInfo, ReversePickingRequestDTO request, Dt_OutboundOrder outboundOrder)
        {
            Dt_StockInfoDetail currentDetail = _stockDetailRepository.QueryFirst(x =>
                x.Barcode == request.Barcode &&
                x.StockId == stockInfo.Id &&
                x.MaterielCode == historyDetail.MaterielCode);
            if (currentDetail != null)
            {
                currentDetail.StockQuantity = historyDetail.StockQuantity;
                currentDetail.Remark = $"撤销拆包拣选,恢复原始数量:{historyDetail.StockQuantity},操作者:{request.Operator}";
                _stockDetailRepository.UpdateData(currentDetail);
            }
            else
            {
                Dt_StockInfoDetail restoreDetail = new Dt_StockInfoDetail
                {
                    StockId = historyDetail.StockId,
                    MaterielCode = historyDetail.MaterielCode,
                    MaterielName = historyDetail.MaterielName,
                    Barcode =historyDetail.Barcode,
                    OrderNo = historyDetail.OrderNo,
                    BatchNo = historyDetail.BatchNo,
                    ProductionDate = historyDetail.ProductionDate,
                    EffectiveDate = historyDetail.EffectiveDate,
                    SerialNumber = historyDetail.SerialNumber,
                    StockQuantity = historyDetail.StockQuantity,
                    OutboundQuantity = historyDetail.OutboundQuantity,
                    Status = historyDetail.Status,
                    Unit = historyDetail.Unit,
                    InboundOrderRowNo = historyDetail.InboundOrderRowNo,
                    SupplyCode = historyDetail.SupplyCode,
                    Creater = request.Operator,
                    CreateDate = DateTime.Now,
                    FactoryArea = historyDetail.FactoryArea,
                    WarehouseCode = historyDetail.WarehouseCode,
                    Remark = $"撤销拆包拣选,重新创建库存明细,条码:{request.Barcode},操作者:{request.Operator}"
                };
                _stockDetailRepository.AddData(restoreDetail);
            }
            List<Dt_MaterialCodeInfo> materialCodeInfos = _basicService.MaterielCodeInfoService.Repository.QueryData(x =>
                x.OldBarcode == request.Barcode &&
                x.OrderNo == request.OrderNo);
            if (materialCodeInfos.Any())
            {
                _basicService.MaterielCodeInfoService.Repository.DeleteData(materialCodeInfos);
            }
            if (outboundOrder.OrderType == InOrderTypeEnum.InternalAllocat.ObjToInt())
            {
                Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfoRepository.QueryFirst(x =>
                    x.Barcode == request.Barcode &&
                    x.OrderNo == request.OrderNo);
                if (allocateMaterialInfo != null)
                {
                    _allocateMaterialInfoRepository.DeleteData(allocateMaterialInfo);
                }
            }
        }
        /// <summary>
        /// æ’¤é”€å®Œæ•´å‡ºåº“操作(恢复被删除的库存明细)
        /// </summary>
        private void ReverseFullOutboundOperation(Dt_StockInfoDetail_Hty historyDetail, Dt_StockInfo stockInfo, ReversePickingRequestDTO request)
        {
            Dt_StockInfoDetail restoreDetail = new Dt_StockInfoDetail
            {
                StockId = historyDetail.StockId,
                MaterielCode = historyDetail.MaterielCode,
                MaterielName = historyDetail.MaterielName,
                Barcode = historyDetail.Barcode,
                OrderNo = historyDetail.OrderNo,
                BatchNo = historyDetail.BatchNo,
                ProductionDate = historyDetail.ProductionDate,
                EffectiveDate = historyDetail.EffectiveDate,
                SerialNumber = historyDetail.SerialNumber,
                StockQuantity = historyDetail.StockQuantity,
                OutboundQuantity = historyDetail.OutboundQuantity - historyDetail.StockQuantity,
                Status = historyDetail.Status,
                Unit = historyDetail.Unit,
                InboundOrderRowNo = historyDetail.InboundOrderRowNo,
                SupplyCode = historyDetail.SupplyCode,
                Creater = request.Operator,
                CreateDate = DateTime.Now,
                FactoryArea = historyDetail.FactoryArea,
                WarehouseCode = historyDetail.WarehouseCode,
                Remark = $"撤销完整出库拣选,恢复库存明细,条码:{request.Barcode},操作者:{request.Operator}"
            };
            _stockDetailRepository.AddData(restoreDetail);
            if (request.OrderType == InOrderTypeEnum.InternalAllocat.ObjToInt())
            {
                Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfoRepository.QueryFirst(x =>
                    x.Barcode == request.Barcode &&
                    x.OrderNo == request.OrderNo);
                if (allocateMaterialInfo != null)
                {
                    _allocateMaterialInfoRepository.DeleteData(allocateMaterialInfo);
                }
            }
        }
        /// <summary>
        /// å›žæ»šå‡ºåº“单明细(扣减已出库数量)
        /// </summary>
        private void RollbackOutboundOrderDetails(string materielCode, string orderNo, decimal rollbackQuantity, int stockId)
        {
            Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == orderNo);
            List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x =>
                x.OrderId == outboundOrder.Id &&
                x.MaterielCode == materielCode &&
                x.OverOutQuantity > 0);
            if (!details.Any()) return;
            decimal remainingRollbackQty = rollbackQuantity;
            foreach (var detail in details)
            {
                if (remainingRollbackQty <= 0) break;
                decimal rollbackQty = Math.Min(remainingRollbackQty, detail.OverOutQuantity);
                detail.OverOutQuantity -= rollbackQty;
                detail.CurrentDeliveryQty -= rollbackQty;
                if (detail.OrderDetailStatus == (int)OrderDetailStatusEnum.Over && detail.OverOutQuantity < detail.OrderQuantity - detail.MoveQty)
                {
                    detail.OrderDetailStatus = (int)OrderDetailStatusEnum.New;
                }
                if (!string.IsNullOrEmpty(detail.ReturnJsonData))
                {
                    List<Barcodes> barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(detail.ReturnJsonData) ?? new List<Barcodes>();
                    var targetBarcode = barcodesList.FirstOrDefault(x => x.Barcode == materielCode);
                    if (targetBarcode != null)
                    {
                        barcodesList.Remove(targetBarcode);
                        detail.ReturnJsonData = JsonConvert.SerializeObject(barcodesList, new JsonSerializerSettings
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        });
                    }
                }
                _detailRepository.UpdateData(detail);
                remainingRollbackQty -= rollbackQty;
            }
        }
        /// <summary>
        /// åˆ é™¤åº“存变动记录(拣选时生成的)
        /// </summary>
        private void DeleteStockChangeRecord(string barcode, string orderNo)
        {
            Dt_StockQuantityChangeRecord changeRecord = _stockChangeRepository.QueryFirst(x =>
                x.OriginalSerilNumber == barcode &&
                x.OrderNo == orderNo &&
                x.ChangeType == (int)StockChangeTypeEnum.Outbound);
            if (changeRecord != null)
            {
                _stockChangeRepository.DeleteData(changeRecord);
            }
        }
        #endregion
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoDetailService.cs
@@ -38,7 +38,7 @@
        {
            var sugarQueryable = Db.Queryable<Dt_StockInfoDetail>().InnerJoin<Dt_StockInfo>((detail, item) => detail.StockId == item.Id)
                .Where((detail, item) => item.StockStatus == StockStatusEmun.入库完成.ObjToInt());
                .Where((detail, item) => item.StockStatus == StockStatusEmun.入库完成.ObjToInt() || item.StockStatus == StockStatusEmun.出库完成.ObjToInt());
            Dictionary<string, SqlSugar.OrderByType> orderbyDic = GetPageDataSort(options, TProperties);
            List<OrderByModel> orderByModels = new List<OrderByModel>();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs
@@ -64,6 +64,36 @@
            }
        }
        /// <summary>
        /// æ’¤é”€æ‹£é€‰æ¡ç æŽ¥å£ï¼ˆåå‘回滚拣选操作)
        /// </summary>
        /// <param name="request">撤销拣选请求参数</param>
        /// <returns>接口响应结果</returns>
        [HttpPost, Route("ReversePickingWithBarcode"), AllowAnonymous]
        public WebResponseContent ReversePickingWithBarcode([FromBody] ReversePickingRequestDTO request)
        {
            try
            {
                lock (lockObj)
                {
                    if (!ModelState.IsValid)
                    {
                        string errorMsg = string.Join("; ", ModelState.Values
                            .SelectMany(v => v.Errors)
                            .Select(e => e.ErrorMessage));
                        return WebResponseContent.Instance.Error(errorMsg);
                    }
                    return _outboundService.ReversePicking(request);
                }
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"撤销拣选操作失败: {ex.Message}");
            }
        }
        [HttpPost, Route("CompleteOutboundWithPallet"), AllowAnonymous]
        public WebResponseContent CompleteOutboundWithPallet([FromBody] OutboundCompletePalletRequestDTO request)
        {