heshaofeng
昨天 673b5a596f611099eaacc310f6e7def0e022daca
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -1,6 +1,8 @@
using MailKit.Search;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OfficeOpenXml;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using SqlSugar;
using System;
@@ -31,6 +33,7 @@
using WIDESEA_Model.Models.Check;
using WIDESEA_Model.Models.Outbound;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
@@ -40,18 +43,19 @@
        /// </summary>
        /// <param name="inTask"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType)
        // æ–¹æ³•增加了 string? SupplierCode å¯é€‰å‚æ•°
        public async Task<WebResponseContent> PalletOutboundTask(int num, int locationType, string? supplierCode = null)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                Dictionary<string, SqlSugar.OrderByType> orderByDict = new Dictionary<string, SqlSugar.OrderByType>()
                {
                     { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
                };
        {
            { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
            { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
        };
                var query = _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt()
@@ -59,7 +63,7 @@
                    .WhereIF(locationType != 0, x => x.LocationType == locationType)
                    .LeftJoin<Dt_LocationInfo>((s, l) => s.LocationCode == l.LocationCode);
                if (query.Count() == 0)
                if (!await query.AnyAsync())
                {
                    return WebResponseContent.Instance.Error("未找到空托盘库存");
                }
@@ -80,17 +84,34 @@
                    }
                    else
                    {
                        query = query.OrderBy(sortSql);
                        query.OrderBy(sortSql);
                    }
                }
                var stockInfos = await query.Take(num).ToListAsync();
                _unitOfWorkManage.BeginTran();
                Dt_PlasticContainerLedger? todayLedger = null;
                var today = DateTime.Now.Date;
                var tomorrow = today.AddDays(1);
                if (!string.IsNullOrWhiteSpace(supplierCode))
                {
                    todayLedger = await _stockRepository.Db.Queryable<Dt_PlasticContainerLedger>()
                        .Where(x => x.SupplyCode == supplierCode)
                        .Where(x => x.CreateDate >= today && x.CreateDate < tomorrow)
                        .FirstAsync();
                }
                string allTaskNums = string.Empty;
                foreach (var stockInfo in stockInfos)
                {
                    Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
                    Dt_LocationInfo locationInfo = await _locationInfoService.Repository.QueryFirstAsync(x => x.LocationCode == stockInfo.LocationCode);
                    if (locationInfo == null)
                    {
                        _unitOfWorkManage.RollbackTran();
                        return WebResponseContent.Instance.Error("未找到空托盘库存对应的货位信息");
                    }
@@ -107,35 +128,78 @@
                        TaskType = TaskTypeEnum.OutEmpty.ObjToInt(),
                        WarehouseId = stockInfo.WarehouseId,
                        PalletType = stockInfo.PalletType
                    };
                    int beforeStatus = locationInfo.LocationStatus;
                    _unitOfWorkManage.BeginTran();
                    stockInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt();
                    locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                    int taskId = BaseDal.AddData(task);
                    int taskId = await BaseDal.AddDataAsync(task);
                    task.TaskId = taskId;
                    allTaskNums += task.TaskNum + ",";
                    stockInfo.StockStatus = StockStatusEmun.出库锁定.ObjToInt();
                    _stockService.StockInfoService.UpdateData(stockInfo);
                    int beforeStatus = locationInfo.LocationStatus;
                    locationInfo.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                    _locationInfoService.UpdateData(locationInfo);
                    _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
                    _unitOfWorkManage.CommitTran();
                    _recordService.LocationStatusChangeRecordSetvice.AddLocationStatusChangeRecord(
                       locationInfo, beforeStatus, StockChangeType.Outbound.ObjToInt(), "", task.TaskNum);
                }
                if (!string.IsNullOrWhiteSpace(supplierCode))
                {
                    var ledgerList = new List<Dt_PlasticContainerLedger>();
                    foreach (var stock in stockInfos)
                    {
                        ledgerList.Add(new Dt_PlasticContainerLedger
                        {
                            SupplyCode = supplierCode,
                            PalletCode = stock.PalletCode,
                            CreateDate = DateTime.Now,
                            Creater = App.User?.ToString()
                        });
                    }
                    _plasticContainerLedger.AddData(ledgerList);
                }
                _unitOfWorkManage.CommitTran();
                return content.OK("空托出库成功!");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        /// <summary>
        /// å®žæ—¶æœç´¢ä¾›åº”商编码(支持输入 r è‡ªåŠ¨åŒ¹é…ï¼Œä¸‹æ‹‰æ¡†ç”¨ï¼‰
        /// </summary>
        /// <param name="keyword">输入的关键词(如 r)</param>
        /// <returns>匹配的供应商编码列表</returns>
        public async Task<WebResponseContent> SearchSupplierCode(string keyword)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(keyword))
                    return WebResponseContent.Instance.OK("没有匹配到该供应商编号");
                var list = await _stockRepository.Db.Queryable<Dt_SupplierInfo>()
                    .Where(x => x.SupplierShortName.StartsWith(keyword))
                    .OrderBy(x => x.SupplierShortName)
                    .Take(20)
                    .Select(x => x.SupplierShortName)
                    .ToListAsync();
                return WebResponseContent.Instance.OK(data:list);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        /// <summary>
        /// å‡ºåº“任务数据处理
