| | |
| | | using System.Text; |
| | | using System.Text.Json; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_Common.CommonEnum; |
| | | using WIDESEA_Common.LocationEnum; |
| | | using WIDESEA_Common.OrderEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Basic; |
| | | using WIDESEA_DTO.Inbound; |
| | |
| | | _dailySequenceService = dailySequenceService; |
| | | } |
| | | |
| | | |
| | | #region æ¥è¯¢æ¹æ³ |
| | | // è·åæªæ£éå表 |
| | | public async Task<List<Dt_OutStockLockInfo>> GetUnpickedList(string orderNo, string palletCode) |
| | | { |
| | |
| | | return summary; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æ ¸å¿ä¸å¡æµç¨ |
| | | /// <summary> |
| | | /// æ£é |
| | |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | |
| | | var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | |
| | | return WebResponseContent.Instance.Error(overPickingValidation.ErrorMessage); |
| | | } |
| | | |
| | | // æ§è¡åæ£é»è¾ |
| | | // æ§è¡åæ£é»è¾ï¼åªå¤çåºååéå®ï¼ä¸å¤ç订åï¼ |
| | | var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, orderNo, palletCode, barcode, actualQty); |
| | | |
| | | // æ´æ°ç¸å
³æ°æ® |
| | | // ç»ä¸æ´æ°è®¢åæ°æ®ï¼ææåæ¯é½å¨è¿éæ´æ°ï¼ |
| | | await UpdateOrderRelatedData(orderDetail.Id, pickingResult.ActualPickedQty, orderNo); |
| | | |
| | | // è®°å½æä½åå² |
| | |
| | | return WebResponseContent.Instance.Error($"æ£é确认失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¶æ£é |
| | | /// </summary> |
| | |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. åºç¡éªè¯ |
| | | |
| | | if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode)) |
| | | return WebResponseContent.Instance.Error("订åå·åæçç ä¸è½ä¸ºç©º"); |
| | | |
| | | // 2. è·ååºååä»»å¡ä¿¡æ¯ |
| | | // è·ååºååä»»å¡ä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode); |
| | | |
| | | if (stockInfo == null) |
| | |
| | | if (task == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡ä¿¡æ¯"); |
| | | |
| | | // 3. åæéè¦ååºçè´§ç© |
| | | var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id); |
| | | if (!returnAnalysis.HasItemsToReturn) |
| | | return await HandleNoReturnItems(orderNo, palletCode,task); |
| | | //åæéè¦ååºçè´§ç© |
| | | //var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id); |
| | | //if (!returnAnalysis.HasItemsToReturn) |
| | | // return await HandleNoReturnItems(orderNo, palletCode, task); |
| | | |
| | | // 4. æ§è¡ååºæä½ |
| | | await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, returnAnalysis); |
| | | var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id); |
| | | if (!statusAnalysis.HasItemsToReturn) |
| | | return await HandleNoReturnItems(orderNo, palletCode, task, stockInfo.Id); |
| | | |
| | | // 5. å建ååºä»»å¡ |
| | | await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, returnAnalysis); |
| | | // 4. æ£æ¥æ¯å¦æè¿è¡ä¸çä»»å¡ |
| | | if (statusAnalysis.HasActiveTasks) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æç {palletCode} æè¿è¡ä¸çä»»å¡ï¼ä¸è½æ§è¡ååºæä½"); |
| | | } |
| | | |
| | | //æ§è¡ååºæä½ |
| | | await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, statusAnalysis); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | // 6. æ´æ°è®¢åç¶æï¼ä¸è§¦åMESåä¼ ï¼ |
| | | |
| | | // å建ååºä»»å¡ |
| | | await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick); |
| | | |
| | | // æ´æ°è®¢åç¶æï¼ä¸è§¦åMESåä¼ ï¼ |
| | | await UpdateOrderStatusForReturn(orderNo); |
| | | |
| | | return WebResponseContent.Instance.OK($"ååºæä½æåï¼å
±ååºæ°éï¼{returnAnalysis.TotalReturnQty}"); |
| | | return WebResponseContent.Instance.OK($"ååºæä½æåï¼å
±ååºæ°éï¼{statusAnalysis.TotalReturnQty}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 空æçåèµ°æ¥å£ï¼å¸¦è®¢åå·ï¼ |
| | | /// éªè¯æçæ¯å¦ççä¸ºç©ºï¼æ¸
çæ°æ®ï¼æ´æ°è®¢åç¶æï¼å建åæçä»»å¡ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode)) |
| | | return WebResponseContent.Instance.Error("订åå·åæçç ä¸è½ä¸ºç©º"); |
| | | |
| | | // æ£æ¥è®¢åæ¯å¦åå¨ |
| | | var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>() |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .FirstAsync(); |
| | | |
| | | if (order == null) |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°è®¢å {orderNo}"); |
| | | |
| | | //æ£æ¥æçæ¯å¦åå¨ä¸å±äºè¯¥è®¢å |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°æç {palletCode} 对åºçåºåä¿¡æ¯"); |
| | | |
| | | var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id); |
| | | |
| | | if (!statusAnalysis.CanRemove) |
| | | { |
| | | if (!statusAnalysis.IsEmptyPallet) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æç {palletCode} ä¸è¿æè´§ç©ï¼ä¸è½åèµ°"); |
| | | } |
| | | if (statusAnalysis.HasActiveTasks) |
| | | { |
| | | return WebResponseContent.Instance.Error($"æç {palletCode} è¿æè¿è¡ä¸çä»»å¡ï¼ä¸è½åèµ°"); |
| | | } |
| | | } |
| | | // æ¸
çé¶åºåæ°æ® |
| | | await CleanupZeroStockData(stockInfo.Id); |
| | | |
| | | // å 餿忶ç¸å
³ä»»å¡ |
| | | await HandleTaskCleanup(orderNo, palletCode); |
| | | |
| | | // æ´æ°è®¢åç¸å
³æ°æ® |
| | | await UpdateOrderData(orderNo, palletCode); |
| | | |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | _logger.LogInformation($"空æçåèµ°æä½æå - 订å: {orderNo}, æç: {palletCode}, æä½äºº: {App.User.UserName}"); |
| | | |
| | | return WebResponseContent.Instance.OK("空æçåèµ°æä½æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"RemoveEmptyPallet失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空æçå走失败: {ex.Message}"); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | #region åæ£ç¡®è®¤ç§ææ¹æ³ |
| | |
| | | { |
| | | // æ¥æ¾åä¸è®¢åä¸çè®°å½ |
| | | lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(it => it.OrderNo == orderNo && it.CurrentBarcode == barcode && it.Status == (int)OutLockStockStatusEnum.åºåºä¸ && it.AssignQuantity > it.PickedQty).FirstAsync(); |
| | | .Where(it => it.OrderNo == orderNo && it.CurrentBarcode == barcode && it.Status == (int)OutLockStockStatusEnum.åºåºä¸ && it.AssignQuantity > it.PickedQty).FirstAsync(); |
| | | |
| | | if (lockInfo == null) |
| | | { |
| | |
| | | adjustedReason = adjustedReason != null |
| | | ? $"{adjustedReason}ï¼é²è¶
æ£éå¶ï¼æç»è°æ´ä¸º{actualQty}" |
| | | : $"é²è¶
æ£éå¶ï¼ä»{plannedQty}è°æ´ä¸º{actualQty}"; |
| | | } |
| | | } |
| | | |
| | | if (adjustedReason != null) |
| | | { |
| | |
| | | |
| | | return ValidationResult<bool>.Success(true); |
| | | } |
| | | |
| | | |
| | | private async Task<PickingResult> ExecutePickingLogic( |
| | | Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail, |
| | | string orderNo, string palletCode, string barcode, decimal actualQty) |
| | | Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail, |
| | | string orderNo, string palletCode, string barcode, decimal actualQty) |
| | | { |
| | | decimal stockQuantity = stockDetail.StockQuantity; |
| | | var result = new PickingResult |
| | |
| | | if (actualQty < stockQuantity) |
| | | { |
| | | await HandleSplitPacking(lockInfo, stockDetail, actualQty, stockQuantity, result); |
| | | // æå
åºæ¯è¿åå®é
æ£éæ°é |
| | | result.ActualPickedQty = actualQty; |
| | | } |
| | | else if (actualQty == stockQuantity) |
| | | { |
| | | await HandleFullPicking(lockInfo, stockDetail, actualQty, result); |
| | | // æ´å
æ£éè¿åå®é
æ£éæ°é |
| | | result.ActualPickedQty = actualQty; |
| | | } |
| | | else |
| | | { |
| | | await HandlePartialPicking(lockInfo, stockDetail, actualQty, stockQuantity, result); |
| | | // é¨åæ£éè¿åè°æ´åçæ°é |
| | | result.ActualPickedQty = result.ActualPickedQty; // å·²ç»å¨æ¹æ³å
è°æ´ |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | private async Task HandleSplitPacking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal actualQty, decimal stockQuantity, PickingResult result) |
| | | decimal actualQty, decimal stockQuantity, PickingResult result) |
| | | { |
| | | decimal remainingStockQty = stockQuantity - actualQty; |
| | | |
| | | // 1. æ´æ°åæ¡ç åºå |
| | | // æ´æ°åæ¡ç åºå |
| | | stockDetail.StockQuantity = remainingStockQty; |
| | | stockDetail.OutboundQuantity = remainingStockQty; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | // 2. çææ°æ¡ç |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | | |
| | | // 3. å建æ°éå®ä¿¡æ¯ |
| | | // å建æ°éå®ä¿¡æ¯ |
| | | var newLockInfo = await CreateSplitLockInfo(lockInfo, actualQty, newBarcode); |
| | | |
| | | // 4. è®°å½æå
åå² |
| | | // è®°å½æå
åå² |
| | | await RecordSplitHistory(lockInfo, stockDetail, actualQty, remainingStockQty, newBarcode); |
| | | |
| | | // 5. æ´æ°åéå®ä¿¡æ¯ |
| | | // æ´æ°åéå®ä¿¡æ¯ |
| | | lockInfo.AssignQuantity = remainingStockQty; |
| | | lockInfo.PickedQty = 0; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // 6. è®¾ç½®ç»æ |
| | | // è®¾ç½®ç»æ |
| | | result.FinalLockInfo = newLockInfo; |
| | | result.FinalBarcode = newBarcode; |
| | | result.SplitResults.AddRange(CreateSplitResults(lockInfo, actualQty, remainingStockQty, newBarcode, stockDetail.Barcode)); |
| | | } |
| | | |
| | | |
| | | |
| | | _logger.LogInformation($"æå
忣宿 - OrderDetailId: {lockInfo.OrderDetailId}, 忣æ°é: {actualQty}"); |
| | | } |
| | | private async Task HandleFullPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal actualQty, PickingResult result) |
| | | { |
| | |
| | | |
| | | if (newOverOutQuantity > currentOrderDetail.NeedOutQuantity) |
| | | { |
| | | |
| | | |
| | | _logger.LogError($"é²è¶
æ£æ£æ¥å¤±è´¥ - OrderDetailId: {orderDetailId}, å·²åºåº: {newOverOutQuantity}, éæ±: {currentOrderDetail.NeedOutQuantity}, æ¬æ¬¡åæ£: {pickedQty}"); |
| | | |
| | | |
| | | |
| | | decimal adjustedQty = currentOrderDetail.NeedOutQuantity - currentOrderDetail.OverOutQuantity; |
| | | |
| | | if (adjustedQty > 0) |
| | |
| | | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("æªæ¾å°å¯¹åºçåºåºéå®ä¿¡æ¯"); |
| | | |
| | | |
| | | if (lockInfo.PickedQty < pickingRecord.PickQuantity) |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error( |
| | |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"æ¡ç {barcode}å·²ç»ååºï¼ä¸è½åæ¶åæ£"); |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Success((pickingRecord, lockInfo, orderDetail)); |
| | | } |
| | | /// <summary> |
| | |
| | | await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.Id == lockInfo.Id) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | await UpdateOrderDetailOnCancel(pickingRecord.OrderDetailId, cancelQty); |
| | | } |
| | | |
| | | private async Task HandleNormalBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty) |
| | |
| | | |
| | | return task; |
| | | } |
| | | |
| | | private async Task<ReturnAnalysisResult> AnalyzeReturnItems(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var result = new ReturnAnalysisResult(); |
| | | |
| | | // æ
åµ1ï¼è·åæªåæ£çåºåºéå®è®°å½ |
| | | var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(it => it.OrderNo == orderNo && |
| | | it.PalletCode == palletCode && |
| | | it.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToListAsync(); |
| | | |
| | | if (remainingLocks.Any()) |
| | | { |
| | | result.HasRemainingLocks = true; |
| | | result.RemainingLocks = remainingLocks; |
| | | result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | } |
| | | |
| | | // æ
åµ2ï¼æ£æ¥æç䏿¯å¦æå
¶ä»åºåè´§ç© |
| | | var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(it => it.StockId == stockId && |
| | | (it.Status == StockStatusEmun.å
¥åºç¡®è®¤.ObjToInt() || |
| | | it.Status == StockStatusEmun.å
¥åºå®æ.ObjToInt() || |
| | | it.Status == StockStatusEmun.åºåºéå®.ObjToInt())) |
| | | .Where(it => it.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | if (palletStockGoods.Any()) |
| | | { |
| | | result.HasPalletStockGoods = true; |
| | | result.PalletStockGoods = palletStockGoods; |
| | | result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity); |
| | | } |
| | | |
| | | // æ
åµ3ï¼æ£æ¥æå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.å·²ååº) |
| | | .ToListAsync(); |
| | | |
| | | if (splitRecords.Any()) |
| | | { |
| | | result.HasSplitRecords = true; |
| | | result.SplitRecords = splitRecords; |
| | | result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId); |
| | | } |
| | | |
| | | result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty; |
| | | result.HasItemsToReturn = result.TotalReturnQty > 0; |
| | | |
| | | return result; |
| | | } |
| | | |
| | | |
| | | private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId) |
| | | { |
| | | decimal totalQty = 0; |
| | |
| | | return totalQty; |
| | | } |
| | | |
| | | private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode,Dt_Task originalTask) |
| | | private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockInfoId) |
| | | { |
| | | // æ£æ¥æ¯å¦ææè´§ç©é½å·²æ£é宿 |
| | | var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode) |
| | | .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.æ£é宿); |
| | | //var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | // .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode) |
| | | // .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.æ£é宿); |
| | | |
| | | if (allPicked) |
| | | //if (allPicked) |
| | | //{ |
| | | // // å é¤åå§åºåºä»»å¡ ç»ç©ºç 空çååº |
| | | // //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | // return WebResponseContent.Instance.OK("ææè´§ç©å·²æ£éå®æï¼æç为空"); |
| | | //} |
| | | //else |
| | | //{ |
| | | // // å é¤åå§åºåºä»»å¡ |
| | | // //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | // return WebResponseContent.Instance.Error("没æéè¦ååºçå©ä½è´§ç©"); |
| | | //} |
| | | try |
| | | { |
| | | // å é¤åå§åºåºä»»å¡ |
| | | await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | return WebResponseContent.Instance.OK("ææè´§ç©å·²æ£éå®æï¼æç为空"); |
| | | var locationtype = 0; |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (stockInfo == null) |
| | | { |
| | | var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>().FirstAsync(x => x.LocationCode == originalTask.SourceAddress); |
| | | locationtype = firstLocation?.LocationType ?? 1; |
| | | } |
| | | else |
| | | { |
| | | locationtype = stockInfo.LocationType; |
| | | } |
| | | |
| | | var targetAddress = originalTask.TargetAddress; |
| | | |
| | | await CleanupZeroStockData(stockInfoId); |
| | | |
| | | var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.ç»çæå.ObjToInt(), PalletCode = palletCode, LocationType = locationtype }; |
| | | emptystockInfo.Details = new List<Dt_StockInfoDetail>(); |
| | | _stockInfoService.AddMaterielGroup(emptystockInfo); |
| | | //空æçå¦ä½å¤ç è¿æä¸ä¸ªåºåºä»»å¡è¦å¤çã |
| | | originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt(); |
| | | |
| | | await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty); |
| | | |
| | | } |
| | | else |
| | | catch (Exception ex) |
| | | { |
| | | // å é¤åå§åºåºä»»å¡ |
| | | await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | return WebResponseContent.Instance.Error("没æéè¦ååºçå©ä½è´§ç©"); |
| | | _logger.LogError($" HandleNoReturnItems 失败: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($" ååºç©ºæç失败ï¼"); |
| | | } |
| | | //空æçå¦ä½å¤ç è¿æä¸ä¸ªåºåºä»»å¡è¦å¤çã |
| | | |
| | | return WebResponseContent.Instance.OK("空æçååºä»»å¡å建æå"); |
| | | |
| | | } |
| | | |
| | | private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo, |
| | | Dt_Task task, ReturnAnalysisResult analysis) |
| | | Dt_Task task, PalletStatusAnalysis analysis) |
| | | { |
| | | // æ
åµ1ï¼å¤çæªåæ£çåºåºéå®è®°å½ |
| | | if (analysis.HasRemainingLocks) |
| | | { |
| | | await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id); |
| | | |
| | | // å
³é®ï¼æ´æ°è®¢åæç»çå·²æ£éæ°é |
| | | await UpdateOrderDetailsOnReturn(analysis.RemainingLocks); |
| | | } |
| | | // await UpdateOrderDetailsOnReturn(analysis.RemainingLocks); |
| | | } |
| | | |
| | | // å¤çæçä¸å
¶ä»åºåè´§ç© |
| | | if (analysis.HasPalletStockGoods) |
| | |
| | | foreach (var stockGood in palletStockGoods) |
| | | { |
| | | _logger.LogInformation($"å¾
ååºè´§ç© - æ¡ç : {stockGood.Barcode}, æ°é: {stockGood.StockQuantity}, å½åç¶æ: {stockGood.Status}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | stockGood.OutboundQuantity = 0; |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | stockGood.OutboundQuantity = 0; |
| | | stockGood.Status = StockStatusEmun.å
¥åºç¡®è®¤.ObjToInt(); |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync(); |
| | |
| | | /// <param name="originalTask"></param> |
| | | /// <param name="analysis"></param> |
| | | /// <returns></returns> |
| | | private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, ReturnAnalysisResult analysis) |
| | | private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum) |
| | | { |
| | | var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>() |
| | | .FirstAsync(x => x.LocationCode == originalTask.SourceAddress); |
| | |
| | | SourceAddress = stations[originalTask.TargetAddress], |
| | | TargetAddress = newLocation.LocationCode, |
| | | TaskStatus = TaskStatusEnum.New.ObjToInt(), |
| | | TaskType = TaskTypeEnum.InPick.ObjToInt(), |
| | | TaskType = taskTypeEnum.ObjToInt(), |
| | | PalletType = originalTask.PalletType, |
| | | WarehouseId = originalTask.WarehouseId |
| | | |
| | | }; |
| | | // ä¿åååºä»»å¡ |
| | | await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); |
| | | var targetAddress = originalTask.TargetAddress; |
| | | |
| | | var targetAddress = originalTask.TargetAddress; |
| | | |
| | | // å é¤åå§åºåºä»»å¡ |
| | | await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.èªå¨å®æ); |
| | | // await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | |
| | | |
| | | |
| | | // ç» ESS åéæµå¨ä¿¡å·ååå»ºä»»å¡ |
| | | await SendESSCommands(palletCode, targetAddress, returnTask); |
| | |
| | | taskType = "putaway", |
| | | taskGroupCode = "", |
| | | groupPriority = 0, |
| | | tasks = new List<TasksType> |
| | | { |
| | | new() |
| | | { |
| | | tasks = new List<TasksType>{ new() { |
| | | taskCode = returnTask.TaskNum.ToString(), |
| | | taskPriority = 0, |
| | | taskDescribe = new TaskDescribeType |
| | |
| | | deadline = 0, |
| | | storageTag = "" |
| | | } |
| | | } |
| | | } |
| | | } } |
| | | }; |
| | | |
| | | var resultTask = await _eSSApiService.CreateTaskAsync(essTask); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private async Task UpdateOrderStatusForReturn(string orderNo) |
| | | { |
| | | try |
| | |
| | | operationType = 1, |
| | | Operator = App.User.UserName, |
| | | orderNo = outboundOrder.UpperOrderNo, |
| | | documentsNO = outboundOrder.OrderNo, |
| | | status = outboundOrder.OrderStatus, |
| | | details = new List<FeedbackOutboundDetailsModel>() |
| | | }; |
| | |
| | | |
| | | #endregion |
| | | |
| | | #region 空æç |
| | | |
| | | /// <summary> |
| | | /// æ¸
çé¶åºåæ°æ® |
| | | /// </summary> |
| | | private async Task CleanupZeroStockData(int stockId) |
| | | { |
| | | try |
| | | { |
| | | // 1. å é¤åºåæ°é为0çæç»è®°å½ |
| | | var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity == 0 && (x.Status == StockStatusEmun.åºåºå®æ.ObjToInt() || x.Status == |
| | | StockStatusEmun.å
¥åºå®æ.ObjToInt())) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | await _stockInfoService.Db.Deleteable<Dt_StockInfo>() |
| | | .Where(x => x.Id == stockId).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¸
çé¶åºåæç»è®°å½ - StockId: {stockId}, å é¤è®°å½æ°: {deleteDetailCount}"); |
| | | |
| | | |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ¸
çé¶åºåæ°æ®å¤±è´¥ - StockId: {stockId}, Error: {ex.Message}"); |
| | | // 注æï¼æ¸
ç失败ä¸åºè¯¥å½±å主æµç¨ |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// å¤ç任塿¸
çï¼æè®¢ååæçï¼ |
| | | /// </summary> |
| | | private async Task HandleTaskCleanup(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | // 1. æ¥æ¾ææä¸è¯¥è®¢ååæçç¸å
³çä»»å¡ |
| | | var tasks = await _taskRepository.Db.Queryable<Dt_Task>().Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode).ToListAsync(); |
| | | |
| | | if (tasks.Any()) |
| | | { |
| | | foreach (var task in tasks) |
| | | { |
| | | task.TaskStatus = (int)TaskStatusEnum.Finish; |
| | | } |
| | | // await _taskRepository.Db.Updateable(tasks).ExecuteCommandAsync(); |
| | | |
| | | _taskRepository.DeleteAndMoveIntoHty(tasks, OperateTypeEnum.èªå¨å®æ); |
| | | _logger.LogInformation($"宿{tasks.Count}个æçä»»å¡ - 订å: {orderNo}, æç: {palletCode}"); |
| | | } |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"å¤ç任塿¸
ç失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | throw new Exception($"任塿¸
ç失败: {ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// æ´æ°è®¢åç¸å
³æ°æ® |
| | | /// </summary> |
| | | private async Task UpdateOrderData(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | // æ£æ¥è®¢åæ¯å¦è¿æå
¶ä»æçå¨å¤çä¸ |
| | | var otherActivePallets = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode != palletCode && |
| | | (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || x.Status == (int)OutLockStockStatusEnum.ååºä¸)) |
| | | .AnyAsync(); |
| | | |
| | | var otherActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode != palletCode |
| | | // && x.TaskStatus.In((int)TaskStatusEnum.å¾
æ§è¡, (int)TaskStatusEnum.æ§è¡ä¸) |
| | | ) |
| | | .AnyAsync(); |
| | | |
| | | // å¦ææ²¡æå
¶ä»æçå¨å¤çï¼æ£æ¥è®¢åæ¯å¦åºè¯¥å®æ |
| | | if (!otherActivePallets && !otherActiveTasks) |
| | | { |
| | | await CheckAndUpdateOrderCompletion(orderNo); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogInformation($"订å {orderNo} è¿æå
¶ä»æçå¨å¤çï¼ä¸æ´æ°è®¢åç¶æ"); |
| | | } |
| | | |
| | | // 3. æ´æ°æ£éè®°å½ç¶æï¼å¯éï¼ |
| | | await UpdatePickingRecordsStatus(orderNo, palletCode); |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ´æ°è®¢åæ°æ®å¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | throw new Exception($"æ´æ°è®¢åæ°æ®å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥å¹¶æ´æ°è®¢åå®æç¶æ |
| | | /// </summary> |
| | | private async Task CheckAndUpdateOrderCompletion(string orderNo) |
| | | { |
| | | var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) |
| | | .Where((o, item) => item.OrderNo == orderNo) |
| | | .Select((o, item) => o) |
| | | .ToListAsync(); |
| | | |
| | | bool allCompleted = true; |
| | | foreach (var detail in orderDetails) |
| | | { |
| | | if (detail.OverOutQuantity < detail.NeedOutQuantity) |
| | | { |
| | | allCompleted = false; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>() |
| | | .FirstAsync(x => x.OrderNo == orderNo); |
| | | |
| | | if (outboundOrder != null && allCompleted && outboundOrder.OrderStatus != (int)OutOrderStatusEnum.åºåºå®æ) |
| | | { |
| | | outboundOrder.OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ; |
| | | await _outboundOrderService.Db.Updateable(outboundOrder).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"订å {orderNo} å·²æ 记为åºåºå®æ"); |
| | | |
| | | // åMESåé¦è®¢å宿ï¼å¦æéè¦ï¼ |
| | | await HandleOrderCompletion(outboundOrder, orderNo); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°æ£éè®°å½ç¶æ |
| | | /// </summary> |
| | | private async Task UpdatePickingRecordsStatus(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | // å¯ä»¥å°ç¸å
³çæ£éè®°å½æ è®°ä¸ºå·²å®æ |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | // è¿éå¯ä»¥æ ¹æ®éè¦æ´æ°æ£éè®°å½çç¶æåæ®µ |
| | | // ä¾å¦ï¼pickingRecord.Status = (int)PickingStatusEnum.已宿; |
| | | |
| | | _logger.LogInformation($"æ¾å°{pickingRecords.Count}æ¡æ£éè®°å½ - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ´æ°æ£éè®°å½ç¶æå¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | |
| | | |
| | | #region è¾
婿¹æ³ |
| | | /// <summary> |
| | | /// ç»ä¸åææçç¶æ - è¿åæçç宿´ç¶æä¿¡æ¯ |
| | | /// </summary> |
| | | private async Task<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var result = new PalletStatusAnalysis |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | StockId = stockId |
| | | }; |
| | | |
| | | // 1. åææªåæ£çåºåºéå®è®°å½ |
| | | var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(it => it.OrderNo == orderNo && |
| | | it.PalletCode == palletCode && |
| | | it.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToListAsync(); |
| | | |
| | | if (remainingLocks.Any()) |
| | | { |
| | | result.HasRemainingLocks = true; |
| | | result.RemainingLocks = remainingLocks; |
| | | result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | } |
| | | |
| | | // 2. åææçä¸çåºåè´§ç© |
| | | var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(it => it.StockId == stockId && |
| | | (it.Status == StockStatusEmun.å
¥åºç¡®è®¤.ObjToInt() || |
| | | it.Status == StockStatusEmun.å
¥åºå®æ.ObjToInt() || |
| | | it.Status == StockStatusEmun.åºåºéå®.ObjToInt())) |
| | | .Where(it => it.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | if (palletStockGoods.Any()) |
| | | { |
| | | result.HasPalletStockGoods = true; |
| | | result.PalletStockGoods = palletStockGoods; |
| | | result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity); |
| | | } |
| | | |
| | | // 3. åææå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(it => it.OrderNo == orderNo && |
| | | it.PalletCode == palletCode && |
| | | !it.IsReverted && |
| | | it.Status != (int)SplitPackageStatusEnum.å·²ååº) |
| | | .ToListAsync(); |
| | | |
| | | if (splitRecords.Any()) |
| | | { |
| | | result.HasSplitRecords = true; |
| | | result.SplitRecords = splitRecords; |
| | | result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId); |
| | | } |
| | | |
| | | // 4. è®¡ç®æ»ååºæ°éå空æçç¶æ |
| | | result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty; |
| | | result.HasItemsToReturn = result.TotalReturnQty > 0; |
| | | result.IsEmptyPallet = !result.HasItemsToReturn; |
| | | |
| | | // 5. æ£æ¥æ¯å¦æè¿è¡ä¸çä»»å¡ |
| | | result.HasActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && x.TaskType == TaskTypeEnum.InPick.ObjToInt() && |
| | | x.PalletCode == palletCode && |
| | | x.TaskStatus == (int)TaskStatusEnum.New) |
| | | .AnyAsync(); |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥æçæ¯å¦ä¸ºç©º |
| | | /// </summary> |
| | | private async Task<bool> IsPalletEmpty(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | // è·ååºåä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (stockInfo == null) |
| | | return false; |
| | | |
| | | // 使ç¨ç»ä¸çç¶æåæ |
| | | var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id); |
| | | return statusAnalysis.IsEmptyPallet; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ£æ¥æçæ¯å¦ä¸ºç©ºå¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// æ£æ¥å¹¶å¤ç空æç |
| | | /// </summary> |
| | | private async Task<bool> CheckAndHandleEmptyPallet(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | // 1. è·ååºåä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (stockInfo == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°æç {palletCode} çåºåä¿¡æ¯"); |
| | | return false; |
| | | } |
| | | |
| | | // 2. 使ç¨ç»ä¸çç¶æåæ |
| | | var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id); |
| | | |
| | | // 3. æ£æ¥æ¯å¦ä¸ºç©ºæç䏿²¡æè¿è¡ä¸çä»»å¡ |
| | | if (!statusAnalysis.IsEmptyPallet || statusAnalysis.HasActiveTasks) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | _logger.LogInformation($"æ£æµå°ç©ºæçï¼å¼å§èªå¨å¤ç - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | //// æ¸
çé¶åºåæ°æ® |
| | | //await CleanupZeroStockData(stockInfo.Id); |
| | | |
| | | //// æ´æ°åºåä¸»è¡¨ç¶æä¸ºç©ºæç |
| | | //await UpdateStockInfoAsEmpty(stockInfo); |
| | | |
| | | //// å¤çåºåºéå®è®°å½ |
| | | //await HandleOutStockLockRecords(orderNo, palletCode); |
| | | |
| | | //// å¤çä»»å¡ç¶æ |
| | | //await HandleTaskStatusForEmptyPallet(orderNo, palletCode); |
| | | |
| | | //// æ´æ°è®¢åæ°æ® |
| | | //await UpdateOrderDataForEmptyPallet(orderNo, palletCode); |
| | | |
| | | ////è®°å½æä½åå² |
| | | //await RecordAutoEmptyPalletOperation(orderNo, palletCode); |
| | | |
| | | _logger.LogInformation($"空æçèªå¨å¤ç宿 - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | return true; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"èªå¨å¤ç空æç失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | private async Task<string> GenerateNewBarcode() |
| | | { |
| | |
| | | OriginalLockQuantity = quantity, |
| | | IsSplitted = 1, |
| | | ParentLockId = originalLock.Id, |
| | | Operator= App.User.UserName, |
| | | FactoryArea=originalLock.FactoryArea, |
| | | lineNo=originalLock.lineNo, |
| | | WarehouseCode=originalLock.WarehouseCode, |
| | | Operator = App.User.UserName, |
| | | FactoryArea = originalLock.FactoryArea, |
| | | lineNo = originalLock.lineNo, |
| | | WarehouseCode = originalLock.WarehouseCode, |
| | | |
| | | }; |
| | | |
| | |
| | | } |
| | | return WebResponseContent.Instance.OK("æ£é确认æå", new { SplitResults = new List<SplitResult>() }); |
| | | } |
| | | |
| | | |
| | | #endregion |
| | | } |
| | | |
| | |
| | | public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>(); |
| | | public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>(); |
| | | } |
| | | public class PalletStatusAnalysis |
| | | { |
| | | public string OrderNo { get; set; } |
| | | public string PalletCode { get; set; } |
| | | public int StockId { get; set; } |
| | | |
| | | // ååºç¸å
³å±æ§ |
| | | public bool HasItemsToReturn { get; set; } |
| | | public bool HasRemainingLocks { get; set; } |
| | | public bool HasPalletStockGoods { get; set; } |
| | | public bool HasSplitRecords { get; set; } |
| | | public decimal RemainingLocksReturnQty { get; set; } |
| | | public decimal PalletStockReturnQty { get; set; } |
| | | public decimal SplitReturnQty { get; set; } |
| | | public decimal TotalReturnQty { get; set; } |
| | | public List<Dt_OutStockLockInfo> RemainingLocks { get; set; } = new List<Dt_OutStockLockInfo>(); |
| | | public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>(); |
| | | public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>(); |
| | | |
| | | // 空æçç¸å
³å±æ§ |
| | | public bool IsEmptyPallet { get; set; } |
| | | public bool HasActiveTasks { get; set; } |
| | | |
| | | // ä¾¿å©æ¹æ³ |
| | | public bool CanReturn => HasItemsToReturn && !HasActiveTasks; |
| | | public bool CanRemove => IsEmptyPallet && !HasActiveTasks; |
| | | } |
| | | #endregion |
| | | } |