From 1a1bc81ac1d9ad656e15b409122e23f7f3763293 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期四, 27 十一月 2025 08:27:41 +0800
Subject: [PATCH] 提交

---
 项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs |  248 ++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 204 insertions(+), 44 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_OutboundService/OutboundOrderDetailService.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_OutboundService/OutboundOrderDetailService.cs"
index 4459d4c..121a74e 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_OutboundService/OutboundOrderDetailService.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_OutboundService/OutboundOrderDetailService.cs"
@@ -336,71 +336,231 @@
         }
 
 
-        public (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>) AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews)
+        public (List<Dt_StockInfo>, Dt_OutboundOrderDetail, List<Dt_OutStockLockInfo>, List<Dt_LocationInfo>)
+       AssignStockOutbound(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews)
         {
+            // 楠岃瘉鐢ㄦ埛閫夋嫨
             (bool, string) checkResult = CheckSelectStockDeital(outboundOrderDetail, stockSelectViews);
             if (!checkResult.Item1) throw new Exception(checkResult.Item2);
 
             Dt_OutboundOrder outboundOrder = _outboundOrderService.Repository.QueryFirst(x => x.Id == outboundOrderDetail.OrderId);
             var originalNeedQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty;
 
-            var needQuantity = originalNeedQuantity;
+            List<Dt_StockInfo> outStocks = new List<Dt_StockInfo>();
+            List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
 
-            List<Dt_StockInfo> outStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(stockSelectViews.Select(x => x.PalletCode).ToList());
-            var assignQuantity = 0m;
-            outStocks.ForEach(x =>
-            {
-                x.Details.ForEach(v =>
-                {
-                    assignQuantity += v.StockQuantity - v.OutboundQuantity;
-                });
-            });
+            decimal remainingNeedQuantity = originalNeedQuantity;
+            decimal totalAssignedFromUserSelection = 0;
 
-            outboundOrderDetail.LockQuantity += assignQuantity;
-            outStocks.ForEach(x =>
+            // 鎸夊厛杩涘厛鍑烘帓搴忕敤鎴烽�夋嫨鐨勫簱瀛�
+            var userSelectedStocks = _stockService.StockInfoService.GetStockInfosByPalletCodes(
+                stockSelectViews.Select(x => x.PalletCode).ToList());
+
+            var sortedUserSelectedStocks = userSelectedStocks
+                .OrderBy(x => x.CreateDate)
+                .ToList();
+
+            // 鍒嗛厤鐢ㄦ埛閫夋嫨鐨勫簱瀛�
+            foreach (var stock in sortedUserSelectedStocks)
             {
-                x.Details.ForEach(v =>
+                if (remainingNeedQuantity <= 0) break;
+
+                // 鑾峰彇鐢ㄦ埛瀵硅鎵樼洏鐨勯�夋嫨鏁伴噺
+                var userSelection = stockSelectViews.FirstOrDefault(x => x.PalletCode == stock.PalletCode);
+                if (userSelection == null) continue;
+
+                // 璁$畻璇ユ墭鐩樺疄闄呭彲鐢ㄦ暟閲�
+                var availableQuantity = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode,
+                    outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode);
+
+                // 纭畾鍒嗛厤鏁伴噺锛氬彇鐢ㄦ埛閫夋嫨鏁伴噺銆佸彲鐢ㄦ暟閲忓拰鍓╀綑闇�姹傜殑鏈�灏忓��
+                var assignQuantity = Math.Min(
+                    Math.Min(userSelection.UseableQuantity, availableQuantity),
+                    remainingNeedQuantity);
+
+                if (assignQuantity <= 0) continue;
+
+                // 鎵ц鍒嗛厤
+                var actualAssigned = AssignStockQuantity(stock, outboundOrderDetail, assignQuantity);
+                if (actualAssigned > 0)
                 {
-                    v.OutboundQuantity = v.StockQuantity;
-                });
-            });
-            needQuantity -= assignQuantity;
-            if (outboundOrderDetail.OrderQuantity > outboundOrderDetail.LockQuantity)
-            {
-                List<Dt_StockInfo> stockInfos = _stockService.StockInfoService.GetUseableStocks(outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo, "");
-                stockInfos = stockInfos.Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode)).ToList();
-                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(stockInfos, outboundOrderDetail.MaterielCode, needQuantity, out decimal residueQuantity);
-                outboundOrderDetail.LockQuantity += needQuantity - residueQuantity;
-                outStocks.AddRange(autoAssignStocks);
-                outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
-                if (residueQuantity > 0)
-                {
-                    outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt();
+                    outStocks.Add(stock);
+                    totalAssignedFromUserSelection += actualAssigned;
+                    remainingNeedQuantity -= actualAssigned;
+
+                    // 鍒涘缓閿佸畾璁板綍
+                    var lockInfo = CreateOutStockLockInfo(outboundOrder, outboundOrderDetail, stock, actualAssigned);
+                    outStockLockInfos.Add(lockInfo);
                 }
             }
 
