From 673b5a596f611099eaacc310f6e7def0e022daca Mon Sep 17 00:00:00 2001
From: heshaofeng <heshaofeng@hnkhzn.com>
Date: 星期三, 29 四月 2026 11:00:55 +0800
Subject: [PATCH] 添加盘点模板和胶箱台账功能

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs |  921 +++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 789 insertions(+), 132 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
index 29100af..26affda 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS\346\227\240\344\273\223\345\202\250\347\211\210/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs"
@@ -1,12 +1,18 @@
-锘縰sing Microsoft.Extensions.Logging;
+锘縰sing 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;
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using System.Transactions;
 using WIDESEA_BasicService;
 using WIDESEA_Common.CommonEnum;
 using WIDESEA_Common.LocationEnum;
@@ -15,13 +21,18 @@
 using WIDESEA_Common.StockEnum;
 using WIDESEA_Common.TaskEnum;
 using WIDESEA_Core;
+using WIDESEA_Core.CodeConfigEnum;
+using WIDESEA_Core.DB;
 using WIDESEA_Core.Helper;
+using WIDESEA_Core.Seed;
 using WIDESEA_DTO.Basic;
 using WIDESEA_DTO.Stock;
+using WIDESEA_IBasicService;
 using WIDESEA_Model.Models;
 using WIDESEA_Model.Models.Basic;
 using WIDESEA_Model.Models.Check;
 using WIDESEA_Model.Models.Outbound;
+
 
 namespace WIDESEA_TaskInfoService
 {
@@ -32,22 +43,75 @@
         /// </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
             {
-                var stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt() && x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt()).WhereIF(locationType != 0, x => x.LocationType == locationType).Take(num).ToList();
+                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 },
+        };
 
-                if (stockInfos.Count() == 0)
+                var query = _stockRepository.Db.Queryable<Dt_StockInfo>()
+                    .Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt()
+                             && x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    .WhereIF(locationType != 0, x => x.LocationType == locationType)
+                    .LeftJoin<Dt_LocationInfo>((s, l) => s.LocationCode == l.LocationCode);
+
+                if (!await query.AnyAsync())
                 {
                     return WebResponseContent.Instance.Error("鏈壘鍒扮┖鎵樼洏搴撳瓨");
                 }
+
+                bool isFirstOrder = true;
+                foreach (var item in orderByDict)
+                {
+                    string fieldName = item.Key.Equals("Column", StringComparison.OrdinalIgnoreCase)
+                        ? $"l.[{item.Key}]"
+                        : $"l.{item.Key}";
+
+                    string sortSql = $"{fieldName} {(item.Value == SqlSugar.OrderByType.Asc ? "ASC" : "DESC")}";
+
+                    if (isFirstOrder)
+                    {
+                        query = query.OrderBy(sortSql);
+                        isFirstOrder = false;
+                    }
+                    else
+                    {
+                        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("鏈壘鍒扮┖鎵樼洏搴撳瓨瀵瑰簲鐨勮揣浣嶄俊鎭�");
                     }
 
@@ -64,58 +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();
-
-                    TaskModel esstask = new TaskModel()
-                    {
-                        taskType = "carry",
-                        taskGroupCode = "",
-                        groupPriority = 0,
-                        tasks = new List<TasksType>
-                        {
-                                new()
-                                {
-                                    taskCode=task.TaskNum.ToString(),
-                                    taskPriority=0,
-                                    taskDescribe=new TaskDescribeType{
-                                    containerCode=stockInfo.PalletCode,
-                                    containerType= "CT_KUBOT_STANDARD",
-                                    fromLocationCode=stockInfo.LocationCode??"",
-                                    toStationCode="",
-                                    toLocationCode="1-2",
-                                    deadline=0,storageTag=""
-                                    }
-                                }
-                        }
-                    };
-                    var result = await _eSSApiService.CreateTaskAsync(esstask);
-
-                    _logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                    _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>
         /// 鍑哄簱浠诲姟鏁版嵁澶勭悊
@@ -308,45 +392,46 @@
                     _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails);
                 }
                 _unitOfWorkManage.CommitTran();
