1
wankeda
2026-01-22 f27080d8476a177b809045d1f036a24dac782ba5
WMS/WIDESEA_WMSServer/WIDESEA_OutboundService/Service/OutboundOrderDetailService.cs
@@ -10,6 +10,7 @@
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.ERP;
using WIDESEA_DTO.Stock;
using WIDESEA_IBasicRepository;
using WIDESEA_IBasicService;
@@ -23,8 +24,11 @@
namespace WIDESEA_OutboundService
{
    public partial class OutboundOrderDetailService : ServiceBase<Dt_OutboundOrderDetail, IOutboundOrderDetailRepository>, IOutboundOrderDetailService
    {
        public WebResponseContent LockOutboundStock(int orderDetailId)
        {
            Dt_OutboundOrderDetail outboundOrderDetail = BaseDal.QueryFirst(x => x.Id == orderDetailId);
@@ -137,7 +141,7 @@
            decimal needQuantity = originalNeedQuantity;
            List<Dt_StockInfo> outStocks = _stockService.StockInfoService.Repository.GetStockInfosByPalletCode(stockSelectViews.Select(x => x.BatchNo).ToList());
            List<Dt_StockInfo> outStocks = _stockService.StockInfoService.Repository.GetStockInfosByPalletCode(stockSelectViews.Select(x => x.PalletCode).ToList());
            //List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrder.MaterialCode, "", outboundOrder.WarehouseId);
            if (!outStocks.Any())
@@ -165,7 +169,7 @@
            if (outboundOrderDetail.OrderQuantity > outboundOrderDetail.LockQuantity)
            {
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrderDetail.MaterielCode);
                stockInfos = stockInfos.Where(x => !stockSelectViews.Select(v => v.BatchNo).Contains(x.BatchNo)).ToList();
                stockInfos = stockInfos.Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode)).ToList();
                List<Dt_StockInfo> autoAssignStocks = _stockService.StockInfoService.GetOutboundStocks(stockInfos, outboundOrderDetail.MaterielCode, needQuantity, out decimal residueQuantity);
                outboundOrderDetail.LockQuantity += needQuantity - residueQuantity;
                outStocks.AddRange(autoAssignStocks);
@@ -273,6 +277,14 @@
                List<Dt_OutStockLockInfo> addOutStockLockInfos = outStockLockInfos.Where(x => x.Id == 0).ToList();
                if (addOutStockLockInfos != null && addOutStockLockInfos.Any())
                {
                    if (tasks != null)
                    {
                        addOutStockLockInfos.ForEach(x =>
                        {
                            x.TaskNum = tasks.FirstOrDefault(v => v.PalletCode == x.PalletCode)?.TaskNum;
                        });
                    }
                    _outStockLockInfoService.Repository.AddData(addOutStockLockInfos);
                }
                List<Dt_OutStockLockInfo> updateOutStockLockInfos = outStockLockInfos.Where(x => x.Id > 0).ToList();
@@ -310,8 +322,16 @@
            }
            Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
            List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
            List<Dt_OutboundOrderDetail> groupDetails = outboundOrderDetails.GroupBy(x => new { x.MaterielCode, x.BatchNo }).Select(x => new Dt_OutboundOrderDetail { OrderQuantity = x.Sum(v => v.OrderQuantity) - x.Sum(v => v.LockQuantity), MaterielCode = x.Key.MaterielCode, BatchNo = x.Key.BatchNo }).ToList();
            Dt_Warehouse warehouse = _basicService.WarehouseService.Repository.QueryFirst(x => x.WarehouseCode == outboundOrder.OutWareHouse);
            List<Dt_OutboundOrderDetail> groupDetails = outboundOrderDetails.GroupBy(x => new { x.MaterielCode, x.BatchNo , x.LocationName}).Select(x => new Dt_OutboundOrderDetail { OrderQuantity = x.Sum(v => v.OrderQuantity) - x.Sum(v => v.LockQuantity), MaterielCode = x.Key.MaterielCode, BatchNo = x.Key.BatchNo,LocationName = x.Key.LocationName}).ToList();
            List<Dt_Warehouse> warehouse = new List<Dt_Warehouse>();
            if (outboundOrder.OutWareHouse == "SC01_BC")
            {
                warehouse = _basicService.WarehouseService.Repository.QueryData(x => x.WarehouseDes == outboundOrder.OutWareHouse || x.WarehouseDes =="SC02_BC");
            }
            else
            {
                warehouse = _basicService.WarehouseService.Repository.QueryData(x => x.WarehouseDes == outboundOrder.OutWareHouse);
            }
            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
            List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
            foreach (var item in groupDetails)
