pan
2025-11-25 bbc4a3a07baf111c9074ceee7728158fb3eedf1a
提交
已修改7个文件
492 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs 162 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/ESSController.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue
@@ -935,21 +935,16 @@
              
              if (res.status) {
                successCount++;
                 this.$message.success(`成功取消`);
              } else {
                errorCount++;
                console.error(`取消拣选失败: ${row.Barcode}`, res.message);
                 this.$message.warning(`取消拣选失败: ${row.currentBarcode} - ${res.message}`);
              }
            } catch (error) {
              errorCount++;
              console.error(`取消拣选失败: ${row.Barcode}`, error);
              this.$message.warning(`取消拣选失败: ${row.currentBarcode} - ${error.message}` );
            }
          }
          if (errorCount === 0) {
            this.$message.success(`成功取消 ${successCount} é¡¹`);
          } else {
            this.$message.warning(`成功取消 ${successCount} é¡¹ï¼Œå¤±è´¥ ${errorCount} é¡¹`);
          }
          }
          
          this.loadData();
          this.selectedPickedRows = [];
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/InvokeMESService.cs
@@ -103,7 +103,7 @@
            return JsonConvert.DeserializeObject<ResponseModel>(body);
        }
        public async Task<ResponseModel> FeedbackAllocate(AllocateDto model)
        public  async Task<ResponseModel> FeedbackAllocate(AllocateDto model)
        {
            string json = JsonConvert.SerializeObject(model, new JsonSerializerSettings
            {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -21,9 +21,9 @@
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        public IRepository<Dt_OutboundOrderDetail> Repository => BaseDal;
        private readonly IStockService _stockService;
        private readonly IOutStockLockInfoService _outStockLockInfoService;
        private readonly ILocationInfoService _locationInfoService;
        private readonly IBasicService _basicService;
@@ -68,16 +68,16 @@
            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
            List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
            // æŒ‰ç‰©æ–™å’Œæ‰¹æ¬¡åˆ†ç»„处理
            // æŒ‰ç‰©æ–™ã€æ‰¹æ¬¡ã€ä¾›åº”商分组
            var groupDetails = outboundOrderDetails
                .GroupBy(x => new { x.MaterielCode, x.BatchNo ,x.SupplyCode})
                .GroupBy(x => new { x.MaterielCode, x.BatchNo, x.SupplyCode })
                .Select(x => new
                {
                    MaterielCode = x.Key.MaterielCode,
                    BatchNo = x.Key.BatchNo,
                    SupplyCode = x.Key.SupplyCode,
                    x.Key.MaterielCode,
                    x.Key.BatchNo,
                    x.Key.SupplyCode,
                    Details = x.ToList(),
                    TotalNeedQuantity = x.Sum(v => v.OrderQuantity - v.OverOutQuantity - v.LockQuantity-v.MoveQty)
                    TotalNeedQuantity = x.Sum(v => v.OrderQuantity - v.OverOutQuantity - v.LockQuantity - v.MoveQty)
                })
                .Where(x => x.TotalNeedQuantity > 0)
                .ToList();
@@ -86,125 +86,98 @@
            {
                var needQuantity = item.TotalNeedQuantity;
                // èŽ·å–å¯ç”¨åº“å­˜ï¼ˆæŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åºï¼‰
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo,item.SupplyCode);
                // èŽ·å–å¯ç”¨åº“å­˜ï¼ˆå·²æŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åºï¼‰
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(
                    item.MaterielCode, item.BatchNo, item.SupplyCode);
                if (!stockInfos.Any())
                {
                    throw new Exception($"物料[{item.MaterielCode}]批次[{item.BatchNo}]未找到可分配库存");
                }
                // åˆ†é…åº“存(按先进先出)
                // åˆ†é…åº“å­˜
                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(
                    stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity);
                if (residueQuantity > 0 && residueQuantity == needQuantity)
                // æ£€æŸ¥åˆ†é…ç»“æžœ
                if (residueQuantity > 0)
                {
                    throw new Exception($"物料[{item.MaterielCode}]库存不足,需要{needQuantity},可用{needQuantity - residueQuantity}");
                    var allocatedQuantity = needQuantity - residueQuantity;
                    throw new Exception($"物料[{item.MaterielCode}]库存不足,需要{needQuantity},实际分配{allocatedQuantity}");
                }
                outStocks.AddRange(autoAssignStocks);
                // æŒ‰å…ˆè¿›å…ˆå‡ºåŽŸåˆ™åˆ†é…é”å®šæ•°é‡åˆ°å„ä¸ªæ˜Žç»†
                DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations, outStockLockInfos, outboundOrder);
                // æŒ‰å…ˆè¿›å…ˆå‡ºåˆ†é…é”å®šæ•°é‡
                DistributeLockQuantityByFIFO(item.Details, autoAssignStocks, stockAllocations,
                    outStockLockInfos, outboundOrder);
            }
            locationInfos.AddRange(_locationInfoService.GetLocationInfos(outStocks.Select(x => x.LocationCode).Distinct().ToList()));
            locationInfos.AddRange(_locationInfoService.GetLocationInfos(
                outStocks.Select(x => x.LocationCode).Distinct().ToList()));
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos);
        }
        /// <summary>
        /// æŒ‰å…ˆè¿›å…ˆå‡ºåŽŸåˆ™åˆ†é…é”å®šæ•°é‡åˆ°å„ä¸ªæ˜Žç»†  
        /// </summary>
        private void DistributeLockQuantityByFIFO(
            List<Dt_OutboundOrderDetail> details,
            List<Dt_StockInfo> assignStocks,
            Dictionary<int, decimal> stockAllocations,
            List<Dt_OutStockLockInfo> outStockLockInfos,
            Dt_OutboundOrder outboundOrder)
        private void DistributeLockQuantityByFIFO(List<Dt_OutboundOrderDetail> details,List<Dt_StockInfo> assignStocks,Dictionary<int, decimal> stockAllocations,List<Dt_OutStockLockInfo> outStockLockInfos,
    Dt_OutboundOrder outboundOrder)
        {
            // æŒ‰å…ˆè¿›å…ˆå‡ºæŽ’序出库单明细(假设先创建的明细需要优先满足)
            var sortedDetails = details
                .Where(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity -d.MoveQty> 0) // åªå¤„理还需要分配的数量
                .OrderBy(x => x.Id)
                .ToList();
            var sortedStocks = assignStocks.OrderBy(x => x.CreateDate).ToList();
            var totalNeedQuantity = details.Sum(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity - d.MoveQty);
            decimal allocatedQuantity = 0;
            if (!sortedDetails.Any()) return;
            // èŽ·å–æ‰€æœ‰åˆ†é…äº†åº“å­˜çš„æ˜Žç»†ï¼ŒæŒ‰å…ˆè¿›å…ˆå‡ºæŽ’åº
            var allocatedStockDetails = assignStocks
                .SelectMany(x => x.Details)
                .Where(x => stockAllocations.ContainsKey(x.Id))
                .OrderBy(x => x.CreateDate)
                .ThenBy(x => x.StockId)
                .ToList();
            // ä¸ºæ¯ä¸ªåº“存明细创建分配记录
            foreach (var stockDetail in allocatedStockDetails)
            foreach (var stock in sortedStocks)
            {
                if (!stockAllocations.TryGetValue(stockDetail.Id, out decimal allocatedQuantity))
                if (allocatedQuantity >= totalNeedQuantity) break;
                if (!stockAllocations.TryGetValue(stock.Id, out decimal stockAllocation) || stockAllocation <= 0)
                    continue;
                if (allocatedQuantity <= 0) continue;
                var sortedDetails = details
                    .Where(d => d.OrderQuantity - d.OverOutQuantity - d.LockQuantity - d.MoveQty > 0)
                    .OrderBy(x => x.Id)
                    .ToList();
                var stockInfo = assignStocks.First(x => x.Id == stockDetail.StockId);
                decimal remainingAllocate = allocatedQuantity;
                // æŒ‰é¡ºåºåˆ†é…ç»™å„个出库单明细
                foreach (var detail in sortedDetails)
                {
                    if (remainingAllocate <= 0) break;
                    if (stockAllocation <= 0) break;
                    // è®¡ç®—这个明细还需要分配的数量
                    var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity-detail.MoveQty;
                    var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity - detail.MoveQty;
                    if (detailNeed <= 0) continue;
                    // åˆ†é…æ•°é‡
                    var assignQuantity = Math.Min(remainingAllocate, detailNeed);
                    var assignQuantity = Math.Min(stockAllocation, detailNeed);
                    // éªŒè¯æ¡ç æ˜¯å¦å­˜åœ¨
                    if (string.IsNullOrEmpty(stockDetail.Barcode))
                    // ä½¿ç”¨åº“存中的有效条码
                    var barcode = stock.Details
                        .Where(d => !string.IsNullOrEmpty(d.Barcode))
                        .Select(d => d.Barcode)
                        .FirstOrDefault();
                    if (string.IsNullOrEmpty(barcode))
                    {
                        throw new Exception($"库存明细ID[{stockDetail.Id}]的条码为空");
                        throw new Exception($"库存ID[{stock.Id}]的条码为空");
                    }
                    // åˆ›å»ºå‡ºåº“锁定信息
                    var lockInfo = _outStockLockInfoService.GetOutStockLockInfo(
                        outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode);
                        outboundOrder, detail, stock, assignQuantity, barcode);
                    outStockLockInfos.Add(lockInfo);
                    // æ›´æ–°æ˜Žç»†çš„锁定数量
                    detail.LockQuantity += assignQuantity;
                    remainingAllocate -= assignQuantity;
                    stockAllocation -= assignQuantity;
                    allocatedQuantity += assignQuantity;
                    if (allocatedQuantity >= totalNeedQuantity) break;
                }
            }
                // å¦‚果还有剩余分配数量,重新分配或记录警告
                if (remainingAllocate > 0)
                {
                    // é‡æ–°åˆ†é…ç»™å…¶ä»–需要分配的明细
                    foreach (var detail in sortedDetails)
                    {
                        if (remainingAllocate <= 0) break;
                        var detailNeed = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity;
                        if (detailNeed <= 0) continue;
                        var assignQuantity = Math.Min(remainingAllocate, detailNeed);
                        var lockInfo = _outStockLockInfoService.GetOutStockLockInfo(
                            outboundOrder, detail, stockInfo, assignQuantity, stockDetail.Barcode);
                        outStockLockInfos.Add(lockInfo);
                        detail.LockQuantity += assignQuantity;
                        remainingAllocate -= assignQuantity;
                    }
                    // å¦‚果还有剩余,记录警告但不抛出异常
                    if (remainingAllocate > 0)
                    {
                        _logger.LogWarning($"库存分配后仍有剩余数量未分配: {remainingAllocate}, æ¡ç : {stockDetail.Barcode}");
                    }
                }
            // éªŒè¯æ˜¯å¦å®Œå…¨åˆ†é…
            if (allocatedQuantity < totalNeedQuantity)
            {
                _logger.LogWarning($"库存分配不完全,需要{totalNeedQuantity},实际分配{allocatedQuantity}");
            }
        }