-                TaskModel esstask = new TaskModel()
-                {
-                    taskType = "carry",
-                    taskGroupCode = "",
-                    groupPriority = 0,
-                    tasks = new List<TasksType>()
-                };
+                return WebResponseContent.Instance.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);
+                //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("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
-                }
+                //_logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                //if (result)
+                //{
+                //    return WebResponseContent.Instance.OK();
+                //}
+                //else
+                //{
+                //    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                //}
             }
             catch (Exception ex)
             {
@@ -384,7 +469,7 @@
                             TargetAddress = outStation,
                             TaskStatus = TaskStatusEnum.New.ObjToInt(),
                             TaskType = taskType.ObjToInt(),
-                            // TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
+                            //TaskNum = BaseDal.GetTaskNum(nameof(SequenceEnum.SeqTaskNum)),
                             PalletType = stockInfo.PalletType,
                             WarehouseId = stockInfo.WarehouseId,
                         };
@@ -581,28 +666,33 @@
         {
             try
             {
-                var allocorder = _allocateOrderRepository.Db.Queryable<Dt_AllocateOrder>().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First();
-                //var allocorder = _allocateOrderDetailRepository.Db.Queryable<Dt_AllocateOrderDetail>()
-                //               .LeftJoin<Dt_AllocateOrder>((detail, order) => detail.OrderId == order.Id)
-                //               .Where((detail, order) => order.Id == orderDetailId)
-                //               .Select((detail, order) => order)
-                //               .First();
-                if (allocorder == null)
-                {
-                    return WebResponseContent.Instance.Error("鎵句笉鍒板崟鎹�");
-                }
-                var outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Includes(x => x.Details).First(x => x.OrderNo == allocorder.OrderNo);
+                //var allocorder = _allocateOrderRepository.Db.Queryable<Dt_AllocateOrder>().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First();
+                ////var allocorder = _allocateOrderDetailRepository.Db.Queryable<Dt_AllocateOrderDetail>()
+                ////               .LeftJoin<Dt_AllocateOrder>((detail, order) => detail.OrderId == order.Id)
+                ////               .Where((detail, order) => order.Id == orderDetailId)
+                ////               .Select((detail, order) => order)
+                ////               .First();
+                //if (allocorder == null)
+                //{
+                //    return WebResponseContent.Instance.Error("鎵句笉鍒板崟鎹�");
+                //}
+
+                var outboundOrder = SqlSugarHelper.DbWMS.Queryable<Dt_OutboundOrder>().Includes(x => x.Details).Where(x => x.Details.Any(o => o.Id == orderDetailId)).First();
+ 
                 if (outboundOrder == null)
                 {
                     return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撳崟鎹�");
                 }
-
-                var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details.Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode && allocItem.LineNo == outItem.lineNo
-                 && allocItem.BarcodeQty == outItem.BarcodeQty && allocItem.WarehouseCode == outItem.WarehouseCode && allocItem.BarcodeUnit == outItem.BarcodeUnit)).First();
-                if (orderdetail == null)
+                if (outboundOrder.Details == null || !outboundOrder.Details.Any()) 
                 {
                     return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撴槑缁嗗崟鎹�");
                 }
+                //var orderdetail = outboundOrder.Details.Where(outItem => allocorder.Details.Any(allocItem => allocItem.MaterielCode == outItem.MaterielCode && allocItem.LineNo == outItem.lineNo
+                // && allocItem.BarcodeQty == outItem.BarcodeQty && allocItem.WarehouseCode == outItem.WarehouseCode && allocItem.BarcodeUnit == outItem.BarcodeUnit)).First();
+                //if (orderdetail == null)
+                //{
+                //    return WebResponseContent.Instance.Error("鎵句笉鍒板嚭搴撴槑缁嗗崟鎹�");
+                //}
 
                 (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = OutboundTaskDataHandle(outboundOrder.Details.First().Id, stockSelectViews, station);
 
@@ -765,46 +855,47 @@
                     _outboundOrderDetailService.Repository.UpdateData(outboundOrderDetails);
                 }
                 _unitOfWorkManage.CommitTran();