@@ -320,16 +340,30 @@
                decimal needQuantity = originalNeedQuantity;
                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, warehouse.WarehouseId);
                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
                ///出库指定库位出库判断
                if (item.LocationName != null && item.LocationName != "")
                {
                   stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, warehouse).Where(x=>x.LocationCode == item.LocationName).ToList();
                }
                else
                {
                    stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, warehouse);
                }
                if (!stockInfos.Any())
                {
                    throw new Exception($"未找到可分配库存");
                }
                List<Dt_StockInfo> autoAssignStocks = _stockService.StockInfoService.GetOutboundStocks(stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity);
                List<Dt_StockInfo> autoAssignStocks = new List<Dt_StockInfo>();
                decimal newResidueQuantity = 0;
                item.LockQuantity += needQuantity - residueQuantity;
                 autoAssignStocks = _stockService.StockInfoService.GetOutboundStocks(stockInfos, item.MaterielCode, needQuantity, out decimal residueQuantity);
                 newResidueQuantity = residueQuantity;
                item.LockQuantity += needQuantity - newResidueQuantity;
                outStocks.AddRange(autoAssignStocks);
                decimal assignQuantity = needQuantity - residueQuantity;
                decimal assignQuantity = needQuantity - newResidueQuantity;
                List<Dt_OutboundOrderDetail> details = outboundOrderDetails.Where(x => !string.IsNullOrEmpty(x.BatchNo) ? x.BatchNo == item.BatchNo : true && x.MaterielCode == item.MaterielCode).ToList();