@@ -218,8 +191,8 @@
        /// <param name="locationStatus"></param>
        /// <param name="tasks"></param>
        /// <returns></returns>
        public WebResponseContent LockOutboundStockDataUpdate(List<Dt_StockInfo> stockInfos, List<Dt_OutboundOrderDetail> outboundOrderDetails,
            List<Dt_OutStockLockInfo> outStockLockInfos, List<Dt_LocationInfo> locationInfos,
        public WebResponseContent LockOutboundStockDataUpdate(List<Dt_StockInfo> stockInfos, List<Dt_OutboundOrderDetail> outboundOrderDetails,
            List<Dt_OutStockLockInfo> outStockLockInfos, List<Dt_LocationInfo> locationInfos,
            LocationStatusEnum locationStatus = LocationStatusEnum.Lock, List<Dt_Task>? tasks = null)
        {
            try
@@ -227,7 +200,7 @@
                // æ›´æ–°åº“存状态
                stockInfos.ForEach(x => x.StockStatus = (int)StockStatusEmun.出库锁定);
                _stockService.StockInfoService.Repository.UpdateData(stockInfos);
                // æ›´æ–°åº“存明细
                var stockDetails = stockInfos.SelectMany(x => x.Details).ToList();
                _stockService.StockInfoDetailService.Repository.UpdateData(stockDetails);
@@ -280,16 +253,16 @@
                        SearchParameters? searchParameters = searchParametersList.FirstOrDefault(x => x.Name == nameof(Dt_InboundOrderDetail.OrderId).FirstLetterToLower());
                        if (searchParameters != null)
                        {
                            sugarQueryable1 = sugarQueryable1.Where(x => x.OrderId== searchParameters.Value.ObjToInt());
                            var  dataList = sugarQueryable1.ToPageList(options.Page, options.Rows, ref totalCount);
                            sugarQueryable1 = sugarQueryable1.Where(x => x.OrderId == searchParameters.Value.ObjToInt());
                            var dataList = sugarQueryable1.ToPageList(options.Page, options.Rows, ref totalCount);
                            return new PageGridData<Dt_OutboundOrderDetail>(totalCount, dataList);
                        }
                    }
                }
            }
            return new PageGridData<Dt_OutboundOrderDetail> ();
            return new PageGridData<Dt_OutboundOrderDetail>();
        }