@@ -1126,127 +1190,196 @@
        /// <returns></returns>
        public async Task<WebResponseContent> TakeOutbound(List<StockViewDTO> stockViews, string outStation)
        {
            WebResponseContent content = new WebResponseContent();
            var content = new WebResponseContent();
            var errorMessages = new List<string>();
            try
            {
                var allFactoryAreas = stockViews.SelectMany(sv => sv.Details)
                                                .Select(x => x.FactoryArea)
                                                .GroupBy(x => x)
                                                .ToList();
                if (allFactoryAreas.Count >= 2)
                if (stockViews == null || !stockViews.Any())
                {
                    return content.Error($"请选择同一厂区区域的库存进行盘点,当前涉及{allFactoryAreas.Count}个不同的厂区");
                    content.Error("未获取到库存数据");
                    content.Data = new { total = 0, success = 0, fail = 0, failMsg = errorMessages };
                    return content;
                }
                List<int> ids = stockViews.Select(x => x.StockId).ToList();
                //获取库存
                List<Dt_StockInfo> stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => ids.Contains(x.Id)).Includes(x => x.Details).ToList();
                if (stockInfos.Count != stockViews.Count)
                var ids = stockViews.Select(x => x.StockId).ToList();
                var stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Where(x => ids.Contains(x.Id))
                    .Includes(x => x.Details)
                    .ToList();
                var locCodes = stockInfos.Select(x => x.LocationCode).ToList();
                var locationInfos = _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                    .Where(x => locCodes.Contains(x.LocationCode))
                    .ToList();
                var finalSuccessStocks = new List<Dt_StockInfo>();
                var finalSuccessLocations = new List<Dt_LocationInfo>();
                foreach (var stock in stockInfos)
                {
                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).ToList().Contains(x.PalletCode));
                    return content.Error($"未找到{stockViewDTO?.PalletCode}库存");
                }
                //获取货位
                List<string> locStrs = stockInfos.Select(x => x.LocationCode).ToList();
                List<Dt_LocationInfo> locationInfos =_locationInfoService.Db.Queryable<Dt_LocationInfo>().Where(x => locStrs.Contains(x.LocationCode)).ToList();
                if (stockInfos.Count != locationInfos.Count)
                {
                    string? locStr = locStrs.FirstOrDefault(x => !locationInfos.Select(x => x.LocationCode).ToList().Contains(x));
                    return content.Error($"未找到{locStr}货位数据");
                }
                Dt_TakeStockOrder takeStockOrder = new Dt_TakeStockOrder()
                {
                    WarehouseId = stockInfos.FirstOrDefault().WarehouseId,
                    TakeStockStatus = TakeStockStatusEnum.盘点中.ObjToInt(),
                    OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
                    AllPalletCode = string.Join(",", stockInfos.Select(item => item.PalletCode).Where(palletCode => !string.IsNullOrEmpty(palletCode))),
                    Remark = outStation
                };
                foreach (var item in stockInfos)
                {
                    if (item.Details.Count <= 0)
                    try
                    {
                        return content.Error($"未找到{item.PalletCode}库存明细数据");
                        if (stock.Details == null || !stock.Details.Any())
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:无库存明细");
                            continue;
                        }
                        var loc = locationInfos.FirstOrDefault(x => x.LocationCode == stock.LocationCode);
                        if (loc == null)
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:库位不存在");
                            continue;
                        }
                        if (loc.EnableStatus != EnableStatusEnum.Normal.ObjToInt() ||
                            loc.LocationStatus != LocationStatusEnum.InStock.ObjToInt() ||
                            stock.StockStatus != StockStatusEmun.入库完成.ObjToInt())
                        {
                            errorMessages.Add($"托盘【{stock.PalletCode}】:状态不允许出库");
                            continue;
                        }
                        finalSuccessStocks.Add(stock);
                        finalSuccessLocations.Add(loc);
                    }
                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || locationInfo.LocationStatus != LocationStatusEnum.InStock.ObjToInt() || item.StockStatus != StockStatusEmun.入库完成.ObjToInt())
                    catch (Exception ex)
                    {
                        return content.Error($"{item.PalletCode}货位或库存状态不满足出库条件");
                    }
                        errorMessages.Add($"托盘【{stock.PalletCode}】异常:{ex.Message}");
                        continue;
                    }
                }
                List<Dt_Task> tasks = GetTasks(stockInfos, TaskTypeEnum.OutInventory,outStation);
                if (tasks == null || tasks.Count <= 0)
                if (finalSuccessStocks.Any())
                {
                    return content.Error($"生成任务失败");
                    var takeStockOrder = new Dt_TakeStockOrder
                    {
                        WarehouseId = finalSuccessStocks.First().WarehouseId,
                        TakeStockStatus = TakeStockStatusEnum.盘点中.ObjToInt(),
                        OrderNo = CreateCodeByRule(nameof(RuleCodeEnum.PDCodeRule)),
                        AllPalletCode = string.Join(",", finalSuccessStocks.Select(x => x.PalletCode)),
                        Remark = outStation
                    };
                    var tasks = GetTasks(finalSuccessStocks, TaskTypeEnum.OutInventory, outStation);
                    if (tasks == null || tasks.Count <= 0)
                    {
                        errorMessages.Add("任务生成失败");
                    }
                    else
                    {
                        finalSuccessStocks.ForEach(x => x.StockStatus = StockStatusEmun.盘点出库锁定.ObjToInt());
                        finalSuccessLocations.ForEach(x => x.LocationStatus = LocationStatusEnum.Lock.ObjToInt());
                        tasks.ForEach(x => x.OrderNo = takeStockOrder.OrderNo);
                        _unitOfWorkManage.BeginTran();
                        _stockRepository.UpdateData(finalSuccessStocks);
                        _takeStockOrder.AddData(takeStockOrder);
                        BaseDal.AddData(tasks);
                        _locationInfoService.UpdateData(finalSuccessLocations);
                        _unitOfWorkManage.CommitTran();
                    }
                }
                stockInfos.ForEach(x =>
                {
                    x.StockStatus = StockStatusEmun.盘点出库锁定.ObjToInt();
                });
                tasks.ForEach(x =>
                {
                    x.OrderNo = takeStockOrder.OrderNo;
                });
                locationInfos.ForEach(x =>
                {
                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
                });
                _unitOfWorkManage.BeginTran();
                //更新库存状态
                _stockRepository.UpdateData(stockInfos);
                _takeStockOrder.AddData(takeStockOrder);
                //新建任务
                BaseDal.AddData(tasks);
                _locationInfoService.UpdateData(locationInfos);
                _unitOfWorkManage.CommitTran();
                content.OK();
                //TaskModel esstask = new TaskModel()
                //{
                //    taskType = "carry",
                //    taskGroupCode = "",
                //    groupPriority = 0,
                //    tasks = new List<TasksType>()
                //};
                //foreach (var task in tasks)
                //{
                //    esstask.
                //       tasks.Add(new TasksType
                //       {
                //           taskCode = task.TaskNum.ToString(),
                //           taskPriority = 0,
                //           taskDescribe = new TaskDescribeType
                //           {
                //               containerCode = task.PalletCode,
                //               containerType = "CT_KUBOT_STANDARD",
                //               fromLocationCode = task.SourceAddress ?? "",
                //               toStationCode = "",
                //               toLocationCode = task.TargetAddress,
                //               deadline = 0,
                //               storageTag = ""
                //           }
                //       }
                //   );
                //}
                //var result = await _eSSApiService.CreateTaskAsync(esstask);
                //_logger.LogInformation("创建任务PalletOutboundTask è¿”回:  " + result);
                //if (result)
                //{
                //    return WebResponseContent.Instance.OK();
                //}
                //else
                //{
                //    return WebResponseContent.Instance.Error("下发机器人任务失败!");
                //}
                //content.OK();
                content.OK($"处理完成:成功【{finalSuccessStocks.Count}】条,失败【{errorMessages.Count}】条");
                content.Data = new
                {
                    total = stockInfos.Count,
                    success = finalSuccessStocks.Count,
                    fail = errorMessages.Count,
                    failMsg = errorMessages
                };
                return content;
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
                content.Error($"系统异常:{ex.Message}");
                content.Data = new
                {
                    total = stockViews?.Count ?? 0,
                    success = 0,
                    fail = 1,
                    failMsg = new List<string> { ex.Message }
                };
                return content;
            }
            return content;
        }
        public async Task<WebResponseContent> BatchOutboundByExcel(IFormFile file, string outStation)
        {
            WebResponseContent content = new WebResponseContent();
            List<StockViewDTO> successStockViews = new List<StockViewDTO>();
            List<string> noStockMaterialCodes = new List<string>();
            try
            {
                if (file == null || file.Length == 0)
                    return content.Error("上传失败:请选择需要导入的Excel文件");
                var ext = Path.GetExtension(file.FileName).ToLower();
                if (ext != ".xls" && ext != ".xlsx")
                    return content.Error("上传失败:仅支持 .xls å’Œ .xlsx æ ¼å¼çš„Excel文件,请重新上传");
                try
                {
                    using var stream = file.OpenReadStream();
                    var excelList = ReadExcel(stream);
                    if (!excelList.Any())
                        return content.Error("导入失败:Excel文件中未读取到任何有效数据");
                    foreach (var item in excelList)
                    {
                        var matchedStocks = _stockRepository.Db.Queryable<Dt_StockInfo>()
                            .Includes(x => x.Details)
                            .Where(x =>
                                x.Details.Any(d =>
                                    d.WarehouseCode == item.WarehouseCode
                                    && d.MaterielCode == item.MaterialCode
                                )
                            )
                            .ToList();
                        if (matchedStocks.Any())
                        {
                            successStockViews.AddRange(matchedStocks.Select(s => new StockViewDTO
                            {
                                StockId = s.Id
                            }));
                        }
                        else
                        {
                            noStockMaterialCodes.Add(item.MaterialCode);
                        }
                    }
                    // æ‰§è¡Œå‡ºåº“
                    if (successStockViews.Any())
                    {
                        var result = await TakeOutbound(successStockViews, outStation);
                        return result;
                    }
                    // æ— ä»»ä½•可出库数据
                    var msg = "未查询到任何可出库库存";
                    if (noStockMaterialCodes.Any())
                    {
                        msg += $",无库存料号:{string.Join("、", noStockMaterialCodes)}";
                    }
                    return content.Error(msg);
                }
                catch (Exception ex)
                {
                    return content.Error($"Excel解析失败:{ex.Message}");
                }
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return content.Error($"批量出库失败:系统异常,{ex.Message}");
            }
        }
        /// <summary>
        /// å•据生成方法
@@ -1584,6 +1717,38 @@
                }
            }
        }
        public static List<ExcelInventoryOutboundDto> ReadExcel(Stream stream)
        {
            var list = new List<ExcelInventoryOutboundDto>();
            using (var package = new ExcelPackage(stream))
            {
                var sheet = package.Workbook.Worksheets.FirstOrDefault();
                if (sheet == null) return list;
                // æ­£ç¡®èŽ·å–è¡Œæ•°
                int rowCount = sheet.Dimension?.End.Row ?? 0;
                // ç¬¬2行开始读
                for (int row = 2; row <= rowCount; row++)
                {
                    string location = sheet.Cells[row, 8].Value?.ToString()?.Trim() ?? "";
                    string materialCode = sheet.Cells[row, 2].Value?.ToString()?.Trim() ?? "";
                    if (string.IsNullOrEmpty(location) || string.IsNullOrEmpty(materialCode))
                        continue;
                    list.Add(new ExcelInventoryOutboundDto
                    {
                        WarehouseCode = location,
                        MaterialCode = materialCode
                    });
                }
            }
            return list;
        }
    }
}