From 9753fb2756f6b4e30ff79d901a7bb86145517c8b Mon Sep 17 00:00:00 2001
From: huangxiaoqiang <huangxiaoqiang@hnkhzn.com>
Date: 星期四, 18 十二月 2025 11:29:13 +0800
Subject: [PATCH] 1
---
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs | 1810 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,807 insertions(+), 3 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/OutboundService.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/OutboundService.cs"
index 0b2aae4..ecc4177 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/OutboundService.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/OutboundService.cs"
@@ -1,9 +1,38 @@
-锘縰sing WIDESEA_IOutboundService;
+锘縰sing AutoMapper;
+using Dm.filter;
+using MailKit.Search;
+using Mapster;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using Org.BouncyCastle.Asn1.Ocsp;
+using Org.BouncyCastle.Crypto;
+using SqlSugar;
+using System.Reflection.Emit;
+using WIDESEA_BasicService;
+using WIDESEA_Common.CommonEnum;
+using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.OrderEnum;
+using WIDESEA_Common.StockEnum;
+using WIDESEA_Common.TaskEnum;
+using WIDESEA_Core;
+using WIDESEA_Core.BaseRepository;
+using WIDESEA_Core.CodeConfigEnum;
+using WIDESEA_Core.Helper;
+using WIDESEA_DTO.Basic;
+using WIDESEA_DTO.CalcOut;
+using WIDESEA_DTO.ReturnMES;
+using WIDESEA_IBasicService;
+using WIDESEA_IOutboundService;
+using WIDESEA_IRecordService;
+using WIDESEA_IStockService;
+using WIDESEA_Model.Models;
namespace WIDESEA_OutboundService
{
- public class OutboundService : IOutboundService
+ public partial class OutboundService : IOutboundService
{
+ private readonly IMapper _mapper;
+ public IUnitOfWorkManage _unitOfWorkManage { get; }
public IOutboundOrderDetailService OutboundOrderDetailService { get; }
@@ -11,11 +40,1786 @@
public IOutStockLockInfoService OutboundStockLockInfoService { get; }
- public OutboundService(IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService)
+ private readonly ISqlSugarClient Db;
+ private readonly IBasicService _basicService;
+
+
+ private readonly IRepository<Dt_OutboundOrderDetail> _detailRepository;
+ private readonly IRepository<Dt_OutboundOrder> _outboundRepository;
+ private readonly IRepository<Dt_OutStockLockInfo> _outboundLockInfoRepository;
+ private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
+ private readonly IRepository<Dt_StockInfoDetail> _stockDetailRepository;
+ private readonly IRepository<Dt_LocationInfo> _locationInfoRepository;
+ private readonly IRepository<Dt_StockQuantityChangeRecord> _stockChangeRepository;
+ private readonly IRepository<Dt_StockInfoDetail_Hty> _stockDetailHistoryRepository;
+ private readonly IFeedbackMesService _feedbackMesService;
+ private readonly IRepository<Dt_Task> _taskRepository;
+ private readonly ILocationInfoService _locationInfoService;
+ private readonly IESSApiService _eSSApiService;
+
+ private Dictionary<string, string> stations = new Dictionary<string, string>
{
+ {"2-1","2-9" },
+ {"3-1","3-9" },
+ };
+
+ private Dictionary<string, string> movestations = new Dictionary<string, string>
+ {
+ {"2-1","2-5" },
+ {"3-1","3-5" },
+ };
+
+ public OutboundService(IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_OutboundOrderDetail> detailRepository, IRepository<Dt_OutboundOrder> outboundRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_StockInfoDetail> stockDetailRepository, IRepository<Dt_StockQuantityChangeRecord> stockChangeRepository, IRepository<Dt_StockInfoDetail_Hty> stockDetailHistoryRepository, IBasicService basicService, IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService, IFeedbackMesService feedbackMesService, IRepository<Dt_Task> taskRepository, ILocationInfoService locationInfoService, IESSApiService eSSApiService)
+ {
+ _mapper = mapper;
+ _unitOfWorkManage = unitOfWorkManage;
+ Db = _unitOfWorkManage.GetDbClient();
OutboundOrderDetailService = outboundOrderDetailService;
OutboundOrderService = outboundOrderService;
OutboundStockLockInfoService = outboundStockLockInfoService;
+ _detailRepository = detailRepository;
+ _outboundRepository = outboundRepository;
+ _outboundLockInfoRepository = outboundLockInfoRepository;
+ _stockInfoRepository = stockInfoRepository;
+ _stockDetailRepository = stockDetailRepository;
+ _locationInfoRepository = basicService.LocationInfoService.Repository;
+ _stockChangeRepository = stockChangeRepository;
+ _stockDetailHistoryRepository = stockDetailHistoryRepository;
+ _basicService = basicService;
+ _feedbackMesService = feedbackMesService;
+ _taskRepository = taskRepository;
+ _locationInfoService = locationInfoService;
+ _eSSApiService = eSSApiService;
}
+
+ #region 鍑哄簱鍒嗛厤
+ /// <summary>
+ /// 鍒嗘嫞鍑哄簱鎿嶄綔
+ /// </summary>
+ /// <param name="request">鍒嗘嫞鍑哄簱璇锋眰</param>
+ /// <returns>鍒嗘嫞鍑哄簱鍝嶅簲</returns>
+ public WebResponseContent ProcessPickingOutbound(PickingOutboundRequestDTO request)
+ {
+ WebResponseContent content = WebResponseContent.Instance;
+
+ PickingOutboundResponseDTO response = new PickingOutboundResponseDTO();
+ try
+ {
+ // 1. 璁$畻鍑哄簱鏁伴噺閫昏緫
+ OutboundCalculationDTO calculationResult = CalcOutboundQuantity(request);
+
+ if (!calculationResult.CanOutbound)
+ {
+ content = WebResponseContent.Instance.Error("鏃犳硶澶勭悊鎷h揣鍑哄簱锛�" + calculationResult.ErrorMessage);
+ _unitOfWorkManage.RollbackTran();
+ return content;
+ }
+
+ // 2. 璋冪敤鍑哄簱澶勭悊閫昏緫锛岄攣瀹氬簱瀛橈紝鐢熸垚鍑哄簱璁板綍绛�
+ List<PickedStockDetailDTO> pickedDetails = new List<PickedStockDetailDTO>();
+
+ // 鑾峰彇鍑哄簱鍗曚俊鎭�
+ Dt_OutboundOrder outboundOrder = calculationResult.OutboundOrder;
+
+ // 鍑哄簱璇︽儏娣诲姞鎴栦慨鏀归泦鍚�
+ List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>();
+
+ List<Dt_OutboundOrderDetail> outboundOrderDetails = new();
+
+ List<Dt_Task> tasks = new List<Dt_Task>();
+ foreach (var materielCalc in calculationResult.MaterielCalculations)
+ {
+ (List<PickedStockDetailDTO> PickedDetails, List<Dt_Task> Tasks, List<Dt_OutStockLockInfo> OutStockLockInfo) materielPickedDetails = ProcessMaterielTaskGeneration(outboundOrder, materielCalc, request, calculationResult.FactoryArea);
+
+ foreach (var item in materielPickedDetails.OutStockLockInfo)
+ {
+ Dt_OutStockLockInfo? outStockLockInfo = materielCalc.OutStockLockInfos.FirstOrDefault(x => x.Id == item.Id && x.Id > 0);
+ if (outStockLockInfo != null)
+ {
+ outStockLockInfo = item;
+ Dt_Task? task = tasks.FirstOrDefault(x => x.PalletCode == item.PalletCode);
+ if (task != null)
+ {
+ outStockLockInfo.TaskNum = task.TaskNum;
+ }
+ }
+ else
+ {
+ Dt_Task? task = tasks.FirstOrDefault(x => x.PalletCode == item.PalletCode);
+ if (task != null)
+ {
+ item.TaskNum = task.TaskNum;
+ }
+ materielCalc.OutStockLockInfos.Add(item);
+ }
+ outStockLockInfos.Add(item);
+ }
+ foreach (var item in materielPickedDetails.Tasks)
+ {
+ Dt_Task? task = tasks.FirstOrDefault(x => x.PalletCode == item.PalletCode);
+ if (task == null)
+ {
+ tasks.Add(item);
+ }
+ }
+
+ pickedDetails.AddRange(materielPickedDetails.PickedDetails);
+
+ decimal allallocatedQuantity = materielCalc.UnallocatedQuantity;
+ // 鏇存柊鍑哄簱鍗曟槑缁嗭紙澧炲姞閿佸畾鏁伴噺锛屼笉澧炲姞宸插嚭鏁伴噺锛�
+ foreach (var detail in materielCalc.Details)
+ {
+ if (allallocatedQuantity <= 0) break;
+
+ decimal lockQuantity = (detail.OrderQuantity - detail.OverOutQuantity);
+ if (lockQuantity < materielCalc.UnallocatedQuantity)
+ {
+ detail.LockQuantity += lockQuantity; // 澧炲姞閿佸畾鏁伴噺 涓嶆洿鏂� OverOutQuantity 鍜� OrderDetailStatus锛屽洜涓鸿繕娌℃湁瀹為檯鍑哄簱
+ outboundOrderDetails.Add(detail);
+ materielCalc.UnallocatedQuantity -= lockQuantity;
+ }
+ else
+ {
+ detail.LockQuantity += materielCalc.UnallocatedQuantity;
+ outboundOrderDetails.Add(detail);
+ break;
+ }
+ }
+ }
+
+ // 3. 鏇存柊鍑哄簱鍗曠姸鎬佷负鍑哄簱涓紙琛ㄧず宸叉湁浠诲姟鍒嗛厤锛�
+ UpdateOutboundOrderStatus(request.OrderNo, (int)OutOrderStatusEnum.鍑哄簱涓�);
+
+ // 4. 鏇存柊鍑哄簱鍗曟槑缁嗛攣瀹氭暟閲�
+ _detailRepository.UpdateData(outboundOrderDetails);
+
+ // 5. 鏇存柊搴撳瓨鐘舵��
+ UpdateStockStatus(pickedDetails.Select(x => x.PalletCode).ToList(), StockStatusEmun.鍑哄簱閿佸畾.ObjToInt());
+
+ // 6. 鏇存柊璐т綅鐘舵��
+ UpdateLocationStatus(pickedDetails.Select(x => x.LocationCode).ToList(), LocationStatusEnum.Lock.ObjToInt());
+
+ // 7. 鏇存柊搴撳瓨璇︽儏
+ UpdateOutStockLockInfo(outStockLockInfos);
+
+ // 8. 娣诲姞浠诲姟鏁版嵁
+ _taskRepository.AddData(tasks);
+
+ _unitOfWorkManage.CommitTran();
+
+ response.Success = true;
+ response.Message = "鍒嗘嫞浠诲姟鍒嗛厤鎴愬姛";
+ response.Tasks = tasks; // 杩斿洖绗竴涓换鍔″彿
+ response.PickedDetails = pickedDetails; // 杩斿洖绗竴涓垎鎷f槑缁�
+ content = WebResponseContent.Instance.OK("鍒嗘嫞浠诲姟鍒嗛厤鎴愬姛", response);
+ return content;
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+
+ content = WebResponseContent.Instance.Error("澶勭悊鎷h揣鍑哄簱澶辫触锛�" + ex.Message);
+ }
+ return content;
+ }
+
+ /// <summary>
+ /// 璁$畻鍑哄簱鏁伴噺閫昏緫
+ /// </summary>
+ /// <param name="request"></param>
+ /// <returns></returns>
+ public OutboundCalculationDTO CalcOutboundQuantity(PickingOutboundRequestDTO request)
+ {
+ OutboundCalculationDTO result = new();
+
+ try
+ {
+ Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == request.OrderNo);
+ if (outboundOrder == null)
+ {
+ result.CanOutbound = false;
+ result.ErrorMessage = $"鍑哄簱鍗� {request.OrderNo} 涓嶅瓨鍦�";
+ return result;
+ }
+
+ result.FactoryArea = outboundOrder.FactoryArea;
+
+ // 鑾峰彇閫夋嫨鐨勫嚭搴撴槑缁�
+ List<Dt_OutboundOrderDetail> selectedDetails = _detailRepository.QueryData(x => x.OrderId == outboundOrder.Id && request.DetailIds.Contains(x.Id));
+
+ if (outboundOrder.IsBatch == 1)
+ {
+ selectedDetails = _detailRepository.QueryData(x => x.OrderId == selectedDetails.First().OrderId && x.WarehouseCode == selectedDetails.First().WarehouseCode && x.MaterielCode == selectedDetails.First().MaterielCode && x.BatchNo == selectedDetails.First().BatchNo && x.SupplyCode == selectedDetails.First().SupplyCode);
+ }
+
+
+ if (!selectedDetails.Any())
+ {
+ result.CanOutbound = false;
+ result.ErrorMessage = $"鏈壘鍒伴�夋嫨鐨勫嚭搴撴槑缁嗕俊鎭�";
+ return result;
+ }
+ if (selectedDetails.Any(x => x.LockQuantity > x.OrderQuantity - x.MoveQty || x.OverOutQuantity > x.OrderQuantity - x.MoveQty))
+ {
+ List<int> selectDetailIds = selectedDetails.Where(x => x.LockQuantity > x.OrderQuantity - x.MoveQty || x.OverOutQuantity > x.OrderQuantity - x.MoveQty).Select(x => x.Id).ToList();
+
+ result.CanOutbound = false;
+ result.ErrorMessage = $"鍑哄簱鏄庣粏淇℃伅{string.Join(",", selectDetailIds)}宸插垎閰嶅畬鎴�";
+ return result;
+ }
+ outboundOrder.Details = selectedDetails;
+ result.OutboundOrder = outboundOrder;
+ result.SelectedDetails = selectedDetails;
+
+ if (outboundOrder.IsBatch == 0)
+ {
+ // 澶氭槑缁嗗嚭搴擄細鎸夌墿鏂欏垎缁勫鐞�
+ result.MaterielCalculations = CalcMaterielOutboundQuantities(outboundOrder, selectedDetails.ToList());
+ }
+ else
+ {
+ // 鍗曟槑缁嗗嚭搴擄細楠岃瘉杈撳叆鐨勫嚭搴撴暟閲�
+ if (!request.OutboundQuantity.HasValue || request.OutboundQuantity.Value <= 0)
+ {
+ result.CanOutbound = false;
+ result.ErrorMessage = "鍗曟槑缁嗗嚭搴撴椂蹇呴』鎸囧畾鍑哄簱鏁伴噺涓斿ぇ浜�0";
+ return result;
+ }
+
+ decimal lockQuantity = selectedDetails.Sum(x => x.LockQuantity);
+ decimal orderQuantity = selectedDetails.Sum(x => x.OrderQuantity);
+ decimal moveQuantity = selectedDetails.Sum(x => x.MoveQty);
+ decimal overQuantity = selectedDetails.Sum(x => x.OverOutQuantity);
+
+ Dt_OutboundOrderDetail? singleDetail = selectedDetails.First();
+
+ //鍒ゆ柇鍙嚭搴撴暟閲�
+ if (orderQuantity - lockQuantity - moveQuantity < request.OutboundQuantity.Value || orderQuantity - overQuantity - moveQuantity < request.OutboundQuantity.Value)
+ {
+ result.CanOutbound = false;
+ result.ErrorMessage = $"鏈鍑哄簱鏁伴噺 {request.OutboundQuantity.Value} 瓒呰繃鍙嚭搴撴暟閲� {orderQuantity - lockQuantity - moveQuantity}";
+ return result;
+ }
+
+ decimal inputQuantity = request.OutboundQuantity.Value;
+ List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
+ foreach (var item in selectedDetails)
+ {
+ inputQuantity -= (item.OrderQuantity - item.MoveQty - item.LockQuantity);
+ outboundOrderDetails.Add(item);
+ if (inputQuantity <= 0)
+ {
+ break;
+ }
+ }
+
+ result.MaterielCalculations = new List<MaterielOutboundCalculationDTO>()
+ {
+ new MaterielOutboundCalculationDTO
+ {
+ MaterielCode = singleDetail.MaterielCode,
+ MaterielName = singleDetail.MaterielName,
+ BatchNo = singleDetail.BatchNo,
+ SupplyCode = singleDetail.SupplyCode,
+ WarehouseCode = singleDetail.WarehouseCode,
+ TotalOrderQuantity = orderQuantity - moveQuantity,
+ TotalOverOutQuantity = overQuantity,
+ AssignedQuantity = lockQuantity,
+ UnallocatedQuantity = request.OutboundQuantity.Value,
+ MovedQuantity = moveQuantity,
+ Details = outboundOrderDetails
+ }
+ };
+
+ outboundOrder.Details = outboundOrderDetails;
+ }
+
+ result.CanOutbound = true;
+ return result;
+ }
+ catch (Exception ex)
+ {
+ result.CanOutbound = false;
+ result.ErrorMessage = ex.Message;
+ return result;
+ }
+
+ }
+
+ /// <summary>
+ /// 澶氬嚭搴撳崟鏄庣粏鏃讹紝鎸夌墿鏂欏垎缁勮绠楀嚭搴撴暟閲�
+ /// </summary>
+ /// <param name="selectedDetails"></param>
+ /// <returns></returns>
+ private List<MaterielOutboundCalculationDTO> CalcMaterielOutboundQuantities(Dt_OutboundOrder outboundOrder, List<Dt_OutboundOrderDetail> selectedDetails)
+ {
+ // 鎸夌墿鏂欏垎缁勶細鐗╂枡缂栧彿銆佹壒娆″彿銆佷緵搴斿晢缂栧彿銆佷粨搴撶紪鍙�
+ List<MaterielOutboundCalculationDTO> materielGroups = selectedDetails
+ .GroupBy(x => new
+ {
+ x.MaterielCode,
+ x.MaterielName,
+ x.BatchNo,
+ x.SupplyCode,
+ x.WarehouseCode
+ })
+ .Select(g => new MaterielOutboundCalculationDTO
+ {
+ MaterielCode = g.Key.MaterielCode,
+ MaterielName = g.Key.MaterielName,
+ BatchNo = g.Key.BatchNo,
+ SupplyCode = g.Key.SupplyCode,
+ WarehouseCode = g.Key.WarehouseCode,
+ TotalOrderQuantity = g.Sum(x => x.OrderQuantity - x.MoveQty),
+ TotalOverOutQuantity = g.Sum(x => x.OverOutQuantity),
+ AssignedQuantity = g.Sum(x => x.LockQuantity),
+ UnallocatedQuantity = g.Sum(x => x.OrderQuantity - x.LockQuantity - x.MoveQty),
+ MovedQuantity = g.Sum(x => x.MoveQty),
+ Details = g.ToList(),
+ OutStockLockInfos = _outboundLockInfoRepository.QueryData(x => x.MaterielCode == g.Key.MaterielCode && x.BatchNo == g.Key.BatchNo && x.OrderType == (int)outboundOrder.OrderType && x.OrderNo == outboundOrder.OrderNo)
+ })
+ .ToList();
+
+ return materielGroups;
+ }
+
+ /// <summary>
+ /// 澶勭悊鐗╂枡鐨勪换鍔$敓鎴�
+ /// </summary>
+ /// <param name="outboundOrder">鍑哄簱璁㈠崟</param>
+ /// <param name="materielCalc">鎸夌墿鏂欑殑鍑哄簱璁$畻缁撴灉</param>
+ /// <param name="request">鍒嗘嫞鍑哄簱璇锋眰</param>
+ /// <param name="factoryArea"></param>
+ /// <returns></returns>
+ /// <exception cref="Exception"></exception>
+ private (List<PickedStockDetailDTO> PickedDetails, List<Dt_Task> Tasks, List<Dt_OutStockLockInfo> OutStockLockInfo) ProcessMaterielTaskGeneration(Dt_OutboundOrder outboundOrder, MaterielOutboundCalculationDTO materielCalc, PickingOutboundRequestDTO request, string factoryArea)
+ {
+ List<PickedStockDetailDTO> pickedDetails = new List<PickedStockDetailDTO>();
+ List<Dt_Task> generatedTasks = new List<Dt_Task>();
+
+ // 鏋勫缓搴撳瓨鏌ヨ鏉′欢锛堝寘鍚簱瀛樿〃銆佸簱瀛樻槑缁嗭級
+ List<Dt_StockInfo> stockQuery = BuildStockQueryWithInfo(materielCalc, factoryArea);
+
+ if (!stockQuery.Any())
+ {
+ throw new Exception($"鐗╂枡 {materielCalc.MaterielCode} 瀵瑰簲鐨勫簱瀛樹笉瀛樺湪");
+ }
+
+ // 鎵归噺璁$畻鎬诲彲鐢ㄥ簱瀛樻暟閲�
+ (Dictionary<int, decimal> AvailableStockMap, Dictionary<int, List<Dt_OutStockLockInfo>> LockStockMap) data = GetBatchAvailableStockQuantities(materielCalc, stockQuery);
+
+ // 鍙敤搴撳瓨鏁伴噺鏄犲皠
+ Dictionary<int, decimal> availableStockMap = data.AvailableStockMap;
+
+ // 鐗╂枡鎬诲彲鐢ㄥ簱瀛樻暟閲�
+ decimal totalAvailableStock = availableStockMap.Values.Sum();
+
+ // 宸查攣瀹氬簱瀛樻暟閲忔槧灏�
+ Dictionary<int, List<Dt_OutStockLockInfo>> lockStockMap = data.LockStockMap;
+
+ // 楠岃瘉鎬诲彲鐢ㄥ簱瀛樻槸鍚︽弧瓒冲嚭搴撻渶姹�
+ if (totalAvailableStock < materielCalc.UnallocatedQuantity)
+ {
+ throw new Exception($"鐗╂枡 {materielCalc.MaterielCode} 鍙敤搴撳瓨 {totalAvailableStock} 涓嶈冻鍑哄簱鏁伴噺 {materielCalc.UnallocatedQuantity}");
+ }
+
+ // 闇�鍒嗛厤鏁伴噺
+ decimal remainingQuantity = materielCalc.UnallocatedQuantity;
+
+ // 宸插垎閰嶇殑鎵樼洏鍒楄〃
+ List<string> allocatedPallets = new List<string>();
+
+ // 鑾峰彇绗竴涓嚭搴撴槑缁�
+ Dt_OutboundOrderDetail firstDetail = materielCalc.Details.First();
+
+ // 棰勫姞杞藉簱瀛樻槑缁嗗拰閿佸畾璁板綍
+ List<int> stockIds = stockQuery.Select(x => x.Id).ToList();
+ Dictionary<int, List<Dt_StockInfoDetail>> stockDetailMap = stockQuery.ToDictionary(x => x.Id, x => x.Details);
+
+ // 璁板綍姣忎釜鎵樼洏鐨勫疄闄呭垎閰嶉噺
+ Dictionary<string, decimal> palletAllocations = new Dictionary<string, decimal>();
+
+ List<Dt_OutStockLockInfo> lockInfoList = new List<Dt_OutStockLockInfo>();
+ foreach (var stock in stockQuery)
+ {
+ if (remainingQuantity <= 0) break;
+
+ // 褰撳墠搴撳瓨鍙敤鏁伴噺
+ decimal availableQuantity = availableStockMap.GetValueOrDefault(stock.Id, 0);
+ if (availableQuantity <= 0) continue;
+
+ // 璁$畻璇ユ墭鐩樺彲鍒嗛厤鏁伴噺
+ decimal allocateQuantity = Math.Min(remainingQuantity, availableQuantity);
+ (decimal ActualAllocatedQuantity, List<Dt_OutStockLockInfo> LockInfoList) actualAllocated = AllocateStockQuantity(stock, allocateQuantity, availableQuantity, outboundOrder, firstDetail, request, lockStockMap.GetValueOrDefault(stock.Id, new List<Dt_OutStockLockInfo>()), stockDetailMap);
+
+ // 鏈鍒嗛厤鐨勬暟閲�
+ decimal actualAllocatedQuantity = actualAllocated.ActualAllocatedQuantity;
+
+ if (actualAllocatedQuantity > 0)
+ {
+ allocatedPallets.Add(stock.PalletCode);
+ palletAllocations[stock.PalletCode] = actualAllocatedQuantity; // 璁板綍瀹為檯鍒嗛厤閲�
+ remainingQuantity -= actualAllocatedQuantity;
+ lockInfoList.AddRange(actualAllocated.LockInfoList);
+ }
+ }
+
+ foreach (var palletCode in allocatedPallets)
+ {
+ Dt_StockInfo stock = stockQuery.First(x => x.PalletCode == palletCode);
+
+ // 鑾峰彇瀹為檯鍒嗛厤鐨勬暟閲�
+ decimal actualAllocatedQuantity = palletAllocations.GetValueOrDefault(palletCode, 0);
+
+ // 璁$畻鍒嗛厤鍚庡墿浣欑殑搴撳瓨鏁伴噺
+ decimal originalAvailableQuantity = availableStockMap.GetValueOrDefault(stock.Id, 0);
+ decimal remainingStockQuantity = Math.Max(0, originalAvailableQuantity - actualAllocatedQuantity);
+
+ pickedDetails.Add(new PickedStockDetailDTO
+ {
+ PalletCode = palletCode,
+ MaterielCode = materielCalc.MaterielCode,
+ OutboundQuantity = actualAllocatedQuantity, // 鏈瀹為檯鍒嗛厤鐨勫嚭搴撻噺
+ RemainingQuantity = remainingStockQuantity, // 鍒嗛厤鍚庡墿浣欑殑鍙敤搴撳瓨
+ LocationCode = stock.LocationCode,
+ OutStockLockInfos = lockInfoList
+ });
+
+ Dt_OutStockLockInfo? outStockLockInfo = lockInfoList.FirstOrDefault(x => x.PalletCode == palletCode);
+ int taskNum = outStockLockInfo?.TaskNum ?? Db.Ado.GetScalar($"SELECT NEXT VALUE FOR SeqTaskNum").ObjToInt();
+
+ Dt_Task task = GenerationOutTask(stock, TaskTypeEnum.Outbound, taskNum, request.OutboundTargetLocation);
+ if (generatedTasks.FirstOrDefault(x => x.PalletCode == stock.PalletCode) == null) generatedTasks.Add(task);
+ }
+
+ return (pickedDetails, generatedTasks, lockInfoList);
+ }
+
+ /// <summary>
+ /// 鐢熸垚鍑哄簱浠诲姟
+ /// </summary>
+ /// <param name="stockInfo"></param>
+ /// <param name="taskType"></param>
+ /// <param name="outStation"></param>
+ /// <returns></returns>
+ public Dt_Task GenerationOutTask(Dt_StockInfo stockInfo, TaskTypeEnum taskType, int taskNum, string outStation)
+ {
+
+ Dt_Task task = new()
+ {
+ CurrentAddress = stockInfo.LocationCode,
+ Grade = 0,
+ PalletCode = stockInfo.PalletCode,
+ NextAddress = outStation,
+ Roadway = "",
+ SourceAddress = stockInfo.LocationCode,
+ TargetAddress = outStation,
+ TaskStatus = TaskStatusEnum.New.ObjToInt(),
+ TaskType = taskType.ObjToInt(),
+ TaskNum = taskNum,
+ PalletType = stockInfo.PalletType,
+ WarehouseId = stockInfo.WarehouseId,
+ };
+
+ return task;
+ }
+
+ /// <summary>
+ /// 鏋勫缓搴撳瓨鏌ヨ鏉′欢锛堝寘鍚簱瀛樹俊鎭拰搴撳瓨鏄庣粏锛�
+ /// </summary>
+ /// <param name="materielCalc"></param>
+ /// <param name="factoryArea"></param>
+ /// <returns></returns>
+ private List<Dt_StockInfo> BuildStockQueryWithInfo(MaterielOutboundCalculationDTO materielCalc, string factoryArea)
+ {
+ // 鍩虹鏌ヨ鏉′欢锛氱墿鏂欑紪鍙枫�佹壒娆″彿锛堝鏋滄彁渚涳級銆佸簱瀛樻暟閲�>0
+ ISugarQueryable<Dt_StockInfoDetail> stockDetails = _stockDetailRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.MaterielCode == materielCalc.MaterielCode && x.StockQuantity > 0);
+
+ // 鏍规嵁鏉′欢娣诲姞渚涘簲鍟嗙紪鍙峰尮閰嶏紙涓嶄负绌烘椂鎵嶉渶瑕佸尮閰嶏級
+ if (!string.IsNullOrEmpty(materielCalc.SupplyCode))
+ {
+ stockDetails = stockDetails.Where(x => x.SupplyCode == materielCalc.SupplyCode);
+ }
+
+ // 鏍规嵁鏉′欢娣诲姞浠撳簱缂栧彿鍖归厤锛堜笉涓虹┖鏃舵墠闇�瑕佸尮閰嶏級
+ if (!string.IsNullOrEmpty(materielCalc.WarehouseCode))
+ {
+ stockDetails = stockDetails.Where(x => x.WarehouseCode == materielCalc.WarehouseCode);
+ }
+
+ // 鏍规嵁鏉′欢娣诲姞鍘傚尯鍖归厤锛堜笉涓虹┖鏃舵墠闇�瑕佸尮閰嶏級
+ if (!string.IsNullOrEmpty(factoryArea))
+ {
+ stockDetails = stockDetails.Where(x => x.FactoryArea == factoryArea);
+ }
+
+ // 鏍规嵁鎵规鍙疯繘琛岃繃婊わ紙濡傛灉鎻愪緵锛�
+ if (!string.IsNullOrEmpty(materielCalc.BatchNo))
+ {
+ stockDetails = stockDetails.Where(x => x.BatchNo == materielCalc.BatchNo);
+ }
+
+ List<Dt_StockInfoDetail> stockDetailList = stockDetails.ToList();
+
+ // 鑾峰彇鍙敤璐т綅缂栧彿
+ List<string> locationCodes = _locationInfoRepository.QueryData(x => (x.LocationStatus == LocationStatusEnum.InStock.ObjToInt() /*|| x.LocationStatus == LocationStatusEnum.Lock.ObjToInt()*/) && x.EnableStatus == EnableStatusEnum.Normal.ObjToInt()).Select(x => x.LocationCode).ToList();
+
+ // 鑾峰彇鎵�鏈夌浉鍏崇殑搴撳瓨淇℃伅
+ List<int> stockIds = stockDetailList.GroupBy(x => x.StockId).Select(x => x.Key).ToList();
+ List<Dt_StockInfo> stockInfos = _stockInfoRepository.QueryData(x => stockIds.Contains(x.Id) && (x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.ObjToInt() /*|| x.StockStatus == StockStatusEmun.鍑哄簱閿佸畾.ObjToInt()*/) && !string.IsNullOrEmpty(x.LocationCode) && locationCodes.Contains(x.LocationCode));
+
+ // 鍦ㄥ唴瀛樹腑鍏宠仈鏁版嵁
+ foreach (var stockInfo in stockInfos)
+ {
+ stockInfo.Details = new List<Dt_StockInfoDetail>();
+ stockInfo.Details = stockDetailList.Where(x => x.StockId == stockInfo.Id).ToList();
+ }
+
+ return stockInfos;
+ }
+
+ /// <summary>
+ /// 鎵归噺鑾峰彇鎵樼洏鍙敤搴撳瓨淇℃伅
+ /// </summary>
+ /// <param name="stockInfos">搴撳瓨淇℃伅鍒楄〃</param>
+ /// <param name="materielCode">鐗╂枡缂栧彿</param>
+ /// <returns>杩斿洖鍊间负(搴撳瓨涓婚敭锛屽彲鐢ㄦ暟閲�)瀛楀吀</returns>
+ private (Dictionary<int, decimal> AvailableStockMap, Dictionary<int, List<Dt_OutStockLockInfo>> LockStockMap) GetBatchAvailableStockQuantities(MaterielOutboundCalculationDTO materielCalc, List<Dt_StockInfo> stockInfos)
+ {
+ List<int> stockIds = stockInfos.Select(x => x.Id).ToList();
+
+ Dictionary<int, decimal> availableStockMap = new Dictionary<int, decimal>(); // 鍙敤搴撳瓨鏁伴噺
+ Dictionary<int, List<Dt_OutStockLockInfo>> lockStockMap = new Dictionary<int, List<Dt_OutStockLockInfo>>(); // 宸查攣瀹氬簱瀛樻暟閲�
+
+ // 鎵归噺鏌ヨ宸插垎閰嶆暟閲�
+ List<Dt_OutStockLockInfo> allocatedData = materielCalc.OutStockLockInfos.Where(x => stockIds.Contains(x.StockId) && x.MaterielCode == materielCalc.MaterielCode).ToList();
+
+ foreach (var stockInfo in stockInfos)
+ {
+ // 璁$畻鎬诲簱瀛樻暟閲�
+ decimal totalQuantity = stockInfo.Details.Sum(x => x.StockQuantity);
+
+ List<Dt_OutStockLockInfo> outStockLockInfos = allocatedData.Where(x => x.StockId == stockInfo.Id && x.MaterielCode == materielCalc.MaterielCode).ToList();
+
+ // 璁$畻宸插垎閰嶆暟閲�
+ decimal allocatedQuantity = outStockLockInfos.Sum(x => x.AllocatedQuantity);
+ availableStockMap[stockInfo.Id] = Math.Max(0, totalQuantity - allocatedQuantity);
+
+ lockStockMap[stockInfo.Id] = outStockLockInfos;
+ }
+
+ return (availableStockMap, lockStockMap);
+ }
+
+ /// <summary>
+ /// 鍒嗛厤搴撳瓨
+ /// </summary>
+ /// <param name="stockInfo">搴撳瓨淇℃伅</param>
+ /// <param name="allocateQuantity">瑕佸垎閰嶇殑鏁伴噺</param>
+ /// <param name="availableQuantity">鍙垎閰嶇殑鏁伴噺</param>
+ /// <param name="outboundOrder">鍑哄簱鍗�</param>
+ /// <param name="detail">鍑哄簱鍗曟槑缁�</param>
+ /// <param name="request"></param>
+ /// <param name="lockInfos"></param>
+ /// <param name="stockDetailMap"></param>
+ /// <returns></returns>
+ private (decimal ActualAllocatedQuantity, List<Dt_OutStockLockInfo> LockInfoList) AllocateStockQuantity(Dt_StockInfo stockInfo, decimal allocateQuantity, decimal availableQuantity, Dt_OutboundOrder outboundOrder, Dt_OutboundOrderDetail detail, PickingOutboundRequestDTO request, List<Dt_OutStockLockInfo> lockInfos, Dictionary<int, List<Dt_StockInfoDetail>> stockDetailMap = null)
+ {
+ decimal actualAllocatedQuantity = Math.Min(allocateQuantity, availableQuantity); // 瀹為檯鍒嗛厤鏁伴噺
+ List<Dt_OutStockLockInfo> lockInfoList = new List<Dt_OutStockLockInfo>();
+ if (actualAllocatedQuantity > 0)
+ {
+ //妫�鏌ョ洰鏍囦綅缃竴鑷存�э細濡傛灉鎵樼洏宸叉湁閿佸畾璁板綍涓旂洰鏍囦綅缃笉鍚岋紝鍒欎笉鍏佽鍒嗛厤
+ if (lockInfos.Any() && !string.IsNullOrEmpty(lockInfos.First().OutboundTargetLocation))
+ {
+ if (!string.Equals(lockInfos.First().OutboundTargetLocation, request.OutboundTargetLocation, StringComparison.OrdinalIgnoreCase))
+ {
+ // 鎵樼洏鐨勭洰鏍囦綅缃笌鏂拌姹傜殑鐩爣浣嶇疆涓嶅悓锛屼笉鍏佽浣跨敤璇ユ墭鐩�
+ return (0, lockInfoList);
+ }
+ }
+
+ Dt_OutStockLockInfo? lockInfo = lockInfos.FirstOrDefault(x => x.StockId == stockInfo.Id && x.Status == OutLockStockStatusEnum.宸插垎閰�.ObjToInt() && x.PalletCode == stockInfo.PalletCode && x.OrderNo == outboundOrder.OrderNo);
+
+ if (lockInfo != null)
+ {
+ // 杩藉姞褰撳墠鏄庣粏ID鍒癘rderDetailIds瀛楁锛堥伩鍏嶉噸澶嶏級
+ List<string> currentIds = lockInfo.OrderDetailIds?.Split(',').ToList() ?? new List<string>();
+ if (!currentIds.Contains(detail.Id.ToString()))
+ {
+ currentIds.Add(detail.Id.ToString());
+ lockInfo.OrderDetailIds = string.Join(",", currentIds);
+ }
+
+ // 璁$畻璇ユ墭鐩樿鐗╂枡鐨勬�荤疮璁″凡鍑哄簱鏁伴噺锛堣法鎵�鏈夊崟鎹級
+ decimal totalAllocatedQuantity = CalcTotalAllocatedQuantity(lockInfos, stockInfo.Id, detail.MaterielCode);
+
+ // 鏇存柊鍒嗛厤鍑哄簱閲�
+ decimal beforeAssignQuantity = totalAllocatedQuantity; // 鏈鍒嗛厤鍓嶇殑鎬荤疮璁¢噺
+ lockInfo.AssignQuantity += actualAllocatedQuantity; // 鏈鍒嗛厤鏁伴噺
+ lockInfo.AllocatedQuantity = beforeAssignQuantity; // 璁板綍鏈鍒嗛厤鍓嶇殑鎬荤疮璁¢噺
+
+ lockInfoList.Add(lockInfo);
+ }
+ else
+ {
+ // 鍒涘缓鏂扮殑閿佸畾璁板綍锛堜娇鐢ㄩ鍔犺浇鐨勫簱瀛樻槑缁嗭級
+ decimal originalQuantity = 0;
+ if (stockDetailMap?.ContainsKey(stockInfo.Id) == true)
+ {
+ originalQuantity = stockDetailMap[stockInfo.Id].Sum(x => x.StockQuantity);
+ }
+
+ // 鑾峰彇璇ョ墿鏂欏湪璇ヨ鍗曚腑鐨勬墍鏈夋槑缁咺D
+ List<string> allDetailIds = (outboundOrder.Details.Where(x =>
+ x.OrderId == outboundOrder.Id &&
+ x.MaterielCode == detail.MaterielCode &&
+ x.BatchNo == detail.BatchNo &&
+ x.SupplyCode == detail.SupplyCode &&
+ x.WarehouseCode == detail.WarehouseCode))
+ .Select(x => x.Id.ToString())
+ .ToList();
+
+ // 璁$畻璇ユ墭鐩樿鐗╂枡鐨勬�荤疮璁″凡鍑哄簱鏁伴噺锛堣法鎵�鏈夊崟鎹級
+ decimal totalAllocatedQuantity = CalcTotalAllocatedQuantity(lockInfos, stockInfo.Id, detail.MaterielCode);
+
+ lockInfo = new Dt_OutStockLockInfo
+ {
+ OrderNo = request.OrderNo,
+ OrderDetailIds = string.Join(",", allDetailIds), // 璁板綍璇ョ墿鏂欑殑鎵�鏈夋槑缁咺D
+ OrderType = outboundOrder.OrderType,
+ BatchNo = detail.BatchNo,
+ MaterielCode = detail.MaterielCode,
+ MaterielName = detail.MaterielName,
+ StockId = stockInfo.Id,
+ OrderQuantity = allDetailIds.SelectMany(id => allDetailIds).Count() > 1
+ ? (outboundOrder.Details.Where(x =>
+ x.OrderId == outboundOrder.Id &&
+ x.MaterielCode == detail.MaterielCode &&
+ x.BatchNo == detail.BatchNo &&
+ x.SupplyCode == detail.SupplyCode &&
+ x.WarehouseCode == detail.WarehouseCode))
+ .Sum(x => x.OrderQuantity)
+ : detail.OrderQuantity, // 濡傛灉鍙湁涓�涓槑缁嗭紝浣跨敤鏄庣粏鏁伴噺
+ OriginalQuantity = originalQuantity,
+ AssignQuantity = actualAllocatedQuantity, // 鏈鍒嗛厤鏁伴噺
+ AllocatedQuantity = totalAllocatedQuantity, // 鏈鍒嗛厤鍓嶇殑鎬荤疮璁″凡鍑哄簱鏁伴噺锛堣法鎵�鏈夊崟鎹級
+ LocationCode = stockInfo.LocationCode,
+ PalletCode = stockInfo.PalletCode,
+ Unit = detail.Unit,
+ OutboundTargetLocation = request.OutboundTargetLocation,
+ Status = OutLockStockStatusEnum.宸插垎閰�.ObjToInt(),
+ SupplyCode = detail.SupplyCode,
+ WarehouseCode = detail.WarehouseCode,
+ FactoryArea = outboundOrder.FactoryArea,
+ TaskNum = Db.Ado.GetScalar($"SELECT NEXT VALUE FOR SeqTaskNum").ObjToInt(),
+ OrderDetailId = 0 // 鏈叧鑱斿叿浣撴槑缁咺D
+ };
+ lockInfoList.Add(lockInfo);
+ }
+ }
+ return (actualAllocatedQuantity, lockInfoList);
+ }
+
+ /// <summary>
+ /// 璁$畻璇ユ墭鐩樼疮璁″凡鍒嗛厤鏁伴噺
+ /// </summary>
+ /// <param name="lockInfos"></param>
+ /// <param name="stockId"></param>
+ /// <param name="materielCode"></param>
+ /// <returns></returns>
+ private decimal CalcTotalAllocatedQuantity(List<Dt_OutStockLockInfo> lockInfos, int stockId, string materielCode)
+ {
+ // 鏌ヨ璇ユ墭鐩樿鐗╂枡鍦ㄦ墍鏈夐攣瀹氳褰曚腑鐨勬渶澶у凡鍒嗛厤鏁伴噺
+ List<Dt_OutStockLockInfo> lockRecords = _outboundLockInfoRepository.QueryData(x =>
+ x.StockId == stockId &&
+ x.MaterielCode == materielCode);
+
+ if (lockRecords == null || !lockRecords.Any())
+ {
+ return 0;
+ }
+
+ // 杩斿洖绱宸插垎閰嶆暟閲�
+ return lockRecords.Sum(x => x.AssignQuantity);
+ }
+
+ /// <summary>
+ /// 鏇存柊鍑哄簱鍗曠姸鎬�
+ /// </summary>
+ public bool UpdateOutboundOrderStatus(string orderNo, int status)
+ {
+ try
+ {
+ Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == orderNo);
+ if (outboundOrder == null) return false;
+
+ outboundOrder.OrderStatus = status;
+ _outboundRepository.UpdateData(outboundOrder);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="palletCodes"></param>
+ /// <param name="status"></param>
+ /// <returns></returns>
+ public bool UpdateStockStatus(List<string> palletCodes, int status)
+ {
+ try
+ {
+ List<Dt_StockInfo> stockInfos = _stockInfoRepository.QueryData(x => palletCodes.Contains(x.PalletCode));
+ stockInfos.ForEach(stockInfo =>
+ {
+ stockInfo.StockStatus = status;
+ });
+
+ _stockInfoRepository.UpdateData(stockInfos);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="locationCodes"></param>
+ /// <param name="status"></param>
+ /// <returns></returns>
+ public bool UpdateLocationStatus(List<string> locationCodes, int status)
+ {
+ try
+ {
+ List<Dt_LocationInfo> locationInfos = _locationInfoRepository.QueryData(x => locationCodes.Contains(x.LocationCode));
+ locationInfos.ForEach(x =>
+ {
+ x.LocationStatus = status;
+ });
+
+ _locationInfoRepository.UpdateData(locationInfos);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="outStockLockInfos"></param>
+ /// <returns></returns>
+ public bool UpdateOutStockLockInfo(List<Dt_OutStockLockInfo> outStockLockInfos)
+ {
+ try
+ {
+ List<Dt_OutStockLockInfo> updateData = outStockLockInfos.Where(x => x.Id > 0).ToList();
+ _outboundLockInfoRepository.UpdateData(updateData);
+
+ List<Dt_OutStockLockInfo> addData = outStockLockInfos.Where(x => x.Id <= 0).ToList();
+ _outboundLockInfoRepository.AddData(addData);
+
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ #endregion
+
+
+
+ public WebResponseContent CompleteOutboundWithPallet(OutboundCompletePalletRequestDTO request)
+ {
+ WebResponseContent content = WebResponseContent.Instance;
+
+ OutboundCompleteResponseDTO response = new();
+ try
+ {
+ // 1. 鏍规嵁鎵樼洏鍙锋煡鎵惧簱瀛樹俊鎭�
+ Dt_StockInfo stockInfo = _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == request.PalletCode).Includes(x => x.Details).First();
+ if (stockInfo == null)
+ {
+ response.Success = false;
+ response.Message = $"鎵樼洏鍙� {request.PalletCode} 瀵瑰簲鐨勫簱瀛樹笉瀛樺湪";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ if (!stockInfo.Details.Any())
+ {
+ response.Success = false;
+ response.Message = $"鎵樼洏 {request.PalletCode} 瀵瑰簲鐨勫簱瀛樻槑缁嗕笉瀛樺湪";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ bool isMatMixed = stockInfo.Details.GroupBy(x => new
+ {
+ x.MaterielCode,
+ x.MaterielName,
+ x.BatchNo,
+ x.SupplyCode,
+ x.WarehouseCode
+ }).Count() > 1;
+
+ if (isMatMixed)
+ {
+ response.Success = false;
+ response.Message = $"娣锋枡鎵樼洏 {request.PalletCode} 涓嶈兘鏁寸鍑哄簱";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ // 2. 鏌ユ壘鍑哄簱鍗曚俊鎭�
+ Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(o => o.OrderNo == request.OrderNo);
+ if (outboundOrder == null)
+ {
+ response.Success = false;
+ response.Message = $"鍑哄簱鍗� {request.OrderNo} 涓嶅瓨鍦�";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ Dt_StockInfoDetail stockInfoDetail = stockInfo.Details.First();
+
+ // 3. 鏌ユ壘閿佸畾璁板綍
+ Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
+ x.OrderNo == request.OrderNo &&
+ x.StockId == stockInfo.Id &&
+ x.MaterielCode == stockInfoDetail.MaterielCode &&
+ x.PalletCode == stockInfo.PalletCode);
+
+ if (lockInfo == null || lockInfo.AssignQuantity <= 0)
+ {
+ response.Success = false;
+ response.Message = $"璇ュ簱瀛樻病鏈夊垎閰嶅嚭搴撻噺锛屾墭鐩樺彿锛歿request.PalletCode}";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ // 鎵惧嚭宸插垎閰嶇殑璁㈠崟鏄庣粏Id
+ List<int> detailIds = new List<int>();
+ string[] ids = lockInfo.OrderDetailIds.Split(",");
+ foreach (string id in ids)
+ {
+ if (int.TryParse(id, out int detailId))
+ {
+ detailIds.Add(detailId);
+ }
+ }
+ // 4. 鏌ユ壘鍑哄簱鍗曟槑缁嗕俊鎭�
+ List<Dt_OutboundOrderDetail> outboundOrderDetails = FindMatchingOutboundDetails(outboundOrder.Id, stockInfoDetail, detailIds);
+ if (!outboundOrderDetails.Any())
+ {
+ response.Success = false;
+ response.Message = $"鏈壘鍒板尮閰嶇殑鍑哄簱鍗曟槑缁嗭紝鐗╂枡锛歿stockInfoDetail.MaterielCode}锛屾壒娆★細{stockInfoDetail.BatchNo}";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ decimal totalStockQuantity = stockInfo.Details.Sum(x => x.StockQuantity);
+
+ // 5. 璁$畻瀹為檯鍑哄簱閲�
+ decimal actualOutboundQuantity = CalculateActualOutboundQuantity(stockInfo.Details, outboundOrderDetails, lockInfo);// 闇�鍑哄簱閲�
+
+ if (actualOutboundQuantity <= 0)
+ {
+ decimal totalAllocatedQuantity = lockInfo.AllocatedQuantity;
+ decimal availableOutboundQuantity = lockInfo.AssignQuantity - totalAllocatedQuantity;
+ decimal detailRemainingQuantity = outboundOrderDetails.Sum(x => x.OrderQuantity - x.OverOutQuantity - x.MoveQty);
+
+ response.Success = false;
+ response.Message = $"鏃犳硶鍑哄簱锛屾墭鐩樺彿锛歿request.PalletCode}锛屽簱瀛橀噺锛歿totalStockQuantity}锛屽凡鍑哄簱锛歿totalAllocatedQuantity}锛屽垎閰嶉噺锛歿lockInfo.AssignQuantity}锛屾槑缁嗗墿浣欙細{detailRemainingQuantity}";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ if (lockInfo.AssignQuantity != totalStockQuantity)
+ {
+ response.Success = false;
+ response.Message = $"鏃犳硶鍑哄簱锛屾墭鐩樺彿锛歿request.PalletCode}锛屽簱瀛橀噺锛歿totalStockQuantity}锛屽垎閰嶉噺锛歿lockInfo.AssignQuantity}";
+ return WebResponseContent.Instance.Error(response.Message);
+ }
+
+ // 6. 寮�鍚簨鍔�
+ _unitOfWorkManage.BeginTran();
+ try
+ {
+ // 鏁寸鍑哄簱鏃犻渶鎷嗗寘
+ PerformFullOutboundOperation(stockInfo, request, lockInfo.TaskNum.GetValueOrDefault());
+
+
+ decimal allocatedQuantity = actualOutboundQuantity;
+ List<Dt_OutboundOrderDetail> updateDetails = new();
+ foreach (var item in outboundOrderDetails)
+ {
+ if (allocatedQuantity <= 0) break;
+
+
+ //if (item.OrderQuantity - item.MoveQty - item.OverOutQuantity >= allocatedQuantity)
+ //{
+ // item.OverOutQuantity += allocatedQuantity;
+ // allocatedQuantity = 0;
+ //}
+ //else
+ //{
+ // allocatedQuantity -= (item.OrderQuantity - item.MoveQty - item.OverOutQuantity);
+ // item.OverOutQuantity = item.OrderQuantity - item.MoveQty;
+ //}
+ List<Barcodes> barcodesList = new List<Barcodes>();
+ List<Dt_StockInfoDetail> stockInfoDetails = stockInfo.Details.Where((x => x.StockQuantity > x.OutboundQuantity)).ToList();
+ foreach (var stockDetail in stockInfoDetails)
+ {
+ if (item.LockQuantity - item.OverOutQuantity >= stockDetail.StockQuantity - stockInfoDetail.OutboundQuantity)
+ {
+ Barcodes barcodes = new Barcodes
+ {
+ Barcode = stockDetail.Barcode,
+ Qty = stockDetail.StockQuantity - stockInfoDetail.OutboundQuantity,
+ SupplyCode = stockDetail?.SupplyCode ?? "",
+ BatchNo = stockDetail?.BatchNo ?? "",
+ Unit = stockDetail?.Unit ?? ""
+ };
+
+ stockDetail.StockQuantity = stockInfoDetail.OutboundQuantity;
+ barcodesList.Add(barcodes);
+ }
+ else
+ {
+ Barcodes barcodes = new Barcodes
+ {
+ Barcode = stockDetail.Barcode,
+ Qty = item.LockQuantity - item.OverOutQuantity,
+ SupplyCode = stockDetail?.SupplyCode ?? "",
+ BatchNo = stockDetail?.BatchNo ?? "",
+ Unit = stockDetail?.Unit ?? ""
+ };
+ stockInfoDetail.OutboundQuantity += item.LockQuantity - item.OverOutQuantity;
+ barcodesList.Add(barcodes);
+ }
+ }
+
+ decimal barcodeQuantity = allocatedQuantity;
+
+ if (item.LockQuantity - item.OverOutQuantity >= allocatedQuantity)
+ {
+ item.OverOutQuantity += allocatedQuantity;
+ item.CurrentDeliveryQty += allocatedQuantity;
+ allocatedQuantity = 0;
+ }
+ else
+ {
+ barcodeQuantity = item.LockQuantity - item.OverOutQuantity;
+ allocatedQuantity -= (item.LockQuantity - item.OverOutQuantity);
+ item.OverOutQuantity = item.LockQuantity;
+ item.CurrentDeliveryQty = item.LockQuantity;
+ }
+
+ updateDetails.Add(item);
+
+
+ if (!string.IsNullOrEmpty(item.ReturnJsonData))
+ {
+ barcodesList.AddRange(JsonConvert.DeserializeObject<List<Barcodes>>(item.ReturnJsonData) ?? new List<Barcodes>());
+ }
+
+ JsonSerializerSettings settings = new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ };
+ item.ReturnJsonData = JsonConvert.SerializeObject(barcodesList, settings);
+ }
+
+ lockInfo.SortedQuantity = lockInfo.SortedQuantity + actualOutboundQuantity;
+
+ if (lockInfo.SortedQuantity == lockInfo.AssignQuantity)
+ {
+ _outboundLockInfoRepository.DeleteAndMoveIntoHty(lockInfo, WIDESEA_Core.Enums.OperateTypeEnum.鑷姩瀹屾垚);
+ }
+ else
+ {
+ // 鏇存柊閿佸畾璁板綍
+ _outboundLockInfoRepository.UpdateData(lockInfo);
+ }
+
+ // 鏇存柊鍑哄簱鍗曟槑缁嗙殑宸插嚭搴撴暟閲�
+ _detailRepository.UpdateData(updateDetails);
+
+ // 鏇存柊閿佸畾璁板綍鐨勭疮璁″凡鍑哄簱鏁伴噺锛堥渶瑕佹洿鏂拌鎵樼洏璇ョ墿鏂欑殑鎵�鏈夌浉鍏宠褰曪級
+ //UpdateLockInfoAllocatedQuantity(stockInfo.Id, stockDetail.MaterielCode, stockDetail.BatchNo, actualOutboundQuantity);
+
+ // 鎻愪氦浜嬪姟
+ _unitOfWorkManage.CommitTran();
+
+ response.Success = true;
+ response.Message = "鍑哄簱瀹屾垚";
+ response.UpdatedDetails = updateDetails;
+
+ // 妫�鏌ュ嚭搴撳崟鏄惁瀹屾垚
+ if (CheckOutboundOrderCompleted(request.OrderNo))
+ {
+ UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt());
+
+
+ //todo: 鍥炰紶MES
+ }
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ response.Success = false;
+ response.Message = $"鍑哄簱澶勭悊澶辫触锛歿ex.Message}";
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+
+ content = WebResponseContent.Instance.OK(data: response);
+ }
+ catch (Exception ex)
+ {
+ content = WebResponseContent.Instance.Error("澶勭悊鍑哄簱瀹屾垚澶辫触锛�" + ex.Message);
+ }
+ return content;
+ }
+
+ /// <summary>
+ /// 璁$畻瀹為檯鍑哄簱鏁伴噺
+ /// </summary>
+ private decimal CalculateActualOutboundQuantity(List<Dt_StockInfoDetail> stockDetails, List<Dt_OutboundOrderDetail> outboundDetails, Dt_OutStockLockInfo lockInfo)
+ {
+ decimal availableOutboundQuantity = lockInfo.AssignQuantity;
+ decimal detailRemainingQuantity = outboundDetails.Sum(x => x.OrderQuantity - x.OverOutQuantity - x.MoveQty);//outboundDetail.OrderQuantity - outboundDetail.OverOutQuantity;
+
+ return Math.Min(
+ Math.Min(availableOutboundQuantity, detailRemainingQuantity),
+ stockDetails.Sum(x => x.StockQuantity));
+ }
+
+ /// <summary>
+ /// 鎵ц瀹屾暣鍑哄簱鎿嶄綔锛堜笉鎷嗗寘锛�
+ /// </summary>
+ private void PerformFullOutboundOperation(Dt_StockInfo stockInfo, OutboundCompletePalletRequestDTO request, int taskNum)
+ {
+ List<Dt_StockInfoDetail_Hty> historyRecords = new List<Dt_StockInfoDetail_Hty>();
+ List<Dt_StockQuantityChangeRecord> changeRecords = new List<Dt_StockQuantityChangeRecord>();
+ foreach (var item in stockInfo.Details)
+ {
+ // 淇濆瓨搴撳瓨鏄庣粏鍒板巻鍙茶褰�
+ Dt_StockInfoDetail_Hty historyRecord = new Dt_StockInfoDetail_Hty
+ {
+ SourceId = item.Id,
+ OperateType = "鍑哄簱瀹屾垚",
+ InsertTime = DateTime.Now,
+ StockId = item.StockId,
+ MaterielCode = item.MaterielCode,
+ MaterielName = item.MaterielName,
+ OrderNo = item.OrderNo,
+ BatchNo = item.BatchNo,
+ ProductionDate = item.ProductionDate,
+ EffectiveDate = item.EffectiveDate,
+ SerialNumber = item.SerialNumber,
+ StockQuantity = item.StockQuantity,
+ OutboundQuantity = item.StockQuantity,
+ Status = item.Status,
+ Unit = item.Unit,
+ InboundOrderRowNo = item.InboundOrderRowNo,
+ SupplyCode = item.SupplyCode,
+ FactoryArea = item.FactoryArea,
+ WarehouseCode = item.WarehouseCode,
+ Barcode = item.Barcode,
+ Remark = $"鏁寸鍑哄簱瀹屾垚鍒犻櫎锛屾潯鐮侊細{request.PalletCode}锛屽師鏁伴噺锛歿item.StockQuantity}锛屽嚭搴撴暟閲忥細{item.StockQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ historyRecords.Add(historyRecord);
+
+ // 璁板綍搴撳瓨鍙樺姩
+ Dt_StockQuantityChangeRecord changeRecord = new Dt_StockQuantityChangeRecord
+ {
+ StockDetailId = item.Id,
+ PalleCode = stockInfo.PalletCode,
+ MaterielCode = item.MaterielCode,
+ MaterielName = item.MaterielName,
+ BatchNo = item.BatchNo,
+ OriginalSerilNumber = item.Barcode,
+ NewSerilNumber = "",
+ OrderNo = request.OrderNo,
+ TaskNum = taskNum,
+ ChangeType = (int)StockChangeTypeEnum.Outbound,
+ ChangeQuantity = -item.StockQuantity,
+ BeforeQuantity = item.StockQuantity,
+ AfterQuantity = 0,
+ SupplyCode = item.SupplyCode,
+ WarehouseCode = item.WarehouseCode,
+ Remark = $"鏁寸鍑哄簱瀹屾垚鍒犻櫎搴撳瓨鏄庣粏锛屾潯鐮侊細{request.PalletCode}锛屽嚭搴撴暟閲忥細{item.StockQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ changeRecords.Add(changeRecord);
+ }
+ _stockDetailHistoryRepository.AddData(historyRecords);
+
+ // 鍒犻櫎搴撳瓨鏄庣粏璁板綍
+ _stockDetailRepository.DeleteData(stockInfo.Details);
+ _stockChangeRepository.AddData(changeRecords);
+ }
+
+ #region 鎷i��
+ /// <summary>
+ /// 鍑哄簱瀹屾垚澶勭悊锛堟壂鎻忔潯鐮佹墸鍑忓簱瀛橈級
+ /// </summary>
+ /// <param name="request">鍑哄簱瀹屾垚璇锋眰</param>
+ /// <returns>鍑哄簱瀹屾垚鍝嶅簲</returns>
+ public WebResponseContent CompleteOutboundWithBarcode(OutboundCompleteRequestDTO request)
+ {
+ WebResponseContent content = WebResponseContent.Instance;
+
+ OutboundCompleteResponseDTO response = new();
+ try
+ {
+ // 1. 鏍规嵁鎵樼洏鍙锋煡鎵惧簱瀛樹俊鎭�
+ Dt_StockInfo stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == request.PalletCode);
+ if (stockInfo == null)
+ {
+ response.Success = false;
+ response.Message = $"鎵樼洏鍙� {request.PalletCode} 瀵瑰簲鐨勫簱瀛樹笉瀛樺湪";
+ return WebResponseContent.Instance.Error($"鎵樼洏鍙� {request.PalletCode} 瀵瑰簲鐨勫簱瀛樹笉瀛樺湪");
+ }
+
+ // 2. 鏍规嵁鏉$爜鏌ユ壘搴撳瓨鏄庣粏
+ Dt_StockInfoDetail stockDetail = _stockDetailRepository.QueryFirst(x => x.Barcode == request.Barcode);
+ if (stockDetail == null)
+ {
+ response.Success = false;
+ response.Message = $"鏉$爜 {request.Barcode} 瀵瑰簲鐨勫簱瀛樻槑缁嗕笉瀛樺湪";
+ return WebResponseContent.Instance.Error($"鏉$爜 {request.Barcode} 瀵瑰簲鐨勫簱瀛樻槑缁嗕笉瀛樺湪");
+ }
+
+ // 3. 楠岃瘉搴撳瓨鏄庣粏涓庢墭鐩樻槸鍚﹀尮閰�
+ if (stockDetail.StockId != stockInfo.Id)
+ {
+ response.Success = false;
+ response.Message = $"鏉$爜 {request.Barcode} 涓嶅睘浜庢墭鐩樺彿 {request.PalletCode} 鐨勫簱瀛樻槑缁�";
+ return WebResponseContent.Instance.Error($"鏉$爜 {request.Barcode} 涓嶅睘浜庢墭鐩樺彿 {request.PalletCode} 鐨勫簱瀛樻槑缁�");
+ }
+
+ // 4. 鏌ユ壘鍑哄簱鍗曚俊鎭�
+ Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(o => o.OrderNo == request.OrderNo);
+ if (outboundOrder == null)
+ {
+ response.Success = false;
+ response.Message = $"鍑哄簱鍗� {request.OrderNo} 涓嶅瓨鍦�";
+ return WebResponseContent.Instance.Error($"鍑哄簱鍗� {request.OrderNo} 涓嶅瓨鍦�");
+ }
+
+ // 5. 鏌ユ壘閿佸畾璁板綍
+ Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
+ x.OrderNo == request.OrderNo &&
+ x.StockId == stockInfo.Id &&
+ x.MaterielCode == stockDetail.MaterielCode &&
+ x.PalletCode == stockInfo.PalletCode);
+
+ if (lockInfo == null || lockInfo.AssignQuantity <= 0)
+ {
+ response.Success = false;
+ response.Message = $"璇ュ簱瀛樻病鏈夊垎閰嶅嚭搴撻噺锛屾潯鐮侊細{request.Barcode}";
+ return WebResponseContent.Instance.Error($"璇ュ簱瀛樻病鏈夊垎閰嶅嚭搴撻噺锛屾潯鐮侊細{request.Barcode}");
+ }
+
+ // 鎵惧嚭宸插垎閰嶇殑璁㈠崟鏄庣粏Id
+ List<int> detailIds = new List<int>();
+ string[] ids = lockInfo.OrderDetailIds.Split(",");
+ foreach (string id in ids)
+ {
+ if (int.TryParse(id, out int detailId))
+ {
+ detailIds.Add(detailId);
+ }
+ }
+ // 6. 鏌ユ壘鍑哄簱鍗曟槑缁嗕俊鎭�
+ List<Dt_OutboundOrderDetail> outboundOrderDetails = FindMatchingOutboundDetails(outboundOrder.Id, stockDetail, detailIds);
+ if (!outboundOrderDetails.Any())
+ {
+ response.Success = false;
+ response.Message = $"鏈壘鍒板尮閰嶇殑鍑哄簱鍗曟槑缁嗭紝鐗╂枡锛歿stockDetail.MaterielCode}锛屾壒娆★細{stockDetail.BatchNo}";
+ return WebResponseContent.Instance.Error($"鏈壘鍒板尮閰嶇殑鍑哄簱鍗曟槑缁嗭紝鐗╂枡锛歿stockDetail.MaterielCode}锛屾壒娆★細{stockDetail.BatchNo}");
+ }
+
+ // 7. 璁$畻瀹為檯鍑哄簱閲�
+ decimal actualOutboundQuantity = CalculateActualOutboundQuantity(stockDetail, outboundOrderDetails, lockInfo);// 闇�鍑哄簱閲�
+
+ if (actualOutboundQuantity <= 0)
+ {
+ decimal totalAllocatedQuantity = lockInfo.AllocatedQuantity;
+ decimal availableOutboundQuantity = lockInfo.AssignQuantity - totalAllocatedQuantity;
+ decimal detailRemainingQuantity = outboundOrderDetails.Sum(x => x.OrderQuantity - x.OverOutQuantity - x.MoveQty);
+
+ response.Success = false;
+ response.Message = $"鏃犳硶鍑哄簱锛屾潯鐮侊細{request.Barcode}锛屽簱瀛橈細{stockDetail.StockQuantity}锛屽凡鍑哄簱锛歿totalAllocatedQuantity}锛屽垎閰嶉噺锛歿lockInfo.AssignQuantity}锛屾槑缁嗗墿浣欙細{detailRemainingQuantity}";
+ return WebResponseContent.Instance.Error($"鏃犳硶鍑哄簱锛屾潯鐮侊細{request.Barcode}锛屽簱瀛橈細{stockDetail.StockQuantity}锛屽凡鍑哄簱锛歿totalAllocatedQuantity}锛屽垎閰嶉噺锛歿lockInfo.AssignQuantity}锛屾槑缁嗗墿浣欙細{detailRemainingQuantity}");
+ }
+
+ //if (actualOutboundQuantity + lockInfo.SortedQuantity > lockInfo.AssignQuantity)
+ //{
+ // response.Success = false;
+ // response.Message = $"鏃犳硶鍑哄簱锛屾潯鐮侊細{request.Barcode}锛屽簱瀛橈細{stockDetail.StockQuantity}锛屽嚭搴撻噺{actualOutboundQuantity + lockInfo.SortedQuantity}澶т簬鍒嗛厤閲弡lockInfo.AssignQuantity}";
+ // return WebResponseContent.Instance.Error($"鏃犳硶鍑哄簱锛屾潯鐮侊細{request.Barcode}锛屽簱瀛橈細{stockDetail.StockQuantity}锛屽嚭搴撻噺{actualOutboundQuantity + lockInfo.SortedQuantity}澶т簬鍒嗛厤閲弡lockInfo.AssignQuantity}");
+ //}
+
+ // 8. 鍒ゆ柇鏄惁闇�瑕佹媶鍖咃紙褰撳嚭搴撴暟閲忓皬浜庡簱瀛樻暟閲忔椂闇�瑕佹媶鍖咃級
+ bool isUnpacked = actualOutboundQuantity < stockDetail.StockQuantity;
+ List<MaterialCodeReturnDTO> returnDTOs = new List<MaterialCodeReturnDTO>();
+
+ // 9. 寮�鍚簨鍔�
+ _unitOfWorkManage.BeginTran();
+ try
+ {
+ decimal beforeQuantity = stockDetail.StockQuantity; // 鍘熷搴撳瓨閲�
+
+ // 鏍规嵁鏄惁鎷嗗寘鎵ц涓嶅悓鐨勬搷浣�
+ if (isUnpacked)
+ {
+ returnDTOs = PerformUnpackOperation(stockDetail, stockInfo, actualOutboundQuantity, request, beforeQuantity, lockInfo.TaskNum.GetValueOrDefault());
+ }
+ else
+ {
+ PerformFullOutboundOperation(stockDetail, stockInfo, actualOutboundQuantity, request, beforeQuantity, lockInfo.TaskNum.GetValueOrDefault());
+ }
+
+ decimal allocatedQuantity = actualOutboundQuantity;
+ List<Dt_OutboundOrderDetail> updateDetails = new();
+ foreach (var item in outboundOrderDetails)
+ {
+ if (allocatedQuantity <= 0) break;
+
+
+ //if (item.OrderQuantity - item.MoveQty - item.OverOutQuantity >= allocatedQuantity)
+ //{
+ // item.OverOutQuantity += allocatedQuantity;
+ // allocatedQuantity = 0;
+ //}
+ //else
+ //{
+ // allocatedQuantity -= (item.OrderQuantity - item.MoveQty - item.OverOutQuantity);
+ // item.OverOutQuantity = item.OrderQuantity - item.MoveQty;
+ //}
+
+ decimal barcodeQuantity = allocatedQuantity;
+
+ if (item.LockQuantity - item.OverOutQuantity >= allocatedQuantity)
+ {
+ item.OverOutQuantity += allocatedQuantity;
+ item.CurrentDeliveryQty += allocatedQuantity;
+ allocatedQuantity = 0;
+ }
+ else
+ {
+ barcodeQuantity = item.LockQuantity - item.OverOutQuantity;
+ allocatedQuantity -= (item.LockQuantity - item.OverOutQuantity);
+ item.OverOutQuantity = item.LockQuantity;
+ item.CurrentDeliveryQty = item.LockQuantity;
+ }
+
+ updateDetails.Add(item);
+
+ List<Barcodes> barcodesList = new List<Barcodes>();
+ Barcodes barcodes = new Barcodes
+ {
+ Barcode = request.Barcode,
+ Qty = barcodeQuantity,
+ SupplyCode = stockDetail?.SupplyCode ?? "",
+ BatchNo = stockDetail?.BatchNo ?? "",
+ Unit = stockDetail?.Unit ?? ""
+ };
+ if (!string.IsNullOrEmpty(item.ReturnJsonData))
+ {
+ barcodesList = JsonConvert.DeserializeObject<List<Barcodes>>(item.ReturnJsonData) ?? new List<Barcodes>();
+ }
+ barcodesList.Add(barcodes);
+ JsonSerializerSettings settings = new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver()
+ };
+ item.ReturnJsonData = JsonConvert.SerializeObject(barcodesList, settings);
+ }
+
+ lockInfo.SortedQuantity = lockInfo.SortedQuantity + actualOutboundQuantity;
+
+ if (lockInfo.SortedQuantity == lockInfo.AssignQuantity)
+ {
+ _outboundLockInfoRepository.DeleteAndMoveIntoHty(lockInfo, WIDESEA_Core.Enums.OperateTypeEnum.鑷姩瀹屾垚);
+ }
+ else
+ {
+ // 鏇存柊閿佸畾璁板綍
+ _outboundLockInfoRepository.UpdateData(lockInfo);
+ }
+
+ // 鏇存柊鍑哄簱鍗曟槑缁嗙殑宸插嚭搴撴暟閲�
+ _detailRepository.UpdateData(updateDetails);
+
+ // 鏇存柊閿佸畾璁板綍鐨勭疮璁″凡鍑哄簱鏁伴噺锛堥渶瑕佹洿鏂拌鎵樼洏璇ョ墿鏂欑殑鎵�鏈夌浉鍏宠褰曪級
+ //UpdateLockInfoAllocatedQuantity(stockInfo.Id, stockDetail.MaterielCode, stockDetail.BatchNo, actualOutboundQuantity);
+
+ // 鎻愪氦浜嬪姟
+ _unitOfWorkManage.CommitTran();
+
+ // 鏋勫缓杩斿洖淇℃伅
+ ScannedStockDetailDTO scannedDetail = new ScannedStockDetailDTO
+ {
+ StockDetailId = stockDetail.Id,
+ PalletCode = stockInfo.PalletCode,
+ MaterielCode = stockDetail.MaterielCode,
+ MaterielName = stockDetail.MaterielName,
+ BatchNo = stockDetail.BatchNo,
+ OriginalBarcode = request.Barcode,
+ BeforeQuantity = beforeQuantity,
+ AfterQuantity = isUnpacked ? actualOutboundQuantity : 0,
+ ChangeQuantity = -actualOutboundQuantity,
+ IsUnpacked = isUnpacked,
+ MaterialCodes = returnDTOs
+ };
+
+ response.Success = true;
+ response.Message = "鍑哄簱瀹屾垚";
+ response.ScannedDetail = scannedDetail;
+ response.UpdatedDetails = updateDetails;
+
+ // 妫�鏌ュ嚭搴撳崟鏄惁瀹屾垚
+ if (CheckOutboundOrderCompleted(request.OrderNo))
+ {
+ UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.鍑哄簱瀹屾垚.ObjToInt());
+
+
+ //todo: 鍥炰紶MES
+ }
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ response.Success = false;
+ response.Message = $"鍑哄簱澶勭悊澶辫触锛歿ex.Message}";
+ return WebResponseContent.Instance.Error(ex.Message);
+ }
+
+ content = WebResponseContent.Instance.OK(data: response);
+ }
+ catch (Exception ex)
+ {
+ content = WebResponseContent.Instance.Error("澶勭悊鍑哄簱瀹屾垚澶辫触锛�" + ex.Message);
+ }
+ return content;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="orderId"></param>
+ /// <param name="stockDetail"></param>
+ /// <returns></returns>
+ private List<Dt_OutboundOrderDetail> FindMatchingOutboundDetails(int orderId, Dt_StockInfoDetail stockDetail, List<int> detailIds)
+ {
+ List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x =>
+ x.OrderId == orderId &&
+ x.MaterielCode == stockDetail.MaterielCode && x.OrderQuantity - x.MoveQty > x.OverOutQuantity && detailIds.Contains(x.Id));
+
+ // 绮剧‘鍖归厤锛氬鐞唍ull鍊肩殑鎵规銆佷緵搴斿晢銆佷粨搴�
+ List<Dt_OutboundOrderDetail> exactMatches = details.Where(x =>
+ (string.IsNullOrEmpty(x.BatchNo)) || x.BatchNo == stockDetail.BatchNo
+ ).Where(x =>
+ (string.IsNullOrEmpty(x.SupplyCode)) || x.SupplyCode == stockDetail.SupplyCode
+ ).Where(x =>
+ (string.IsNullOrEmpty(x.WarehouseCode)) || x.WarehouseCode == stockDetail.WarehouseCode
+ ).ToList();
+
+ return exactMatches;
+ }
+
+ /// <summary>
+ /// 璁$畻瀹為檯鍑哄簱鏁伴噺
+ /// </summary>
+ private decimal CalculateActualOutboundQuantity(Dt_StockInfoDetail stockDetail, List<Dt_OutboundOrderDetail> outboundDetails, Dt_OutStockLockInfo lockInfo)
+ {
+ decimal availableOutboundQuantity = lockInfo.AssignQuantity;
+ decimal detailRemainingQuantity = outboundDetails.Sum(x => x.OrderQuantity - x.OverOutQuantity - x.MoveQty);//outboundDetail.OrderQuantity - outboundDetail.OverOutQuantity;
+
+ return Math.Min(
+ Math.Min(availableOutboundQuantity, detailRemainingQuantity),
+ stockDetail.StockQuantity);
+ }
+
+ /// <summary>
+ /// 鎵ц鎷嗗寘鎿嶄綔
+ /// </summary>
+ /// <param name="stockDetail"></param>
+ /// <param name="stockInfo"></param>
+ /// <param name="actualOutboundQuantity"></param>
+ /// <param name="request"></param>
+ /// <param name="beforeQuantity"></param>
+ /// <param name="taskNum"></param>
+ /// <returns></returns>
+ private List<MaterialCodeReturnDTO> PerformUnpackOperation(Dt_StockInfoDetail stockDetail, Dt_StockInfo stockInfo,
+ decimal actualOutboundQuantity, OutboundCompleteRequestDTO request, decimal beforeQuantity, int taskNum)
+ {
+ string newBarcode = GenerateNewBarcode();
+
+ string remark = $"鎷嗗寘璁板綍锛屽師鏉$爜锛歿request.Barcode}锛屽師鏁伴噺锛歿stockDetail.StockQuantity}锛屽嚭搴撴潯鐮侊細{newBarcode}锛� 鍑哄簱鏁伴噺锛歿actualOutboundQuantity}锛屽洖搴撴潯鐮侊細{request.Barcode}锛屽洖搴撴暟閲忥細{stockDetail.StockQuantity - actualOutboundQuantity},鎿嶄綔鑰咃細{request.Operator}";
+
+ List<Dt_MaterialCodeInfo> materialCodeInfos = CreateMaterialCodeInfos(stockDetail, newBarcode, actualOutboundQuantity, remark);
+
+ List<MaterialCodeReturnDTO> returnDTOs = _mapper.Map<List<MaterialCodeReturnDTO>>(materialCodeInfos);
+
+ // 淇濆瓨鍘熷搴撳瓨鏄庣粏鍒板巻鍙茶褰�
+ Dt_StockInfoDetail_Hty originalHistoryRecord = new Dt_StockInfoDetail_Hty
+ {
+ SourceId = stockDetail.Id,
+ OperateType = "鎷嗗寘-鍘熷璁板綍",
+ InsertTime = DateTime.Now,
+ StockId = stockDetail.StockId,
+ MaterielCode = stockDetail.MaterielCode,
+ MaterielName = stockDetail.MaterielName,
+ OrderNo = stockDetail.OrderNo,
+ BatchNo = stockDetail.BatchNo,
+ ProductionDate = stockDetail.ProductionDate,
+ EffectiveDate = stockDetail.EffectiveDate,
+ SerialNumber = stockDetail.SerialNumber,
+ StockQuantity = stockDetail.StockQuantity,
+ OutboundQuantity = stockDetail.OutboundQuantity,
+ Status = stockDetail.Status,
+ Unit = stockDetail.Unit,
+ InboundOrderRowNo = stockDetail.InboundOrderRowNo,
+ SupplyCode = stockDetail.SupplyCode,
+ FactoryArea = stockDetail.FactoryArea,
+ WarehouseCode = stockDetail.WarehouseCode,
+ Remark = $"鎷嗗寘鍓嶅師濮嬭褰曪紝鍘熸潯鐮侊細{request.Barcode}锛屽師鏁伴噺锛歿stockDetail.StockQuantity}锛屽嚭搴撴暟閲忥細{actualOutboundQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ _stockDetailHistoryRepository.AddData(originalHistoryRecord);
+
+ // 淇濆瓨鍓╀綑閮ㄥ垎鍒板巻鍙茶褰�
+ decimal remainingQuantity = stockDetail.StockQuantity - actualOutboundQuantity;
+ if (remainingQuantity > 0)
+ {
+ // 鏇存柊鍘熷簱瀛樻槑缁�
+ stockDetail.StockQuantity = remainingQuantity;
+ //stockDetail.Barcode = newBarcode;
+ stockDetail.Remark = $"鎷嗗寘鍚庢洿鏂帮紝鍘熸潯鐮侊細{request.Barcode}锛屾柊鏁伴噺锛歿remainingQuantity}锛屾搷浣滆�咃細{request.Operator}";
+ _stockDetailRepository.UpdateData(stockDetail);
+ }
+
+ // 璁板綍鎷嗗寘鍙樺姩
+ Dt_StockQuantityChangeRecord unpackChangeRecord = new Dt_StockQuantityChangeRecord
+ {
+ StockDetailId = stockDetail.Id,
+ PalleCode = stockInfo.PalletCode,
+ MaterielCode = stockDetail.MaterielCode,
+ MaterielName = stockDetail.MaterielName,
+ BatchNo = stockDetail.BatchNo,
+ OriginalSerilNumber = request.Barcode,
+ NewSerilNumber = newBarcode,
+ OrderNo = request.OrderNo,
+ TaskNum = taskNum,
+ ChangeType = (int)StockChangeTypeEnum.Outbound,
+ ChangeQuantity = -actualOutboundQuantity,
+ BeforeQuantity = beforeQuantity,
+ AfterQuantity = beforeQuantity - actualOutboundQuantity,
+ SupplyCode = stockDetail.SupplyCode,
+ WarehouseCode = stockDetail.WarehouseCode,
+ Remark = $"鎷嗗寘鍑哄簱锛屽師鏉$爜锛歿request.Barcode}锛屾柊鏉$爜锛歿newBarcode}锛屽嚭搴撴暟閲忥細{actualOutboundQuantity}锛屽墿浣欙細{remainingQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ _stockChangeRepository.AddData(unpackChangeRecord);
+
+ return returnDTOs;
+ }
+
+ /// <summary>
+ /// 鎵ц瀹屾暣鍑哄簱鎿嶄綔锛堜笉鎷嗗寘锛�
+ /// </summary>
+ private void PerformFullOutboundOperation(Dt_StockInfoDetail stockDetail, Dt_StockInfo stockInfo,
+ decimal actualOutboundQuantity, OutboundCompleteRequestDTO request, decimal beforeQuantity, int taskNum)
+ {
+ // 淇濆瓨搴撳瓨鏄庣粏鍒板巻鍙茶褰�
+ Dt_StockInfoDetail_Hty historyRecord = new Dt_StockInfoDetail_Hty
+ {
+ SourceId = stockDetail.Id,
+ OperateType = "鍑哄簱瀹屾垚",
+ InsertTime = DateTime.Now,
+ StockId = stockDetail.StockId,
+ MaterielCode = stockDetail.MaterielCode,
+ MaterielName = stockDetail.MaterielName,
+ OrderNo = stockDetail.OrderNo,
+ BatchNo = stockDetail.BatchNo,
+ ProductionDate = stockDetail.ProductionDate,
+ EffectiveDate = stockDetail.EffectiveDate,
+ SerialNumber = stockDetail.SerialNumber,
+ StockQuantity = stockDetail.StockQuantity,
+ OutboundQuantity = stockDetail.OutboundQuantity + actualOutboundQuantity,
+ Status = stockDetail.Status,
+ Unit = stockDetail.Unit,
+ InboundOrderRowNo = stockDetail.InboundOrderRowNo,
+ SupplyCode = stockDetail.SupplyCode,
+ FactoryArea = stockDetail.FactoryArea,
+ WarehouseCode = stockDetail.WarehouseCode,
+ Remark = $"鍑哄簱瀹屾垚鍒犻櫎锛屾潯鐮侊細{request.Barcode}锛屽師鏁伴噺锛歿stockDetail.StockQuantity}锛屽嚭搴撴暟閲忥細{actualOutboundQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ _stockDetailHistoryRepository.AddData(historyRecord);
+
+ // 鍒犻櫎搴撳瓨鏄庣粏璁板綍
+ _stockDetailRepository.DeleteData(stockDetail);
+
+ // 璁板綍搴撳瓨鍙樺姩
+ Dt_StockQuantityChangeRecord changeRecord = new Dt_StockQuantityChangeRecord
+ {
+ StockDetailId = stockDetail.Id,
+ PalleCode = stockInfo.PalletCode,
+ MaterielCode = stockDetail.MaterielCode,
+ MaterielName = stockDetail.MaterielName,
+ BatchNo = stockDetail.BatchNo,
+ OriginalSerilNumber = request.Barcode,
+ NewSerilNumber = "",
+ OrderNo = request.OrderNo,
+ TaskNum = taskNum,
+ ChangeType = (int)StockChangeTypeEnum.Outbound,
+ ChangeQuantity = -actualOutboundQuantity,
+ BeforeQuantity = beforeQuantity,
+ AfterQuantity = 0,
+ SupplyCode = stockDetail.SupplyCode,
+ WarehouseCode = stockDetail.WarehouseCode,
+ Remark = $"鍑哄簱瀹屾垚鍒犻櫎搴撳瓨鏄庣粏锛屾潯鐮侊細{request.Barcode}锛屽嚭搴撴暟閲忥細{actualOutboundQuantity}锛屾搷浣滆�咃細{request.Operator}"
+ };
+ _stockChangeRepository.AddData(changeRecord);
+ }
+
+ /// <summary>
+ /// 鐢熸垚鏂扮殑鏉$爜
+ /// </summary>
+ /// <returns>鏂版潯鐮�</returns>
+ private string GenerateNewBarcode()
+ {
+ // 浣跨敤鏃堕棿鎴冲拰闅忔満鏁扮敓鎴愬敮涓�鏉$爜
+ string newBarcode = string.Empty;
+
+ newBarcode = _basicService.CreateCodeByRule(RuleCodeEnum.NewBarcodeRule.ToString());
+
+ return newBarcode;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="stockDetail"></param>
+ /// <param name="newBarcode"></param>
+ /// <param name="splitQuantity"></param>
+ /// <param name="afterQuantity"></param>
+ /// <param name="remark"></param>
+ /// <returns></returns>
+ private List<Dt_MaterialCodeInfo> CreateMaterialCodeInfos(Dt_StockInfoDetail stockDetail, string newBarcode, decimal splitQuantity, string remark)
+ {
+ List<Dt_MaterialCodeInfo> materialCodeInfos = new List<Dt_MaterialCodeInfo>();
+
+ Dt_MaterielInfo? materielInfo = _basicService.MaterielInfoService.Repository.QueryFirst(x => x.MaterielCode == stockDetail.MaterielCode);
+
+ Dt_MaterialCodeInfo outMaterialCodeInfo = new Dt_MaterialCodeInfo()
+ {
+ AfterQuantity = splitQuantity,
+ BatchNo = stockDetail.BatchNo,
+ FactoryArea = stockDetail.FactoryArea,
+ MaterialName = materielInfo?.MaterielName ?? stockDetail.MaterielName,
+ MaterialSpec = materielInfo?.MaterielSpec ?? "",
+ MaterialCode = stockDetail.MaterielCode,
+ NewBarcode = newBarcode,
+ OldBarcode = stockDetail.Barcode,
+ OriginalQuantity = stockDetail.StockQuantity,
+ PruchaseOrderNo = stockDetail.OrderNo,
+ SuplierCode = stockDetail.SupplyCode,
+ Unit = stockDetail.Unit,
+ Date = DateTime.Now.ToString("yyyy-MM-dd"),
+ Remark = remark
+ };
+ materialCodeInfos.Add(outMaterialCodeInfo);
+
+ Dt_MaterialCodeInfo returnMaterialCodeInfo = new Dt_MaterialCodeInfo()
+ {
+ AfterQuantity = stockDetail.StockQuantity - splitQuantity,
+ BatchNo = stockDetail.BatchNo,
+ FactoryArea = stockDetail.FactoryArea,
+ MaterialName = materielInfo?.MaterielName ?? stockDetail.MaterielName,
+ MaterialSpec = materielInfo?.MaterielSpec ?? "",
+ MaterialCode = stockDetail.MaterielCode,
+ NewBarcode = stockDetail.Barcode,
+ OldBarcode = stockDetail.Barcode,
+ OriginalQuantity = stockDetail.StockQuantity,
+ PruchaseOrderNo = stockDetail.OrderNo,
+ SuplierCode = stockDetail.SupplyCode,
+ Unit = stockDetail.Unit,
+ Date = DateTime.Now.ToString("yyyy-MM-dd"),
+ Remark = remark
+ };
+
+ materialCodeInfos.Add(returnMaterialCodeInfo);
+
+ _basicService.MaterielCodeInfoService.Repository.AddData(materialCodeInfos);
+ return materialCodeInfos;
+ }
+
+ /// <summary>
+ /// 鏇存柊璇ユ墭鐩樿鐗╂枡鐨勬墍鏈夐攣瀹氳褰曠殑绱宸插嚭搴撴暟閲�
+ /// </summary>
+ /// <param name="stockId">搴撳瓨ID</param>
+ /// <param name="materielCode">鐗╂枡缂栧彿</param>
+ /// <param name="batchNo">鎵规鍙�</param>
+ /// <param name="actualOutboundQuantity">鏈瀹為檯鍑哄簱鏁伴噺</param>
+ /// <returns></returns>
+ private void UpdateLockInfoAllocatedQuantity(int stockId, string materielCode, string batchNo, decimal actualOutboundQuantity)
+ {
+ // 鏌ヨ璇ユ墭鐩樿鐗╂枡鐨勬墍鏈夐攣瀹氳褰�
+ List<Dt_OutStockLockInfo> lockRecords = _outboundLockInfoRepository.QueryData(x =>
+ x.StockId == stockId &&
+ x.MaterielCode == materielCode &&
+ x.BatchNo == batchNo);
+
+ if (lockRecords != null && lockRecords.Any())
+ {
+ // 鏇存柊鎵�鏈夌浉鍏宠褰曠殑AllocatedQuantity
+ foreach (var record in lockRecords)
+ {
+ record.AllocatedQuantity += actualOutboundQuantity;
+ }
+
+ // 鎵归噺鏇存柊
+ _outboundLockInfoRepository.UpdateData(lockRecords);
+ }
+ }
+
+ /// <summary>
+ /// 妫�鏌ュ嚭搴撳崟鏄惁瀹屾垚
+ /// </summary>
+ public bool CheckOutboundOrderCompleted(string orderNo)
+ {
+ Dt_OutboundOrder outboundOrder = _outboundRepository.QueryFirst(x => x.OrderNo == orderNo);
+ if (outboundOrder == null) return false;
+
+ List<Dt_OutboundOrderDetail> details = _detailRepository.QueryData(x => x.OrderId == outboundOrder.Id);
+
+ // 妫�鏌ユ墍鏈夋槑缁嗙殑宸插嚭鏁伴噺鏄惁閮界瓑浜庡崟鎹暟閲�
+ return details.All(x => x.OverOutQuantity >= x.OrderQuantity - x.MoveQty);
+ }
+
+ #endregion
+
+ #region 鍙栫┖绠�
+ public async Task<WebResponseContent> EmptyBox(string palletCode)
+ {
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ var stock = await _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Includes(x=>x.Details).Where(x => x.PalletCode == palletCode).FirstAsync();
+
+ if (stock == null)
+ {
+ return content.Error($"鏈壘鍒版墭鐩榹palletCode}搴撳瓨淇℃伅");
+ }
+ if (stock.Details.Count > 0)
+ {
+ return content.Error($"鎵樼洏{palletCode}杩樺瓨鍦ㄥ簱瀛樹俊鎭笉鍏佽鍙栬蛋");
+ }
+ Dt_StockInfo_Hty stockInfo_Hty = stock.Adapt<Dt_StockInfo_Hty>();
+ stockInfo_Hty.SourceId = stock.Id;
+ stockInfo_Hty.OperateType = "鍙栫┖绠�";
+ stockInfo_Hty.InsertTime = DateTime.Now;
+
+ _unitOfWorkManage.BeginTran();
+ await _outboundRepository.Db.InsertNav(stockInfo_Hty).IncludesAllFirstLayer().ExecuteCommandAsync();
+ await _stockInfoRepository.DeleteDataByIdAsync(stock.Id);
+ _unitOfWorkManage.CommitTran();
+ return content.OK();
+ }
+ catch (Exception ex)
+ {
+ _unitOfWorkManage.RollbackTran();
+ return content.Error(ex.Message);
+ }
+ }
+
+ #endregion
+
+ #region
+ public async Task<WebResponseContent> ReturnToWarehouse(string palletCode, string OrderNo, string station)
+ {
+ WebResponseContent content = new WebResponseContent();
+ try
+ {
+ var stock = await _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.PalletCode == palletCode).FirstAsync();
+
+ if (stock == null)
+ {
+ return content.Error($"鏈壘鍒版墭鐩榹palletCode}搴撳瓨淇℃伅涓嶅厑璁稿洖搴�");
+ }
+
+ if (stock.Details.Count <= 0)
+ {
+ stock.PalletType = (int)PalletTypeEnum.Empty;
+ stock.StockStatus = (int)StockStatusEmun.缁勭洏鏆傚瓨;
+ stock.LocationCode = "";
+ }
+ else if (stock.Details.Count > 0)
+ {
+ Dt_OutStockLockInfo lockInfo = _outboundLockInfoRepository.QueryFirst(x =>
+ x.OrderNo == OrderNo &&
+ x.StockId == stock.Id &&
+ x.PalletCode == palletCode);
+
+ if (lockInfo != null && lockInfo.SortedQuantity != lockInfo.AssignQuantity)
+ {
+ return content.Error($"鎵樼洏{palletCode}搴撳瓨鏈嫞閫夊畬涓嶅厑璁稿洖搴�");
+ }
+ stock.StockStatus = (int)StockStatusEmun.缁勭洏鏆傚瓨;
+ stock.LocationCode = "";
+ }
+
+ var task = await _taskRepository.Db.Queryable<Dt_Task>()
+ .Where(x => x.PalletCode == palletCode)
+ .FirstAsync();
+
+ if (task != null)
+ {
+ return content.Error($"鎵樼洏{palletCode}瀛樺湪浠诲姟鍥炲簱澶辫触!");
+ }
+
+ // 鍒嗛厤鏂拌揣浣�
+ var newLocation = _locationInfoService.AssignLocation(stock.LocationType);
+
+ var newTask = new Dt_Task()
+ {
+ CurrentAddress = stations[station],
+ Grade = 0,
+ PalletCode = palletCode,
+ NextAddress = "",
+ OrderNo = OrderNo,
+ Roadway = newLocation.RoadwayNo,
+ SourceAddress = stations[station],
+ TargetAddress = newLocation.LocationCode,
+ TaskStatus = (int)TaskStatusEnum.New,
+ TaskType = stock.Details.Count > 0 ? (int)TaskTypeEnum.InPick : (int)TaskTypeEnum.InEmpty,
+ PalletType = stock.PalletType,
+ WarehouseId = stock.WarehouseId
+ };
+ _stockInfoRepository.UpdateData(stock);
+ _taskRepository.AddData(newTask);
+
+ //var moveResult = await _eSSApiService.MoveContainerAsync(new MoveContainerRequest
+ //{
+ // slotCode = movestations[station],
+ // containerCode = palletCode
+ //});
+ return content.OK();
+
+ }
+ catch (Exception ex)
+ {
+ return content.Error(ex.Message);
+ }
+ }
+
+ #endregion
}
}
--
Gitblit v1.9.3