@@ -299,12 +272,12 @@
            if (!checkResult.Item1) throw new Exception(checkResult.Item2);
            Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId);
            var originalNeedQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity-outboundOrderDetail.MoveQty;
            var originalNeedQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty;
            var needQuantity = originalNeedQuantity;
            List<Dt_StockInfo> outStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockSelectViews.Select(x => x.PalletCode).ToList());
            var assignQuantity =0m;
            var assignQuantity = 0m;
            outStocks.ForEach(x =>
            {
                x.Details.ForEach(v =>
@@ -324,9 +297,9 @@
            needQuantity -= assignQuantity;
            if (outboundOrderDetail.OrderQuantity > outboundOrderDetail.LockQuantity)
            {
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo,"");
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo, "");
                stockInfos = stockInfos.Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode)).ToList();
                var (autoAssignStocks, stockAllocations)   = _stockService.StockInfoService.GetOutboundStocks(stockInfos, outboundOrderDetail.MaterielCode, needQuantity, out decimal residueQuantity);
                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(stockInfos, outboundOrderDetail.MaterielCode, needQuantity, out decimal residueQuantity);
                outboundOrderDetail.LockQuantity += needQuantity - residueQuantity;
                outStocks.AddRange(autoAssignStocks);
                outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -293,14 +293,15 @@
                _unitOfWorkManage.CommitTran();
                // åˆ›å»ºå›žåº“任务
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick);
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick,task.PalletType);
                // æ›´æ–°è®¢å•状态(不触发MES回传)
                await UpdateOrderStatusForReturn(orderNo);
                return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{statusAnalysis.TotalReturnQty}");
                return WebResponseContent.Instance.OK($"回库操作成功");
                //return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{statusAnalysis.TotalReturnQty}");
            }
            catch (Exception ex)
            {
@@ -616,7 +617,7 @@
            result.FinalBarcode = newBarcode;
            result.SplitResults.AddRange(CreateSplitResults(lockInfo, actualQty, remainingStockQty, newBarcode, stockDetail.Barcode));
            _logger.LogInformation($"拆包分拣完成 - OrderDetailId: {lockInfo.OrderDetailId}, åˆ†æ‹£æ•°é‡: {actualQty}");
        }