@@ -345,7 +379,17 @@
                        {
                            palletAssignQuantity = outStockLockInfos.Where(x => x.MaterielCode == item.MaterielCode && x.PalletCode == autoAssignStocks[j].PalletCode).Sum(x => x.AssignQuantity);//出库详情已分配数量
                        }
                        decimal palletOutboundQuantity = autoAssignStocks[j].Details.Sum(x => x.OutboundQuantity);
                        decimal palletOutboundQuantity = 0;
                        if (warehouse.Any(x => x.WarehouseCode.Contains("DW")) || warehouse.Any(x => x.WarehouseCode.Contains("YS")))
                        {
                            palletOutboundQuantity = autoAssignStocks[j].Details.Where(x => x.BatchNo == item.BatchNo).Sum(x => x.OutboundQuantity);
                        }
                        else
                        {
                             palletOutboundQuantity = autoAssignStocks[j].Details.Sum(x => x.OutboundQuantity);
                        }
                        if (palletAssignQuantity < palletOutboundQuantity)//如果出库详情已分配数量小于托盘已分配数量,则可以继续添加该托盘出库信息
                        {
                            decimal orderDetailNeedQuantity = details[i].OrderQuantity - detailAssignQuantity;
@@ -367,12 +411,175 @@
                    }
                }
                locationInfos.AddRange(_basicService.LocationInfoService.Repository.GetLocationInfos(outStocks.Select(x => x.LocationCode).ToList()));
            }
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos);
        }
        /// <summary>
        /// 低温、药水库存分配
        /// </summary>
        /// <param name="outboundOrderDetails"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public (List<Dt_StockInfo>, List<Dt_OutboundOrderDetail>, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) DWANDYSAssignStockOutbound(List<Dt_OutboundOrderDetail> outboundOrderDetails)
        {
            if (!outboundOrderDetails.Any())
            {
                throw new Exception($"未找到出库单明细信息");
            }
            if (outboundOrderDetails.GroupBy(x => x.OrderId).Count() > 1)
            {
                throw new Exception($"请勿同时操作多个单据明细");
            }
            Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == outboundOrderDetails.FirstOrDefault().OrderId);
            List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
            List<Dt_OutboundOrderDetail> groupDetails = outboundOrderDetails.GroupBy(x => new { x.MaterielCode, x.BatchNo, x.LocationName }).Select(x => new Dt_OutboundOrderDetail { OrderQuantity = x.Sum(v => v.OrderQuantity) - x.Sum(v => v.LockQuantity), MaterielCode = x.Key.MaterielCode, BatchNo = x.Key.BatchNo, LocationName = x.Key.LocationName }).ToList();
            List<Dt_Warehouse> warehouse = new List<Dt_Warehouse>();
            warehouse = _basicService.WarehouseService.Repository.QueryData(x => x.WarehouseDes == outboundOrder.OutWareHouse);
            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
            List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>();
            List<string> PalletCodes = new List<string>();
            foreach(var groupDetail in groupDetails)
            {
                Dt_StockInfo dt_StockInfo = new Dt_StockInfo();
                List<Dt_StockInfoDetail> dt_StockInfoDetails = _stockService.StockInfoDetailService.Repository.QueryData(x => x.MaterielCode == groupDetail.MaterielCode && x.BatchNo == groupDetail.BatchNo);
                foreach(var stockInfoDetail in dt_StockInfoDetails)
                {
                    dt_StockInfo = _stockService.StockInfoService.Repository.QueryFirst(x => x.Id == stockInfoDetail.StockId && x.StockStatus == StockStatusEmun.入库完成.ObjToInt());
                    if(dt_StockInfo != null)
                    {
                        PalletCodes.Add(dt_StockInfo.PalletCode);
                    }
                }
                if(!PalletCodes.Any())
                {
                    throw new Exception($"未找到可分配库存");
                }
            }
            foreach(var PalletCode in PalletCodes.Distinct())
            {
                List<Dt_StockInfo> stockInfos = new List<Dt_StockInfo>();
                if (outboundOrderDetails.All(x => x.OrderQuantity == x.LockQuantity)) break;
                foreach (var item in groupDetails)
                {
                    if (item.OrderQuantity == item.LockQuantity) continue;
                    ///出库指定库位出库判断
                    if (item.LocationName != null && item.LocationName != "")
                    {
                        //stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, warehouse).Where(x => x.LocationCode == item.LocationName).ToList();
                        List<Dt_StockInfo> nowStockInfos = Db.Queryable<Dt_StockInfo>()
                            .Where(x => x.PalletCode == PalletCode && x.Details.Any(v => v.BatchNo == item.BatchNo && v.MaterielCode == item.MaterielCode) && x.LocationCode == item.LocationName)
                            .Includes(x => x.Details).ToList();
                        if (stockInfos.Count() == 0 && nowStockInfos.Count() > 0)
                        {
                            stockInfos.AddRange(nowStockInfos);
                        }
                    }
                    else
                    {
                        //stockInfos = _stockService.StockInfoService.GetUseableStocks(item.MaterielCode, item.BatchNo, warehouse);
                        List<Dt_StockInfo> nowStockInfos = Db.Queryable<Dt_StockInfo>()
                            .Where(x => x.PalletCode == PalletCode && x.Details.Any(v => v.BatchNo == item.BatchNo && v.MaterielCode == item.MaterielCode))
                            .Includes(x => x.Details).ToList();
                        if(stockInfos.Count() == 0 && nowStockInfos.Count() > 0)
                        {
                            stockInfos.AddRange(nowStockInfos);
                        }
                    }
                }
                if(stockInfos.Count() == 0)
                {
                    continue;
                }
                List<Dt_StockInfo> autoAssignStocks = new List<Dt_StockInfo>();
                List<IStockInfoService.residueQuantity> newResidueQuantitys = new List<IStockInfoService.residueQuantity>();
                if (warehouse.Any(x => x.WarehouseCode.Contains("DW")) || warehouse.Any(x => x.WarehouseCode.Contains("YS")))
                {
                    autoAssignStocks = _stockService.StockInfoService.DWANDYSGetOutboundStocks(stockInfos,groupDetails, out List<IStockInfoService.residueQuantity> residueQuantitys);
                    newResidueQuantitys.AddRange(residueQuantitys);
                    outStocks.AddRange(autoAssignStocks);
                }
                foreach(var residueQuantity in newResidueQuantitys)
                {
                    foreach (var item in groupDetails)
                    {
                        if(residueQuantity.MaterielCode == item.MaterielCode && residueQuantity.BatchNo == item.BatchNo)
                        {
                            decimal originalNeedQuantity = item.OrderQuantity;
                            decimal needQuantity = originalNeedQuantity - item.LockQuantity;
                            item.LockQuantity += needQuantity - residueQuantity.NewNeendQuantity;
                            decimal assignQuantity = needQuantity -  residueQuantity.NewNeendQuantity;
                            List<Dt_OutboundOrderDetail> details = outboundOrderDetails.Where(x => !string.IsNullOrEmpty(x.BatchNo) ? x.BatchNo == item.BatchNo : true && x.MaterielCode == item.MaterielCode).ToList();
                            for (int i = 0; i < details.Count; i++)
                            {
                                decimal orderQuantity = details[i].OrderQuantity;
                                for (int j = 0; j < autoAssignStocks.Count; j++)
                                {
                                    decimal detailAssignQuantity = outStockLockInfos.Where(x => !string.IsNullOrEmpty(x.BatchNo) ? x.BatchNo == item.BatchNo : true && x.MaterielCode == item.MaterielCode && x.OrderDetailId == details[i].Id).Sum(x => x.AssignQuantity);//出库订单明细已分配数量
                                    decimal palletAssignQuantity = outStockLockInfos.Where(x => x.BatchNo == item.BatchNo && x.MaterielCode == item.MaterielCode && x.PalletCode == autoAssignStocks[j].PalletCode).Sum(x => x.AssignQuantity);//出库详情已分配数量
                                    if (string.IsNullOrEmpty(item.BatchNo))
                                    {
                                        palletAssignQuantity = outStockLockInfos.Where(x => x.MaterielCode == item.MaterielCode && x.PalletCode == autoAssignStocks[j].PalletCode).Sum(x => x.AssignQuantity);//出库详情已分配数量
                                    }
                                    decimal palletOutboundQuantity = 0;
                                    if (warehouse.Any(x => x.WarehouseCode.Contains("DW")) || warehouse.Any(x => x.WarehouseCode.Contains("YS")))
                                    {
                                        palletOutboundQuantity = autoAssignStocks[j].Details.Where(x => x.BatchNo == item.BatchNo && x.MaterielCode == item.MaterielCode).Sum(x => x.OutboundQuantity);
                                    }
                                    if (palletAssignQuantity < palletOutboundQuantity)//如果出库详情已分配数量小于托盘已分配数量,则可以继续添加该托盘出库信息
                                    {
                                        decimal orderDetailNeedQuantity = details[i].OrderQuantity - detailAssignQuantity;
                                        if (orderDetailNeedQuantity > autoAssignStocks[j].Details.Where(x => x.MaterielCode == details[i].MaterielCode && x.BatchNo == details[i].BatchNo).Sum(v => v.OutboundQuantity) - palletAssignQuantity)
                                        {
                                            //details[i].LockQuantity += autoAssignStocks[j].Details.Sum(x => x.OutboundQuantity) - palletAssignQuantity;
                                            Dt_StockInfoDetail dt_StockInfoDetail = new Dt_StockInfoDetail();
                                            foreach(var autoStockDestail in autoAssignStocks[j].Details)
                                            {
                                                if(autoStockDestail.MaterielCode == details[i].MaterielCode && autoStockDestail.BatchNo == details[i].BatchNo)
                                                {
                                                    dt_StockInfoDetail = autoStockDestail;
                                                }
                                            }
                                            Dt_OutStockLockInfo outStockLockInfo = _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, details[i], autoAssignStocks[j],dt_StockInfoDetail.OutboundQuantity);
                                            outStockLockInfos.Add(outStockLockInfo);
                                            details[i].LockQuantity += outStockLockInfo.AssignQuantity;
                                            break;
                                        }
                                        else
                                        {
                                            Dt_OutStockLockInfo outStockLockInfo = _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, details[i], autoAssignStocks[j], details[i].OrderQuantity - details[i].LockQuantity);
                                            outStockLockInfos.Add(outStockLockInfo);
                                            details[i].LockQuantity = details[i].OrderQuantity;
                                            break;
                                        }
                                    }
                                }
                            }
                            locationInfos.AddRange(_basicService.LocationInfoService.Repository.GetLocationInfos(autoAssignStocks.Select(x => x.LocationCode).ToList()));
                        }
                     }
                }
                newResidueQuantitys = new List<IStockInfoService.residueQuantity>();
            }
            return (outStocks, outboundOrderDetails, outStockLockInfos, locationInfos);
        }
        private (bool, string) CheckSelectStockDeital(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews)
        {
            if (outboundOrderDetail == null)
@@ -440,5 +647,87 @@
            }
            return (true, "成功");
        }
        public string ToCancelOutFeedbackERP = WIDESEA_Core.Helper.AppSettings.Configuration["CancelOutFeedbackERP"];
        public WebResponseContent CancelOutFeedbackERP(int[] keys)
        {
            List<Dt_OutboundOrderDetail> outboundOrderDetails = _outboundRepository.OutboundOrderDetailRepository.QueryData(x => keys.Contains(x.Id));
            if (outboundOrderDetails == null || outboundOrderDetails.Count == 0)
            {
                return WebResponseContent.Instance.Error("未找到出库单明细信息");
            }
            if (outboundOrderDetails.FirstOrDefault(x => x.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && x.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) != null)
            {
                return WebResponseContent.Instance.Error("所选出库单明细存在出库中或已完成");
            }
            Dt_OutboundOrder outboundOrder = _outboundRepository.OutboundOrderRepository.QueryFirst(x => x.Id == outboundOrderDetails[0].OrderId);
            if(outboundOrder == null)
            {
                return WebResponseContent.Instance.Error("未通过该明细找到出库单信息");
            }
            if (!outboundOrder.System.Equals("ERP"))
            {
                return WebResponseContent.Instance.Error("该出库单据非ERP推送,无法取消");
            }
            string ids ="";
            foreach (var item in outboundOrderDetails)
            {
                ids = item.LinId;
                var postContent = new MultipartFormDataContent();
                postContent.Add(new StringContent(ids), "ids");
                string result = string.Empty;
                HttpClient client = null;
                try
                {
                    using (client = new HttpClient())
                    {
                        // 2. 发送请求
                        string url = $"{ToCancelOutFeedbackERP}?ids={Uri.EscapeDataString(ids)}";
                        var response = HttpHelper.Post<WebResponseContent>(url, "出库明细取消回传ERP");
                        if (response.Code !=0)
                        {
                            throw new Exception($"操作失败: {response.msg ?? "未提供错误信息"}");
                        }
                    }
                    _unitOfWorkManage.BeginTran();
                    _outboundRepository.OutboundOrderDetailRepository.DeleteAndMoveIntoHty(outboundOrderDetails, OperateType.人工取消);
                    //检查该主订单是否还有剩余明细
                    int remainingDetailsCount = _outboundRepository.OutboundOrderDetailRepository
                        .Db.Queryable<Dt_OutboundOrderDetail>()
                        .Where(d => d.OrderId == outboundOrder.Id)
                        .Count();
                    // 如果没有剩余明细,再删除主订单
                    if (remainingDetailsCount == 0)
                    {
                        _outboundRepository.OutboundOrderRepository.DeleteAndMoveIntoHty(outboundOrder, OperateType.人工取消);
                    }
                    _unitOfWorkManage.CommitTran();
                    // 假设ERP返回成功时返回成功响应
                    return WebResponseContent.Instance.OK(result);
                }
                catch (HttpRequestException ex)
                {
                    _unitOfWorkManage.RollbackTran();
                    // 处理HTTP请求相关异常
                    return WebResponseContent.Instance.Error($"HTTP请求错误: {ex.Message}");
                }
                catch (Exception ex)
                {
                    _unitOfWorkManage.RollbackTran();
                    // 处理其他异常
                    return WebResponseContent.Instance.Error($"处理失败: {ex.Message}");
                }
            }
            return WebResponseContent.Instance.OK();
        }
    }
}