+                return WebResponseContent.Instance.OK();
                 //PushTasksToWCS(tasks);
-                TaskModel esstask = new TaskModel()
-                {
-                    taskType = "carry",
-                    taskGroupCode = "",
-                    groupPriority = 0,
-                    tasks = new List<TasksType>()
-                };
+                //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);
+                //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("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
-                }
+                //_logger.LogInformation("鍒涘缓浠诲姟PalletOutboundTask 杩斿洖:  " + result);
+                //if (result)
+                //{
+                //    return WebResponseContent.Instance.OK();
+                //}
+                //else
+                //{
+                //    return WebResponseContent.Instance.Error("涓嬪彂鏈哄櫒浜轰换鍔″け璐ワ紒");
+                //}
 
             }
             catch (Exception ex)
@@ -815,7 +906,7 @@
 
         }
 
-
+        
         #region 鍒嗘壒鍒嗛厤搴撳瓨
         #region 鍐呭瓨閿佺鐞嗗櫒
         private static readonly ConcurrentDictionary<string, SemaphoreSlim> _materialLocks =
@@ -1092,6 +1183,572 @@
 
         #endregion
 
+        /// <summary>
+        /// 閫夊畾搴撳瓨鐢熸垚鐩樼偣鍗曞嚭搴�
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> TakeOutbound(List<StockViewDTO> stockViews, string outStation)
+        {
+            var content = new WebResponseContent();
+            var errorMessages = new List<string>();
 
+            try
+            {
+                if (stockViews == null || !stockViews.Any())
+                {
+                    content.Error("鏈幏鍙栧埌搴撳瓨鏁版嵁");
+                    content.Data = new { total = 0, success = 0, fail = 0, failMsg = errorMessages };
+                    return content;
+                }
+
+                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)
+                {
+                    try
+                    {
+                        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);
+                    }
+                    catch (Exception ex)
+                    {
+                        errorMessages.Add($"鎵樼洏銆恵stock.PalletCode}銆戝紓甯革細{ex.Message}");
+                        continue;
+                    }
+                }
+
+                if (finalSuccessStocks.Any())
+                {
+                    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();
+                    }
+                }
+
+                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();
+                content.Error($"绯荤粺寮傚父锛歿ex.Message}");
+                content.Data = new
+                {
+                    total = stockViews?.Count ?? 0,
+                    success = 0,
+                    fail = 1,
+                    failMsg = new List<string> { ex.Message }
+                };
+                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 鏍煎紡鐨凟xcel鏂囦欢锛岃閲嶆柊涓婁紶");
+
+                try
+                {
+                    using var stream = file.OpenReadStream();
+                    var excelList = ReadExcel(stream);
+
+                    if (!excelList.Any())
+                        return content.Error("瀵煎叆澶辫触锛欵xcel鏂囦欢涓湭璇诲彇鍒颁换浣曟湁鏁堟暟鎹�");
+
+                    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瑙f瀽澶辫触锛歿ex.Message}");
+                }
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return content.Error($"鎵归噺鍑哄簱澶辫触锛氱郴缁熷紓甯革紝{ex.Message}");
+            }
+        }
+        /// <summary>
+        /// 鍗曟嵁鐢熸垚鏂规硶
+        /// </summary>
+        static object lock_code = new object();
+        public string CreateCodeByRule(string ruleCode)
+        {
+            lock (lock_code)
+            {
+
+                string code = string.Empty;
+                DateTime dateTime = DateTime.Now;
+                DateTime now = DateTime.Now;
+                try
+                {
+                    if (string.IsNullOrEmpty(ruleCode))
+                        throw new ArgumentNullException(nameof(ruleCode));
+                    SqlSugarClient sugarClient = new SqlSugarClient(new ConnectionConfig
+                    {
+                        IsAutoCloseConnection = true,
+                        DbType = DbType.SqlServer,
+                        ConnectionString = DBContext.ConnectionString
+                    });
+                    Dt_CodeRuleConfig codeRuleConfig = sugarClient.Queryable<Dt_CodeRuleConfig>().Where(x => x.RuleCode == ruleCode).First();
+                    if (codeRuleConfig == null)
+                        throw new ArgumentNullException(nameof(codeRuleConfig));
+                    if (codeRuleConfig.ModifyDate != null)
+                    {
+                        dateTime = Convert.ToDateTime(codeRuleConfig.ModifyDate);
+                    }
+                    else
+                    {
+                        dateTime = Convert.ToDateTime(codeRuleConfig.CreateDate);
+                    }
+
+                    if (now.Year == dateTime.Year && now.Month == dateTime.Month && now.Day == dateTime.Day)
+                    {
+                        now = dateTime;
+                        codeRuleConfig.CurrentVal = Convert.ToInt32(codeRuleConfig.CurrentVal) + 1;
+                    }
+                    else
+                    {
+                        codeRuleConfig.CurrentVal = 1;
+                    }
+                    codeRuleConfig.ModifyDate = DateTime.Now;
+                    code = codeRuleConfig.StartStr + codeRuleConfig.Format;
+                    code = code.Replace($"[{CodeFormatTypeEnum.YYYY}]", now.Year.ToString().PadLeft(4, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.MM}]", now.Month.ToString().PadLeft(2, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.DD}]", now.Day.ToString().PadLeft(2, '0'));
+                    code = code.Replace($"[{CodeFormatTypeEnum.ST}]", codeRuleConfig.StartStr?.ToString() ?? "");
+                    code = code.Replace($"[{CodeFormatTypeEnum.NUM}]", codeRuleConfig.CurrentVal.ToString().PadLeft(codeRuleConfig.Length, '0'));
+                    Dictionary<string, object> keyValuePairs = new Dictionary<string, object>() { { nameof(codeRuleConfig.CurrentVal), codeRuleConfig.CurrentVal }, { nameof(codeRuleConfig.Id), codeRuleConfig.Id }, { nameof(codeRuleConfig.ModifyDate), DateTime.Now } };
+                    sugarClient.Updateable(keyValuePairs).AS(MainDb.CodeRuleConfig).WhereColumns(nameof(codeRuleConfig.Id)).ExecuteCommand();
+                    sugarClient.Updateable(codeRuleConfig);
+
+                }
+                catch (Exception ex)
+                {
+
+                }
+                return code;
+            }
+        }
+
+        /// <summary>
+        /// 閫夊畾搴撳瓨鍚屽尯鍩熺Щ搴�
+        /// </summary>
+        /// <param name="id"></param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> AreaOutbound(List<StockViewDTO> stockViews)
+        {
+            WebResponseContent content = new WebResponseContent();
+            try
+            {
+                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)
+                {
+                    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}璐т綅鏁版嵁");
+                }
+               
+                foreach (var item in stockInfos)
+                {
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
+                    if (locationInfo == null || (locationInfo.EnableStatus == EnableStatusEnum.Disable.ObjToInt() || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt()) || item.StockStatus != StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    {
+                        return content.Error($"{item.PalletCode}璐т綅鎴栧簱瀛樼姸鎬佷笉婊¤冻鍑哄簱鏉′欢");
+                    }
+                }
+                List<Dt_Task> tasks = AreaGetTasks(stockInfos, TaskTypeEnum.AreaRelocation);
+                if (tasks == null || tasks.Count <= 0)
+                {
+                    return content.Error($"鐢熸垚浠诲姟澶辫触");
+                }
+                stockInfos.ForEach(x =>
+                {
+                    x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                });
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = "鏃犲崟鎹Щ搴�";
+                });
+                locationInfos.ForEach(x =>
+                {
+                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                });
+                _unitOfWorkManage.BeginTran();
+                //鏇存柊搴撳瓨鐘舵��
+                _stockRepository.UpdateData(stockInfos);
+                //鏂板缓浠诲姟
+                BaseDal.AddData(tasks);
+                _locationInfoService.UpdateData(locationInfos);
+                _unitOfWorkManage.CommitTran();
+                content.OK();
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
+            }
+            return content;
+        }
+
+        public List<Dt_Task> AreaGetTasks(List<Dt_StockInfo> stockInfos, TaskTypeEnum taskType)
+        {
+            // 浣跨敤 TransactionScope 鍖呰9鏁翠釜鎿嶄綔锛岀‘淇濇墍鏈夋暟鎹簱鎿嶄綔鍦ㄥ悓涓�浜嬪姟涓�
+            using (var scope = new TransactionScope(TransactionScopeOption.Required,
+                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
+            {
+                try
+                {
+                    List<Dt_Task> tasks = new List<Dt_Task>();
+                    List<Dt_LocationInfo> locationInfos = _locationInfoService.Repository.QueryData(
+                        x => stockInfos.Select(x => x.LocationCode).Contains(x.LocationCode));
+
+                    for (int i = 0; i < stockInfos.Count; i++)
+                    {
+                        Dt_StockInfo stockInfo = stockInfos[i];
+
+                        if (stockInfo != null)
+                        {
+                            Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == stockInfo.LocationCode);
+                            var newLocation = _locationInfoService.AssignLocation(stockInfo.LocationType);
+
+                            if (newLocation == null)
+                            {
+                                throw new Exception($"鍦▄stockInfo.PalletCode}鏃舵病鏈夌┖闂插簱浣嶅彲杩涜鍚屽尯鍩熺Щ搴擄紝璇锋鏌ユ垨鍑忓皯绉诲簱鏂欑");
+                            }
+
+                            if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                            {
+                                Dt_Task task = new()
+                                {
+                                    CurrentAddress = stockInfo.LocationCode,
+                                    Grade = 0,
+                                    PalletCode = stockInfo.PalletCode,
+                                    NextAddress = "",
+                                    Roadway = locationInfo.RoadwayNo,
+                                    SourceAddress = stockInfo.LocationCode,
+                                    TargetAddress = newLocation.LocationCode,
+                                    TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                                    TaskType = taskType.ObjToInt(),
+                                    PalletType = stockInfo.PalletType,
+                                    WarehouseId = stockInfo.WarehouseId,
+                                };
+                                tasks.Add(task);
+                            }
+
+                            newLocation.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                            _locationInfoService.UpdateData(newLocation);
+                        }
+                    }
+
+                    // 鎻愪氦浜嬪姟
+                    scope.Complete();
+                    return tasks;
+                }
+                catch (Exception ex)
+                {
+                    // TransactionScope 浼氳嚜鍔ㄥ洖婊氫簨鍔★紝鏃犻渶鎵嬪姩 Rollback
+                    throw new Exception(ex.Message);
+                }
+            }
+        }
+
+        /// <summary>
+        /// 閫夊畾搴撳瓨璺ㄥ尯鍩熺Щ搴�
+        /// </summary>
+        /// <param name="stockViews"></param>
+        /// <param name="targetLocationType">鐩爣璐т綅绫诲瀷</param>
+        /// <returns></returns>
+        public async Task<WebResponseContent> CrossAreaOutbound(List<StockViewDTO> stockViews, int targetLocationType)
+        {
+            WebResponseContent content = new WebResponseContent();
+            try
+            {
+                if(targetLocationType == (int)LocationTypeEnum.Electronic)
+                {
+                    return content.Error("鐢靛瓙浠撲笉鍏佽璺ㄥ尯鍩熺Щ搴�");
+                }
+                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)
+                {
+                    StockViewDTO? stockViewDTO = stockViews.FirstOrDefault(x => !stockInfos.Select(x => x.PalletCode).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).Contains(x));
+                    return content.Error($"鏈壘鍒皗locStr}璐т綅鏁版嵁");
+                }
+
+                foreach (var item in stockInfos)
+                {
+                    if (item.PalletType != PalletTypeEnum.Empty.ObjToInt())
+                    {
+                        return content.Error($"鎵樼洏銆恵item.PalletCode}銆戦潪绌虹锛屼粎绌虹鍏佽璺ㄥ尯鍩熺Щ搴擄紒");
+                    }
+                    Dt_LocationInfo? locationInfo = locationInfos.FirstOrDefault(x => x.LocationCode == item.LocationCode);
+                    if (locationInfo == null || locationInfo.EnableStatus != EnableStatusEnum.Normal.ObjToInt() || item.StockStatus != StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt())
+                    {
+                        return content.Error($"{item.PalletCode}璐т綅鎴栧簱瀛樼姸鎬佷笉婊¤冻鍑哄簱鏉′欢");
+                    }
+                }
+
+                List<Dt_Task> tasks = CrossAreaGetTasks(stockInfos, targetLocationType, TaskTypeEnum.CrossAreaRelocation);
+
+                if (tasks == null || tasks.Count <= 0)
+                {
+                    return content.Error("鐢熸垚璺ㄥ尯鍩熺Щ搴撲换鍔″け璐�");
+                }
+
+                stockInfos.ForEach(x =>
+                {
+                    x.StockStatus = StockStatusEmun.鍑哄簱閿佸畾.ObjToInt();
+                });
+
+                tasks.ForEach(x =>
+                {
+                    x.OrderNo = "璺ㄥ尯鍩熺Щ搴�";
+                });
+
+                locationInfos.ForEach(x =>
+                {
+                    x.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                });
+
+                _unitOfWorkManage.BeginTran();
+                _stockRepository.UpdateData(stockInfos);
+                BaseDal.AddData(tasks);
+                _locationInfoService.UpdateData(locationInfos);
+                _unitOfWorkManage.CommitTran();
+
+                content.OK();
+            }
+            catch (Exception ex)
+            {
+                _unitOfWorkManage.RollbackTran();
+                return await Task.FromResult(WebResponseContent.Instance.Error(ex.Message));
+            }
+            return content;
+        }
+
+
+        /// <summary>
+        /// 鐢熸垚璺ㄥ尯鍩熺Щ搴撲换鍔�
+        /// </summary>
+        /// <param name="stockInfos">搴撳瓨鍒楄〃</param>
+        /// <param name="targetAreaCode">鐩爣鍖哄煙缂栫爜</param>
+        /// <param name="taskType">浠诲姟绫诲瀷</param>
+        /// <returns></returns>
+        public List<Dt_Task> CrossAreaGetTasks(List<Dt_StockInfo> stockInfos, int targetLocationType, TaskTypeEnum taskType)
+        {
+            using (var scope = new TransactionScope(TransactionScopeOption.Required,
+                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
+            {
+                try
+                {
+                    List<Dt_Task> tasks = new List<Dt_Task>();
+
+                    foreach (var stockInfo in stockInfos)
+                    {
+                        Dt_LocationInfo newLocation = _locationInfoService.AssignLocation(targetLocationType);
+
+                        if (newLocation == null)
+                        {
+                            throw new Exception($"{stockInfo.PalletCode} 娌℃湁绌洪棽璐т綅鍙繘琛岃法鍖哄煙绉诲簱");
+                        }
+
+                        if (!tasks.Exists(x => x.PalletCode == stockInfo.PalletCode))
+                        {
+                            Dt_Task task = new()
+                            {
+                                CurrentAddress = stockInfo.LocationCode,
+                                Grade = 0,
+                                PalletCode = stockInfo.PalletCode,
+                                NextAddress = "",
+                                Roadway = newLocation.RoadwayNo,
+                                SourceAddress = stockInfo.LocationCode,
+                                TargetAddress = newLocation.LocationCode,
+                                TaskStatus = TaskStatusEnum.New.ObjToInt(),
+                                TaskType = taskType.ObjToInt(),
+                                PalletType = stockInfo.PalletType,
+                                WarehouseId = stockInfo.WarehouseId,
+                            };
+                            tasks.Add(task);
+                        }
+
+                        newLocation.LocationStatus = LocationStatusEnum.Lock.ObjToInt();
+                        _locationInfoService.UpdateData(newLocation);
+                    }
+
+                    scope.Complete();
+                    return tasks;
+                }
+                catch (Exception ex)
+                {
+                    throw new Exception(ex.Message);
+                }
+            }
+        }
+
+        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;
+
+                // 姝g‘鑾峰彇琛屾暟
+                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;
+        }
     }
+
 }

--
Gitblit v1.9.3