@@ -1037,7 +1038,7 @@
            return task;
        }
        private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId)
        {
            decimal totalQty = 0;
@@ -1045,11 +1046,14 @@
            foreach (var splitRecord in splitRecords)
            {
                if (splitRecord.Status != (int)SplitPackageStatusEnum.已撤销)
                    continue;
                // æ£€æŸ¥åŽŸæ¡ç 
                if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId)
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId &&
                           it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .FirstAsync();
                    if (originalStock != null && originalStock.StockQuantity > 0)
@@ -1063,7 +1067,7 @@
                if (!processedBarcodes.Contains(splitRecord.NewBarcode))
                {
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId)
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId && it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
@@ -1111,11 +1115,13 @@
                else
                {
                    locationtype = stockInfo.LocationType;
                    _stockInfoService.DeleteData(stockInfo);
                }
                var targetAddress = originalTask.TargetAddress;
                await CleanupZeroStockData(stockInfoId);
                var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.组盘暂存.ObjToInt(), PalletCode = palletCode, LocationType = locationtype };
                emptystockInfo.Details = new List<Dt_StockInfoDetail>();
@@ -1123,7 +1129,7 @@
                //空托盘如何处理  è¿˜æœ‰ä¸€ä¸ªå‡ºåº“任务要处理。
                originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt();
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty);
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty,PalletTypeEnum.Empty.ObjToInt());
            }
            catch (Exception ex)
@@ -1143,19 +1149,41 @@
            {
                await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id);
               // await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
            }
                // await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
            }
            // å¤„理托盘上其他库存货物
            if (analysis.HasPalletStockGoods)
            {
                await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
                var validStockGoods = analysis.PalletStockGoods
            .Where(x => x.Status != StockStatusEmun.出库完成.ObjToInt())
            .ToList();
                if (validStockGoods.Any())
                {
                    await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
                }
                else
                {
                    _logger.LogInformation("没有有效的库存货物需要回库");
                }
            }
            // å¤„理拆包记录
            if (analysis.HasSplitRecords)
            {
                await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
                var validSplitRecords = analysis.SplitRecords
            .Where(x => x.Status != (int)SplitPackageStatusEnum.已拣选)
            .ToList();
                if (validSplitRecords.Any())
                {
                    await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
                }
                else
                {
                    _logger.LogInformation("没有有效的拆包记录需要处理");
                }
            }
            // æ›´æ–°åº“存主表状态