-            List<Dt_OutStockLockInfo> outStockLockInfos = _outStockLockInfoService.GetOutStockLockInfos(outboundOrder, outboundOrderDetail, outStocks);
+            // 妫�鏌ョ敤鎴烽�夋嫨鏄惁鑷冲皯鍒嗛厤浜嗕竴閮ㄥ垎
+            if (totalAssignedFromUserSelection <= 0)
+            {
+                throw new Exception("鐢ㄦ埛閫夋嫨鐨勫簱瀛樻棤娉曞垎閰嶄换浣曟暟閲�");
+            }
+
+            // 濡傛灉鐢ㄦ埛閫夋嫨鐨勫簱瀛樹笉澶燂紝鑷姩鍒嗛厤鍓╀綑閮ㄥ垎
+            decimal autoAssignedQuantity = 0;
+            if (remainingNeedQuantity > 0)
+            {
+                List<Dt_StockInfo> autoStocks = _stockService.StockInfoService.GetUseableStocks(
+                    outboundOrderDetail.MaterielCode, outboundOrderDetail.BatchNo, "");
+
+                // 鎺掗櫎鐢ㄦ埛宸查�夋嫨鐨勬墭鐩�
+                autoStocks = autoStocks
+                    .Where(x => !stockSelectViews.Select(v => v.PalletCode).Contains(x.PalletCode))
+                    .ToList();
+
+                var (autoAssignStocks, stockAllocations) = _stockService.StockInfoService.GetOutboundStocks(
+                    autoStocks, outboundOrderDetail.MaterielCode, remainingNeedQuantity, out decimal residueQuantity);
+
+                // 妫�鏌ヨ嚜鍔ㄥ垎閰嶇粨鏋�
+                autoAssignedQuantity = remainingNeedQuantity - residueQuantity;
+                if (autoAssignedQuantity <= 0 && remainingNeedQuantity > 0)
+                {
+                    // 閮ㄥ垎鍒嗛厤鏄彲浠ユ帴鍙楃殑锛岃褰曡鍛婁絾涓嶆姤閿�
+                    _logger.LogWarning($"鑷姩鍒嗛厤澶辫触锛屽墿浣欓渶姹倇remainingNeedQuantity}鏃犳硶婊¤冻");
+                }
+                else if (autoAssignedQuantity > 0)
+                {
+                    outStocks.AddRange(autoAssignStocks);
+
+                    // 涓鸿嚜鍔ㄥ垎閰嶇殑搴撳瓨鍒涘缓閿佸畾璁板綍
+                    var autoLockInfos = CreateLockInfosForAutoAssign(outboundOrder, outboundOrderDetail, autoAssignStocks, stockAllocations);
+                    outStockLockInfos.AddRange(autoLockInfos);
+                }
+            }
+
+            // 鏇存柊閿佸畾鏁伴噺
+            outboundOrderDetail.LockQuantity += totalAssignedFromUserSelection + autoAssignedQuantity;
+
+            // 鏇存柊鐘舵��
+            UpdateOrderDetailStatus(outboundOrderDetail, remainingNeedQuantity - autoAssignedQuantity);
 
             List<Dt_LocationInfo> locationInfos = _locationInfoService.GetLocationInfos(outStocks.Select(x => x.LocationCode).ToList());
 
             return (outStocks, outboundOrderDetail, outStockLockInfos, locationInfos);
         }
-        private (bool, string) CheckSelectStockDeital(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews)
+
+        // 杈呭姪鏂规硶
+        private decimal CalculateAvailableQuantity(Dt_StockInfo stock, string materielCode, string batchNo, string supplyCode)
         {
-            if (outboundOrderDetail == null)
-            {
-                return (false, "鏈壘鍒板嚭搴撳崟鏄庣粏淇℃伅");
-            }
-            if (outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.New.ObjToInt() && outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt())
-            {
-                return (false, "璇ユ槑缁嗕笉鍙搷浣�");
-            }
-            //if (stockSelectViews.Sum(x => x.UseableQuantity) > outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty)
-            //{
-            //    return (false, "閫夋嫨鏁伴噺瓒呭嚭鍗曟嵁鏁伴噺");
-            //}
-            return (true, "鎴愬姛");
+            var relevantDetails = stock.Details
+                .Where(d => d.MaterielCode == materielCode &&
+                           (string.IsNullOrEmpty(batchNo) || d.BatchNo == batchNo) &&
+                           (string.IsNullOrEmpty(supplyCode) || d.SupplyCode == supplyCode))
+                .ToList();
+
+            return relevantDetails.Sum(d => d.StockQuantity - d.OutboundQuantity);
         }
 
