| | |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. éªè¯åæ£è¯·æ± |
| | | // éªè¯åæ£è¯·æ± |
| | | var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!validationResult.IsValid) |
| | | { |
| | |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | // 2. æ£æ¥æ¯å¦éè¦èªå¨æå
|
| | | // æ£æ¥æ¯å¦éè¦èªå¨æå
|
| | | var autoSplitResult = await CheckAndAutoSplitIfNeeded(lockInfo, stockDetail, palletCode); |
| | | if (autoSplitResult != null) |
| | | { |
| | |
| | | (lockInfo, orderDetail, stockDetail, batch) = refreshedValidation.Data; |
| | | |
| | | |
| | | // ãéè¦ãè°ç¨èªå¨æå
åéªè¯ |
| | | // è°ç¨èªå¨æå
åéªè¯ |
| | | decimal splitQuantity = autoSplitResult.FirstOrDefault()?.quantityTotal.ObjToDecimal()??0 ; |
| | | bool autoSplitValid = await ValidateAfterAutoSplit(lockInfo, orderDetail, stockDetail, splitQuantity,originalStockQtyBeforeSplit); |
| | | |
| | |
| | | _logger.LogInformation($"èªå¨æå
éªè¯éè¿ï¼ç»§ç»æ§è¡åæ£"); |
| | | } |
| | | |
| | | // 3. 计ç®å®é
æ£éæ°é |
| | | // 计ç®å®é
æ£éæ°é |
| | | decimal actualPickedQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (actualPickedQty <= 0) |
| | |
| | | |
| | | _logger.LogInformation($"å¼å§æ£é - æ°é: {actualPickedQty}"); |
| | | |
| | | // 4. æ§è¡åæ£é»è¾ |
| | | // æ§è¡åæ£é»è¾ |
| | | var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); |
| | | |
| | | // 5. æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | // æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo); |
| | | |
| | | // 6. è®°å½æ£éåå² |
| | | //è®°å½æ£éåå² |
| | | await RecordPickingHistory(pickingResult, orderNo, palletCode); |
| | | |
| | | // 7. æ£éåéªè¯ |
| | | // æ£éåéªè¯ |
| | | await ValidateAfterPicking(orderNo, palletCode, barcode, actualPickedQty); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("忣æå", new |
| | | { |
| | | PickedQuantity = actualPickedQty, |
| | | Barcode = barcode, |
| | | MaterialCode = lockInfo.MaterielCode, |
| | | AutoSplitted = autoSplitResult != null, |
| | | RemainingStock = stockDetail.StockQuantity, |
| | | CurrentOutbound = stockDetail.OutboundQuantity, |
| | | // 妿æ¯èªå¨æå
ï¼è¿åç¸å
³ä¿¡æ¯ |
| | | UnallocatedCreated = autoSplitResult != null ? "æ¯" : "å¦" |
| | | }); |
| | | return WebResponseContent.Instance.OK("忣æå", autoSplitResult); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | |
| | | |
| | | |
| | | /// <summary> |
| | | /// èªå¨æå
åéªè¯æ°æ®ä¸è´æ§ - ä¿®æ£ç |
| | | /// éç¹ä¿®æ£ï¼æ£ç¡®çååºåææå¼è®¡ç® |
| | | /// èªå¨æå
åéªè¯æ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task<bool> ValidateAfterAutoSplit(Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, |
| | | Dt_StockInfoDetail originalStockDetail, decimal splitQuantity, decimal originalStockQtyBeforeSplit) |
| | |
| | | bool allValid = true; |
| | | List<string> validationErrors = new List<string>(); |
| | | |
| | | // 1. éæ°è·åææ°çæ°æ®ï¼æå
åçå½åç¶æï¼ |
| | | // éæ°è·åææ°çæ°æ®ï¼æå
åçå½åç¶æï¼ |
| | | var refreshedOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetail.Id); |
| | | |
| | |
| | | var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Id == originalStockDetail.Id); |
| | | |
| | | // 2. ãæ ¸å¿ä¿®æ£ãéªè¯ååºåæç»æ°æ® |
| | | // éè¦ï¼æå
åååºåçææå¼ = åé
æ°é (å 为èªå¨æå
æ¶ï¼ååºååºåªä¿çåé
æ°é) |
| | | decimal expectedOriginalStockQty = lockInfo.AssignQuantity; // åºè¯¥æ¯120ï¼è䏿¯ç¨è®¡ç® |
| | | // éªè¯ååºåæç»æ°æ® |
| | | decimal expectedOriginalStockQty = lockInfo.AssignQuantity; |
| | | |
| | | _logger.LogInformation($"åºåéªè¯åºå:"); |
| | | _logger.LogInformation($" æå
åååºå: {originalStockQtyBeforeSplit}"); |
| | |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 3. éªè¯æ°åºåæç»ï¼æå
产ççï¼ |
| | | // éªè¯æ°åºåæç»ï¼æå
产ççï¼ |
| | | // æ¥æ¾æ°æ¡ç ï¼éè¿æå
è®°å½ï¼ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OutStockLockInfoId == lockInfo.Id && |
| | |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 5. éªè¯è®¢åæç»æ°æ®æªæ¹å |
| | | // éªè¯è®¢åæç»æ°æ®æªæ¹å |
| | | if (Math.Abs(refreshedOrderDetail.AllocatedQuantity - orderDetail.AllocatedQuantity) > 0.01m) |
| | | { |
| | | string error = $"订åæç»åé
æ°éå¼å¸¸ååï¼æå
å: {orderDetail.AllocatedQuantity}, æå
å: {refreshedOrderDetail.AllocatedQuantity}"; |
| | |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 6. éªè¯åéå®è®°å½æ°æ®æªæ¹åï¼åé
æ°éä¸åï¼ |
| | | // éªè¯åéå®è®°å½æ°æ®æªæ¹åï¼åé
æ°éä¸åï¼ |
| | | if (Math.Abs(refreshedLockInfo.AssignQuantity - lockInfo.AssignQuantity) > 0.01m) |
| | | { |
| | | string error = $"éå®è®°å½åé
æ°éå¼å¸¸ååï¼æå
å: {lockInfo.AssignQuantity}, æå
å: {refreshedLockInfo.AssignQuantity}"; |
| | |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 7. ãæ°å¢ãéªè¯æ»åºå宿 |
| | | // æ°å¢ãéªè¯æ»åºå宿 |
| | | // æå
åæ»åºå = ååºåæ°é |
| | | // æå
åæ»åºå = ååºåç°ææ°é + æ°åºåæ°é |
| | | decimal totalStockAfterSplit = refreshedStockDetail.StockQuantity; |
| | |
| | | { |
| | | _logger.LogInformation($"å¼å§æ£éåéªè¯"); |
| | | |
| | | // 1. éªè¯åºåæç» |
| | | // éªè¯åºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode); |
| | | |
| | | // 2. æ¥æ¾è¯¥æ¡ç çæææ£éè®°å½ |
| | | // æ¥æ¾è¯¥æ¡ç çæææ£éè®°å½ |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | |
| | | |
| | | |
| | | #region å走空箱é»è¾ |
| | | |
| | | |
| | | /// <summary> |
| | | /// éªè¯ç©ºç®±åèµ°æ¡ä»¶ |
| | | /// </summary> |
| | | private async Task<ValidationResult<List<Dt_OutStockLockInfo>>> ValidateEmptyPalletRemoval(string orderNo, string palletCode) |
| | | { |
| | | _logger.LogInformation($"å¼å§éªè¯ç©ºæçåèµ° - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | // è·åæççææéå®è®°å½ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | if (!lockInfos.Any()) |
| | | return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该æç没æéå®è®°å½"); |
| | | |
| | | // è·ååºåä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .FirstAsync(x => x.PalletCode == palletCode); |
| | | |
| | | if (stockInfo == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°æççåºåä¿¡æ¯ï¼å¯è½å·²è¢«æ¸
ç"); |
| | | // 妿æ¾ä¸å°åºåä¿¡æ¯ï¼åªæ£æ¥éå®è®°å½ |
| | | } |
| | | else |
| | | { |
| | | // æ£æ¥æç䏿¯å¦è¿æåºåè´§ç© |
| | | var remainingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockInfo.Id && |
| | | x.StockQuantity > 0) // åªæ£æ¥æ°é大äº0çåºå |
| | | .ToListAsync(); |
| | | |
| | | if (remainingStock.Any()) |
| | | { |
| | | var remainingQty = remainingStock.Sum(x => x.StockQuantity); |
| | | var barcodes = string.Join(", ", remainingStock.Select(x => x.Barcode).Take(5)); // åªæ¾ç¤ºå5个 |
| | | return ValidationResult<List<Dt_OutStockLockInfo>>.Error( |
| | | $"æçä¸è¿æåºåè´§ç©ï¼æ°é{remainingQty}ï¼æ¡ç : {barcodes}ï¼ä¸è½å走空箱"); |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦ææªå®æçéå®è®°å½ï¼ç¶æä¸ºåºåºä¸æååºä¸ï¼ |
| | | var unfinishedLocks = lockInfos.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); |
| | | |
| | | if (unfinishedLocks.Any()) |
| | | { |
| | | var unfinishedCount = unfinishedLocks.Count; |
| | | var unfinishedQty = unfinishedLocks.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | return ValidationResult<List<Dt_OutStockLockInfo>>.Error( |
| | | $"æçè¿æ{unfinishedCount}æ¡æªå®æè®°å½ï¼å©ä½æ°é{unfinishedQty}ï¼ä¸è½å走空箱"); |
| | | } |
| | | |
| | | // è·å已宿çéå®è®°å½ï¼ç¶æä¸ºæ£é宿æå·²åèµ°ï¼ |
| | | var completedLocks = lockInfos.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.æ£é宿 || |
| | | x.Status == (int)OutLockStockStatusEnum.å·²åèµ°).ToList(); |
| | | |
| | | if (!completedLocks.Any()) |
| | | return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该æç没æå·²å®ææ£éçè®°å½"); |
| | | |
| | | _logger.LogInformation($"空æçéªè¯éè¿ - æ¾å° {completedLocks.Count} æ¡å·²å®æè®°å½"); |
| | | |
| | | return ValidationResult<List<Dt_OutStockLockInfo>>.Success(completedLocks); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ¸
ç已宿çéå®è®°å½ |
| | | /// </summary> |
| | | private async Task CleanupCompletedLocks(List<Dt_OutStockLockInfo> completedLocks) |
| | | { |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | | // æ è®°éå®è®°å½ä¸ºå·²åèµ° |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²åèµ°; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // æ¸
ç对åºçåºåè®°å½ç¶æ |
| | | await CleanupStockInfo(lockInfo); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// <summary> |
| | | /// æ¸
çåºåä¿¡æ¯ - 宿´ä¿®æ£ç |
| | | /// ç¡®ä¿OutboundQuantityæ£ç¡®æ¸
é¶ |
| | | /// </summary> |
| | |
| | | _logger.LogInformation($"æ¸
çåç¶æ - åºå: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // ãéè¦ãæ£æ¥åºåæ°éæ¯å¦åºè¯¥ä¸º0 |
| | | // 妿éå®ç¶ææ¯æ£é宿ï¼ç论ä¸åºååºè¯¥ä¸º0 |
| | | if (lockInfo.Status == (int)OutLockStockStatusEnum.æ£é宿) |
| | | { |
| | | if (stockDetail.StockQuantity > 0) |
| | |
| | | |
| | | // æ¸
çåºåååºåºæ°é |
| | | stockDetail.StockQuantity = 0; |
| | | stockDetail.OutboundQuantity = 0; // ãä¿®æ£ãç¡®ä¿åºåºæ°éæ¸
é¶ |
| | | stockDetail.OutboundQuantity = 0; |
| | | stockDetail.Status = (int)StockStatusEmun.å·²æ¸
ç; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | |
| | | decimal originalOutbound = stock.OutboundQuantity; |
| | | |
| | | stock.StockQuantity = 0; |
| | | stock.OutboundQuantity = 0; // ãä¿®æ£ãç¡®ä¿åºåºæ°éæ¸
é¶ |
| | | stock.OutboundQuantity = 0; |
| | | stock.Status = (int)StockStatusEmun.å·²æ¸
ç; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stock).ExecuteCommandAsync(); |
| | |
| | | if (orderDetail.AllocatedQuantity != totalLockAssignQuantity) |
| | | { |
| | | _logger.LogWarning($"订åæç»åé
æ°éä¸éå®ä¿¡æ¯ä¸ä¸è´ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQuantity}"); |
| | | // è¿éä¸ç´æ¥è¿åé误ï¼å 为æå
æä½æ¬èº«ä¸ä¼å¯¼è´ä¸ä¸è´ï¼åªæ¯è®°å½è¦å |
| | | } |
| | | |
| | | _logger.LogInformation($"æå
éªè¯éè¿ - åæ¡ç : {originalBarcode}, æå
æ°é: {splitQuantity}"); |
| | |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ§è¡æå¨æå
é»è¾ - ä¿®å¤çæ¬ |
| | | /// æ§è¡æå¨æå
é»è¾ |
| | | /// </summary> |
| | | private async Task<List<SplitResult>> ExecuteManualSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | |
| | | MaterielCode = stockDetail.MaterielCode, |
| | | OrderNo = stockDetail.OrderNo, |
| | | BatchNo = stockDetail.BatchNo, |
| | | StockQuantity = newStockQuantity, // ä¿®å¤ï¼ä½¿ç¨æ£ç¡®çæå
æ°é |
| | | OutboundQuantity = 0, // æ°æ¡ç åå§åºåºæ°é为0 |
| | | StockQuantity = newStockQuantity, |
| | | OutboundQuantity = 0, |
| | | Barcode = newBarcode, |
| | | Status = (int)StockStatusEmun.åºåºéå®, |
| | | SupplyCode = stockDetail.SupplyCode, |
| | |
| | | BarcodeUnit = stockDetail.BarcodeUnit, |
| | | BusinessType = stockDetail.BusinessType, |
| | | InboundOrderRowNo = stockDetail.InboundOrderRowNo, |
| | | FactoryArea = stockDetail.FactoryArea, |
| | | WarehouseCode = stockDetail.WarehouseCode, |
| | | }; |
| | | |
| | | await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æ°åºåæç»æå - æ¡ç : {newBarcode}, åºåæ°é: {newStockQuantity}"); |
| | | |
| | | // ä¿®æ£ï¼æ´æ°ååºåæç» - ç¡®ä¿æ°æ®ä¸è´æ§ |
| | | // æ´æ°ååºåæç» |
| | | stockDetail.StockQuantity = originalRemainingStockQuantity; |
| | | |
| | | // ç¡®ä¿ä¸ä¼ä¸ºè´æ° |
| | |
| | | { |
| | | _logger.LogWarning($"ååºåæ°éåºç°è´æ°ï¼é置为0"); |
| | | stockDetail.StockQuantity = 0; |
| | | } |
| | | |
| | | // åºåºæ°éä¿æä¸åï¼å ä¸ºæ¯æå
ï¼ä¸æ¯å®é
åºåº |
| | | // stockDetail.OutboundQuantity = stockDetail.OutboundQuantity; |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ´æ°ååºåæç» - æ¡ç : {stockDetail.Barcode}, " + |
| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯æå
å订åæç»çåé
æ°éæ¯å¦ä¿æä¸å |
| | | /// </summary> |
| | | private async Task ValidateOrderDetailAllocationAfterSplit(long orderDetailId, decimal originalTotalAssignQty) |
| | | { |
| | | try |
| | | { |
| | | // è·å订åæç»çææéå®ä¿¡æ¯çæ»åé
æ°é |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderDetailId == orderDetailId) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalLockAssignQty = allLocks.Sum(x => x.AssignQuantity); |
| | | |
| | | _logger.LogInformation($"æå
ååé
æ°ééªè¯ - 订åæç»ID: {orderDetailId}"); |
| | | _logger.LogInformation($"åå§æ»åé
æ°é: {originalTotalAssignQty}, å½åæ»åé
æ°é: {totalLockAssignQty}"); |
| | | |
| | | // æå¨æå
åæ»åé
æ°éåºè¯¥ä¿æä¸å |
| | | if (Math.Abs(originalTotalAssignQty - totalLockAssignQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"æå
åæ»åé
æ°éåçåå - ææ: {originalTotalAssignQty}, å®é
: {totalLockAssignQty}"); |
| | | |
| | | // 妿ååå¾å°ï¼å¯è½æ¯ç²¾åº¦é®é¢ï¼è®°å½ä½ä¸æåºå¼å¸¸ |
| | | if (Math.Abs(originalTotalAssignQty - totalLockAssignQty) > 1.0m) |
| | | { |
| | | throw new InvalidOperationException($"æå
åæ»åé
æ°éå¼å¸¸ååï¼ææ: {originalTotalAssignQty}, å®é
: {totalLockAssignQty}"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | _logger.LogInformation($"æå
ååé
æ°ééªè¯éè¿ - æ»åé
æ°éä¿æä¸å"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"éªè¯æå
ååé
æ°é失败 - OrderDetailId: {orderDetailId}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// <summary> |
| | | /// éªè¯æå
åæ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidateDataConsistencyAfterSplit(long orderDetailId, decimal expectedAllocatedQty, decimal expectedLockQty) |
| | |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. æ¥æ¾æå
è®°å½å¹¶éªè¯ |
| | | // æ¥æ¾æå
è®°å½å¹¶éªè¯ |
| | | var validationResult = await ValidateCancelSplitRequest(orderNo, palletCode, newBarcode); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data; |
| | | |
| | | // 2. æ¥æ¾åå§éå®ä¿¡æ¯ |
| | | // æ¥æ¾åå§éå®ä¿¡æ¯ |
| | | var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); |
| | | |
| | | // 3. æ£æ¥è¯¥æ¡ç æ¯å¦è¢«å次æå
|
| | | // æ£æ¥è¯¥æ¡ç æ¯å¦è¢«å次æå
|
| | | var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted) |
| | | .ToListAsync(); |
| | |
| | | return WebResponseContent.Instance.Error("该æ¡ç å·²è¢«åæ¬¡æå
ï¼è¯·å
åæ¶åç»çæå
æä½"); |
| | | } |
| | | |
| | | // 4. æ§è¡åæ¶æå
é»è¾ |
| | | // æ§è¡åæ¶æå
é»è¾ |
| | | await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | |
| | | if (orderDetail == null) |
| | | throw new InvalidOperationException("æªæ¾å°è®¢åæç»"); |
| | | |
| | | // 1. æ¢å¤è®¢åæç»çåé
æ°éï¼èªå¨æå
ä¼å¢å åé
æ°éï¼ |
| | | // æ¢å¤è®¢åæç»çåé
æ°é |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | |
| | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"èªå¨æå
åæ¶æ¢å¤è®¢åæç» - åé
æ°é: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}"); |
| | | |
| | | // 2. æ¢å¤ååºåï¼å°æå
çæ°éå åååºåï¼ |
| | | // æ¢å¤ååºå |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | | |
| | |
| | | _logger.LogInformation($"èªå¨æå
åæ¶æ¢å¤ååºå - æ¡ç : {originalStock.Barcode}, æ°é: {originalStockQty} -> {originalStock.StockQuantity}"); |
| | | } |
| | | |
| | | // 3. å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | // å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | await DeleteNewSplitRecords(newLockInfo, newStockDetail); |
| | | } |
| | | |
| | |
| | | { |
| | | _logger.LogInformation($"å¤çæå¨æå
åæ¶ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | |
| | | // 1. æ¢å¤åéå®ä¿¡æ¯çåé
æ°é |
| | | // æ¢å¤åéå®ä¿¡æ¯çåé
æ°é |
| | | decimal originalAssignQty = originalLockInfo.AssignQuantity; |
| | | decimal originalOrderQty = originalLockInfo.OrderQuantity; |
| | | |
| | |
| | | await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æå¨æå
åæ¶æ¢å¤åéå®ä¿¡æ¯ - åé
æ°é: {originalAssignQty} -> {originalLockInfo.AssignQuantity}"); |
| | | |
| | | // 2. æ¢å¤ååºåæç» |
| | | // æ¢å¤ååºåæç» |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | | |
| | |
| | | _logger.LogInformation($"æå¨æå
åæ¶æ¢å¤ååºå - æ¡ç : {originalStock.Barcode}, æ°é: {originalStockQty} -> {originalStock.StockQuantity}"); |
| | | } |
| | | |
| | | // 3. å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | // å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | await DeleteNewSplitRecords(newLockInfo, newStockDetail); |
| | | } |
| | | |
| | |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯åæ¶æå
åæ°æ®ä¸è´æ§ - ææ°çæ¬ |
| | | /// </summary> |
| | |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. æ¥æ¾ææç¸å
³çæå
è®°å½ï¼å½¢ææå
é¾ï¼ |
| | | // æ¥æ¾ææç¸å
³çæå
è®°å½ï¼å½¢ææå
é¾ï¼ |
| | | var splitChain = await GetSplitPackageChain(orderNo, startBarcode); |
| | | |
| | | if (!splitChain.Any()) |
| | |
| | | |
| | | _logger.LogInformation($"æ¾å°æå
é¾ï¼å
± {splitChain.Count} æ¡è®°å½"); |
| | | |
| | | // 2. æ¶éæå
é¾ä¸æ¶åçæææ¡ç ï¼å
æ¬åæ¡ç åæ°æ¡ç ï¼ |
| | | // æ¶éæå
é¾ä¸æ¶åçæææ¡ç ï¼å
æ¬åæ¡ç åæ°æ¡ç ï¼ |
| | | var allBarcodesInChain = new List<string> { startBarcode }; |
| | | allBarcodesInChain.AddRange(splitChain.Select(x => x.NewBarcode)); |
| | | |
| | | // 3. æ£æ¥æå
é¾ä¸æ¯å¦æå·²è¢«åæ£çæ¡ç |
| | | // æ£æ¥æå
é¾ä¸æ¯å¦æå·²è¢«åæ£çæ¡ç |
| | | var pickedBarcodesInfo = await GetPickedBarcodesInfo(orderNo, allBarcodesInChain); |
| | | |
| | | if (pickedBarcodesInfo.Any()) |
| | |
| | | $"以䏿¡ç å·²è¢«åæ£ï¼è¯·å
忶忣ï¼{pickedBarcodes}"); |
| | | } |
| | | |
| | | // 4. ææå
顺åºååºåæ¶ï¼ä»ææ°çå¼å§åæ¶ï¼ |
| | | // ææå
顺åºååºåæ¶ï¼ä»ææ°çå¼å§åæ¶ï¼ |
| | | var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList(); |
| | | |
| | | foreach (var splitRecord in reversedChain) |
| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæ¡ç çæå
忣éç¶æ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> GetBarcodeSplitAndPickStatus(string orderNo, string barcode) |
| | | { |
| | | try |
| | | { |
| | | // 1. è·åæå
ä¿¡æ¯ |
| | | var splitChain = await GetSplitPackageChain(orderNo, barcode); |
| | | var isOriginalBarcode = !splitChain.Any(x => x.NewBarcode == barcode); |
| | | |
| | | // 2. è·åæ£éä¿¡æ¯ |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | var totalPickedQty = pickingRecords.Sum(x => x.PickQuantity); |
| | | |
| | | // 3. è·åéå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == barcode && x.OrderNo == orderNo) |
| | | .FirstAsync(); |
| | | |
| | | // 4. è·ååºåä¿¡æ¯ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.Barcode == barcode) |
| | | .FirstAsync(); |
| | | |
| | | var statusInfo = new BarcodeStatusInfoDto |
| | | { |
| | | Barcode = barcode, |
| | | OrderNo = orderNo, |
| | | IsOriginalBarcode = isOriginalBarcode, |
| | | SplitChainCount = splitChain.Count, |
| | | HasBeenPicked = pickingRecords.Any(), |
| | | TotalPickedQuantity = totalPickedQty, |
| | | PickRecordCount = pickingRecords.Count, |
| | | LockInfoStatus = lockInfo?.Status ?? 0, |
| | | LockInfoPickedQty = lockInfo?.PickedQty ?? 0, |
| | | LockInfoAssignQty = lockInfo?.AssignQuantity ?? 0, |
| | | StockQuantity = stockDetail?.StockQuantity ?? 0, |
| | | StockStatus = stockDetail?.Status ?? 0, |
| | | CanCancelSplit = !pickingRecords.Any(), // æªè¢«åæ£æè½åæ¶æå
|
| | | NeedCancelPickFirst = pickingRecords.Any() // éè¦å
忶忣 |
| | | }; |
| | | |
| | | // 5. è·åæä½å»ºè®® |
| | | statusInfo.OperationSuggestions = GetOperationSuggestions(statusInfo); |
| | | |
| | | return WebResponseContent.Instance.OK("è·åç¶ææå", statusInfo); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"è·åæ¡ç ç¶æå¤±è´¥ - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error("è·åæ¡ç ç¶æå¤±è´¥"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// è·åæä½å»ºè®® |
| | | /// </summary> |
| | | private List<string> GetOperationSuggestions(BarcodeStatusInfoDto statusInfo) |
| | | { |
| | | var suggestions = new List<string>(); |
| | | |
| | | if (statusInfo.HasBeenPicked) |
| | | { |
| | | suggestions.Add($"该æ¡ç å·²è¢«åæ£ï¼æ°éï¼{statusInfo.TotalPickedQuantity}ï¼ï¼å¦éåæ¶æå
ï¼è¯·å
忶忣"); |
| | | |
| | | if (statusInfo.IsOriginalBarcode) |
| | | { |
| | | suggestions.Add("è¿æ¯åæ¡ç ï¼åæ¶åæ£åå°æ¢å¤ä¸ºå¯åæ£ç¶æ"); |
| | | } |
| | | else |
| | | { |
| | | suggestions.Add("è¿æ¯æå
çæçæ°æ¡ç ï¼åæ¶åæ£åæè½åæ¶æå
"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (statusInfo.IsOriginalBarcode && statusInfo.SplitChainCount > 0) |
| | | { |
| | | suggestions.Add("è¿æ¯åæ¡ç ï¼å¯ä»¥åæ¶æå
é¾"); |
| | | } |
| | | else if (!statusInfo.IsOriginalBarcode) |
| | | { |
| | | suggestions.Add("è¿æ¯æå
çæçæ°æ¡ç ï¼å¯ä»¥åç¬åæ¶æå
"); |
| | | } |
| | | } |
| | | |
| | | if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.æ£é宿) |
| | | { |
| | | suggestions.Add("éå®ç¶æï¼æ£é宿"); |
| | | } |
| | | else if (statusInfo.LockInfoStatus == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | { |
| | | suggestions.Add("éå®ç¶æï¼åºåºä¸"); |
| | | } |
| | | |
| | | return suggestions; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åå·²è¢«åæ£çæ¡ç ä¿¡æ¯ |
| | | /// </summary> |
| | |
| | | #endregion |
| | | |
| | | #region ç»ä¸ååºé»è¾ |
| | | private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode) |
| | | { |
| | | // å
å°è¯éè¿è®¢åå·åæçå·æ¥æ¾ä»»å¡ |
| | | var task = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (task == null) |
| | | { |
| | | // 妿æ¾ä¸å°ï¼åéè¿æçå·æ¥æ¾ |
| | | task = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | private async Task<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var result = new PalletStatusAnalysis |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | StockId = stockId |
| | | }; |
| | | |
| | | // åææªåæ£çåºåºéå®è®°å½ |
| | | 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); |
| | | _logger.LogInformation($"åç°{remainingLocks.Count}æ¡æªåæ£éå®è®°å½ï¼æ»æ°é: {result.RemainingLocksReturnQty}"); |
| | | } |
| | | |
| | | // åææçä¸çåºåè´§ç© |
| | | 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); |
| | | _logger.LogInformation($"åç°{palletStockGoods.Count}个åºåè´§ç©ï¼æ»æ°é: {result.PalletStockReturnQty}"); |
| | | |
| | | // è®°å½è¯¦ç»ç¶æåå¸ |
| | | var statusGroups = palletStockGoods.GroupBy(x => x.Status); |
| | | foreach (var group in statusGroups) |
| | | { |
| | | _logger.LogInformation($"åºåç¶æ{group.Key}: {group.Count()}个货ç©ï¼æ°é: {group.Sum(x => x.StockQuantity)}"); |
| | | } |
| | | } |
| | | |
| | | //åææå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(it => it.OrderNo == orderNo && |
| | | it.PalletCode == palletCode && |
| | | !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.å·²æ£é && |
| | | it.Status != (int)SplitPackageStatusEnum.å·²ååº) |
| | | .ToListAsync(); |
| | | |
| | | if (splitRecords.Any()) |
| | | { |
| | | result.HasSplitRecords = true; |
| | | result.SplitRecords = splitRecords; |
| | | result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId); |
| | | |
| | | _logger.LogInformation($"åç°{splitRecords.Count}æ¡æªæ£éæå
è®°å½ï¼æ»æ°é: {result.SplitReturnQty}"); |
| | | } |
| | | |
| | | // 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(); |
| | | |
| | | _logger.LogInformation($"æçç¶æåæå®æ - 订å: {orderNo}, æç: {palletCode}, " + |
| | | $"æ»ååºæ°é: {result.TotalReturnQty}, æ¯å¦ç©ºæç: {result.IsEmptyPallet}, " + |
| | | $"æè¿è¡ä¸ä»»å¡: {result.HasActiveTasks}"); |
| | | |
| | | return result; |
| | | } |
| | | private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId) |
| | | { |
| | | decimal totalQty = 0; |
| | | var processedBarcodes = new HashSet<string>(); |
| | | |
| | | foreach (var splitRecord in splitRecords) |
| | | { |
| | | if (splitRecord.Status != (int)SplitPackageStatusEnum.å·²æ¤é) |
| | | continue; |
| | | // æ£æ¥åæ¡ç |
| | | if (!processedBarcodes.Contains(splitRecord.OriginalBarcode)) |
| | | { |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId && |
| | | it.Status != StockStatusEmun.åºåºå®æ.ObjToInt()) |
| | | .FirstAsync(); |
| | | |
| | | if (originalStock != null && originalStock.StockQuantity > 0) |
| | | { |
| | | totalQty += originalStock.StockQuantity; |
| | | processedBarcodes.Add(splitRecord.OriginalBarcode); |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥æ°æ¡ç |
| | | if (!processedBarcodes.Contains(splitRecord.NewBarcode)) |
| | | { |
| | | var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId && it.Status != StockStatusEmun.åºåºå®æ.ObjToInt()) |
| | | .FirstAsync(); |
| | | |
| | | if (newStock != null && newStock.StockQuantity > 0) |
| | | { |
| | | totalQty += newStock.StockQuantity; |
| | | processedBarcodes.Add(splitRecord.NewBarcode); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return totalQty; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç»ä¸ååºæ¹æ³ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> ExecutePalletReturn(string orderNo, string palletCode, string returnReason = "åæ¹ååº") |
| | | { |
| | | ReturnTaskInfo returnTaskInfo = null; |
| | | try |
| | | { |
| | | _logger.LogInformation($"ãå¢å¼ºååºå¼å§ã订å: {orderNo}, æç: {palletCode}"); |
| | |
| | | return WebResponseContent.Instance.Error("订åå·åæçç ä¸è½ä¸ºç©º"); |
| | | |
| | | // è·ååºåä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .FirstAsync(x => x.PalletCode == palletCode); |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode); |
| | | |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°æç {palletCode} 对åºçåºåä¿¡æ¯"); |
| | |
| | | var validationResult = await ValidateDataBeforeReturn(orderNo, palletCode, stockId); |
| | | if (!validationResult.IsValid) |
| | | { |
| | | _logger.LogWarning($"ååºåæ°æ®éªè¯å¤±è´¥: {validationResult.ErrorMessage}"); |
| | | // å¯ä»¥æ ¹æ®å®é
æ
åµå³å®æ¯å¦ç»§ç» |
| | | _logger.LogWarning($"ååºåæ°æ®éªè¯å¤±è´¥: {validationResult.ErrorMessage}"); |
| | | } |
| | | |
| | | // åææçç¶æ |
| | |
| | | _logger.LogInformation($"ãæ ååºç©åãå¤ç空æç"); |
| | | var result = await HandleEmptyPalletReturn(orderNo, palletCode, stockInfo); |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | // å¨äºå¡æäº¤åå¤çESSå½ä»¤ |
| | | if (result.Status && result.Data is ReturnTaskInfo taskInfo) |
| | | { |
| | | await ProcessESSAfterTransaction(palletCode, taskInfo); |
| | | } |
| | | return result; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"空箱ååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空箱ååºå¤±è´¥ï¼{ex.Message}"); |
| | | _logger.LogError($"空箱ååºExecutePalletReturn失败: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空箱ååºExecutePalletReturn失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | // æ´æ°è®¢åç¶æ |
| | | await UpdateOrderStatusAfterReturn(orderNo); |
| | | |
| | | |
| | | |
| | | |
| | | // å建ååºä»»å¡ |
| | | try |
| | | { |
| | | await CreateReturnTask(orderNo, palletCode, stockInfo); |
| | | // å建ååºä»»å¡ï¼ä½ä¸åéESSå½ä»¤ |
| | | returnTaskInfo = await CreateReturnTaskWithoutESS(orderNo, palletCode, stockInfo); |
| | | |
| | | } |
| | | catch (Exception taskEx) |
| | | { |
| | | _logger.LogError($"ååºä»»å¡å建失败: {taskEx.Message}"); |
| | | _logger.LogError($"ååºä»»å¡CreateReturnTaskWithoutESSå建失败: {taskEx.Message}"); |
| | | // ä»»å¡å建失败ä¸å½±åæ°æ®ååº |
| | | } |
| | | _unitOfWorkManage.CommitTran(); |
| | | // 8. ååºåéªè¯ |
| | | |
| | | // å¨äºå¡æäº¤åå¤çESSå½ä»¤ |
| | | if (returnTaskInfo != null && returnTaskInfo.ShouldSendESS) |
| | | { |
| | | await ProcessESSAfterTransaction(palletCode, returnTaskInfo); |
| | | } |
| | | // ååºåéªè¯ |
| | | await ValidateDataAfterReturn(orderNo, palletCode, stockId); |
| | | |
| | | return WebResponseContent.Instance.OK($"ååºæåï¼ååºæ°éï¼{statusAnalysis.TotalReturnQty}", new |
| | |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"ååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"ååºå¤±è´¥ï¼{ex.Message}"); |
| | | _logger.LogError($"ExecutePalletReturn ååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"ExecutePalletReturn ååºå¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// äºå¡æäº¤åå¤çESSå½ä»¤ - ç¬ç«æ¹æ³ï¼é¿å
äºå¡é |
| | | /// </summary> |
| | | private async Task ProcessESSAfterTransaction(string palletCode, ReturnTaskInfo taskInfo) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çESSå½ä»¤ - æç: {palletCode}"); |
| | | |
| | | if (taskInfo == null || !taskInfo.ShouldSendESS || taskInfo.ReturnTask == null) |
| | | { |
| | | _logger.LogWarning($"æ éåéESSå½ä»¤æä»»å¡ä¿¡æ¯ä¸å®æ´"); |
| | | return; |
| | | } |
| | | |
| | | // åéESSå½ä»¤ |
| | | await SendESSCommands(palletCode, taskInfo.OriginalTaskTargetAddress, taskInfo.ReturnTask); |
| | | |
| | | _logger.LogInformation($"ESSå½ä»¤å¤ç宿 - æç: {palletCode}, ä»»å¡å·: {taskInfo.ReturnTask.TaskNum}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å¤çESSå½ä»¤å¤±è´¥ - æç: {palletCode}, Error: {ex.Message}"); |
| | | // è¿é䏿åºå¼å¸¸ï¼å ä¸ºæ°æ®ååºå·²ç»æåï¼ESSå½ä»¤åé失败å¯ä»¥ç¨åéè¯ |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// å建ååºä»»å¡ï¼ä¸åéESSå½ä»¤ï¼- ç¨äºäºå¡å
å¤ç |
| | | /// </summary> |
| | | private async Task<ReturnTaskInfo> CreateReturnTaskWithoutESS(string orderNo, string palletCode, Dt_StockInfo stockInfo) |
| | | { |
| | | try |
| | | { |
| | | // è·åå½åä»»å¡ä¿¡æ¯ |
| | | var currentTask = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (currentTask == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°å½åä»»å¡ - 订å: {orderNo}, æç: {palletCode}"); |
| | | return null; |
| | | } |
| | | |
| | | // åé
æ°è´§ä½ |
| | | var newLocation = _locationInfoService.AssignLocation(stockInfo.LocationType); |
| | | |
| | | var returnTask = new Dt_Task() |
| | | { |
| | | CurrentAddress = stations[currentTask.TargetAddress], |
| | | Grade = 0, |
| | | PalletCode = palletCode, |
| | | NextAddress = "", |
| | | OrderNo = orderNo, |
| | | Roadway = newLocation.RoadwayNo, |
| | | SourceAddress = stations[currentTask.TargetAddress], |
| | | TargetAddress = newLocation.LocationCode, |
| | | TaskStatus = TaskStatusEnum.New.ObjToInt(), |
| | | TaskType = TaskTypeEnum.InPick.ObjToInt(), |
| | | PalletType = stockInfo.PalletType, |
| | | WarehouseId = currentTask.WarehouseId |
| | | }; |
| | | |
| | | try |
| | | { |
| | | await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"å建ååºä»»å¡æå: {returnTask.TaskNum}, 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | // å é¤åå§åºåºä»»å¡ |
| | | _logger.LogInformation($"å¼å§å é¤åå²ä»»å¡: {orderNo}, {currentTask.TaskNum}"); |
| | | var result = _task_HtyService.DeleteAndMoveIntoHty(currentTask, OperateTypeEnum.人工å é¤); |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | |
| | | if (!result) |
| | | { |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | } |
| | | _logger.LogInformation($"å é¤åå²ä»»å¡å®æ: {currentTask.TaskNum}, å½±åè¡æ°: {result}"); |
| | | |
| | | // è¿åä»»å¡ä¿¡æ¯ï¼ä½ä¸åéESSå½ä»¤ |
| | | return new ReturnTaskInfo |
| | | { |
| | | ShouldSendESS = true, |
| | | PalletCode = palletCode, |
| | | OriginalTaskTargetAddress = currentTask.TargetAddress, |
| | | ReturnTask = returnTask |
| | | }; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å建ååºä»»å¡å¤±è´¥ - 订å: {orderNo}, æç: {palletCode}, Error: {ex.Message}"); |
| | | throw new Exception($"å建ååºä»»å¡å¤±è´¥ - 订å: {orderNo}, æç: {palletCode}", ex); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å建ååºä»»å¡å¤±è´¥ - 订å: {orderNo}, æç: {palletCode}, Error: {ex.Message}"); |
| | | return null; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// å¢å¼ºçååºåæ°æ®éªè¯ |
| | | /// </summary> |
| | |
| | | |
| | | try |
| | | { |
| | | // 1. éªè¯åºåæ°æ® |
| | | // éªè¯åºåæ°æ® |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .ToListAsync(); |
| | |
| | | errors.Add($"åç°è´æ°åºå: {string.Join(", ", negativeStocks.Select(x => $"{x.Barcode}:{x.StockQuantity}"))}"); |
| | | } |
| | | |
| | | // 2. éªè¯éå®è®°å½ |
| | | // éªè¯éå®è®°å½ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | |
| | | errors.Add($"åç°å·²æ£éæ°é大äºåé
æ°éçéå®è®°å½"); |
| | | } |
| | | |
| | | // 3. éªè¯æå
è®°å½ |
| | | // éªè¯æå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | |
| | | var locallyProcessedBarcodes = new HashSet<string>(); |
| | | decimal totalProcessedQty = 0; |
| | | |
| | | // 1. å¤çå·²åé
çéå®è®°å½ |
| | | // å¤çå·²åé
çéå®è®°å½ |
| | | if (statusAnalysis.HasRemainingLocks) |
| | | { |
| | | _logger.LogInformation($"å¤çå·²åé
éå®è®°å½ - {statusAnalysis.RemainingLocks.Count} æ¡"); |
| | |
| | | } |
| | | } |
| | | |
| | | // 2. å¤çæªåé
çéå®è®°å½ |
| | | // å¤çæªåé
çéå®è®°å½ |
| | | if (statusAnalysis.HasUnallocatedLocks) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
éå®è®°å½ - {statusAnalysis.UnallocatedLocks.Count} æ¡"); |
| | |
| | | } |
| | | } |
| | | |
| | | // 3. å¤çæªåé
çåºåè´§ç© |
| | | // å¤çæªåé
çåºåè´§ç© |
| | | if (statusAnalysis.HasPalletStockGoods) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
åºåè´§ç© - {statusAnalysis.PalletStockGoods.Count} 个"); |
| | |
| | | } |
| | | } |
| | | |
| | | // 4. ãä¿®æ£ãå¤çæå
è®°å½ - åªå¤çæªè¢«å
¶ä»é»è¾è¦ççæ¡ç |
| | | // å¤çæå
è®°å½ - åªå¤çæªè¢«å
¶ä»é»è¾è¦ççæ¡ç |
| | | if (statusAnalysis.HasSplitRecords && statusAnalysis.SplitReturnQty > 0) |
| | | { |
| | | _logger.LogInformation($"å¤çæå
è®°å½ç¸å
³åºå - æ°å¢æ°é: {statusAnalysis.SplitReturnQty}"); |
| | |
| | | |
| | | |
| | | /// <summary> |
| | | /// å¤çå·²åé
éå®è®°å½çååº |
| | | /// å·²åé
éå®è®°å½éè¦ï¼1.æ¢å¤åºå 2.æ´æ°éå®ç¶æ 3.åå°è®¢åæç»åé
æ°é |
| | | /// </summary> |
| | | private async Task ProcessSingleLockReturn(Dt_OutStockLockInfo lockInfo, decimal returnQty) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çå·²åé
éå®è®°å½ååº - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | if (returnQty <= 0) |
| | | { |
| | | _logger.LogInformation($"ååºæ°éæ æ({returnQty})ï¼è·³è¿å¤ç"); |
| | | return; |
| | | } |
| | | |
| | | // 1. éªè¯éå®è®°å½ç¶æ |
| | | if (lockInfo.Status != (int)OutLockStockStatusEnum.åºåºä¸ && |
| | | lockInfo.Status != (int)OutLockStockStatusEnum.ååºä¸) |
| | | { |
| | | _logger.LogWarning($"éå®è®°å½ç¶æä¸æ¯åºåºä¸æååºä¸ï¼è·³è¿å¤ç - ç¶æ: {lockInfo.Status}"); |
| | | return; |
| | | } |
| | | |
| | | // 2. è·åå
³èçåºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogError($"æªæ¾å°åºåæç» - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}"); |
| | | throw new InvalidOperationException($"åºåæç»ä¸åå¨: {lockInfo.CurrentBarcode}"); |
| | | } |
| | | |
| | | // 3. è·åå
³èç订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == lockInfo.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | { |
| | | _logger.LogError($"æªæ¾å°è®¢åæç» - OrderDetailId: {lockInfo.OrderDetailId}"); |
| | | throw new InvalidOperationException($"订åæç»ä¸åå¨: {lockInfo.OrderDetailId}"); |
| | | } |
| | | |
| | | // è®°å½åå§å¼ï¼ç¨äºæ¥å¿ååæ»æ£æ¥ï¼ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStockStatus = stockDetail.Status; |
| | | |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | decimal originalOverOutQty = orderDetail.OverOutQuantity; |
| | | |
| | | decimal originalLockPickedQty = lockInfo.PickedQty; |
| | | decimal originalLockAssignQty = lockInfo.AssignQuantity; |
| | | int originalLockStatus = lockInfo.Status; |
| | | |
| | | _logger.LogInformation($"ååºåæ°æ®ç¶æ:"); |
| | | _logger.LogInformation($" åºå - æ¡ç : {stockDetail.Barcode}, æ°é: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStockStatus)}"); |
| | | _logger.LogInformation($" 订åæç» - åé
: {originalAllocatedQty}, éå®: {originalLockQty}, å·²åºåº: {originalOverOutQty}"); |
| | | _logger.LogInformation($" éå®è®°å½ - åé
: {originalLockAssignQty}, å·²æ£é: {originalLockPickedQty}, ç¶æ: {GetLockStatusName(originalLockStatus)}"); |
| | | |
| | | // 4. ãæ ¸å¿é»è¾ãæ¢å¤åºåæ°æ® |
| | | // 4.1 å¢å åºåæ°é |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // 4.2 åå°åºåºæ°éï¼ä½éç¡®ä¿ä¸ä¼åºç°è´æ°ï¼ |
| | | if (stockDetail.OutboundQuantity >= returnQty) |
| | | { |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | } |
| | | else |
| | | { |
| | | // 妿åºåºæ°éå°äºååºæ°éï¼è¯´ææ°æ®å¼å¸¸ |
| | | _logger.LogWarning($"åºåºæ°é({stockDetail.OutboundQuantity})å°äºååºæ°é({returnQty})ï¼æ°æ®å¼å¸¸"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // 4.3 æ´æ°åºåç¶æ |
| | | if (stockDetail.OutboundQuantity <= 0 && stockDetail.StockQuantity > 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | _logger.LogInformation($"åºåç¶ææ´æ°ä¸º: å
¥åºå®æ"); |
| | | } |
| | | else if (stockDetail.StockQuantity > 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | _logger.LogInformation($"åºåç¶æä¿æä¸º: åºåºéå®"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"åºåæ´æ°å®æ - æ¡ç : {stockDetail.Barcode}"); |
| | | _logger.LogInformation($" åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | _logger.LogInformation($" åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | _logger.LogInformation($" ç¶æ: {GetStockStatusName(originalStockStatus)} -> {GetStockStatusName(stockDetail.Status)}"); |
| | | |
| | | // 5. æ´æ°éå®è®°å½ç¶æ |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | // ãéè¦ã妿éå®è®°å½æ¯é¨åæ£éåååºï¼éè¦è°æ´å·²æ£éæ°é |
| | | // ä½ä¸è¬æ
åµä¸ï¼ååºçæ¯æªæ£éçé¨åï¼æä»¥PickedQtyä¿æä¸å |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"éå®è®°å½æ´æ°å®æ - ID: {lockInfo.Id}"); |
| | | _logger.LogInformation($" ç¶æ: {GetLockStatusName(originalLockStatus)} -> {GetLockStatusName(lockInfo.Status)}"); |
| | | _logger.LogInformation($" åé
æ°é: {originalLockAssignQty} (ä¸å)"); |
| | | _logger.LogInformation($" å·²æ£éæ°é: {originalLockPickedQty} (ä¸å)"); |
| | | |
| | | // 6. æ´æ°è®¢åæç»æ°æ® |
| | | // 6.1 åå°å·²åé
æ°é |
| | | if (orderDetail.AllocatedQuantity >= returnQty) |
| | | { |
| | | orderDetail.AllocatedQuantity -= returnQty; |
| | | } |
| | | else |
| | | { |
| | | // 妿åé
æ°éå°äºååºæ°éï¼è¯´ææ°æ®å¼å¸¸ |
| | | _logger.LogWarning($"åé
æ°é({orderDetail.AllocatedQuantity})å°äºååºæ°é({returnQty})ï¼é置为0"); |
| | | orderDetail.AllocatedQuantity = 0; |
| | | } |
| | | |
| | | // 6.2 åå°é宿°éï¼åºä¸åé
æ°éä¿æåæ¥ï¼ |
| | | if (orderDetail.LockQuantity >= returnQty) |
| | | { |
| | | orderDetail.LockQuantity -= returnQty; |
| | | } |
| | | else |
| | | { |
| | | _logger.LogWarning($"é宿°é({orderDetail.LockQuantity})å°äºååºæ°é({returnQty})ï¼é置为0"); |
| | | orderDetail.LockQuantity = 0; |
| | | } |
| | | |
| | | // 6.3 å·²åºåºæ°éä¿æä¸åï¼ååºä¸å½±åå·²åºåºæ°éï¼ |
| | | // orderDetail.OverOutQuantity = orderDetail.OverOutQuantity; |
| | | |
| | | // 6.4 æ´æ°æ¹æ¬¡åé
ç¶æ |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"订åæç»æ´æ°å®æ - ID: {orderDetail.Id}"); |
| | | _logger.LogInformation($" åé
æ°é: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}"); |
| | | _logger.LogInformation($" é宿°é: {originalLockQty} -> {orderDetail.LockQuantity}"); |
| | | _logger.LogInformation($" å·²åºåºæ°é: {originalOverOutQty} (ä¿æä¸å)"); |
| | | |
| | | // 7. éªè¯ååºåçæ°æ®ä¸è´æ§ |
| | | await ValidateAfterLockReturn(lockInfo, stockDetail, orderDetail, returnQty); |
| | | |
| | | _logger.LogInformation($"å·²åé
éå®è®°å½ååºå¤ç宿 - éå®ID: {lockInfo.Id}, ååºæ°é: {returnQty}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å¤çå·²åé
éå®è®°å½ååºå¤±è´¥ - éå®ID: {lockInfo.Id}, Error: {ex.Message}"); |
| | | throw new InvalidOperationException($"å¤çéå®è®°å½ååºå¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éå®è®°å½ååºåéªè¯æ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidateAfterLockReturn(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | Dt_OutboundOrderDetail orderDetail, decimal returnQty) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§ååºåæ°æ®éªè¯ - éå®ID: {lockInfo.Id}"); |
| | | |
| | | bool allValid = true; |
| | | List<string> validationErrors = new List<string>(); |
| | | |
| | | // 1. éæ°è·åææ°æ°æ® |
| | | var refreshedStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Id == stockDetail.Id); |
| | | |
| | | var refreshedOrder = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetail.Id); |
| | | |
| | | var refreshedLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == lockInfo.Id); |
| | | |
| | | // 2. éªè¯åºåæ°æ® |
| | | // 计ç®åºåæ°éåºè¯¥å¢å ååºæ°é |
| | | decimal expectedStockQty = stockDetail.StockQuantity + returnQty; |
| | | if (Math.Abs(refreshedStock.StockQuantity - expectedStockQty) > 0.01m) |
| | | { |
| | | string error = $"åºåæ°é䏿£ç¡®ï¼ææ: {expectedStockQty}, å®é
: {refreshedStock.StockQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // éªè¯åºåç¶æåºä¸ºå
¥åºå®ææåºåºéå® |
| | | if (refreshedStock.Status != (int)StockStatusEmun.å
¥åºå®æ && |
| | | refreshedStock.Status != (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | string error = $"åºåç¶æå¼å¸¸ï¼ææ:å
¥åºå®ææåºåºéå®, å®é
:{GetStockStatusName(refreshedStock.Status)}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 3. éªè¯éå®è®°å½ç¶æ |
| | | if (refreshedLock.Status != (int)OutLockStockStatusEnum.å·²ååº) |
| | | { |
| | | string error = $"éå®è®°å½ç¶æå¼å¸¸ï¼ææ:å·²ååº, å®é
:{GetLockStatusName(refreshedLock.Status)}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 4. éªè¯è®¢åæç»æ°æ® |
| | | // 计ç®åé
æ°éåºè¯¥åå°ååºæ°é |
| | | decimal expectedAllocatedQty = Math.Max(0, orderDetail.AllocatedQuantity - returnQty); |
| | | if (Math.Abs(refreshedOrder.AllocatedQuantity - expectedAllocatedQty) > 0.01m) |
| | | { |
| | | string error = $"订ååé
æ°é䏿£ç¡®ï¼ææ: {expectedAllocatedQty}, å®é
: {refreshedOrder.AllocatedQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // åé
æ°éåºä¸é宿°éä¸è´ |
| | | if (Math.Abs(refreshedOrder.AllocatedQuantity - refreshedOrder.LockQuantity) > 0.01m) |
| | | { |
| | | string error = $"订ååé
æ°éä¸é宿°éä¸ä¸è´ï¼åé
: {refreshedOrder.AllocatedQuantity}, éå®: {refreshedOrder.LockQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 5. æ°æ®å
³èæ§éªè¯ |
| | | // éå®è®°å½çå·²æ£éæ°é + å½ååºåçåºåºæ°éåºçäºåå§åºåºæ°é |
| | | decimal totalOutboundFromLock = refreshedLock.PickedQty + refreshedStock.OutboundQuantity; |
| | | |
| | | // æ¥æ¾è¯¥æ¡ç çæææªåæ¶æ£éè®°å½ |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == stockDetail.Barcode && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalPickedFromRecords = pickingRecords.Sum(x => x.PickQuantity); |
| | | |
| | | if (Math.Abs(totalOutboundFromLock - totalPickedFromRecords) > 0.01m) |
| | | { |
| | | string error = $"æ°æ®å
³èæ§å¼å¸¸ï¼é宿£é({refreshedLock.PickedQty})+åºååºåº({refreshedStock.OutboundQuantity})={totalOutboundFromLock}, 使£éè®°å½æ»å={totalPickedFromRecords}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // 6. è¾åºéªè¯ç»æ |
| | | if (allValid) |
| | | { |
| | | _logger.LogInformation($"ååºåæ°æ®éªè¯å
¨é¨éè¿"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogError($"ååºåæ°æ®éªè¯å¤±è´¥ï¼åç°{validationErrors.Count}个é®é¢"); |
| | | foreach (var error in validationErrors.Take(3)) // åªæ¾ç¤ºå3个é®é¢ |
| | | { |
| | | _logger.LogError($"éªè¯é®é¢: {error}"); |
| | | } |
| | | |
| | | // 妿é®é¢ä¸¥éï¼å¯ä»¥æåºå¼å¸¸ |
| | | if (validationErrors.Any(e => e.Contains("å¼å¸¸"))) |
| | | { |
| | | throw new InvalidOperationException($"ååºåæ°æ®éªè¯å¤±è´¥: {string.Join("; ", validationErrors.Take(2))}"); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ååºåéªè¯å¼å¸¸: {ex.Message}"); |
| | | // ä¸éæ°æåºï¼é¿å
å½±å主æµç¨ |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çå个æ¡ç ååºï¼éç¨æ¹æ³ï¼- åºåå·²åé
åæªåé
|
| | | /// </summary> |
| | | private async Task ProcessSingleBarcodeReturn(string barcode, int stockId, decimal returnQty, bool isUnallocated = false) |
| | |
| | | |
| | | _logger.LogInformation($"ååºåç¶æ - åºå: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // ãä¿®å¤ãæ ¹æ®æ¯å¦æªåé
å³å®å¤çé»è¾ |
| | | // æ ¹æ®æ¯å¦æªåé
å³å®å¤çé»è¾ |
| | | if (isUnallocated) |
| | | { |
| | | // æªåé
éå®ï¼åªæ¢å¤ç¶æï¼ä¸æ¹ååºåæ°é |
| | |
| | | return; |
| | | } |
| | | |
| | | // ãä¿®å¤ãè·ååºåæç» |
| | | // è·ååºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | |
| | | _logger.LogInformation($"æªåé
éå®ååºåç¶æ:"); |
| | | _logger.LogInformation($" åºå - æ¡ç : {stockDetail.Barcode}, æ°é: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // ãä¿®å¤å
³é®ãå¯¹äºæªåé
éå®è®°å½ï¼åºåæ°éä¸åºæ¹åï¼ |
| | | // å¯¹äºæªåé
éå®è®°å½ï¼åºåæ°éä¸åºæ¹åï¼ |
| | | // å 为åºåæ¬æ¥å°±åå¨ï¼åªæ¯ç¶æè¢«éå® |
| | | // stockDetail.StockQuantity ä¿æä¸å |
| | | |
| | |
| | | { |
| | | _logger.LogInformation($"å¼å§ååºåæ°æ®éªè¯"); |
| | | |
| | | // 1. éªè¯åºåç¶æåæ°é |
| | | // éªè¯åºåç¶æåæ°é |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .ToListAsync(); |
| | |
| | | _logger.LogError($"åç°è´æ°åºåæ°éï¼æ¡ç : {string.Join(", ", unreasonableStocks.Select(x => x.Barcode))}"); |
| | | } |
| | | |
| | | // 2. éªè¯éå®è®°å½ç¶æ |
| | | // éªè¯éå®è®°å½ç¶æ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | |
| | | } |
| | | } |
| | | |
| | | // 3. æ°æ®ä¸è´æ§éªè¯ |
| | | // æ°æ®ä¸è´æ§éªè¯ |
| | | decimal totalExpectedReturnQty = lockInfos |
| | | .Where(x => x.Status == (int)OutLockStockStatusEnum.å·²ååº) |
| | | .Sum(x => x.AssignQuantity - x.PickedQty); |
| | |
| | | _logger.LogInformation($" å·²ååºéå®è®°å½æ°é: {lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.å·²ååº)}"); |
| | | _logger.LogInformation($" æ»ååºæ°éï¼éå®è®°å½è®¡ç®ï¼: {totalExpectedReturnQty}"); |
| | | |
| | | // 4. éªè¯åºåæ°éä¸éå®è®°å½çä¸è´æ§ |
| | | // éªè¯åºåæ°éä¸éå®è®°å½çä¸è´æ§ |
| | | foreach (var lockInfo in lockInfos.Where(x => !string.IsNullOrEmpty(x.CurrentBarcode))) |
| | | { |
| | | var stock = stockDetails.FirstOrDefault(x => x.Barcode == lockInfo.CurrentBarcode); |
| | |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯ååºååæ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task<bool> ValidateReturnData(string orderNo, string palletCode, int stockId, bool isBefore = true) |
| | | { |
| | | string phase = isBefore ? "ååºå" : "ååºå"; |
| | | try |
| | | { |
| | | |
| | | _logger.LogInformation($"ã{phase}æ°æ®éªè¯ãå¼å§ - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | // 1. æ£æ¥åºåæç» |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalStockQty = stockDetails.Sum(x => x.StockQuantity); |
| | | _logger.LogInformation($"{phase}åºåæ»é: {totalStockQty}"); |
| | | |
| | | // 2. æ£æ¥éå®è®°å½ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | // æ£æ¥éå®è®°å½ç¶æåå¸ |
| | | var statusGroups = lockInfos.GroupBy(x => x.Status) |
| | | .Select(g => new { Status = g.Key, Count = g.Count() }) |
| | | .ToList(); |
| | | |
| | | foreach (var group in statusGroups) |
| | | { |
| | | _logger.LogInformation($"{phase}éå®ç¶æ {GetLockStatusName(group.Status)}: {group.Count} æ¡"); |
| | | } |
| | | |
| | | // 3. åºæ¬éªè¯ |
| | | bool isValid = true; |
| | | |
| | | // éªè¯1: 妿éå®è®°å½ç¶æä¸º"æ£é宿"ï¼å¯¹åºåºååºè¯¥ä¸º0 |
| | | var completedLocks = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿).ToList(); |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) |
| | | { |
| | | var stock = stockDetails.FirstOrDefault(x => x.Barcode == lockInfo.CurrentBarcode); |
| | | if (stock != null && stock.StockQuantity > 0) |
| | | { |
| | | _logger.LogWarning($"{phase}éªè¯è¦å - éå®ID {lockInfo.Id} æ£é宿ä½åºåä¸ä¸º0: {stock.StockQuantity}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // éªè¯2: åºåç¶æä¸è´æ§ |
| | | foreach (var stock in stockDetails) |
| | | { |
| | | if (stock.Status == (int)StockStatusEmun.åºåºéå® && stock.StockQuantity == 0) |
| | | { |
| | | _logger.LogWarning($"{phase}éªè¯è¦å - æ¡ç {stock.Barcode} ç¶æä¸ºåºåºéå®ä½åºå为0"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ã{phase}æ°æ®éªè¯ã宿 - ç¶æ: {(isValid ? "éè¿" : "æè¦å")}"); |
| | | return isValid; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"{phase} æ°æ®éªè¯å¤±è´¥: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | private string GetLockStatusName(int status) |
| | | { |
| | | return status switch |
| | |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ§è¡ååºæ°æ®æä½ - ç®åçæ¬ |
| | | /// </summary> |
| | | private async Task ExecuteReturnDataOperations(PalletStatusAnalysis statusAnalysis) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ§è¡ååºæ°æ®æä½ - 订å: {statusAnalysis.OrderNo}, æç: {statusAnalysis.PalletCode}"); |
| | | |
| | | try |
| | | { |
| | | // ä½¿ç¨ HashSet é¿å
éå¤å¤çæ¡ç |
| | | var processedBarcodes = new HashSet<string>(); |
| | | decimal totalReturnedQty = 0; |
| | | |
| | | // 1. å¤çå·²åé
çæªåæ£éå®è®°å½ |
| | | if (statusAnalysis.HasRemainingLocks) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.RemainingLocks.Count} æ¡å·²åé
æªåæ£éå®è®°å½"); |
| | | |
| | | foreach (var lockInfo in statusAnalysis.RemainingLocks) |
| | | { |
| | | if (string.IsNullOrEmpty(lockInfo.CurrentBarcode) || processedBarcodes.Contains(lockInfo.CurrentBarcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éå¤æç©ºæ¡ç çéå®è®°å½ - ID: {lockInfo.Id}"); |
| | | continue; |
| | | } |
| | | |
| | | // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (returnQty > 0) |
| | | { |
| | | _logger.LogInformation($"å¤çéå®è®°å½ååº - ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // å¤çåºå |
| | | await ProcessStockForReturn(lockInfo.CurrentBarcode, statusAnalysis.StockId, returnQty); |
| | | |
| | | // æ 记为已ååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // åå°è®¢åæç»çåé
æ°é |
| | | if (lockInfo.OrderDetailId > 0) |
| | | { |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == lockInfo.OrderDetailId); |
| | | await ReduceOrderDetailAllocation(orderDetail, returnQty); |
| | | } |
| | | |
| | | processedBarcodes.Add(lockInfo.CurrentBarcode); |
| | | totalReturnedQty += returnQty; |
| | | |
| | | _logger.LogInformation($"éå®è®°å½ååºå®æ - ID: {lockInfo.Id}, ååºæ°é: {returnQty}"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogInformation($"éå®è®°å½æ éååº - ID: {lockInfo.Id}, å·²æ£é宿"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 2. å¤çæªåé
çåºåè´§ç© |
| | | if (statusAnalysis.HasPalletStockGoods) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.PalletStockGoods.Count} 个æªåé
åºåè´§ç©"); |
| | | |
| | | foreach (var stockDetail in statusAnalysis.PalletStockGoods) |
| | | { |
| | | if (string.IsNullOrEmpty(stockDetail.Barcode) || processedBarcodes.Contains(stockDetail.Barcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éå¤æç©ºæ¡ç çåºå - åºåID: {stockDetail.Id}"); |
| | | continue; |
| | | } |
| | | |
| | | if (stockDetail.StockQuantity > 0) |
| | | { |
| | | decimal returnQty = stockDetail.StockQuantity; |
| | | _logger.LogInformation($"å¤çæªåé
åºåååº - æ¡ç : {stockDetail.Barcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // ç´æ¥æ¢å¤åºåç¶æ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | processedBarcodes.Add(stockDetail.Barcode); |
| | | totalReturnedQty += returnQty; |
| | | |
| | | _logger.LogInformation($"æªåé
åºåååºå®æ - æ¡ç : {stockDetail.Barcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 3. å¤çæå
è®°å½ç¸å
³çæ¡ç |
| | | if (statusAnalysis.HasSplitRecords) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.SplitRecords.Count} æ¡æå
è®°å½"); |
| | | |
| | | // æ¶éæå
ç¸å
³çæææ¡ç |
| | | var splitBarcodes = new List<string>(); |
| | | foreach (var splitRecord in statusAnalysis.SplitRecords) |
| | | { |
| | | if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode)) |
| | | splitBarcodes.Add(splitRecord.OriginalBarcode); |
| | | if (!string.IsNullOrEmpty(splitRecord.NewBarcode)) |
| | | splitBarcodes.Add(splitRecord.NewBarcode); |
| | | } |
| | | |
| | | // å»é |
| | | splitBarcodes = splitBarcodes.Distinct().ToList(); |
| | | |
| | | foreach (var barcode in splitBarcodes) |
| | | { |
| | | if (processedBarcodes.Contains(barcode)) |
| | | { |
| | | _logger.LogInformation($"æå
æ¡ç å·²å¤ç: {barcode}"); |
| | | continue; |
| | | } |
| | | |
| | | // æ¥æ¾åºå |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == statusAnalysis.StockId); |
| | | |
| | | if (stockDetail != null && stockDetail.StockQuantity > 0) |
| | | { |
| | | decimal returnQty = stockDetail.StockQuantity; |
| | | _logger.LogInformation($"å¤çæå
ç¸å
³åºåååº - æ¡ç : {barcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | processedBarcodes.Add(barcode); |
| | | totalReturnedQty += returnQty; |
| | | |
| | | _logger.LogInformation($"æå
åºåååºå®æ - æ¡ç : {barcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ååºæ°æ®æä½å®æ - æ»ååºæ°é: {totalReturnedQty}, å¤çæ¡ç æ°: {processedBarcodes.Count}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ååºæ°æ®æä½å¤±è´¥ - 订å: {statusAnalysis.OrderNo}, æç: {statusAnalysis.PalletCode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// å¤çåºåååº - 宿´ä¿®æ£ç |
| | | /// ç¡®ä¿OutboundQuantityæ£ç¡®åå° |
| | | /// </summary> |
| | | private async Task ProcessStockForReturn(string barcode, int stockId, decimal returnQty) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¤çåºåååº - æ¡ç : {barcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°åºåæç» - æ¡ç : {barcode}"); |
| | | return; |
| | | } |
| | | |
| | | // è®°å½åå§å¼ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | _logger.LogInformation($"ååºåç¶æ - åºå: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // ãæ ¸å¿ä¿®æ£ãç¡®ä¿ååºæä½æ£ç¡®å¤ç |
| | | // 1. åºåæ°éå¢å ï¼ååºçè´§ç©åå°åºåï¼ |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // 2. åºåºæ°éåå°ï¼å ä¸ºè´§ç©æ²¡æåºåºï¼èæ¯ååºäºï¼ |
| | | // ä½éè¦ç¡®ä¿ä¸ä¼åºç°è´æ° |
| | | if (stockDetail.OutboundQuantity >= returnQty) |
| | | { |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | } |
| | | else |
| | | { |
| | | // 妿åºåºæ°éå°äºååºæ°éï¼è¯´ææ°æ®å¼å¸¸ |
| | | _logger.LogWarning($"åºåºæ°é({stockDetail.OutboundQuantity})å°äºååºæ°é({returnQty})ï¼éç½®åºåºæ°é为0"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // 3. æ´æ°ç¶æä¸ºå
¥åºå®æ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"åºåååºå®æ - æ¡ç : {barcode}"); |
| | | _logger.LogInformation($" åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | _logger.LogInformation($" åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | _logger.LogInformation($" ç¶æ: {GetStockStatusName(originalStatus)} -> {GetStockStatusName(stockDetail.Status)}"); |
| | | |
| | | // éªè¯ååºåçæ°æ® |
| | | await ValidateStockAfterReturn(barcode, stockId, returnQty); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å¤çåºåååºå¤±è´¥ - æ¡ç : {barcode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯ååºååºåæ°æ® |
| | | /// </summary> |
| | | private async Task ValidateStockAfterReturn(string barcode, int stockId, decimal returnQty) |
| | | { |
| | | try |
| | | { |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId); |
| | | |
| | | // æ£æ¥åºåºæ°éæ¯å¦ä¸º0æåç |
| | | if (stockDetail.OutboundQuantity > stockDetail.StockQuantity) |
| | | { |
| | | _logger.LogWarning($"ååºåæ°æ®å¼å¸¸ - åºåºæ°é({stockDetail.OutboundQuantity})大äºåºåæ°é({stockDetail.StockQuantity})"); |
| | | } |
| | | |
| | | // æ£æ¥ç¶ææ¯å¦æ£ç¡® |
| | | if (stockDetail.Status != (int)StockStatusEmun.å
¥åºå®æ) |
| | | { |
| | | _logger.LogWarning($"ååºåç¶æå¼å¸¸ - ææ:å
¥åºå®æ, å®é
:{GetStockStatusName(stockDetail.Status)}"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"éªè¯ååºåæ°æ®å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 为éå®è®°å½åå°è®¢åæç»çåé
æ°é |
| | | /// </summary> |
| | | private async Task ReduceOrderDetailAllocationForLock(long orderDetailId, decimal reduceQty) |
| | | { |
| | | if (orderDetailId <= 0) |
| | | return; |
| | | |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | decimal originalAllocated = orderDetail.AllocatedQuantity; |
| | | decimal originalLock = orderDetail.LockQuantity; |
| | | |
| | | // éªè¯åå°æ°éä¸ä¼å¯¼è´è´æ° |
| | | if (orderDetail.AllocatedQuantity < reduceQty) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éä¸è¶³ï¼è°æ´åå°æ°é - å计ååå°: {reduceQty}, å®é
å¯ç¨: {orderDetail.AllocatedQuantity}"); |
| | | reduceQty = orderDetail.AllocatedQuantity; |
| | | } |
| | | |
| | | // åå°åé
æ°éåé宿°é |
| | | orderDetail.AllocatedQuantity -= reduceQty; |
| | | orderDetail.LockQuantity -= reduceQty; |
| | | |
| | | // ç¡®ä¿æ°éä¸ä¼ä¸ºè´æ° |
| | | if (orderDetail.AllocatedQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.AllocatedQuantity + reduceQty}, åå°: {reduceQty}"); |
| | | orderDetail.AllocatedQuantity = 0; |
| | | } |
| | | |
| | | if (orderDetail.LockQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"é宿°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.LockQuantity + reduceQty}, åå°: {reduceQty}"); |
| | | orderDetail.LockQuantity = 0; |
| | | } |
| | | |
| | | // æ´æ°æ¹æ¬¡åé
ç¶æ |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"åå°è®¢åæç»åé
- OrderDetailId: {orderDetail.Id}, " + |
| | | $"åé
æ°é: {originalAllocated} -> {orderDetail.AllocatedQuantity}, " + |
| | | $"é宿°é: {originalLock} -> {orderDetail.LockQuantity}, " + |
| | | $"åå°æ°é: {reduceQty}"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæªåé
çéå®è®°å½ååº - ä¿®æ£ç |
| | | /// 注æï¼æªåé
éå®è®°å½æ²¡æç»å®è®¢åæç»ï¼ä¸éè¦åå°è®¢åæç»çåé
æ°é |
| | | /// </summary> |
| | | private async Task HandleUnallocatedLocksReturn(List<Dt_OutStockLockInfo> unallocatedLocks) |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çæªåé
éå®è®°å½ååº - å
± {unallocatedLocks.Count} æ¡è®°å½"); |
| | | |
| | | foreach (var lockInfo in unallocatedLocks) |
| | | { |
| | | // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (returnQty <= 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿æªåé
éå®è®°å½ - éå®ID: {lockInfo.Id}, å·²æ£éå®æææ éååº"); |
| | | continue; |
| | | } |
| | | |
| | | _logger.LogInformation($"å¤çæªåé
éå®è®°å½ååº - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | | // ãä¿®æ£ãæ¢å¤åºåæ°éï¼åºåºæ°éä¿æä¸åï¼å 为æªåé
éå®çåºåºæ°éæ¬æ¥å°±æ¯0ï¼ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // åºåºæ°éä¿æä¸º0ä¸åï¼ |
| | | // stockDetail.OutboundQuantity = stockDetail.OutboundQuantity; // ä¿æä¸å |
| | | |
| | | // æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ¢å¤æªåé
åºå - æ¡ç : {stockDetail.Barcode}, åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | } |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°æªåé
éå®ç¶æ - éå®ID: {lockInfo.Id}, ç¶æ: åºåºä¸ -> å·²ååº"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æªåé
éå®è®°å½ååºå¤ç宿 - å
±å¤ç {unallocatedLocks.Count} æ¡è®°å½"); |
| | | } |
| | | |
| | | private async Task HandleAllocatedLocksReturn(List<Dt_OutStockLockInfo> allocatedLocks) |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çå·²åé
éå®è®°å½ååº - å
± {allocatedLocks.Count} æ¡è®°å½"); |
| | | |
| | | // æè®¢åæç»åç»å¤ç |
| | | var orderDetailGroups = allocatedLocks.GroupBy(x => x.OrderDetailId); |
| | | |
| | | foreach (var group in orderDetailGroups) |
| | | { |
| | | var orderDetailId = group.Key; |
| | | var groupLocks = group.ToList(); |
| | | |
| | | _logger.LogInformation($"å¤ç订åæç» {orderDetailId} ç {groupLocks.Count} æ¡éå®è®°å½"); |
| | | |
| | | // è·å订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°è®¢åæç» - OrderDetailId: {orderDetailId}"); |
| | | continue; |
| | | } |
| | | |
| | | decimal totalReturnQtyForDetail = 0; |
| | | |
| | | foreach (var lockInfo in groupLocks) |
| | | { |
| | | // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (returnQty <= 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éå®è®°å½ - éå®ID: {lockInfo.Id}, å·²æ£éå®æææ éååº"); |
| | | continue; |
| | | } |
| | | |
| | | _logger.LogInformation($"å¤çå·²åé
éå®è®°å½ååº - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | await RestoreStockForLockInfo(lockInfo, returnQty); |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°å·²åé
éå®ç¶æ - éå®ID: {lockInfo.Id}, ç¶æ: åºåºä¸ -> å·²ååº"); |
| | | |
| | | totalReturnQtyForDetail += returnQty; |
| | | } |
| | | |
| | | // åå°è®¢åæç»çåé
æ°é |
| | | if (totalReturnQtyForDetail > 0) |
| | | { |
| | | await ReduceOrderDetailAllocation(orderDetail, totalReturnQtyForDetail); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"å·²åé
éå®è®°å½ååºå¤ç宿 - å
±å¤ç {allocatedLocks.Count} æ¡è®°å½"); |
| | | } |
| | | /// <summary> |
| | | /// æ¢å¤éå®è®°å½å¯¹åºçåºå |
| | | /// </summary> |
| | | private async Task RestoreStockForLockInfo(Dt_OutStockLockInfo lockInfo, decimal returnQty) |
| | | { |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | | // è®°å½æ¢å¤åçåºåç¶æ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | // æ¢å¤åºåæ°éï¼åºåºæ°éåå°ï¼åºåæ°éå¢å |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // ç¡®ä¿æ°éä¸ä¼ä¸ºè´æ° |
| | | if (stockDetail.OutboundQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åºåºæ°éåºç°è´æ°ï¼é置为0ãåå¼: {stockDetail.OutboundQuantity + returnQty}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | _logger.LogInformation($"åºåç¶ææ´æ°ä¸ºå
¥åºå®æ - æ¡ç : {stockDetail.Barcode}"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¢å¤åºåç¶æ - æ¡ç : {stockDetail.Barcode}, " + |
| | | $"åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}, " + |
| | | $"åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°å¯¹åºçåºåä¿¡æ¯ - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// å建ååºä»»å¡ |
| | | /// </summary> |
| | | private async Task CreateReturnTask(string orderNo, string palletCode, Dt_StockInfo stockInfo) |
| | |
| | | _logger.LogInformation($"å建ååºä»»å¡æå - 订å: {orderNo}, æç: {palletCode}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°ååºåç订åç¶æ |
| | | /// </summary> |
| | |
| | | // æ¸
çé¶åºåæ°æ® |
| | | await CleanupZeroStockData(stockInfo.Id); |
| | | |
| | | // è·åå½åä»»å¡ |
| | | var currentTask = await GetCurrentTask(orderNo, palletCode); |
| | | if (currentTask == null) |
| | | { |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å½åä»»å¡"); |
| | | } |
| | | // å建空æçåºåè®°å½ |
| | | var emptyStockInfo = new Dt_StockInfo() |
| | | { |
| | |
| | | emptyStockInfo.Details = new List<Dt_StockInfoDetail>(); |
| | | _stockInfoService.AddMaterielGroup(emptyStockInfo); |
| | | |
| | | // å建空æçååºä»»å¡ |
| | | await CreateReturnTask(orderNo, palletCode, emptyStockInfo); |
| | | // å建ååºä»»å¡ï¼ä¸åéESSå½ä»¤ï¼ |
| | | var returnTaskInfo = await CreateEmptyPalletReturnTask(orderNo, palletCode, emptyStockInfo, currentTask); |
| | | |
| | | return WebResponseContent.Instance.OK("空æçååºæå"); |
| | | return WebResponseContent.Instance.OK("空æçååºæå", returnTaskInfo); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"空æçååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空æçååºå¤±è´¥: {ex.Message}"); |
| | | _logger.LogError($"空æçååºå¤±è´¥ HandleEmptyPalletReturn: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空æçååºå¤±è´¥ HandleEmptyPalletReturn: {ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// åææçç¶æç¨äºååº |
| | | /// ç¡®ä¿ä¸ä¼é误è¯å«éè¦ååºçç©å |
| | | |
| | | private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode) |
| | | { |
| | | // å
å°è¯éè¿è®¢åå·åæçå·æ¥æ¾ä»»å¡ |
| | | var task = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (task == null) |
| | | { |
| | | // 妿æ¾ä¸å°ï¼åéè¿æçå·æ¥æ¾ |
| | | task = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | } |
| | | |
| | | return task; |
| | | } |
| | | |
| | | // <summary> |
| | | /// å建空æçååºä»»å¡ï¼ä¸åéESSå½ä»¤ï¼ |
| | | /// </summary> |
| | | private async Task<ReturnTaskInfo> CreateEmptyPalletReturnTask(string orderNo, string palletCode, Dt_StockInfo emptyStockInfo, Dt_Task currentTask) |
| | | { |
| | | try |
| | | { |
| | | // åé
æ°è´§ä½ |
| | | var newLocation = _locationInfoService.AssignLocation(emptyStockInfo.LocationType); |
| | | |
| | | var returnTask = new Dt_Task() |
| | | { |
| | | CurrentAddress = stations[currentTask.TargetAddress], |
| | | Grade = 0, |
| | | PalletCode = palletCode, |
| | | NextAddress = "", |
| | | OrderNo = orderNo, |
| | | Roadway = newLocation.RoadwayNo, |
| | | SourceAddress = stations[currentTask.TargetAddress], |
| | | TargetAddress = newLocation.LocationCode, |
| | | TaskStatus = TaskStatusEnum.New.ObjToInt(), |
| | | TaskType = TaskTypeEnum.InEmpty.ObjToInt(), |
| | | PalletType = PalletTypeEnum.Empty.ObjToInt(), |
| | | WarehouseId = currentTask.WarehouseId |
| | | }; |
| | | |
| | | await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"å建空æçååºä»»å¡æå: {returnTask.TaskNum}"); |
| | | |
| | | // å é¤åå§åºåºä»»å¡ |
| | | var result = _task_HtyService.DeleteAndMoveIntoHty(currentTask, OperateTypeEnum.人工å é¤); |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | |
| | | if (!result) |
| | | { |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // è¿åä»»å¡ä¿¡æ¯ |
| | | return new ReturnTaskInfo |
| | | { |
| | | ShouldSendESS = true, |
| | | PalletCode = palletCode, |
| | | OriginalTaskTargetAddress = currentTask.TargetAddress, |
| | | ReturnTask = returnTask |
| | | }; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å建空æçååºä»»å¡å¤±è´¥: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åææçç¶æç¨äºååº - ä¿®æ£çï¼è§£å³æå
è®°å½éå¤è®¡ç®é®é¢ï¼ |
| | |
| | | |
| | | try |
| | | { |
| | | // 1. é¦å
è·åæç䏿æçåºåæç»ï¼åºç¡æ°æ®ï¼ |
| | | // é¦å
è·åæç䏿æçåºåæç»ï¼åºç¡æ°æ®ï¼ |
| | | var allStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | _logger.LogInformation($"æ¾å° {allStockDetails.Count} 个æåºåçæç»è®°å½"); |
| | | |
| | | // 2. åæææéå®è®°å½ï¼å·²åé
åæªåé
ï¼ |
| | | // åæææéå®è®°å½ï¼å·²åé
åæªåé
ï¼ |
| | | var allLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | |
| | | result.HasRemainingLocks = true; |
| | | result.RemainingLocks = allocatedLocks; |
| | | |
| | | // ãä¿®å¤ã对äºå·²åé
éå®è®°å½ï¼ååºæ°éæ¯æªæ£éçé¨å |
| | | // 对äºå·²åé
éå®è®°å½ï¼ååºæ°éæ¯æªæ£éçé¨å |
| | | result.RemainingLocksReturnQty = allocatedLocks.Sum(x => |
| | | { |
| | | var returnQty = x.AssignQuantity - x.PickedQty; |
| | |
| | | result.HasUnallocatedLocks = true; |
| | | result.UnallocatedLocks = unallocatedLocks; |
| | | |
| | | // ãä¿®å¤ãå¯¹äºæªåé
éå®è®°å½ï¼ååºæ°éæ¯å®çåé
æ°éï¼å ä¸ºæªæ£éè¿ï¼ |
| | | // å¯¹äºæªåé
éå®è®°å½ï¼ååºæ°éæ¯å®çåé
æ°éï¼å ä¸ºæªæ£éè¿ï¼ |
| | | // ä½å®é
ä¸ï¼åºåæ¬æ¥å°±åå¨ï¼åªæ¯ç¶æéè¦æ¢å¤ |
| | | result.UnallocatedLocksReturnQty = unallocatedLocks.Sum(x => x.AssignQuantity); |
| | | |
| | |
| | | _logger.LogInformation($"åç° {unallocatedLocks.Count} æ¡æªåé
éå®è®°å½ï¼ååºæ°éï¼ç¶ææ¢å¤ï¼: {result.UnallocatedLocksReturnQty}"); |
| | | } |
| | | |
| | | // 3. ãéè¦ä¿®å¤ãéæ°è®¡ç®æ»ååºæ°é |
| | | // éæ°è®¡ç®æ»ååºæ°é |
| | | // 对äºå·²åé
éå®ï¼ååºæ°é = æªæ£éæ°é |
| | | // å¯¹äºæªåé
éå®ï¼æ²¡æå®é
çåºåæ°éååï¼åªæ¯ç¶ææ¢å¤ |
| | | result.TotalReturnQty = result.RemainingLocksReturnQty; // åªè®¡ç®å·²åé
éå®çååºæ°é |
| | |
| | | _logger.LogError($"ååºåæå¤±è´¥ - 订å: {orderNo}, æç: {palletCode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// éªè¯æ²¡æé夿¡ç |
| | | /// </summary> |
| | | private async Task ValidateNoDuplicateBarcodes(PalletStatusAnalysis analysis, List<Dt_StockInfoDetail> allStockDetails) |
| | | { |
| | | try |
| | | { |
| | | // æ£æ¥AllBarcodes䏿¯å¦æéå¤ |
| | | var duplicateBarcodes = analysis.AllBarcodes |
| | | .GroupBy(b => b) |
| | | .Where(g => g.Count() > 1) |
| | | .Select(g => g.Key) |
| | | .ToList(); |
| | | |
| | | if (duplicateBarcodes.Any()) |
| | | { |
| | | _logger.LogError($"åç°é夿¡ç : {string.Join(", ", duplicateBarcodes)}"); |
| | | |
| | | // èªå¨å»é |
| | | analysis.AllBarcodes = analysis.AllBarcodes.Distinct().ToList(); |
| | | _logger.LogWarning($"å·²èªå¨å»éï¼æ¡ç æ°: {analysis.AllBarcodes.Count}"); |
| | | } |
| | | |
| | | // æ£æ¥æ¯ä¸ªæ¡ç çå®é
åºå |
| | | decimal totalStockFromBarcodes = 0; |
| | | foreach (var barcode in analysis.AllBarcodes) |
| | | { |
| | | var stock = allStockDetails.FirstOrDefault(x => x.Barcode == barcode); |
| | | if (stock != null) |
| | | { |
| | | totalStockFromBarcodes += stock.StockQuantity; |
| | | _logger.LogInformation($"æ¡ç åºå - {barcode}: {stock.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ååºåææ»åºå: {analysis.TotalReturnQty}, æ¡ç å®é
æ»åºå: {totalStockFromBarcodes}"); |
| | | |
| | | // 妿åæçæ°é大äºå®é
åºåï¼è¯´ææéå¤è®¡ç® |
| | | if (analysis.TotalReturnQty > totalStockFromBarcodes * 1.1m) // å
许10%ç误差 |
| | | { |
| | | _logger.LogError($"ååºæ°é({analysis.TotalReturnQty})ææ¾å¤§äºå®é
åºå({totalStockFromBarcodes})ï¼å¯è½åå¨éå¤è®¡ç®ï¼"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"éªè¯é夿¡ç 失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// éªè¯åæç»æï¼ç¡®ä¿æ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidateAnalysisResults(PalletStatusAnalysis analysis, int stockId) |
| | | { |
| | | _logger.LogInformation($"å¼å§éªè¯åæç»æ - 订å: {analysis.OrderNo}, æç: {analysis.PalletCode}"); |
| | | |
| | | try |
| | | { |
| | | // 1. éªè¯éå®è®°å½ååºåæç»çå¹é
|
| | | foreach (var lockInfo in analysis.RemainingLocks.Concat(analysis.UnallocatedLocks)) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) |
| | | { |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogWarning($"éå®è®°å½ {lockInfo.Id} çæ¡ç {lockInfo.CurrentBarcode} å¨åºåæç»ä¸ä¸åå¨"); |
| | | } |
| | | else if (stockDetail.StockQuantity <= 0) |
| | | { |
| | | _logger.LogWarning($"éå®è®°å½ {lockInfo.Id} çæ¡ç {lockInfo.CurrentBarcode} åºåæ°é为0æè´æ°"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 2. éªè¯æ»ååºæ°éçåçæ§ |
| | | // è·åæçä¸çæ»åºåæ°é |
| | | var totalStockOnPallet = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .SumAsync(x => x.StockQuantity); |
| | | |
| | | if (analysis.TotalReturnQty > totalStockOnPallet) |
| | | { |
| | | _logger.LogWarning($"æ»ååºæ°é {analysis.TotalReturnQty} å¤§äºæçæ»åºå {totalStockOnPallet}ï¼å¯è½åå¨è®¡ç®é误"); |
| | | } |
| | | |
| | | // 3. éªè¯æ¡ç çå¯ä¸æ§ |
| | | var duplicateBarcodes = analysis.AllBarcodes |
| | | .GroupBy(x => x) |
| | | .Where(g => g.Count() > 1) |
| | | .Select(g => g.Key) |
| | | .ToList(); |
| | | |
| | | if (duplicateBarcodes.Any()) |
| | | { |
| | | _logger.LogWarning($"åç°éå¤çæ¡ç : {string.Join(", ", duplicateBarcodes)}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"åæç»æéªè¯å®æ"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"åæç»æéªè¯å¤±è´¥ - Error: {ex.Message}"); |
| | | // 䏿åºå¼å¸¸ï¼åªè®°å½é误 |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæªåæ£çéå®è®°å½ååº |
| | | /// ç¡®ä¿ä¸ä¼é误ç»å®æ¡ç æ°éå°é宿°é |
| | | /// </summary> |
| | | private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks) |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çæªåæ£éå®è®°å½ååº - å
± {remainingLocks.Count} æ¡è®°å½"); |
| | | |
| | | // æè®¢åæç»åç»å¤çï¼ç¡®ä¿è®¢åæç»æ°æ®çä¸è´æ§ |
| | | var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId); |
| | | |
| | | foreach (var group in orderDetailGroups) |
| | | { |
| | | var orderDetailId = group.Key; |
| | | var groupLocks = group.ToList(); |
| | | |
| | | _logger.LogInformation($"å¤ç订åæç» {orderDetailId} ç {groupLocks.Count} æ¡éå®è®°å½"); |
| | | |
| | | // è·å订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°è®¢åæç» - OrderDetailId: {orderDetailId}"); |
| | | continue; |
| | | } |
| | | |
| | | decimal totalReturnQtyForDetail = 0; |
| | | |
| | | foreach (var lockInfo in groupLocks) |
| | | { |
| | | // åªå¤çç¶æä¸ºåºåºä¸çéå®è®°å½ |
| | | if (lockInfo.Status != (int)OutLockStockStatusEnum.åºåºä¸) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éåºåºä¸ç¶æçéå®è®°å½ - éå®ID: {lockInfo.Id}, ç¶æ: {lockInfo.Status}"); |
| | | continue; |
| | | } |
| | | |
| | | // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (returnQty <= 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éå®è®°å½ - éå®ID: {lockInfo.Id}, å·²æ£éå®æææ éååº"); |
| | | continue; |
| | | } |
| | | |
| | | _logger.LogInformation($"å¤çéå®è®°å½ååº - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | | // è®°å½æ¢å¤åçåºåç¶æ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | // åªæ¢å¤å®é
çåºåæ°éï¼ä¸å建æ°çæ¡ç æç»å® |
| | | // æ¢å¤åºåæ°éï¼åºåºæ°éåå°ï¼åºåæ°éå¢å |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // ç¡®ä¿æ°éä¸ä¼ä¸ºè´æ° |
| | | if (stockDetail.OutboundQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åºåºæ°éåºç°è´æ°ï¼é置为0ãåå¼: {stockDetail.OutboundQuantity + returnQty}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | _logger.LogInformation($"åºåç¶ææ´æ°ä¸ºå
¥åºå®æ - æ¡ç : {stockDetail.Barcode}"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¢å¤åºåç¶æ - æ¡ç : {stockDetail.Barcode}, " + |
| | | $"åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}, " + |
| | | $"åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°å¯¹åºçåºåä¿¡æ¯ - æ¡ç : {lockInfo.CurrentBarcode}, StockId: {lockInfo.StockId}"); |
| | | // éè¦ï¼å¦ææ¾ä¸å°åºåä¿¡æ¯ï¼è·³è¿æ¤éå®è®°å½ï¼é¿å
æ°æ®ä¸ä¸è´ |
| | | continue; |
| | | } |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååºï¼ä½ä¸ä¿®æ¹åé
æ°é |
| | | // åé
æ°éå¨è®¢åæç»å±é¢ç»ä¸å¤ç |
| | | var originalStatus = lockInfo.Status; |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°éå®ç¶æ - éå®ID: {lockInfo.Id}, ç¶æ: {originalStatus} -> {lockInfo.Status}"); |
| | | |
| | | totalReturnQtyForDetail += returnQty; |
| | | |
| | | _logger.LogInformation($"éå®è®°å½ååºå®æ - éå®ID: {lockInfo.Id}, ååºæ°é: {returnQty}"); |
| | | } |
| | | |
| | | // åå°è®¢åæç»çåé
æ°é |
| | | if (totalReturnQtyForDetail > 0) |
| | | { |
| | | await ReduceOrderDetailAllocation(orderDetail, totalReturnQtyForDetail); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"æªåæ£éå®è®°å½ååºå¤ç宿 - å
±å¤ç {remainingLocks.Count} æ¡è®°å½"); |
| | | } |
| | | /// <summary> |
| | | /// å¤çæªåé
çåºåè´§ç©ååº |
| | | /// ç¡®ä¿ä¸ä¼å建æ°çéå®è®°å½ |
| | | /// </summary> |
| | | private async Task HandleUnallocatedStockReturn(List<Dt_StockInfoDetail> stockGoods) |
| | | { |
| | | _logger.LogInformation($"å¼å§å¤çæªåé
åºåååº - å
± {stockGoods.Count} 个货ç©"); |
| | | |
| | | foreach (var stockDetail in stockGoods) |
| | | { |
| | | if (stockDetail.StockQuantity <= 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿é¶åºåè´§ç© - æ¡ç : {stockDetail.Barcode}"); |
| | | continue; |
| | | } |
| | | |
| | | _logger.LogInformation($"å¤çæªåé
åºåååº - æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | |
| | | // æ£æ¥æ¯å¦å·²ç»æå¯¹åºçéå®è®°å½ |
| | | var existingLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == stockDetail.Barcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | |
| | | if (existingLock != null) |
| | | { |
| | | _logger.LogWarning($"åºåæ¡ç {stockDetail.Barcode} å·²æéå®è®°å½ï¼è·³è¿ç´æ¥ååºå¤ç"); |
| | | continue; |
| | | } |
| | | |
| | | // è®°å½æ¢å¤åçç¶æ |
| | | var originalStatus = stockDetail.Status; |
| | | |
| | | // ç´æ¥æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æï¼ä¸å建任ä½éå®è®°å½ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æªåé
åºåååºå®æ - æ¡ç : {stockDetail.Barcode}, ç¶æ: {originalStatus} -> {stockDetail.Status}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æªåé
åºåååºå¤ç宿 - å
±å¤ç {stockGoods.Count} 个货ç©"); |
| | | } |
| | | /// <summary> |
| | | /// åå°è®¢åæç»çåé
æ°é |
| | | /// ç¡®ä¿åé
æ°éçåå°æ¯åç¡®ç |
| | | /// </summary> |
| | | private async Task ReduceOrderDetailAllocation(Dt_OutboundOrderDetail orderDetail, decimal reduceQty) |
| | | { |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | decimal originalAllocated = orderDetail.AllocatedQuantity; |
| | | decimal originalLock = orderDetail.LockQuantity; |
| | | |
| | | // éªè¯åå°æ°éä¸ä¼å¯¼è´è´æ° |
| | | if (orderDetail.AllocatedQuantity < reduceQty) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éä¸è¶³ï¼è°æ´åå°æ°é - å计ååå°: {reduceQty}, å®é
å¯ç¨: {orderDetail.AllocatedQuantity}"); |
| | | reduceQty = orderDetail.AllocatedQuantity; |
| | | } |
| | | |
| | | // åå°åé
æ°éåé宿°é |
| | | orderDetail.AllocatedQuantity -= reduceQty; |
| | | orderDetail.LockQuantity -= reduceQty; |
| | | |
| | | // ç¡®ä¿æ°éä¸ä¼ä¸ºè´æ° |
| | | if (orderDetail.AllocatedQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.AllocatedQuantity + reduceQty}, åå°: {reduceQty}"); |
| | | orderDetail.AllocatedQuantity = 0; |
| | | } |
| | | |
| | | if (orderDetail.LockQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"é宿°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.LockQuantity + reduceQty}, åå°: {reduceQty}"); |
| | | orderDetail.LockQuantity = 0; |
| | | } |
| | | |
| | | // æ´æ°æ¹æ¬¡åé
ç¶æ |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"åå°è®¢åæç»åé
- OrderDetailId: {orderDetail.Id}, " + |
| | | $"åé
æ°é: {originalAllocated} -> {orderDetail.AllocatedQuantity}, " + |
| | | $"é宿°é: {originalLock} -> {orderDetail.LockQuantity}, " + |
| | | $"åå°æ°é: {reduceQty}"); |
| | | |
| | | // éªè¯æ°æ®ä¸è´æ§ |
| | | await ValidateOrderDetailConsistency(orderDetail.Id); |
| | | } |
| | | /// <summary> |
| | | /// éªè¯è®¢åæç»æ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidateOrderDetailConsistency(long orderDetailId) |
| | | { |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | // è®¡ç®ææç¸å
³éå®è®°å½çæ»åé
æ°é |
| | | var relatedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderDetailId == orderDetailId && |
| | | x.Status != (int)OutLockStockStatusEnum.å·²ååº) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalLockAssignQty = relatedLocks.Sum(x => x.AssignQuantity); |
| | | |
| | | // éªè¯è®¢åæç»åé
æ°éä¸éå®è®°å½æ»åé
æ°éçä¸è´æ§ |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - totalLockAssignQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"æ°æ®ä¸ä¸è´è¦å - OrderDetailId: {orderDetailId}, " + |
| | | $"订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, " + |
| | | $"éå®è®°å½æ»åé
æ°é: {totalLockAssignQty}"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogInformation($"æ°æ®ä¸è´æ§éªè¯éè¿ - OrderDetailId: {orderDetailId}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¹ååº - è°ç¨ç»ä¸ååºæ¹æ³ |
| | | /// </summary> |
| | |
| | | return await ExecutePalletReturn(orderNo, palletCode, "åæ¹ååº"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å©ä½ååº - è°ç¨ç»ä¸ååºæ¹æ³ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason) |
| | | { |
| | | return await ExecutePalletReturn(orderNo, palletCode, reason); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// å走空箱 - ä¿®æ£çï¼æ£ç¡®å¤çæªåé
éå®è®°å½ |
| | | /// å走空箱 -æ£ç¡®å¤çæªåé
éå®è®°å½ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode) |
| | | { |
| | |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. å
å°è¯æ§è¡ååºæä½ï¼ç¡®ä¿ææç©åé½ååº |
| | | // å
å°è¯æ§è¡ååºæä½ï¼ç¡®ä¿ææç©åé½ååº |
| | | _logger.LogInformation($"æ¥éª¤1: å
æ§è¡ååºæä½"); |
| | | var returnResult = await ExecutePalletReturn(orderNo, palletCode, "å走空箱åååº"); |
| | | |
| | |
| | | _logger.LogWarning($"ååºæä½å¯è½å¤±è´¥ææ ç©å: {returnResult.Message}"); |
| | | } |
| | | |
| | | // 2. éªè¯ç©ºç®±åèµ°æ¡ä»¶ï¼å¿
é¡»å
¨é¨å®ææ£éæå·²ååºï¼ |
| | | // éªè¯ç©ºç®±åèµ°æ¡ä»¶ï¼å¿
é¡»å
¨é¨å®ææ£éæå·²ååºï¼ |
| | | _logger.LogInformation($"æ¥éª¤2: éªè¯ç©ºç®±åèµ°æ¡ä»¶"); |
| | | |
| | | // è·åæççææéå®è®°å½ï¼å
æ¬å·²ååºåå·²åèµ°çï¼ |
| | |
| | | return WebResponseContent.Instance.Error("该æç没æéå®è®°å½"); |
| | | } |
| | | |
| | | // ãä¿®æ£ãæ£æ¥æ¯å¦ææªå®æçéå®è®°å½ |
| | | // æ£æ¥æ¯å¦ææªå®æçéå®è®°å½ |
| | | var unfinishedLocks = allLockInfos.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); |
| | |
| | | |
| | | _logger.LogInformation($"éªè¯éè¿ï¼æ¾å° {completedLocks.Count} æ¡å·²å®æè®°å½"); |
| | | |
| | | // 3. æ¸
ç已宿çéå®è®°å½ï¼æ 记为已åèµ°ï¼ |
| | | // æ¸
ç已宿çéå®è®°å½ï¼æ 记为已åèµ°ï¼ |
| | | _logger.LogInformation($"æ¥éª¤3: æ¸
çéå®è®°å½"); |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | // 4. æ¸
ç对åºçåºåè®°å½ç¶æ |
| | | // æ¸
ç对åºçåºåè®°å½ç¶æ |
| | | _logger.LogInformation($"æ¥éª¤4: æ¸
çåºåè®°å½"); |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | | await CleanupStockInfo(lockInfo); |
| | | } |
| | | |
| | | // 5. æ´æ°ç¸å
³è®¢åç¶æ |
| | | // æ´æ°ç¸å
³è®¢åç¶æ |
| | | _logger.LogInformation($"æ¥éª¤5: æ´æ°è®¢åç¶æ"); |
| | | await UpdateOrderStatusAfterPalletRemoval(orderNo); |
| | | |
| | | // 6. è®°å½æä½åå² |
| | | _logger.LogInformation($"æ¥éª¤6: è®°å½æä½åå²"); |
| | | await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks); |
| | | // è®°å½æä½åå² |
| | | //_logger.LogInformation($"æ¥éª¤6: è®°å½æä½åå²"); |
| | | //await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | |
| | | return WebResponseContent.Instance.Error($"å走空箱失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ¶ééè¦ååºçæ¡ç |
| | | /// </summary> |
| | | private async Task<List<string>> CollectReturnBarcodes(PalletStatusAnalysis status) |
| | | { |
| | | var returnBarcodes = new HashSet<string>(); |
| | | |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§æ¶éååºæ¡ç - 订å: {status.OrderNo}, æç: {status.PalletCode}"); |
| | | |
| | | // 1. æ¶éæªåæ£éå®è®°å½çæ¡ç |
| | | if (status.HasRemainingLocks) |
| | | { |
| | | foreach (var lockInfo in status.RemainingLocks) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) |
| | | { |
| | | returnBarcodes.Add(lockInfo.CurrentBarcode); |
| | | _logger.LogInformation($"æ·»å éå®è®°å½æ¡ç : {lockInfo.CurrentBarcode}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 2. æ¶éæçä¸åºåè´§ç©çæ¡ç |
| | | if (status.HasPalletStockGoods) |
| | | { |
| | | foreach (var stockDetail in status.PalletStockGoods) |
| | | { |
| | | if (!string.IsNullOrEmpty(stockDetail.Barcode) && stockDetail.StockQuantity > 0) |
| | | { |
| | | returnBarcodes.Add(stockDetail.Barcode); |
| | | _logger.LogInformation($"æ·»å åºåè´§ç©æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 3. æ¶éæå
è®°å½ç¸å
³çæ¡ç |
| | | if (status.HasSplitRecords) |
| | | { |
| | | foreach (var splitRecord in status.SplitRecords) |
| | | { |
| | | // æ·»å åæ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode)) |
| | | { |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == status.StockId); |
| | | |
| | | if (originalStock != null && originalStock.StockQuantity > 0) |
| | | { |
| | | returnBarcodes.Add(splitRecord.OriginalBarcode); |
| | | _logger.LogInformation($"æ·»å æå
åæ¡ç : {splitRecord.OriginalBarcode}, æ°é: {originalStock.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | // æ·»å æ°æ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.NewBarcode)) |
| | | { |
| | | var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.NewBarcode && x.StockId == status.StockId); |
| | | |
| | | if (newStock != null && newStock.StockQuantity > 0) |
| | | { |
| | | returnBarcodes.Add(splitRecord.NewBarcode); |
| | | _logger.LogInformation($"æ·»å æå
æ°æ¡ç : {splitRecord.NewBarcode}, æ°é: {newStock.StockQuantity}"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ååºæ¡ç æ¶é宿 - å
± {returnBarcodes.Count} 个æ¡ç : {string.Join(", ", returnBarcodes)}"); |
| | | |
| | | return returnBarcodes.ToList(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æ¶éååºæ¡ç 失败 - Error: {ex.Message}"); |
| | | return returnBarcodes.ToList(); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region ååºæä½æ ¸å¿æ¹æ³ |
| | | |
| | | /// <summary> |
| | | /// æ§è¡ååºæä½ - å¢å¼ºçæ¬ |
| | | /// </summary> |
| | | private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo, |
| | | Dt_Task task, PalletStatusAnalysis statusAnalysis) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ§è¡ååºæä½ - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | // å¤çæªåæ£çéå®è®°å½ |
| | | if (statusAnalysis.HasRemainingLocks) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.RemainingLocks.Count} æ¡æªåæ£éå®è®°å½"); |
| | | await HandleRemainingLocksReturn(statusAnalysis.RemainingLocks); |
| | | } |
| | | |
| | | //å¤çæçä¸çåºåè´§ç© |
| | | if (statusAnalysis.HasPalletStockGoods) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.PalletStockGoods.Count} 个åºåè´§ç©"); |
| | | await HandlePalletStockGoodsReturn(statusAnalysis.PalletStockGoods, stockInfo.Id); |
| | | } |
| | | |
| | | //å¤çæå
è®°å½ |
| | | if (statusAnalysis.HasSplitRecords) |
| | | { |
| | | _logger.LogInformation($"å¤ç {statusAnalysis.SplitRecords.Count} æ¡æå
è®°å½"); |
| | | await HandleSplitRecordsReturn(statusAnalysis.SplitRecords, stockInfo.Id); |
| | | } |
| | | |
| | | _logger.LogInformation($"ååºæä½å®æ - æ»ååºæ°é: {statusAnalysis.TotalReturnQty}"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæªåæ£çéå®è®°å½ååº |
| | | /// </summary> |
| | | |
| | | |
| | | /// <summary> |
| | | /// å¤çæçä¸çåºåè´§ç©ååº |
| | | /// </summary> |
| | | private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods, int stockId) |
| | | { |
| | | foreach (var stockDetail in palletStockGoods) |
| | | { |
| | | // åªå¤çåºåºéå®ç¶æçåºå |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå® && stockDetail.StockQuantity > 0) |
| | | { |
| | | // æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¢å¤åºåè´§ç© - æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæå
è®°å½ååº |
| | | /// </summary> |
| | | private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, int stockId) |
| | | { |
| | | foreach (var splitRecord in splitRecords) |
| | | { |
| | | // å¤çæ°æ¡ç |
| | | var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == splitRecord.NewBarcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | |
| | | if (newLockInfo != null) |
| | | { |
| | | await HandleSingleLockReturn(newLockInfo); |
| | | } |
| | | |
| | | // å¤çåæ¡ç |
| | | var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == splitRecord.OriginalBarcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | |
| | | if (originalLockInfo != null) |
| | | { |
| | | await HandleSingleLockReturn(originalLockInfo); |
| | | } |
| | | |
| | | // æ´æ°æå
è®°å½ç¶æä¸ºå·²ååº |
| | | splitRecord.Status = (int)SplitPackageStatusEnum.å·²ååº; |
| | | await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | private async Task HandleSingleLockReturn(Dt_OutStockLockInfo lockInfo) |
| | | { |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (returnQty <= 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿éå®è®°å½ - éå®ID: {lockInfo.Id}, å·²æ£éå®æææ éååº"); |
| | | return; |
| | | } |
| | | |
| | | _logger.LogInformation($"å¤çæå
ç¸å
³éå®è®°å½ - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); |
| | | |
| | | // æ¢å¤åºå |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | stockDetail.StockQuantity += returnQty; |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | |
| | | if (stockDetail.OutboundQuantity < 0) |
| | | { |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¢å¤æå
ç¸å
³åºå - æ¡ç : {stockDetail.Barcode}, " + |
| | | $"åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | } |
| | | |
| | | // æ´æ°éå®ç¶æ |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // åå°è®¢åæç»çåé
æ°é |
| | | if (lockInfo.OrderDetailId > 0) |
| | | { |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == lockInfo.OrderDetailId); |
| | | |
| | | if (orderDetail != null) |
| | | { |
| | | await ReduceOrderDetailAllocation(orderDetail, returnQty); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"æå
ç¸å
³éå®è®°å½ååºå®æ - éå®ID: {lockInfo.Id}, ååºæ°é: {returnQty}"); |
| | | } |
| | | /// <summary> |
| | | /// éæ¾ææéå®ä»¥ä¾¿éæ°åé
|
| | | /// </summary> |
| | | private async Task ReleaseAllLocksForReallocation(string orderNo, string palletCode, PalletStatusAnalysis statusAnalysis) |
| | | { |
| | | // æ´æ°è®¢åæç»çå·²åé
æ°é |
| | | if (statusAnalysis.HasRemainingLocks) |
| | | { |
| | | var orderDetailGroups = statusAnalysis.RemainingLocks.GroupBy(x => x.OrderDetailId); |
| | | |
| | | foreach (var group in orderDetailGroups) |
| | | { |
| | | var orderDetailId = group.Key; |
| | | var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail != null) |
| | | { |
| | | orderDetail.AllocatedQuantity -= returnedQty; |
| | | orderDetail.LockQuantity = orderDetail.AllocatedQuantity; |
| | | |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°è®¢åæç» - OrderDetailId: {orderDetailId}, åå°åé
æ°é: {returnedQty}"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ¶ééè¦ååºçæ¡ç ï¼é¿å
éå¤ï¼ |
| | | /// </summary> |
| | | private async Task<HashSet<string>> CollectBarcodesForReturn(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var barcodes = new HashSet<string>(); |
| | | |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§æ¶éååºæ¡ç - 订å: {orderNo}, æç: {palletCode}, StockId: {stockId}"); |
| | | |
| | | // 1. ä»éå®è®°å½æ¶é |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ |
| | | // || x.Status == (int)OutLockStockStatusEnum.åºåºéå®) |
| | | )) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var lockInfo in lockInfos) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) |
| | | { |
| | | barcodes.Add(lockInfo.CurrentBarcode); |
| | | _logger.LogInformation($"ä»éå®è®°å½æ·»å æ¡ç : {lockInfo.CurrentBarcode}"); |
| | | } |
| | | } |
| | | |
| | | // 2. ä»åºåæç»æ¶éï¼ç¶æä¸ºåºåºéå®çï¼ |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && |
| | | x.Status == (int)StockStatusEmun.åºåºéå® && |
| | | x.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var stockDetail in stockDetails) |
| | | { |
| | | if (!barcodes.Contains(stockDetail.Barcode) && !string.IsNullOrEmpty(stockDetail.Barcode)) |
| | | { |
| | | barcodes.Add(stockDetail.Barcode); |
| | | _logger.LogInformation($"ä»åºåæç»æ·»å æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | // 3. 仿å
è®°å½æ¶é |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | !x.IsReverted && |
| | | x.Status != (int)SplitPackageStatusEnum.å·²æ£é) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var splitRecord in splitRecords) |
| | | { |
| | | // æ·»å åæ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode) && !barcodes.Contains(splitRecord.OriginalBarcode)) |
| | | { |
| | | barcodes.Add(splitRecord.OriginalBarcode); |
| | | _logger.LogInformation($"仿å
è®°å½æ·»å åæ¡ç : {splitRecord.OriginalBarcode}"); |
| | | } |
| | | |
| | | // æ·»å æ°æ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.NewBarcode) && !barcodes.Contains(splitRecord.NewBarcode)) |
| | | { |
| | | barcodes.Add(splitRecord.NewBarcode); |
| | | _logger.LogInformation($"仿å
è®°å½æ·»å æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¡ç æ¶é宿 - å
± {barcodes.Count} 个æ¡ç : {string.Join(", ", barcodes)}"); |
| | | |
| | | return barcodes; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æ¶éååºæ¡ç 失败 - Error: {ex.Message}"); |
| | | return barcodes; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç»ä¸å¤çæ¡ç ååºï¼é¿å
éå¤å¤çï¼ |
| | | /// </summary> |
| | | private async Task ProcessBarcodeReturn(string barcode, int stockId, decimal returnQty, HashSet<string> processedBarcodes) |
| | | { |
| | | if (returnQty <= 0) |
| | | return; |
| | | |
| | | // æ£æ¥æ¯å¦å·²å¤çè¿ |
| | | if (processedBarcodes.Contains(barcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²å¤ççæ¡ç : {barcode}"); |
| | | return; |
| | | } |
| | | |
| | | // è·ååºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°æ¡ç 对åºçåºåæç»: {barcode}"); |
| | | return; |
| | | } |
| | | |
| | | // è®°å½åå§å¼ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | _logger.LogInformation($"å¤çæ¡ç ååº - {barcode}: åå§åºå={originalStockQty}, åå§åºåº={originalOutboundQty}, ç¶æ={originalStatus}"); |
| | | |
| | | // éªè¯æ°æ®ä¸è´æ§ |
| | | if (originalOutboundQty < returnQty) |
| | | { |
| | | _logger.LogWarning($"åºåºæ°éå°äºååºæ°éï¼è°æ´ååºæ°é - æ¡ç : {barcode}, åºåºæ°é: {originalOutboundQty}, ååºæ°é: {returnQty}"); |
| | | returnQty = originalOutboundQty; |
| | | } |
| | | |
| | | // æ´æ°åºåï¼åºåºæ°éåå°ï¼åºåæ°éå¢å |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // ç¡®ä¿ä¸ä¼åºç°è´æ° |
| | | if (stockDetail.OutboundQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åºåºæ°éåºç°è´æ°ï¼é置为0 - æ¡ç : {barcode}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // æ´æ°ç¶æ |
| | | if (stockDetail.OutboundQuantity <= 0 && stockDetail.StockQuantity > 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | _logger.LogInformation($"åºåç¶ææ´æ°ä¸ºå
¥åºå®æ - æ¡ç : {barcode}"); |
| | | } |
| | | else if (stockDetail.StockQuantity > 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | _logger.LogInformation($"åºåç¶æä¿æä¸ºåºåºéå® - æ¡ç : {barcode}"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | // æ 记为已å¤ç |
| | | processedBarcodes.Add(barcode); |
| | | |
| | | _logger.LogInformation($"æ¡ç ååºå®æ - {barcode}: " + |
| | | $"åºå {originalStockQty} -> {stockDetail.StockQuantity}, " + |
| | | $"åºåº {originalOutboundQty} -> {stockDetail.OutboundQuantity}, " + |
| | | $"ç¶æ {originalStatus} -> {stockDetail.Status}"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæå
è®°å½ååº - é¿å
éå¤ |
| | | /// </summary> |
| | | private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, int stockId, HashSet<string> processedBarcodes) |
| | | { |
| | | if (!splitRecords.Any()) |
| | | return; |
| | | |
| | | _logger.LogInformation($"å¼å§å¤çæå
è®°å½ååº - å
± {splitRecords.Count} æ¡è®°å½"); |
| | | |
| | | foreach (var splitRecord in splitRecords) |
| | | { |
| | | // åªå¤çæªæ¤éçæå
è®°å½ |
| | | if (splitRecord.IsReverted) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²æ¤éçæå
è®°å½ - ID: {splitRecord.Id}"); |
| | | continue; |
| | | } |
| | | |
| | | // å¤çæ°æ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.NewBarcode) && !processedBarcodes.Contains(splitRecord.NewBarcode)) |
| | | { |
| | | var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.NewBarcode && x.StockId == stockId); |
| | | |
| | | if (newStock != null && newStock.StockQuantity > 0) |
| | | { |
| | | // æå
çæ°æ¡ç ååºæ°éåºè¯¥æ¯å
¶åºåæ°é |
| | | await ProcessBarcodeReturn(splitRecord.NewBarcode, stockId, newStock.StockQuantity, processedBarcodes); |
| | | } |
| | | } |
| | | |
| | | // å¤çåæ¡ç |
| | | if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode) && !processedBarcodes.Contains(splitRecord.OriginalBarcode)) |
| | | { |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == stockId); |
| | | |
| | | if (originalStock != null && originalStock.StockQuantity > 0) |
| | | { |
| | | // åæ¡ç çååºæ°éåºè¯¥æ¯æå
åå©ä½çæ°é |
| | | await ProcessBarcodeReturn(splitRecord.OriginalBarcode, stockId, originalStock.StockQuantity, processedBarcodes); |
| | | } |
| | | } |
| | | |
| | | // æ´æ°æå
è®°å½ç¶æä¸ºå·²ååº |
| | | splitRecord.Status = (int)SplitPackageStatusEnum.å·²ååº; |
| | | await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æå
è®°å½ç¶ææ´æ°ä¸ºå·²ååº - è®°å½ID: {splitRecord.Id}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æå
è®°å½ååºå¤ç宿"); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç®åçååºæ¹æ³ - ç»è¿å¤æéªè¯ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> SimplePalletReturn(string orderNo, string palletCode, string returnReason = "ç®åååº") |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"ãç®åååºå¼å§ã订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. è·ååºåä¿¡æ¯ï¼è·³è¿å¤æéªè¯ï¼ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() |
| | | .FirstAsync(x => x.PalletCode == palletCode); |
| | | |
| | | if (stockInfo == null) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°æç {palletCode} 对åºçåºåä¿¡æ¯"); |
| | | } |
| | | |
| | | // 2. ç´æ¥æ¥æ¾éè¦ååºçæ¡ç ï¼ç®åé»è¾ï¼ |
| | | var barcodesToReturn = await GetBarcodesForSimpleReturn(orderNo, palletCode, stockInfo.Id); |
| | | |
| | | if (!barcodesToReturn.Any()) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"ãæ ååºç©åãå¤ç空æç"); |
| | | var result = await HandleEmptyPalletReturn(orderNo, palletCode, stockInfo); |
| | | _unitOfWorkManage.CommitTran(); |
| | | return result; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"空箱ååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空箱ååºå¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | // 3. ç®åå¤çæ¯ä¸ªæ¡ç |
| | | foreach (var barcode in barcodesToReturn) |
| | | { |
| | | await ProcessSimpleBarcodeReturn(barcode, stockInfo.Id); |
| | | } |
| | | |
| | | // 4. æ´æ°è®¢åç¶æï¼ç®åï¼ |
| | | await UpdateOrderStatusAfterReturn(orderNo); |
| | | |
| | | |
| | | // 5. å建ååºä»»å¡ |
| | | await CreateReturnTask(orderNo, palletCode, stockInfo); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | return WebResponseContent.Instance.OK($"ç®åååºæåï¼å¤ç {barcodesToReturn.Count} 个æ¡ç "); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"ç®åååºå¤±è´¥: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"ååºå¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// ç®åè·åååºæ¡ç |
| | | /// </summary> |
| | | private async Task<List<string>> GetBarcodesForSimpleReturn(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var barcodes = new List<string>(); |
| | | |
| | | try |
| | | { |
| | | // 1. ä»éå®è®°å½è·å |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .Select(x => x.CurrentBarcode) |
| | | .ToListAsync(); |
| | | |
| | | barcodes.AddRange(lockInfos.Where(b => !string.IsNullOrEmpty(b))); |
| | | |
| | | // 2. ä»åºåæç»è·å |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity > 0) |
| | | .Select(x => x.Barcode) |
| | | .ToListAsync(); |
| | | |
| | | barcodes.AddRange(stockDetails.Where(b => !string.IsNullOrEmpty(b))); |
| | | |
| | | // å»é |
| | | return barcodes.Distinct().ToList(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"è·åååºæ¡ç 失败: {ex.Message}"); |
| | | return barcodes; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç®åå¤çæ¡ç ååº |
| | | /// </summary> |
| | | private async Task ProcessSimpleBarcodeReturn(string barcode, int stockId) |
| | | { |
| | | try |
| | | { |
| | | // 1. è·ååºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == stockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°æ¡ç 对åºçåºåæç»: {barcode}"); |
| | | return; |
| | | } |
| | | |
| | | // 2. 妿æ¯åºåºéå®ç¶æï¼æ¢å¤ä¸ºå
¥åºå®æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå®) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ¡ç ç¶ææ¢å¤ - {barcode}: åºåºéå® -> å
¥åºå®æ"); |
| | | } |
| | | |
| | | // 3. æ´æ°ç¸å
³çéå®è®°å½ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == barcode && |
| | | (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.æ£é宿)) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var lockInfo in lockInfos) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"éå®è®°å½ç¶ææ´æ° - ID: {lockInfo.Id}: å·²ååº"); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å¤çæ¡ç ååºå¤±è´¥ - æ¡ç : {barcode}, Error: {ex.Message}"); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | #region è¾
婿¹æ³ |
| | | |
| | | /// <summary> |
| | | /// å¤ç没æååºç©åçæ
åµ |
| | | /// </summary> |
| | | private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockId) |
| | | { |
| | | _logger.LogInformation($"æç {palletCode} 没æéè¦ååºçç©å"); |
| | | |
| | | // æ£æ¥æ¯å¦æ¯ç©ºæç |
| | | var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockId); |
| | | if (statusAnalysis.IsEmptyPallet) |
| | | { |
| | | try |
| | | { |
| | | 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; |
| | | _stockInfoService.DeleteData(stockInfo); |
| | | } |
| | | |
| | | var targetAddress = originalTask.TargetAddress; |
| | | |
| | | await CleanupZeroStockData(stockId); |
| | | |
| | | |
| | | 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, PalletTypeEnum.Empty.ObjToInt()); |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($" HandleNoReturnItems 失败: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($" ååºç©ºæç失败ï¼"); |
| | | } |
| | | return WebResponseContent.Instance.OK("空æçååºä»»å¡å建æå"); |
| | | } |
| | | else |
| | | { |
| | | return WebResponseContent.Instance.Error("æçç¶æå¼å¸¸ï¼æç©å使 æ³è®¡ç®ååºæ°é"); |
| | | } |
| | | } |
| | | #region è¾
婿¹æ³ |
| | | |
| | | private async Task CleanupZeroStockData(int stockId) |
| | | { |
| | | try |
| | | { |
| | | // 1. å é¤åºåæ°é为0çæç»è®°å½ |
| | | // å é¤åºåæ°é为0çæç»è®°å½ |
| | | var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity == 0) |
| | | .ExecuteCommandAsync(); |
| | |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ¸
çé¶åºåæ°æ®å¤±è´¥ - StockId: {stockId}, Error: {ex.Message}"); |
| | | // 注æï¼æ¸
ç失败ä¸åºè¯¥å½±å主æµç¨ |
| | | |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// æ´æ°è®¢åç¶æï¼ååºåï¼ |
| | | /// </summary> |
| | | private async Task UpdateOrderStatusForReturn(string orderNo) |
| | | { |
| | | // æ£æ¥è®¢åæ¯å¦æææçé½å·²å®ææå·²ååº |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ToListAsync(); |
| | | |
| | | var activeLocks = allLocks.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); |
| | | |
| | | // å¦ææ²¡ææ´»è·çéå®è®°å½ï¼æ´æ°è®¢åç¶æ |
| | | if (!activeLocks.Any()) |
| | | { |
| | | await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() |
| | | .SetColumns(x => new Dt_OutboundOrder |
| | | { |
| | | OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ, |
| | | }) |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°è®¢åç¶æä¸ºåºåºå®æ - 订å: {orderNo}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | #endregion |
| | | |
| | | #region éªè¯æ¹æ³ |
| | |
| | | _logger.LogInformation($"éè¦èªå¨æå
- åºå: {stockDetail.StockQuantity}, åé
: {lockInfo.AssignQuantity}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | // æ§è¡èªå¨æå
|
| | | var splitResult = await ExecuteAutoSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); |
| | | |
| | | // å°æå
æ°éä¼ éç»è°ç¨æ¹ï¼ç¨äºéªè¯ |
| | | if (splitResult != null && splitResult.Any()) |
| | | { |
| | | // å¨è¿åç»æä¸æºå¸¦æå
æ°éä¿¡æ¯ |
| | | foreach (var result in splitResult) |
| | | { |
| | | result.quantityTotal = splitQuantity.ToString("F2"); |
| | | } |
| | | } |
| | | var splitResult = await ExecuteAutoSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); |
| | | |
| | | return splitResult; |
| | | } |
| | |
| | | /// ååï¼åªå离ç©çåºåï¼ä¸æ¹åå订åçä»»ä½åé
ååºåºæ°é |
| | | /// </summary> |
| | | private async Task<List<SplitResult>> ExecuteAutoSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | | decimal splitQuantity,string palletCode) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ§è¡èªå¨æå
é»è¾ - åæ¡ç : {stockDetail.Barcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | try |
| | | { |
| | | // 1. éªè¯æå
æ°éåçæ§ |
| | | // éªè¯æå
æ°éåçæ§ |
| | | if (splitQuantity <= 0) |
| | | throw new InvalidOperationException($"æå
æ°éå¿
须大äº0ï¼å½åå¼: {splitQuantity}"); |
| | | |
| | | if (stockDetail.StockQuantity < lockInfo.AssignQuantity + splitQuantity) |
| | | throw new InvalidOperationException($"åºåæ°éä¸è¶³ä»¥è¿è¡èªå¨æå
ï¼åºå: {stockDetail.StockQuantity}, éè¦: {lockInfo.AssignQuantity + splitQuantity}"); |
| | | |
| | | // 2. çææ°æ¡ç |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | | _logger.LogInformation($"çææ°æ¡ç : {newBarcode}"); |
| | | |
| | | // 3. ãæ ¸å¿ä¿®æ£ãæ´æ°ååºåæç»ï¼åªåå°ç©çåºåï¼ä¸å½±ååºåºæ°é |
| | | // ãæ ¸å¿ä¿®æ£ãæ´æ°ååºåæç»ï¼åªåå°ç©çåºåï¼ä¸å½±ååºåºæ°é |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | stockDetail.StockQuantity -= splitQuantity; // ä»
åºååå° |
| | | // stockDetail.OutboundQuantity ä¿æä¸åï¼ |
| | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ´æ°ååºåæç»ï¼æ¡ç {stockDetail.Barcode} åºå {originalStockQty} -> {stockDetail.StockQuantity}ï¼åºåºæ°éä¸å({stockDetail.OutboundQuantity})"); |
| | | |
| | | // 4. å建æ°åºåæç»ï¼å¤ä½é¨åï¼- åºåºæ°é为0 |
| | | // å建æ°åºåæç»ï¼å¤ä½é¨åï¼- åºåºæ°é为0 |
| | | var newStockDetail = new Dt_StockInfoDetail |
| | | { |
| | | StockId = stockDetail.StockId, |
| | |
| | | OrderNo = stockDetail.OrderNo, |
| | | BatchNo = stockDetail.BatchNo, |
| | | StockQuantity = splitQuantity, // æ°åºåæ°é |
| | | OutboundQuantity = 0, // ãéç¹ãåå§åºåºæ°é为0 |
| | | OutboundQuantity = 0, // åå§åºåºæ°é为0 |
| | | Barcode = newBarcode, |
| | | Status = (int)StockStatusEmun.åºåºéå®, // ä»ä¸ºéå®ç¶æï¼ä½æªç»å®è®¢å |
| | | SupplyCode = stockDetail.SupplyCode, |
| | |
| | | BarcodeUnit = stockDetail.BarcodeUnit, |
| | | BusinessType = stockDetail.BusinessType, |
| | | InboundOrderRowNo = stockDetail.InboundOrderRowNo, |
| | | FactoryArea=stockDetail.FactoryArea, |
| | | WarehouseCode = stockDetail.WarehouseCode, |
| | | }; |
| | | |
| | | await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æ°åºåæç»ï¼æ¡ç {newBarcode}ï¼åºå {splitQuantity}ï¼åºåº 0"); |
| | | |
| | | // 5. å建æ°éå®ä¿¡æ¯ - æ 记为æªåé
|
| | | // å建æ°éå®ä¿¡æ¯ - æ 记为æªåé
|
| | | var newLockInfo = new Dt_OutStockLockInfo |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | OrderDetailId = 0, // ãéç¹ãä¸ç»å®å°å
·ä½è®¢åæç»ï¼è¡¨ç¤ºæªåé
|
| | | OrderDetailId = 0, // ä¸ç»å®å°å
·ä½è®¢åæç»ï¼è¡¨ç¤ºæªåé
|
| | | OutboundBatchNo = lockInfo.OutboundBatchNo, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | MaterielName = lockInfo.MaterielName, |
| | |
| | | WarehouseCode = lockInfo.WarehouseCode, |
| | | BarcodeQty = lockInfo.BarcodeQty, |
| | | BarcodeUnit = lockInfo.BarcodeUnit, |
| | | IsUnallocated = 1 // ãéç¹ãæç¡®æ 记为"æªåé
"çéå®è®°å½ |
| | | IsUnallocated = 1 // æç¡®æ 记为"æªåé
"çéå®è®°å½ |
| | | }; |
| | | |
| | | await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æªåé
éå®è®°å½ï¼ID {newLockInfo.Id}ï¼æ¡ç {newBarcode}ï¼æ°é {splitQuantity}"); |
| | | |
| | | // 6. ãå
³é®ä¿®æ£ãåéå®è®°å½åå订åæç»æ°æ®å®å
¨ä¿æä¸åï¼ |
| | | //åéå®è®°å½åå订åæç»æ°æ®å®å
¨ä¿æä¸åï¼ |
| | | // - ä¸ä¿®æ¹ lockInfo çä»»ä½å段 |
| | | // - ä¸ä¿®æ¹å
³èç Dt_OutboundOrderDetail ç AllocatedQuantity å LockQuantity |
| | | |
| | |
| | | await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, true, originalStockQty); |
| | | |
| | | // å建æå
ç»æå表 |
| | | var splitResults = CreateSplitResults(lockInfo, splitQuantity, lockInfo.AssignQuantity, newBarcode, stockDetail.Barcode); |
| | | var splitResults = CreateSplitResults(lockInfo, splitQuantity, stockDetail.StockQuantity, newBarcode, stockDetail.Barcode); |
| | | |
| | | _logger.LogInformation($"èªå¨æå
é»è¾æ§è¡å®æ - åå»ºäºæªåé
çåºååéå®è®°å½"); |
| | | |
| | |
| | | _logger.LogError($"èªå¨æå
é»è¾æ§è¡å¤±è´¥ - åæ¡ç : {stockDetail.Barcode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯èªå¨æå
åæ°æ®ä¸è´æ§ - ä¿®æ£ç |
| | | /// ååï¼éªè¯æªåé
éå®è®°å½çå建ï¼è䏿¯è®¢åæç»æ°éçåå |
| | | /// </summary> |
| | | private async Task ValidateDataConsistencyAfterAutoSplit(long orderDetailId, decimal originalAllocatedQty, decimal originalLockQty, decimal splitQuantity) |
| | | { |
| | | // éæ°è·å订åæç»æ°æ® |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | // ãä¿®æ£ãèªå¨æå
åï¼è®¢åæç»åé
æ°éåºè¯¥ä¿æä¸åï¼ |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - originalAllocatedQty) > 0.01m) |
| | | { |
| | | _logger.LogError($"èªå¨æå
å订åæç»åé
æ°éå¼å¸¸ååï¼ææä¿æä¸å: {originalAllocatedQty}, å®é
: {orderDetail.AllocatedQuantity}"); |
| | | // è®°å½ä¸¥éé误ï¼ä½ä¸æåºå¼å¸¸ï¼ç产ç¯å¢å¯è½éè¦åè¦ï¼ |
| | | } |
| | | |
| | | if (Math.Abs(orderDetail.LockQuantity - originalLockQty) > 0.01m) |
| | | { |
| | | _logger.LogError($"èªå¨æå
å订åæç»é宿°éå¼å¸¸ååï¼ææä¿æä¸å: {originalLockQty}, å®é
: {orderDetail.LockQuantity}"); |
| | | } |
| | | |
| | | // éªè¯æªåé
éå®è®°å½çå建 |
| | | // æ¥æ¾ç¶éå®è®°å½ |
| | | var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.OrderDetailId == orderDetailId); |
| | | |
| | | if (parentLockInfo != null) |
| | | { |
| | | // æ¥æ¾æªåé
çåéå®è®°å½ï¼èªå¨æå
çæçï¼ |
| | | var unallocatedChildLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.ParentLockId == parentLockInfo.Id && |
| | | x.IsUnallocated == 1 && |
| | | x.OrderDetailId == 0) |
| | | .ToListAsync(); |
| | | |
| | | if (unallocatedChildLocks.Any()) |
| | | { |
| | | decimal totalUnallocatedQty = unallocatedChildLocks.Sum(x => x.AssignQuantity); |
| | | _logger.LogInformation($"éªè¯éè¿ï¼å建äº{unallocatedChildLocks.Count}æ¡æªåé
éå®è®°å½ï¼æ»æ°é: {totalUnallocatedQty}"); |
| | | |
| | | if (Math.Abs(totalUnallocatedQty - splitQuantity) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"æªåé
éå®è®°å½æ»æ°é䏿å
æ°éä¸å¹é
ï¼æå
æ°é: {splitQuantity}, æªåé
æ»æ°: {totalUnallocatedQty}"); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°èªå¨æå
çæçæªåé
éå®è®°å½"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"èªå¨æå
æ°æ®ä¸è´æ§éªè¯å®æ"); |
| | | } |
| | | |
| | | #endregion |
| | |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | // ãæ ¸å¿ä¿®æ£ãç¡®ä¿OutboundQuantityåªå¢å æ¬æ¬¡æ£éæ°éï¼ä¸å
å«å
¶ä» |
| | | // ç¡®ä¿OutboundQuantityåªå¢å æ¬æ¬¡æ£éæ°éï¼ä¸å
å«å
¶ä» |
| | | stockDetail.StockQuantity -= actualPickedQty; |
| | | stockDetail.OutboundQuantity += actualPickedQty; // åªå¢å æ¬æ¬¡æ£éæ°é |
| | | |
| | |
| | | { |
| | | _logger.LogInformation($"éªè¯æ£éæ°æ®ä¸è´æ§ - æ¡ç : {stockDetail.Barcode}"); |
| | | |
| | | // 1. éªè¯åºåæç»çOutboundQuantityå¢å éçäºæ£éæ°é |
| | | // éªè¯åºåæç»çOutboundQuantityå¢å éçäºæ£éæ°é |
| | | var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Id == stockDetail.Id); |
| | | |
| | |
| | | if (Math.Abs(outboundIncrease - pickedQty) > 0.01m) |
| | | { |
| | | _logger.LogError($"æ£éæ°æ®ä¸ä¸è´ï¼åºåºæ°éå¢å {outboundIncrease}ï¼ä½æ£éæ°éæ¯ {pickedQty}"); |
| | | // ä¿®å¤ï¼ç¡®ä¿OutboundQuantityæ£ç¡® |
| | | // ç¡®ä¿OutboundQuantityæ£ç¡® |
| | | refreshedStockDetail.OutboundQuantity = stockDetail.OutboundQuantity + pickedQty; |
| | | await _stockInfoDetailService.Db.Updateable(refreshedStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogWarning($"已修å¤åºåºæ°éï¼{stockDetail.OutboundQuantity} -> {refreshedStockDetail.OutboundQuantity}"); |
| | | } |
| | | |
| | | // 2. éªè¯éå®è®°å½çå·²æ£éæ°é |
| | | // éªè¯éå®è®°å½çå·²æ£éæ°é |
| | | var refreshedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == lockInfo.Id); |
| | | |
| | |
| | | { |
| | | _logger.LogInformation($"å¼å§æ¢å¤æ£éæ°æ® - æ£éè®°å½ID: {pickingRecord.Id}, æ¡ç : {pickingRecord.Barcode}, æ£éæ°é: {pickingRecord.PickQuantity}"); |
| | | |
| | | // 1. æ¢å¤éå®ä¿¡æ¯ |
| | | // æ¢å¤éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == pickingRecord.OutStockLockId); |
| | | |
| | |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // 2. æ¢å¤åºåä¿¡æ¯ |
| | | // æ¢å¤åºåä¿¡æ¯ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == pickingRecord.Barcode); |
| | | |
| | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // 3. éæ°æ£æ¥è®¢åç¶æ |
| | | // éæ°æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(pickingRecord.OrderNo); |
| | | |
| | | _logger.LogInformation($"æ¢å¤æ¹æ¬¡åè®¢åæ°æ®å®æ"); |
| | |
| | | orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.New.ObjToInt(); |
| | | } |
| | | } |
| | | private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo) |
| | | { |
| | | // æ¢å¤åºåç¶æ - ååºååºåå为å¯ç¨ç¶æ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | | // ååºååºåç¶ææ¢å¤ä¸ºå
¥åºå®æï¼å¯ç¨ç¶æï¼ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°æ¹æ¬¡ç¶æï¼ååºï¼ |
| | | /// </summary> |
| | | private async Task UpdateBatchStatusForReturn(string outboundBatchNo, List<Dt_OutStockLockInfo> returnedLocks) |
| | | { |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == outboundBatchNo); |
| | | |
| | | if (batch != null) |
| | | { |
| | | // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ |
| | | var returnedQty = returnedLocks.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | batch.CompletedQuantity -= returnedQty; |
| | | |
| | | // æ´æ°æ¹æ¬¡ç¶æ |
| | | if (batch.CompletedQuantity <= 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.å·²ååº; |
| | | } |
| | | else if (batch.CompletedQuantity < batch.BatchQuantity) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | } |
| | | else |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | } |
| | | |
| | | batch.Operator = App.User.UserName; |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°è®¢åæç»ï¼ååºåï¼ |
| | | /// </summary> |
| | | private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks) |
| | | { |
| | | var orderDetailGroups = returnedLocks.GroupBy(x => x.OrderDetailId); |
| | | |
| | | foreach (var group in orderDetailGroups) |
| | | { |
| | | var orderDetailId = group.Key; |
| | | var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail != null) |
| | | { |
| | | orderDetail.AllocatedQuantity -= returnedQty; |
| | | // LockQuantity 忥åå°ï¼ä¿æä¸å·²åé
æ°éä¸è´ |
| | | orderDetail.LockQuantity = orderDetail.AllocatedQuantity; |
| | | |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum, int palletType) |
| | | { |
| | | var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>() |
| | | .FirstAsync(x => x.LocationCode == originalTask.SourceAddress); |
| | | |
| | | // åé
æ°è´§ä½ |
| | | var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType); |
| | | |
| | | Dt_Task returnTask = new() |
| | | { |
| | | CurrentAddress = stations[originalTask.TargetAddress], |
| | | Grade = 0, |
| | | PalletCode = palletCode, |
| | | NextAddress = "", |
| | | // OrderNo = originalTask.OrderNo, |
| | | OrderNo = orderNo, |
| | | Roadway = newLocation.RoadwayNo, |
| | | SourceAddress = stations[originalTask.TargetAddress], |
| | | TargetAddress = newLocation.LocationCode, |
| | | TaskStatus = TaskStatusEnum.New.ObjToInt(), |
| | | TaskType = taskTypeEnum.ObjToInt(), |
| | | PalletType = palletType, |
| | | WarehouseId = originalTask.WarehouseId |
| | | |
| | | }; |
| | | // ä¿åååºä»»å¡ |
| | | var insertcount = await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); |
| | | if (insertcount <= 0) |
| | | { |
| | | throw new Exception("å建任å¡å¤±è´¥ï¼"); |
| | | } |
| | | var targetAddress = originalTask.TargetAddress; |
| | | |
| | | _logger.LogInformation($"CreateReturnTaskAndHandleESS åæ¹å é¤åå²ä»»å¡: {orderNo} ï¼ {originalTask.TaskNum}"); |
| | | // å é¤åå§åºåºä»»å¡ |
| | | //_taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.èªå¨å®æ); |
| | | var result = _task_HtyService.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.人工å é¤); |
| | | await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | |
| | | if (!result) |
| | | { |
| | | await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); |
| | | } |
| | | _logger.LogInformation($"CreateReturnTaskAndHandleESS åæ¹å é¤åå²ä»»å¡: {orderNo} ï¼ {originalTask.TaskNum},å½±åè¡ {result}"); |
| | | |
| | | |
| | | // ç» ESS åéæµå¨ä¿¡å·ååå»ºä»»å¡ |
| | | await SendESSCommands(palletCode, targetAddress, returnTask); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç»ESSä¸ä»»å¡ |
| | | /// </summary> |
| | |
| | | { |
| | | try |
| | | { |
| | | // 1. åéæµå¨ä¿¡å· |
| | | // åéæµå¨ä¿¡å· |
| | | var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest |
| | | { |
| | | slotCode = movestations[targetAddress], |
| | | containerCode = palletCode |
| | | }); |
| | | |
| | | //if (moveResult) |
| | | //{ |
| | | // 2. å建ååºä»»å¡ |
| | | |
| | | var essTask = new TaskModel() |
| | | { |
| | | taskType = "putaway", |
| | |
| | | |
| | | var resultTask = await _eSSApiService.CreateTaskAsync(essTask); |
| | | _logger.LogInformation($"ReturnRemaining åå»ºä»»å¡æå: {resultTask}"); |
| | | //} |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | |
| | | { |
| | | var splitHistory = new Dt_SplitPackageRecord |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | FactoryArea = lockInfo.FactoryArea, |
| | | TaskNum = lockInfo.TaskNum, |
| | | OutStockLockInfoId = lockInfo.Id, |
| | | StockId = stockDetail.StockId, |
| | | Operator = App.User.UserName, |
| | | IsReverted = false, |
| | | OriginalBarcode = stockDetail.Barcode, |
| | | NewBarcode = newBarcode, |
| | | SplitQty = splitQty, |
| | | // RemainQuantity = remainQty, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | SplitTime = DateTime.Now, |
| | | OrderNo = lockInfo.OrderNo, |
| | | PalletCode = lockInfo.PalletCode, |
| | | |
| | | |
| | | |
| | | Status = (int)SplitPackageStatusEnum.å·²æå
, |
| | | IsAutoSplit = isAutoSplit, |
| | | // SplitType = isAutoSplit ? "èªå¨æå
" : "æå¨æå
" |
| | | OriginalStockQuantity = originalStockQuantity ?? stockDetail.StockQuantity, |
| | | //RemainingStockQuantity = stockDetail.StockQuantity - splitQty |
| | | TaskNum = lockInfo.TaskNum |
| | | |
| | | }; |
| | | |
| | | await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync(); |
| | |
| | | #endregion |
| | | |
| | | #region DTOç±» |
| | | |
| | | /// <summary> |
| | | /// æ¡ç ç¶æä¿¡æ¯DTO |
| | | /// ååºä»»å¡ä¿¡æ¯ç±» |
| | | /// </summary> |
| | | public class BarcodeStatusInfoDto |
| | | public class ReturnTaskInfo |
| | | { |
| | | public string Barcode { get; set; } |
| | | public string OrderNo { get; set; } |
| | | public bool IsOriginalBarcode { get; set; } |
| | | public int SplitChainCount { get; set; } |
| | | public bool HasBeenPicked { get; set; } |
| | | public decimal TotalPickedQuantity { get; set; } |
| | | public int PickRecordCount { get; set; } |
| | | public int LockInfoStatus { get; set; } |
| | | public decimal LockInfoPickedQty { get; set; } |
| | | public decimal LockInfoAssignQty { get; set; } |
| | | public decimal StockQuantity { get; set; } |
| | | public int StockStatus { get; set; } |
| | | public bool CanCancelSplit { get; set; } |
| | | public bool NeedCancelPickFirst { get; set; } |
| | | public List<string> OperationSuggestions { get; set; } = new List<string>(); |
| | | /// <summary> |
| | | /// æ¯å¦éè¦åéESSå½ä»¤ |
| | | /// </summary> |
| | | public bool ShouldSendESS { get; set; } |
| | | |
| | | /// <summary> |
| | | /// æçç |
| | | /// </summary> |
| | | public string PalletCode { get; set; } |
| | | |
| | | /// <summary> |
| | | /// åå§ä»»å¡çç®æ å°å |
| | | /// </summary> |
| | | public string OriginalTaskTargetAddress { get; set; } |
| | | |
| | | /// <summary> |
| | | /// ååºä»»å¡ |
| | | /// </summary> |
| | | public Dt_Task ReturnTask { get; set; } |
| | | } |
| | | public class PickedBarcodeInfo |
| | | { |
| | |
| | | public decimal PickedQty { get; set; } |
| | | public int PickRecordCount { get; set; } |
| | | } |
| | | /// <summary> |
| | | /// èªå¨æå
ç»æ |
| | | /// </summary> |
| | | public class AutoSplitResult |
| | | { |
| | | public string NewBarcode { get; set; } |
| | | public decimal SplitQuantity { get; set; } |
| | | } |
| | | |
| | | |
| | | public class PickingResult |
| | | { |
| | |
| | | { |
| | | public Dt_OutStockLockInfo LockInfo { get; set; } |
| | | public Dt_StockInfoDetail StockDetail { get; set; } |
| | | } |
| | | |
| | | public class SplitResultDto |
| | | { |
| | | public string NewBarcode { get; set; } |
| | | } |
| | | |
| | | public class ValidationResult<T> |