@@ -1179,7 +1207,13 @@
            foreach (var lockInfo in remainingLocks)
            {
                decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (returnQty <= 0)
                {
                    _logger.LogWarning($"锁定记录{lockInfo.Id}无需回库,分配数量: {lockInfo.AssignQuantity}, å·²æ‹£é€‰: {lockInfo.PickedQty}");
                    continue;
                }
                _logger.LogInformation($"处理锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                // æŸ¥æ‰¾å¯¹åº”的库存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
@@ -1195,23 +1229,23 @@
                else
                {
                    // åˆ›å»ºæ–°çš„库存记录
                    var newStockDetail = new Dt_StockInfoDetail
                    {
                        StockId = lockInfo.StockId,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        OrderNo = lockInfo.OrderNo,
                        BatchNo = lockInfo.BatchNo,
                        StockQuantity = returnQty,
                        OutboundQuantity = 0,
                        Barcode = lockInfo.CurrentBarcode,
                        InboundOrderRowNo = "",
                        Status = StockStatusEmun.入库确认.ObjToInt(),
                        SupplyCode = lockInfo.SupplyCode,
                        WarehouseCode = lockInfo.WarehouseCode,
                        Unit = lockInfo.Unit,
                    };
                    await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                    //var newStockDetail = new Dt_StockInfoDetail
                    //{
                    //    StockId = lockInfo.StockId,
                    //    MaterielCode = lockInfo.MaterielCode,
                    //    MaterielName = lockInfo.MaterielName,
                    //    OrderNo = lockInfo.OrderNo,
                    //    BatchNo = lockInfo.BatchNo,
                    //    StockQuantity = returnQty,
                    //    OutboundQuantity = 0,
                    //    Barcode = lockInfo.CurrentBarcode,
                    //    InboundOrderRowNo = "",
                    //    Status = StockStatusEmun.入库确认.ObjToInt(),
                    //    SupplyCode = lockInfo.SupplyCode,
                    //    WarehouseCode = lockInfo.WarehouseCode,
                    //    Unit = lockInfo.Unit,
                    //};
                    //await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                }
            }
        }
@@ -1255,23 +1289,41 @@
            {
                _logger.LogInformation($"待回库货物 - æ¡ç : {stockGood.Barcode}, æ•°é‡: {stockGood.StockQuantity}, å½“前状态: {stockGood.Status}");
                // æ¢å¤åº“存状态
                stockGood.OutboundQuantity = 0;
                stockGood.Status = StockStatusEmun.入库确认.ObjToInt();
                if (stockGood.Status != StockStatusEmun.出库完成.ObjToInt())
                {
                    stockGood.OutboundQuantity = 0;
                    stockGood.Status = StockStatusEmun.入库确认.ObjToInt();
                    await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
                await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
                    _logger.LogInformation($"库存货物回库完成 - æ¡ç : {stockGood.Barcode}, æ–°çŠ¶æ€: {stockGood.Status}");
                }
                else
                {
                    _logger.LogWarning($"跳过已出库完成的货物 - æ¡ç : {stockGood.Barcode}");
                }
            }
        }
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, string orderNo, string palletCode)
        {
            var validRecords = splitRecords.Where(x => x.Status != (int)SplitPackageStatusEnum.已拣选).ToList();
            if (!validRecords.Any())
            {
                _logger.LogInformation("没有需要回库的拆包记录");
                return;
            }
            _logger.LogInformation($"更新{validRecords.Count}条拆包记录状态为已回库");
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已回库,
                    Operator = App.User.UserName
                })
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && !x.IsReverted)
                .Where(x => validRecords.Select(r => r.Id).Contains(x.Id))
                .ExecuteCommandAsync();
        }
@@ -1290,7 +1342,7 @@
        /// <param name="originalTask"></param>
        /// <param name="analysis"></param>
        /// <returns></returns>
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum)
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum,int palletType)
        {
            var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                .FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
@@ -1304,15 +1356,16 @@
                Grade = 0,
                PalletCode = palletCode,
                NextAddress = "",
                OrderNo = originalTask.OrderNo,
                // OrderNo = originalTask.OrderNo,
                OrderNo = orderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = taskTypeEnum.ObjToInt(),
                PalletType = originalTask.PalletType,
                PalletType = palletType,
                WarehouseId = originalTask.WarehouseId
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
@@ -1420,10 +1473,10 @@
                        .ExecuteCommandAsync();
                    // åªæœ‰æ­£å¸¸åˆ†æ‹£å®Œæˆæ—¶æ‰å‘MES反馈
                    if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                    {
                        await HandleOrderCompletion(outboundOrder, orderNo);
                    }
                    //if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                    //{
                    //    await HandleOrderCompletion(outboundOrder, orderNo);
                    //}
                }
            }
            catch (Exception ex)