+        private decimal AssignStockQuantity(Dt_StockInfo stock, Dt_OutboundOrderDetail detail, decimal assignQuantity)
+        {
+            decimal remainingAssign = assignQuantity;
+
+            // 鎸夊厛杩涘厛鍑哄垎閰嶅簱瀛樻槑缁�
+            var sortedDetails = stock.Details
+                .Where(d => d.MaterielCode == detail.MaterielCode &&
+                           d.BatchNo == detail.BatchNo &&
+                           (d.StockQuantity - d.OutboundQuantity) > 0)
+                .OrderBy(d => d.CreateDate)
+                .ToList();
+
+            foreach (var stockDetail in sortedDetails)
+            {
+                if (remainingAssign <= 0) break;
+
+                var available = stockDetail.StockQuantity - stockDetail.OutboundQuantity;
+                var assign = Math.Min(available, remainingAssign);
+
+                stockDetail.OutboundQuantity += assign;
+                remainingAssign -= assign;
+            }
+
+            return assignQuantity - remainingAssign; // 杩斿洖瀹為檯鍒嗛厤鏁伴噺
+        }
+
+        private Dt_OutStockLockInfo CreateOutStockLockInfo(Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail,
+            Dt_StockInfo stock, decimal quantity)
+        {
+            var barcode = stock.Details
+                .Where(d => !string.IsNullOrEmpty(d.Barcode))
+                .Select(d => d.Barcode)
+                .FirstOrDefault();
+
+            return _outStockLockInfoService.GetOutStockLockInfo(outboundOrder, detail, stock, quantity, barcode);
+        }
+
+        private List<Dt_OutStockLockInfo> CreateLockInfosForAutoAssign(Dt_OutboundOrder outboundOrder,
+            Dt_OutboundOrderDetail detail, List<Dt_StockInfo> stocks, Dictionary<int, decimal> allocations)
+        {
+            var lockInfos = new List<Dt_OutStockLockInfo>();
+
+            foreach (var stock in stocks.OrderBy(x => x.CreateDate))
+            {
+                if (allocations.TryGetValue(stock.Id, out decimal quantity) && quantity > 0)
+                {
+                    var lockInfo = CreateOutStockLockInfo(outboundOrder, detail, stock, quantity);
+                    lockInfos.Add(lockInfo);
+                }
+            }
+
+            return lockInfos;
+        }
+
+        private void UpdateOrderDetailStatus(Dt_OutboundOrderDetail detail, decimal remainingQuantity)
+        {
+            if (remainingQuantity <= 0)
+            {
+                detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
+            }
+            else
+            {
+                detail.OrderDetailStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt();
+            }
+        }
+
+        private (bool, string) CheckSelectStockDeital(Dt_OutboundOrderDetail outboundOrderDetail, List<StockSelectViewDTO> stockSelectViews)
+        {
+            var needQuantity = outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity - outboundOrderDetail.MoveQty;
+
+            if (needQuantity <= 0)
+            {
+                return (false, "鍑哄簱鍗曟槑缁嗘棤闇�鍒嗛厤搴撳瓨");
+            }
+
+            // 妫�鏌ユ�婚�夋嫨鏁伴噺鏄惁澶т簬0
+            var totalSelected = stockSelectViews.Sum(x => x.UseableQuantity);
+            if (totalSelected <= 0)
+            {
+                return (false, "鐢ㄦ埛閫夋嫨鐨勫簱瀛樻暟閲忓繀椤诲ぇ浜�0");
+            }
+
+            // 妫�鏌ユ瘡涓墭鐩樼殑鍙敤鏁伴噺
+            foreach (var selection in stockSelectViews)
+            {
+                if (selection.UseableQuantity <= 0)
+                {
+                    return (false, $"鎵樼洏[{selection.PalletCode}]鐨勯�夋嫨鏁伴噺蹇呴』澶т簬0");
+                }
+
+                var stock = _stockService.StockInfoService.GetStockInfoByPalletCode(selection.PalletCode);
+                if (stock == null)
+                {
+                    return (false, $"鎵樼洏[{selection.PalletCode}]涓嶅瓨鍦�");
+                }
+
+                var available = CalculateAvailableQuantity(stock, outboundOrderDetail.MaterielCode,
+                    outboundOrderDetail.BatchNo, outboundOrderDetail.SupplyCode);
+
+                if (available <= 0)
+                {
+                    return (false, $"鎵樼洏[{selection.PalletCode}]娌℃湁鍙敤搴撳瓨");
+                }
+            }
+
+            return (true, "楠岃瘉閫氳繃");
+        }
     }
 }

--
Gitblit v1.9.3