@@ -1432,7 +1485,7 @@
            }
        }
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
@@ -1728,7 +1781,7 @@
                StockId = stockId
            };
            // 1. åˆ†æžæœªåˆ†æ‹£çš„出库锁定记录
            // åˆ†æžæœªåˆ†æ‹£çš„出库锁定记录
            var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
@@ -1740,9 +1793,10 @@
                result.HasRemainingLocks = true;
                result.RemainingLocks = remainingLocks;
                result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                _logger.LogInformation($"发现{remainingLocks.Count}条未分拣锁定记录,总数量: {result.RemainingLocksReturnQty}");
            }
            // 2. åˆ†æžæ‰˜ç›˜ä¸Šçš„库存货物
            // åˆ†æžæ‰˜ç›˜ä¸Šçš„库存货物
            var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.StockId == stockId &&
                     (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
@@ -1756,13 +1810,21 @@
                result.HasPalletStockGoods = true;
                result.PalletStockGoods = palletStockGoods;
                result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity);
                _logger.LogInformation($"发现{palletStockGoods.Count}个库存货物,总数量: {result.PalletStockReturnQty}");
                // è®°å½•详细状态分布
                var statusGroups = palletStockGoods.GroupBy(x => x.Status);
                foreach (var group in statusGroups)
                {
                    _logger.LogInformation($"库存状态{group.Key}: {group.Count()}个货物,数量: {group.Sum(x => x.StockQuantity)}");
                }
            }
            // 3. åˆ†æžæ‹†åŒ…记录
            //分析拆包记录
            var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           !it.IsReverted &&
                           !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.已拣选 &&
                           it.Status != (int)SplitPackageStatusEnum.已回库)
                .ToListAsync();
@@ -1771,6 +1833,8 @@
                result.HasSplitRecords = true;
                result.SplitRecords = splitRecords;
                result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
                _logger.LogInformation($"发现{splitRecords.Count}条未拣选拆包记录,总数量: {result.SplitReturnQty}");
            }
            // 4. è®¡ç®—总回库数量和空托盘状态
@@ -1785,6 +1849,10 @@
                           x.TaskStatus == (int)TaskStatusEnum.New)
                .AnyAsync();
            _logger.LogInformation($"托盘状态分析完成 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, " +
                                  $"总回库数量: {result.TotalReturnQty}, æ˜¯å¦ç©ºæ‰˜ç›˜: {result.IsEmptyPallet}, " +
                                  $"有进行中任务: {result.HasActiveTasks}");
            return result;
        }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -201,12 +201,9 @@
            if (!string.IsNullOrEmpty(supplyCode))
            {
                query = query.Where(x => x.Details.Any(d => d.SupplyCode == supplyCode));
            }
            }
            var stocks = query.ToList();
            return stocks.OrderBy(x => x.Details.Where(d => d.MaterielCode == materielCode && (string.IsNullOrEmpty(supplyCode) || d.SupplyCode == supplyCode) &&
                           (string.IsNullOrEmpty(lotNo) || d.BatchNo == lotNo)).Min(d => d.CreateDate)).ToList();
            return query.OrderBy(x => x.CreateDate).ToList();
            //ISugarQueryable<Dt_LocationInfo> sugarQueryable = Db.Queryable<Dt_LocationInfo>().Where(x => locationCodes.Contains(x.LocationCode));
            //ISugarQueryable<Dt_StockInfo> sugarQueryable1 = Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.Details.Any(v => v.MaterielCode == materielCode));
            //return sugarQueryable.InnerJoin(sugarQueryable1, (a, b) => a.LocationCode == b.LocationCode).Select((a, b) => b).OrderBy(a => a.CreateDate).Includes(a => a.Details).ToList();
@@ -227,7 +224,7 @@
        public List<Dt_StockInfo> GetStockInfosByPalletCodes(List<string> palletCodes)
        {
            return Db.Queryable<Dt_StockInfo>().Where(x => palletCodes.Contains(x.PalletCode)).Includes(x => x.Details).ToList();
            return Db.Queryable<Dt_StockInfo>().Where(x => palletCodes.Contains(x.PalletCode)).Includes(x => x.Details).OrderBy(x => x.CreateDate).ToList();
        }
 
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -41,6 +41,7 @@
using WIDESEA_DTO.Allocate;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
using WIDESEA_DTO.Task;
using WIDESEA_IAllocateService;
using WIDESEA_IBasicService;
@@ -126,7 +127,7 @@
                if (int.TryParse(taskNum, out var newTaskNum))
                {
                    task = BaseDal.QueryFirst(x => x.TaskNum == newTaskNum);
                    task =await BaseDal.QueryFirstAsync(x => x.TaskNum == newTaskNum);
                    if (task == null)
                    {
                        return WebResponseContent.Instance.Error("未找到任务信息");
@@ -141,10 +142,28 @@
                MethodInfo? methodInfo = GetType().GetMethod(((TaskTypeEnum)task.TaskType) + "TaskCompleted");
                if (methodInfo != null)
                {
                    WebResponseContent? responseContent = (WebResponseContent?)methodInfo.Invoke(this, new object[] { task });
                    if (responseContent != null)
                    object? taskResult = methodInfo.Invoke(this, new object[] { task });
                    if (taskResult is Task<WebResponseContent> asyncTask)
                    {
                        return responseContent;
                        try
                        {
                            // 3. å¼‚步等待 Task å®Œæˆï¼Œè‡ªåŠ¨è§£æžå‡º WebResponseContent
                            WebResponseContent responseContent = await asyncTask;
                            if (responseContent != null)
                            {
                                return responseContent;
                            }
                        }
                        catch (AggregateException ex)
                        {
                            _logger.LogError($"TaskService TaskCompleted  taskResult:   {ex.Message} ");
                            return WebResponseContent.Instance.Error(ex.Message);
                        }
                        catch(Exception ex)
                        {
                            _logger.LogError(ex, $"Unexpected error in {task.TaskType}");
                            return WebResponseContent.Instance.Error(ex.Message);
                        }
                    }
                }
                return WebResponseContent.Instance.Error("未找到任务类型对应业务处理逻辑");
@@ -363,7 +382,7 @@
            return WebResponseContent.Instance.OK();
        }
        public WebResponseContent OutboundTaskCompleted(Dt_Task task)
        public async Task<WebResponseContent> OutboundTaskCompleted(Dt_Task task)
        {
            _logger.LogInformation($"TaskService  OutboundTaskCompleted: {task.TaskNum}");
            //查货位
@@ -375,7 +394,7 @@
            locationInfo.LocationStatus = LocationStatusEnum.Free.ObjToInt();
            _locationInfoService.Repository.UpdateData(locationInfo);
            var outloks = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>().Where(x => x.TaskNum == task.TaskNum).ToList();
            var outloks =await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>().Where(x => x.TaskNum == task.TaskNum).ToListAsync();
            var stockids = outloks.Select(x => x.StockId).ToList();
@@ -439,12 +458,22 @@
                stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
                _stockRepository.UpdateData(stockInfo);
                var outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().First(x => x.OrderNo == task.OrderNo);
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? WIDESEA_Core.Enums.OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforelocationStatus, StockChangeType.Inbound.ObjToInt(), "", task.TaskNum);
                if (outboundOrder != null)
                {
                    await HandleOutboundOrderToMESCompletion(outboundOrder, outboundOrder.OrderNo);
                }
                else
                {
                    _logger.LogInformation($"TaskService  InEmptyTaskCompleted: {task.TaskNum} ,未找到出库单。  ");
                }
                return content;
            }
@@ -460,25 +489,30 @@
            try
            {
                //查库存
                Dt_StockInfo stockInfo = _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.PalletCode == task.PalletCode).First();
                Dt_StockInfo stockInfo =await _stockRepository.Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.PalletCode == task.PalletCode).FirstAsync();
                if (stockInfo == null)
                {
                    _logger.LogInformation($"TaskService  InPickTaskCompleted: æœªæ‰¾åˆ°æ‰˜ç›˜å¯¹åº”的组盘信息.{task.TaskNum}");
                    return WebResponseContent.Instance.Error($"未找到托盘对应的组盘信息");
                }
                if (stockInfo.Details.Count == 0 && stockInfo.PalletType != PalletTypeEnum.Empty.ObjToInt())
                {
                    _logger.LogInformation($"TaskService  InPickTaskCompleted: æœªæ‰¾åˆ°è¯¥æ‰˜ç›˜åº“存明细信息.{task.TaskNum}");
                    return WebResponseContent.Instance.Error($"未找到该托盘库存明细信息");
                }
                //查货位
                Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == task.TargetAddress);
                if (locationInfo == null)
                {
                    _logger.LogInformation($"TaskService  InPickTaskCompleted:  æœªæ‰¾åˆ°å¯¹åº”的终点货位信息 {task.TaskNum}.");
                    return WebResponseContent.Instance.Error($"未找到对应的终点货位信息");
                }
                var beforelocationStatus = locationInfo.LocationStatus;
                // èŽ·å–æ‰€æœ‰å›žåº“ä¸­çš„å‡ºåº“é”å®šè®°å½•
                var returnLocks = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                var returnLocks =await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == task.OrderNo && it.PalletCode == task.PalletCode && it.Status == (int)OutLockStockStatusEnum.回库中)
                    .ToList();
                    .ToListAsync();
                // æ›´æ–°å‡ºåº“锁定记录状态为回库完成
                foreach (var lockInfo in returnLocks)
                {
@@ -512,17 +546,98 @@
                    locationInfo.LocationStatus = LocationStatusEnum.InStock.ObjToInt();
                }
                _locationInfoService.Repository.UpdateData(locationInfo);
                var outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().First(x => x.OrderNo == task.OrderNo);
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                BaseDal.DeleteData(task);
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforelocationStatus, StockChangeType.Inbound.ObjToInt(), "", task.TaskNum);
                if (outboundOrder != null)
                {
                    await HandleOutboundOrderToMESCompletion(outboundOrder, outboundOrder.OrderNo);
                }
                else
                {
                    _logger.LogInformation($"TaskService  InPickTaskCompleted: {task.TaskNum} ,未找到出库单。  ");
                }
            }
            catch (Exception ex)
            {
                _logger.LogInformation($"TaskService  InPickTaskCompleted: {task.TaskNum} , {ex.Message}");
            }
            return WebResponseContent.Instance.OK();
            return await Task.FromResult(WebResponseContent.Instance.OK());
        }
        private async Task HandleOutboundOrderToMESCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
        {
            // èŽ·å–è®¢å•æ˜Žç»†æ•°æ®
            var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .LeftJoin<Dt_OutboundOrder>((d, o) => d.OrderId == o.Id)
                .Where((d, o) => o.OrderNo == orderNo)
                .Select((d, o) => d)
                .ToListAsync();
            var feedmodel = new FeedbackOutboundRequestModel
            {
                reqCode = Guid.NewGuid().ToString(),
                reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                business_type = outboundOrder.BusinessType,
                factoryArea = outboundOrder.FactoryArea,
                operationType = 1,
                Operator = App.User.UserName,
                orderNo = outboundOrder.UpperOrderNo,
                documentsNO = outboundOrder.OrderNo,
                status = outboundOrder.OrderStatus,
                details = new List<FeedbackOutboundDetailsModel>()
            };
            // ä½¿ç”¨è®¢å•明细的OverOutQuantity作为回传数量
            foreach (var detail in orderDetails)
            {
                // èŽ·å–è¯¥æ˜Žç»†å¯¹åº”çš„æ¡ç ä¿¡æ¯ï¼ˆä»Žé”å®šè®°å½•ï¼‰
                var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.OrderDetailId == detail.Id &&
                               x.Status == (int)OutLockStockStatusEnum.拣选完成)
                    .ToListAsync();
                var detailModel = new FeedbackOutboundDetailsModel
                {
                    materialCode = detail.MaterielCode,
                    lineNo = detail.lineNo, // æ³¨æ„ï¼šè¿™é‡Œå¯èƒ½éœ€è¦è°ƒæ•´å­—段名
                    warehouseCode = detail.WarehouseCode,
                    qty = detail.OverOutQuantity, // ä½¿ç”¨è®¢å•明细的已出库数量
                    currentDeliveryQty = detail.OverOutQuantity,
                    unit = detail.Unit,
                    barcodes = detailLocks.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel
                    {
                        barcode = lockInfo.CurrentBarcode,
                        supplyCode = lockInfo.SupplyCode,
                        batchNo = lockInfo.BatchNo,
                        unit = lockInfo.Unit,
                        qty = lockInfo.PickedQty // æ¡ç çº§åˆ«çš„æ•°é‡ä»ç”¨é”å®šè®°å½•
                    }).ToList()
                };
                feedmodel.details.Add(detailModel);
            }
            var result = await _invokeMESService.FeedbackOutbound(feedmodel);
            if (result != null && result.code == 200)
            {
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(x => x.ReturnToMESStatus == 1)
                    .Where(x => x.OrderId == outboundOrder.Id)
                    .ExecuteCommandAsync();
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.ReturnToMESStatus == 1)
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
@@ -611,7 +726,7 @@
                _locationStatusChangeRecordService.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), stockInfo.Details.FirstOrDefault()?.OrderNo ?? "", task.TaskNum);
                return WebResponseContent.Instance.OK();
                return await Task.FromResult(WebResponseContent.Instance.OK());
            }
            catch (Exception ex)
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/ESSController.cs
@@ -217,7 +217,7 @@
            _logger.LogInformation("任务完成: TaskCode={TaskCode}, Container={Container}, Robot={Robot}",
                request.TaskCode, request.ContainerCode, request.RobotCode);
            _taskService.TaskCompleted(request.TaskCode);
            await _taskService.TaskCompleted(request.TaskCode);
            // æ ¹æ®ä¸åŒçš„任务类型进行特殊处理
            if (request.Weight.HasValue)
            {