| | |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using WIDESEA_BasicService; |
| | | using WIDESEA_Common.CommonEnum; |
| | | using WIDESEA_Common.OrderEnum; |
| | | using WIDESEA_Common.StockEnum; |
| | | using WIDESEA_Common.TaskEnum; |
| | | using WIDESEA_Core; |
| | | using WIDESEA_Core.BaseRepository; |
| | | using WIDESEA_Core.BaseServices; |
| | | using WIDESEA_Core.Enums; |
| | | using WIDESEA_Core.Helper; |
| | | using WIDESEA_DTO.Basic; |
| | | using WIDESEA_DTO.Outbound; |
| | | using WIDESEA_IAllocateService; |
| | | using WIDESEA_IBasicService; |
| | | using WIDESEA_IOutboundService; |
| | | using WIDESEA_IStockService; |
| | | using WIDESEA_ITaskInfoService; |
| | | using WIDESEA_Model.Models; |
| | | using WIDESEA_Model.Models.Basic; |
| | | using WIDESEA_Model.Models.Outbound; |
| | | using static WIDESEA_OutboundService.OutboundBatchPickingService; |
| | | |
| | | namespace WIDESEA_OutboundService |
| | | { |
| | |
| | | private readonly IDailySequenceService _dailySequenceService; |
| | | private readonly IAllocateService _allocateService; |
| | | private readonly IRepository<Dt_OutboundBatch> _outboundBatchRepository; |
| | | private readonly ITask_HtyService _task_HtyService; |
| | | private readonly ILogger<OutboundPickingService> _logger; |
| | | |
| | | private Dictionary<string, string> stations = new Dictionary<string, string> |
| | |
| | | public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, |
| | | IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, |
| | | IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, |
| | | IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService) : base(BaseDal) |
| | | IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, ITask_HtyService task_HtyService) : base(BaseDal) |
| | | { |
| | | _unitOfWorkManage = unitOfWorkManage; |
| | | _stockInfoService = stockInfoService; |
| | |
| | | _invokeMESService = invokeMESService; |
| | | _dailySequenceService = dailySequenceService; |
| | | _allocateService = allocateService; |
| | | _outboundBatchRepository = outboundBatchRepository; |
| | | _task_HtyService = task_HtyService; |
| | | } |
| | | |
| | | // <summary> |
| | | /// è·åæççéå®ä¿¡æ¯ |
| | | /// </summary> |
| | | public async Task<List<PalletLockInfoDto>> GetPalletLockInfos(string orderNo, string palletCode) |
| | | { |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .Select(x => new |
| | | { |
| | | x.Id, |
| | | x.OrderNo, |
| | | x.BatchNo, |
| | | x.MaterielCode, |
| | | x.CurrentBarcode, |
| | | x.AssignQuantity, |
| | | x.PickedQty, |
| | | x.Status, |
| | | x.LocationCode, |
| | | x.PalletCode |
| | | }).ToListAsync(); |
| | | |
| | | var lockInfoDtos = lockInfos.Select(x => new PalletLockInfoDto |
| | | { |
| | | Id = x.Id, |
| | | OrderNo = x.OrderNo, |
| | | BatchNo = x.BatchNo, |
| | | MaterielCode = x.MaterielCode, |
| | | CurrentBarcode = x.CurrentBarcode, |
| | | AssignQuantity = x.AssignQuantity, |
| | | PickedQty = x.PickedQty, |
| | | Status = x.Status, |
| | | LocationCode = x.LocationCode, |
| | | PalletCode = x.PalletCode, |
| | | CanSplit = (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ && x.AssignQuantity - x.PickedQty > 0), |
| | | CanPick = (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ && x.PickedQty < x.AssignQuantity) |
| | | }).ToList(); |
| | | |
| | | return lockInfoDtos; |
| | | } |
| | | #region æ¥è¯¢æ¹æ³ |
| | | |
| | | /// <summary> |
| | | /// è·åæççå·²æ£éå表 |
| | | /// </summary> |
| | | public async Task<List<PalletPickedInfoDto>> GetPalletPickedList(string orderNo, string palletCode) |
| | | { |
| | | var pickedList = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | !x.IsCancelled) |
| | | .Select(x => new PalletPickedInfoDto |
| | | { |
| | | Id = x.Id, |
| | | OrderNo = x.OrderNo, |
| | | OrderDetailId = x.OrderDetailId, |
| | | PalletCode = x.PalletCode, |
| | | Barcode = x.Barcode, |
| | | MaterielCode = x.MaterielCode, |
| | | PickedQty = x.PickQuantity, |
| | | PickTime = x.PickTime, |
| | | Operator = x.Operator, |
| | | LocationCode = x.LocationCode |
| | | }) |
| | | .ToListAsync(); |
| | | |
| | | return pickedList; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæçç¶æ |
| | | /// </summary> |
| | | public async Task<PalletStatusDto> GetPalletStatus(string orderNo, string palletCode) |
| | | { |
| | | // è·åæççéå®ä¿¡æ¯ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | if (!lockInfos.Any()) |
| | | { |
| | | return new PalletStatusDto |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | Status = (int)PalletStatusEnum.æ ä»»å¡, |
| | | StatusText = "æ ä»»å¡", |
| | | TotalItems = 0, |
| | | CompletedItems = 0, |
| | | PendingItems = 0 |
| | | }; |
| | | } |
| | | |
| | | var totalItems = lockInfos.Count; |
| | | var completedItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿); |
| | | var pendingItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸); |
| | | |
| | | var status = PalletStatusEnum.æ£éä¸; |
| | | if (pendingItems == 0 && completedItems > 0) |
| | | { |
| | | status = PalletStatusEnum.已宿; |
| | | } |
| | | else if (pendingItems > 0 && completedItems == 0) |
| | | { |
| | | status = PalletStatusEnum.æªå¼å§; |
| | | } |
| | | else if (pendingItems > 0 && completedItems > 0) |
| | | { |
| | | status = PalletStatusEnum.æ£éä¸; |
| | | } |
| | | |
| | | return new PalletStatusDto |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | Status = (int)status, |
| | | StatusText = GetPalletStatusText(status), |
| | | TotalItems = totalItems, |
| | | CompletedItems = completedItems, |
| | | PendingItems = pendingItems |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·åæå
ä¿¡æ¯ |
| | | /// </summary> |
| | | public async Task<SplitPackageInfoDto> GetSplitPackageInfo(string orderNo, string palletCode, string barcode) |
| | | { |
| | | // æ¥æ¾éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.CurrentBarcode == barcode |
| | | //&& x.Status == (int)OutLockStockStatusEnum.åºåºä¸ |
| | | ) |
| | | .FirstAsync(); |
| | | |
| | | if (lockInfo == null) |
| | | throw new Exception("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | |
| | | // 计ç®å©ä½å¯ææ°é |
| | | var remainQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | return new SplitPackageInfoDto |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | Barcode = barcode, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | RemainQuantity = remainQuantity, |
| | | AssignQuantity = lockInfo.AssignQuantity, |
| | | PickedQty = lockInfo.PickedQty |
| | | }; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 忹忣 |
| | | |
| | | |
| | | /// <summary> |
| | | /// åæ¹åæ£ç¡®è®¤ |
| | | /// åæ¹åæ£ç¡®è®¤ - ä¿®æ£çï¼å
嫿å
åéªè¯ï¼ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string palletCode, string barcode) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"ã忣å¼å§ã订å: {orderNo}, æç: {palletCode}, æ¡ç : {barcode}"); |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. éªè¯åæ£è¯·æ± |
| | | // éªè¯åæ£è¯·æ± |
| | | var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!validationResult.IsValid) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | } |
| | | |
| | | var (lockInfo, orderDetail, stockDetail, batch) = validationResult.Data; |
| | | |
| | | // 使ç¨éå®ä¿¡æ¯çåé
æ°éä½ä¸ºå®é
忣æ°é |
| | | var actualPickedQty = lockInfo.AssignQuantity; |
| | | _logger.LogInformation($"éªè¯éè¿ - éå®ID: {lockInfo.Id}, åé
æ°é: {lockInfo.AssignQuantity}, å·²æ£é: {lockInfo.PickedQty}"); |
| | | _logger.LogInformation($"åºåä¿¡æ¯ - æ¡ç : {stockDetail.Barcode}, åºåæ°é: {stockDetail.StockQuantity}, åºåºæ°é: {stockDetail.OutboundQuantity}"); |
| | | |
| | | // 2. æ§è¡åæ£é»è¾ |
| | | |
| | | decimal originalStockQtyBeforeSplit = stockDetail.StockQuantity; |
| | | decimal originalOutboundQtyBeforeSplit = stockDetail.OutboundQuantity; |
| | | |
| | | // è®°å½æå
åçå
³é®æ°æ®ï¼ç¨äºåç»éªè¯ï¼ |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | // æ£æ¥æ¯å¦éè¦èªå¨æå
|
| | | var autoSplitResult = await CheckAndAutoSplitIfNeeded(lockInfo, stockDetail, palletCode); |
| | | if (autoSplitResult != null) |
| | | { |
| | | _logger.LogInformation($"æ§è¡äºèªå¨æå
ï¼éæ°è·åæ°æ®"); |
| | | |
| | | // éæ°è·åææ°çéå®ä¿¡æ¯ååºåä¿¡æ¯ |
| | | var refreshedValidation = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!refreshedValidation.IsValid) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error(refreshedValidation.ErrorMessage); |
| | | } |
| | | |
| | | (lockInfo, orderDetail, stockDetail, batch) = refreshedValidation.Data; |
| | | |
| | | |
| | | // è°ç¨èªå¨æå
åéªè¯ |
| | | decimal splitQuantity = autoSplitResult.FirstOrDefault()?.quantityTotal.ObjToDecimal()??0 ; |
| | | bool autoSplitValid = await ValidateAfterAutoSplit(lockInfo, orderDetail, stockDetail, splitQuantity,originalStockQtyBeforeSplit); |
| | | |
| | | if (!autoSplitValid) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error("èªå¨æå
åæ°æ®éªè¯å¤±è´¥ï¼è¯·æ£æ¥ç³»ç»æ¥å¿"); |
| | | } |
| | | |
| | | _logger.LogInformation($"èªå¨æå
éªè¯éè¿ï¼ç»§ç»æ§è¡åæ£"); |
| | | } |
| | | |
| | | // 计ç®å®é
æ£éæ°é |
| | | decimal actualPickedQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | if (actualPickedQty <= 0) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error("该æ¡ç å·²æ£éå®æï¼æ éé夿£é"); |
| | | } |
| | | |
| | | _logger.LogInformation($"å¼å§æ£é - æ°é: {actualPickedQty}"); |
| | | |
| | | // æ§è¡åæ£é»è¾ |
| | | var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); |
| | | |
| | | // 3. æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | // æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo); |
| | | |
| | | // 4. è®°å½æ£éåå² |
| | | //è®°å½æ£éåå² |
| | | await RecordPickingHistory(pickingResult, orderNo, palletCode); |
| | | |
| | | // æ£éåéªè¯ |
| | | await ValidateAfterPicking(orderNo, palletCode, barcode, actualPickedQty); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("忣æå", new |
| | | { |
| | | PickedQuantity = actualPickedQty, |
| | | Barcode = barcode, |
| | | MaterialCode = lockInfo.MaterielCode |
| | | }); |
| | | return WebResponseContent.Instance.OK("忣æå", autoSplitResult); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | |
| | | return WebResponseContent.Instance.Error($"åæ£å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// èªå¨æå
åéªè¯æ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task<bool> ValidateAfterAutoSplit(Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, |
| | | Dt_StockInfoDetail originalStockDetail, decimal splitQuantity, decimal originalStockQtyBeforeSplit) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§èªå¨æå
åéªè¯ - åæ¡ç : {originalStockDetail.Barcode}, æå
æ°é: {splitQuantity}"); |
| | | _logger.LogInformation($"æå
åååºåæ°é: {originalStockQtyBeforeSplit}, åé
æ°é: {lockInfo.AssignQuantity}"); |
| | | |
| | | bool allValid = true; |
| | | List<string> validationErrors = new List<string>(); |
| | | |
| | | // éæ°è·åææ°çæ°æ®ï¼æå
åçå½åç¶æï¼ |
| | | var refreshedOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetail.Id); |
| | | |
| | | var refreshedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == lockInfo.Id); |
| | | |
| | | var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Id == originalStockDetail.Id); |
| | | |
| | | // éªè¯ååºåæç»æ°æ® |
| | | decimal expectedOriginalStockQty = lockInfo.AssignQuantity; |
| | | |
| | | _logger.LogInformation($"åºåéªè¯åºå:"); |
| | | _logger.LogInformation($" æå
åååºå: {originalStockQtyBeforeSplit}"); |
| | | _logger.LogInformation($" åé
æ°é: {lockInfo.AssignQuantity}"); |
| | | _logger.LogInformation($" æå
æ°é: {splitQuantity}"); |
| | | _logger.LogInformation($" ææååºå: {expectedOriginalStockQty}"); |
| | | _logger.LogInformation($" å®é
ååºå: {refreshedStockDetail.StockQuantity}"); |
| | | |
| | | // å
许å°é误差çéªè¯ |
| | | if (Math.Abs(refreshedStockDetail.StockQuantity - expectedOriginalStockQty) > 0.01m) |
| | | { |
| | | // é¢å¤æ£æ¥ï¼å¦æååºåæ°éä¸åçï¼å¯è½æ¯æ°æ®é®é¢ |
| | | if (refreshedStockDetail.StockQuantity < 0) |
| | | { |
| | | string error = $"ååºåæ°éå¼å¸¸ï¼è´æ°ï¼ï¼å®é
: {refreshedStockDetail.StockQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | else if (refreshedStockDetail.StockQuantity > originalStockQtyBeforeSplit) |
| | | { |
| | | string error = $"ååºåæ°éå¼å¸¸ï¼å¤§äºæå
åï¼ï¼æå
å: {originalStockQtyBeforeSplit}, å½å: {refreshedStockDetail.StockQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | else |
| | | { |
| | | // å¯è½æ¯åçç误差ï¼è®°å½è¦åä½ä¸æ 记为失败 |
| | | _logger.LogWarning($"ååºåæ°é䏿æå¼æå·®å¼ï¼ææ: {expectedOriginalStockQty}, å®é
: {refreshedStockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | // éªè¯ååºåçåºåºæ°éæ¯å¦ä¿æä¸å |
| | | if (Math.Abs(refreshedStockDetail.OutboundQuantity - originalStockDetail.OutboundQuantity) > 0.01m) |
| | | { |
| | | string error = $"ååºååºåºæ°éä¸åºååï¼æå
å: {originalStockDetail.OutboundQuantity}, æå
å: {refreshedStockDetail.OutboundQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // éªè¯æ°åºåæç»ï¼æå
产ççï¼ |
| | | // æ¥æ¾æ°æ¡ç ï¼éè¿æå
è®°å½ï¼ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OutStockLockInfoId == lockInfo.Id && |
| | | !x.IsReverted && |
| | | x.IsAutoSplit == true) |
| | | .OrderByDescending(x => x.SplitTime) |
| | | .ToListAsync(); |
| | | |
| | | if (splitRecords.Any()) |
| | | { |
| | | var latestSplit = splitRecords.First(); |
| | | if (!string.IsNullOrEmpty(latestSplit.NewBarcode)) |
| | | { |
| | | var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == latestSplit.NewBarcode && x.StockId == originalStockDetail.StockId); |
| | | |
| | | if (newStockDetail != null) |
| | | { |
| | | // æ°åºåçææå¼ = æå
æ°é |
| | | decimal expectedNewStockQty = splitQuantity; |
| | | |
| | | _logger.LogInformation($"æ°åºåéªè¯:"); |
| | | _logger.LogInformation($" æ°æ¡ç : {newStockDetail.Barcode}"); |
| | | _logger.LogInformation($" æææ°é: {expectedNewStockQty}"); |
| | | _logger.LogInformation($" å®é
æ°é: {newStockDetail.StockQuantity}"); |
| | | _logger.LogInformation($" åºåºæ°é: {newStockDetail.OutboundQuantity} (åºä¸º0)"); |
| | | |
| | | if (Math.Abs(newStockDetail.StockQuantity - expectedNewStockQty) > 0.01m) |
| | | { |
| | | string error = $"æ°åºåæ°é䏿£ç¡®ï¼ææ: {expectedNewStockQty}, å®é
: {newStockDetail.StockQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // æ°åºååºåºæ°éåºä¸º0 |
| | | if (Math.Abs(newStockDetail.OutboundQuantity - 0) > 0.01m) |
| | | { |
| | | string error = $"æ°åºååºåºæ°éä¸ä¸º0ï¼å®é
: {newStockDetail.OutboundQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 4. éªè¯æªåé
éå®è®°å½ |
| | | var unallocatedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.ParentLockId == lockInfo.Id && |
| | | x.IsUnallocated == 1 && |
| | | x.OrderDetailId == 0) |
| | | .ToListAsync(); |
| | | |
| | | if (!unallocatedLocks.Any()) |
| | | { |
| | | string error = $"æªæ¾å°èªå¨æå
åå»ºçæªåé
éå®è®°å½"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // éªè¯è®¢åæç»æ°æ®æªæ¹å |
| | | if (Math.Abs(refreshedOrderDetail.AllocatedQuantity - orderDetail.AllocatedQuantity) > 0.01m) |
| | | { |
| | | string error = $"订åæç»åé
æ°éå¼å¸¸ååï¼æå
å: {orderDetail.AllocatedQuantity}, æå
å: {refreshedOrderDetail.AllocatedQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // éªè¯åéå®è®°å½æ°æ®æªæ¹åï¼åé
æ°éä¸åï¼ |
| | | if (Math.Abs(refreshedLockInfo.AssignQuantity - lockInfo.AssignQuantity) > 0.01m) |
| | | { |
| | | string error = $"éå®è®°å½åé
æ°éå¼å¸¸ååï¼æå
å: {lockInfo.AssignQuantity}, æå
å: {refreshedLockInfo.AssignQuantity}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // æ°å¢ãéªè¯æ»åºå宿 |
| | | // æå
åæ»åºå = ååºåæ°é |
| | | // æå
åæ»åºå = ååºåç°ææ°é + æ°åºåæ°é |
| | | decimal totalStockAfterSplit = refreshedStockDetail.StockQuantity; |
| | | if (splitRecords.Any() && !string.IsNullOrEmpty(splitRecords.First().NewBarcode)) |
| | | { |
| | | var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecords.First().NewBarcode); |
| | | if (newStock != null) |
| | | { |
| | | totalStockAfterSplit += newStock.StockQuantity; |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"åºå宿éªè¯:"); |
| | | _logger.LogInformation($" æå
åæ»åºå: {originalStockQtyBeforeSplit}"); |
| | | _logger.LogInformation($" æå
åæ»åºå: {totalStockAfterSplit}"); |
| | | _logger.LogInformation($" å·®å¼: {originalStockQtyBeforeSplit - totalStockAfterSplit}"); |
| | | |
| | | // å
许å¾å°çæµ®ç¹æ°è¯¯å·® |
| | | if (Math.Abs(originalStockQtyBeforeSplit - totalStockAfterSplit) > 0.02m) |
| | | { |
| | | string error = $"åºåä¸å®æï¼æå
å: {originalStockQtyBeforeSplit}, æå
åæ»åºå: {totalStockAfterSplit}"; |
| | | validationErrors.Add(error); |
| | | allValid = false; |
| | | _logger.LogError(error); |
| | | } |
| | | |
| | | // æ±æ»éªè¯ç»æ |
| | | if (allValid) |
| | | { |
| | | _logger.LogInformation($"â
èªå¨æå
åéªè¯å
¨é¨éè¿"); |
| | | } |
| | | else |
| | | { |
| | | string errorSummary = $"èªå¨æå
åéªè¯å¤±è´¥ï¼åç°{validationErrors.Count}个é®é¢ï¼" + |
| | | string.Join("; ", validationErrors.Take(3)); |
| | | _logger.LogError(errorSummary); |
| | | |
| | | // è®°å½è¯¦ç»é®é¢å°æ¥å¿ |
| | | for (int i = 0; i < validationErrors.Count; i++) |
| | | { |
| | | _logger.LogError($"é®é¢{i + 1}: {validationErrors[i]}"); |
| | | } |
| | | } |
| | | |
| | | return allValid; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"èªå¨æå
åéªè¯å¼å¸¸: {ex.Message}"); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ£éåéªè¯ |
| | | /// </summary> |
| | | private async Task ValidateAfterPicking(string orderNo, string palletCode, string barcode, decimal pickedQty) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§æ£éåéªè¯"); |
| | | |
| | | // éªè¯åºåæç» |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode); |
| | | |
| | | // æ¥æ¾è¯¥æ¡ç çæææ£éè®°å½ |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalPickedFromRecords = pickingRecords.Sum(x => x.PickQuantity); |
| | | |
| | | _logger.LogInformation($"æ£ééªè¯ - æ¡ç : {barcode}"); |
| | | _logger.LogInformation($" åºååºåºæ°é: {stockDetail.OutboundQuantity}"); |
| | | _logger.LogInformation($" æ£éè®°å½æ»å: {totalPickedFromRecords}"); |
| | | |
| | | if (Math.Abs(stockDetail.OutboundQuantity - totalPickedFromRecords) > 0.01m) |
| | | { |
| | | _logger.LogError($"æ£éæ°æ®ä¸ä¸è´ï¼åºååºåºæ°é({stockDetail.OutboundQuantity}) â æ£éè®°å½æ»å({totalPickedFromRecords})"); |
| | | |
| | | // èªå¨ä¿®å¤ï¼ä»¥æ£éè®°å½æ»å为å |
| | | decimal originalOutbound = stockDetail.OutboundQuantity; |
| | | stockDetail.OutboundQuantity = totalPickedFromRecords; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogWarning($"å·²èªå¨ä¿®å¤åºåºæ°é: {originalOutbound} -> {totalPickedFromRecords}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ£éåéªè¯å®æ"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æ£éåéªè¯å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 忶忣 |
| | | /// </summary> |
| | |
| | | return WebResponseContent.Instance.Error("æªæ¾å°åæ£è®°å½"); |
| | | |
| | | // æ¢å¤éå®ä¿¡æ¯ååºå |
| | | await RevertPickingData(pickingRecord); |
| | | var revertResult = await RevertPickingData(pickingRecord); |
| | | |
| | | //æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await RevertBatchAndOrderData(pickingRecord); |
| | | // æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await RevertBatchAndOrderData(pickingRecord, revertResult); |
| | | |
| | | // æ è®°åæ£è®°å½ä¸ºå·²åæ¶ |
| | | pickingRecord.IsCancelled = true; |
| | |
| | | return WebResponseContent.Instance.Error($"åæ¶åæ£å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | |
| | | #region å走空箱é»è¾ |
| | | /// <summary> |
| | | /// æ¸
çåºåä¿¡æ¯ - 宿´ä¿®æ£ç |
| | | /// ç¡®ä¿OutboundQuantityæ£ç¡®æ¸
é¶ |
| | | /// </summary> |
| | | private async Task CleanupStockInfo(Dt_OutStockLockInfo lockInfo) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"æ¸
çåºåä¿¡æ¯ - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}"); |
| | | |
| | | // æ¥æ¾éå®çåºåæç» |
| | | 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; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | _logger.LogInformation($"æ¸
çåç¶æ - åºå: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // ãéè¦ãæ£æ¥åºåæ°éæ¯å¦åºè¯¥ä¸º0 |
| | | if (lockInfo.Status == (int)OutLockStockStatusEnum.æ£é宿) |
| | | { |
| | | if (stockDetail.StockQuantity > 0) |
| | | { |
| | | _logger.LogWarning($"æ£é宿ä½åºåä¸ä¸º0 - æ¡ç : {stockDetail.Barcode}, åºå: {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | // æ¸
çåºåååºåºæ°é |
| | | stockDetail.StockQuantity = 0; |
| | | stockDetail.OutboundQuantity = 0; |
| | | stockDetail.Status = (int)StockStatusEmun.å·²æ¸
ç; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¸
çåºåæç»å®æ"); |
| | | _logger.LogInformation($" åºåæ°é: {originalStockQty} -> 0"); |
| | | _logger.LogInformation($" åºåºæ°é: {originalOutboundQty} -> 0"); |
| | | _logger.LogInformation($" ç¶æ: {GetStockStatusName(originalStatus)} -> å·²æ¸
ç"); |
| | | } |
| | | else |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°æ¡ç 对åºçåºåæç»: {lockInfo.CurrentBarcode}"); |
| | | } |
| | | |
| | | // æ¸
ç该æçä¸çææåºåï¼é¿å
éæ¼ï¼ |
| | | var allStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == lockInfo.StockId && x.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | if (allStockDetails.Any()) |
| | | { |
| | | _logger.LogInformation($"æ¸
çæçä¸å
¶ä»åºå - å
± {allStockDetails.Count} æ¡è®°å½"); |
| | | |
| | | foreach (var stock in allStockDetails) |
| | | { |
| | | decimal originalQty = stock.StockQuantity; |
| | | decimal originalOutbound = stock.OutboundQuantity; |
| | | |
| | | stock.StockQuantity = 0; |
| | | stock.OutboundQuantity = 0; |
| | | stock.Status = (int)StockStatusEmun.å·²æ¸
ç; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stock).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ¸
çéæ¼åºå - æ¡ç : {stock.Barcode}, åºå: {originalQty}->0, åºåº: {originalOutbound}->0"); |
| | | } |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æ¸
çåºåä¿¡æ¯å¤±è´¥ - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, Error: {ex.Message}"); |
| | | // 䏿åºå¼å¸¸ï¼ç»§ç»å¤çå
¶ä»è®°å½ |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°è®¢åç¶æ |
| | | /// </summary> |
| | | private async Task UpdateOrderStatusAfterPalletRemoval(string orderNo) |
| | | { |
| | | // æ£æ¥è®¢åæ¯å¦æææçé½å·²å®æ |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ToListAsync(); |
| | | |
| | | var unfinishedPallets = allLocks |
| | | .GroupBy(x => x.PalletCode) |
| | | .Where(g => g.Any(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸)) |
| | | .ToList(); |
| | | |
| | | // å¦ææ²¡ææªå®æçæçï¼æ´æ°è®¢åç¶æä¸ºåºåºå®æ |
| | | if (!unfinishedPallets.Any()) |
| | | { |
| | | await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() |
| | | .SetColumns(x => new Dt_OutboundOrder |
| | | { |
| | | OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ, |
| | | }) |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è®°å½ç©ºç®±åèµ°åå² |
| | | /// </summary> |
| | | private async Task RecordEmptyPalletRemoval(string orderNo, string palletCode, List<Dt_OutStockLockInfo> completedLocks) |
| | | { |
| | | //var removalRecord = new Dt_EmptyPalletRemoval |
| | | //{ |
| | | // OrderNo = orderNo, |
| | | // PalletCode = palletCode, |
| | | // RemovalTime = DateTime.Now, |
| | | // Operator = App.User.UserName, |
| | | // CompletedItemsCount = completedLocks.Count, |
| | | // TotalPickedQuantity = completedLocks.Sum(x => x.PickedQty) |
| | | //}; |
| | | |
| | | //await Db.Insertable(removalRecord).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æå¨æå
|
| | | private List<SplitResult> CreateSplitResults(Dt_OutStockLockInfo lockInfo, decimal splitQty, decimal remainQty, string newBarcode, string originalBarcode) |
| | | { |
| | | return new List<SplitResult> |
| | | { |
| | | new SplitResult |
| | | { |
| | | materialCode = lockInfo.MaterielCode, |
| | | supplierCode = lockInfo.SupplyCode, |
| | | quantityTotal = splitQty.ToString("F2"), |
| | | batchNumber = newBarcode, |
| | | batch = lockInfo.BatchNo, |
| | | factory = lockInfo.FactoryArea, |
| | | date = DateTime.Now.ToString("yyyy-MM-dd"), |
| | | }, |
| | | new SplitResult |
| | | { |
| | | materialCode = lockInfo.MaterielCode, |
| | | supplierCode = lockInfo.SupplyCode, |
| | | quantityTotal = remainQty.ToString("F2"), |
| | | batchNumber = originalBarcode, |
| | | batch = lockInfo.BatchNo, |
| | | factory = lockInfo.FactoryArea, |
| | | date = DateTime.Now.ToString("yyyy-MM-dd"), |
| | | } |
| | | }; |
| | | } |
| | | |
| | | |
| | | #region æå¨æå
|
| | | |
| | | /// <summary> |
| | | /// æå¨æå
|
| | |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // éªè¯æå
è¯·æ± |
| | | // éªè¯æå
è¯·æ± |
| | | var validationResult = await ValidateSplitRequest(orderNo, palletCode, originalBarcode, splitQuantity); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (lockInfo, stockDetail) = validationResult.Data; |
| | | |
| | | // . æ§è¡æå
é»è¾ |
| | | var splitResult = await ExecuteSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); |
| | | // æ§è¡æå
é»è¾ |
| | | var splitResult = await ExecuteManualSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("æå¨æå
æå", new |
| | | { |
| | | NewBarcode = splitResult.NewBarcode, |
| | | OriginalBarcode = originalBarcode, |
| | | SplitQuantity = splitQuantity |
| | | }); |
| | | return WebResponseContent.Instance.OK("æå¨æå
æå", splitResult); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | /// <summary> |
| | | /// éªè¯æå
è¯·æ± - å¢å¼ºåé
æ°éæ§å¶ |
| | | /// </summary> |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest( |
| | | string orderNo, string palletCode, string originalBarcode, decimal splitQuantity) |
| | | { |
| | | _logger.LogInformation($"å¼å§éªè¯æå
è¯·æ± - 订å: {orderNo}, æç: {palletCode}, åæ¡ç : {originalBarcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | #region åæ¶æå
|
| | | // æ¥æ¾éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.CurrentBarcode == originalBarcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | |
| | | if (lockInfo == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°ææçéå®ä¿¡æ¯ - 订å: {orderNo}, æç: {palletCode}, æ¡ç : {originalBarcode}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¾å°éå®ä¿¡æ¯ - åé
æ°é: {lockInfo.AssignQuantity}, å·²æ£é: {lockInfo.PickedQty}, è®¢åæ°é: {lockInfo.OrderQuantity}"); |
| | | |
| | | // è·å订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == lockInfo.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°è®¢åæç» - OrderDetailId: {lockInfo.OrderDetailId}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°è®¢åæç»"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¾å°è®¢åæç» - å·²åé
æ°é: {orderDetail.AllocatedQuantity}, é宿°é: {orderDetail.LockQuantity}"); |
| | | |
| | | // è·ååºåä¿¡æ¯ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | _logger.LogWarning($"æªæ¾å°åºåä¿¡æ¯ - æ¡ç : {originalBarcode}, StockId: {lockInfo.StockId}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°å¯¹åºçåºåä¿¡æ¯"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¾å°åºåä¿¡æ¯ - åºåæ°é: {stockDetail.StockQuantity}, åºåºæ°é: {stockDetail.OutboundQuantity}"); |
| | | |
| | | // éªè¯æå
æ°éä¸è½å¤§äºåºåæ°é |
| | | if (stockDetail.StockQuantity < splitQuantity) |
| | | { |
| | | _logger.LogWarning($"æå
æ°é大äºåºåæ°é - æå
æ°é: {splitQuantity}, åºåæ°é: {stockDetail.StockQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"æå
æ°éä¸è½å¤§äºåºåæ°éï¼å½ååºåï¼{stockDetail.StockQuantity}"); |
| | | } |
| | | |
| | | // éªè¯æå
æ°éä¸è½å¤§äºéå®ä¿¡æ¯çåé
æ°é |
| | | if (lockInfo.AssignQuantity < splitQuantity) |
| | | { |
| | | _logger.LogWarning($"æå
æ°é大äºåé
æ°é - æå
æ°é: {splitQuantity}, åé
æ°é: {lockInfo.AssignQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"æå
æ°éä¸è½å¤§äºåé
æ°éï¼å½ååé
æ°éï¼{lockInfo.AssignQuantity}"); |
| | | } |
| | | |
| | | // éªè¯æå
æ°éä¸è½å¤§äºéå®ä¿¡æ¯çæªæ£éæ°é |
| | | decimal remainingToPick = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | if (remainingToPick < splitQuantity) |
| | | { |
| | | _logger.LogWarning($"æå
æ°éå¤§äºæªæ£éæ°é - æå
æ°é: {splitQuantity}, æªæ£éæ°é: {remainingToPick}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"æå
æ°éä¸è½å¤§äºæªæ£éæ°éï¼å½åæªæ£éï¼{remainingToPick}"); |
| | | } |
| | | |
| | | // éªè¯æå
ååéå®ä¿¡æ¯çåé
æ°éä¸ä¼ä¸ºè´æ° |
| | | if (lockInfo.AssignQuantity - splitQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"æå
ååé
æ°éä¸ºè´æ° - å½ååé
æ°é: {lockInfo.AssignQuantity}, æå
æ°é: {splitQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error($"æå
ååé
æ°éä¸è½ä¸ºè´æ°"); |
| | | } |
| | | |
| | | // éªè¯è®¢åæç»çåé
æ°éæ¯å¦è¶³å¤ |
| | | // 注æï¼æå¨æå
ä¸ä¼æ¹å订åæç»çåé
æ°éï¼å 为æ»åé
æ°éä¸å |
| | | // åªæ¯ä»ä¸ä¸ªéå®ä¿¡æ¯è½¬ç§»å°å¦ä¸ä¸ªéå®ä¿¡æ¯ |
| | | decimal totalLockAssignQuantity = await GetTotalLockAssignQuantity(orderDetail.Id); |
| | | if (orderDetail.AllocatedQuantity != totalLockAssignQuantity) |
| | | { |
| | | _logger.LogWarning($"订åæç»åé
æ°éä¸éå®ä¿¡æ¯ä¸ä¸è´ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQuantity}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æå
éªè¯éè¿ - åæ¡ç : {originalBarcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¶æå
|
| | | /// è·å订åæç»çææéå®ä¿¡æ¯çæ»åé
æ°é |
| | | /// </summary> |
| | | private async Task<decimal> GetTotalLockAssignQuantity(long orderDetailId) |
| | | { |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderDetailId == orderDetailId) |
| | | .ToListAsync(); |
| | | |
| | | return lockInfos.Sum(x => x.AssignQuantity); |
| | | } |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ§è¡æå¨æå
é»è¾ - ä¿®å¤çæ¬ |
| | | /// </summary> |
| | | private async Task<List<SplitResult>> ExecuteManualSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ§è¡æå¨æå
é»è¾ - åæ¡ç : {stockDetail.Barcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | try |
| | | { |
| | | // éªè¯åºåæ°éæ¯å¦è¶³å¤ |
| | | decimal availableStock = stockDetail.StockQuantity; |
| | | if (availableStock < splitQuantity) |
| | | { |
| | | throw new InvalidOperationException($"åºåä¸è¶³ï¼å½ååºå: {availableStock}, éè¦æå
: {splitQuantity}"); |
| | | } |
| | | |
| | | // éªè¯æå
æ°éä¸è½çäºæå¤§äºåéå®ä¿¡æ¯åé
æ°é |
| | | if (splitQuantity >= lockInfo.AssignQuantity) |
| | | { |
| | | throw new InvalidOperationException($"æå
æ°éä¸è½çäºæå¤§äºåéå®ä¿¡æ¯åé
æ°éï¼æå
æ°é: {splitQuantity}, ååé
æ°é: {lockInfo.AssignQuantity}"); |
| | | } |
| | | |
| | | // 计ç®å©ä½æ°é |
| | | decimal remainQty = lockInfo.AssignQuantity - splitQuantity; |
| | | |
| | | // éªè¯å©ä½æ°éæ¯å¦åç |
| | | if (remainQty <= 0) |
| | | { |
| | | throw new InvalidOperationException($"æå
åå©ä½æ°éå¿
须大äº0ï¼å½åå©ä½: {remainQty}"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æå
è®¡ç® - ååé
æ°é: {lockInfo.AssignQuantity}, æå
æ°é: {splitQuantity}, å©ä½æ°é: {remainQty}"); |
| | | _logger.LogInformation($"ååºåä¿¡æ¯ - åºåæ°é: {stockDetail.StockQuantity}, åºåºæ°é: {stockDetail.OutboundQuantity}"); |
| | | |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | | _logger.LogInformation($"çææ°æ¡ç : {newBarcode}"); |
| | | |
| | | // è®°å½æå
åçå
³é®æ°æ® |
| | | decimal originalLockAssignQty = lockInfo.AssignQuantity; |
| | | decimal originalLockOrderQty = lockInfo.OrderQuantity; |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | // ä¿®å¤ï¼è®¡ç®æ°åºåæç»çæ£ç¡®æ°é |
| | | // æ°æ¡ç åºè¯¥åªææå
æ°éï¼è䏿¯2å |
| | | decimal newStockQuantity = splitQuantity; |
| | | decimal originalRemainingStockQuantity = originalStockQty - splitQuantity; |
| | | |
| | | _logger.LogInformation($"æ°éåé
- æ°æ¡ç æ°é: {newStockQuantity}, åæ¡ç å©ä½æ°é: {originalRemainingStockQuantity}"); |
| | | |
| | | // å建æ°åºåæç» - ä¿®å¤æ°éé®é¢ |
| | | var newStockDetail = new Dt_StockInfoDetail |
| | | { |
| | | StockId = stockDetail.StockId, |
| | | MaterielCode = stockDetail.MaterielCode, |
| | | OrderNo = stockDetail.OrderNo, |
| | | BatchNo = stockDetail.BatchNo, |
| | | StockQuantity = newStockQuantity, // ä¿®å¤ï¼ä½¿ç¨æ£ç¡®çæå
æ°é |
| | | OutboundQuantity = 0, // æ°æ¡ç åå§åºåºæ°é为0 |
| | | Barcode = newBarcode, |
| | | Status = (int)StockStatusEmun.åºåºéå®, |
| | | SupplyCode = stockDetail.SupplyCode, |
| | | Unit = stockDetail.Unit, |
| | | BarcodeQty = stockDetail.BarcodeQty, |
| | | BarcodeUnit = stockDetail.BarcodeUnit, |
| | | BusinessType = stockDetail.BusinessType, |
| | | InboundOrderRowNo = stockDetail.InboundOrderRowNo, |
| | | }; |
| | | |
| | | await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æ°åºåæç»æå - æ¡ç : {newBarcode}, åºåæ°é: {newStockQuantity}"); |
| | | |
| | | // æ´æ°ååºåæç» |
| | | stockDetail.StockQuantity = originalRemainingStockQuantity; |
| | | |
| | | // ç¡®ä¿ä¸ä¼ä¸ºè´æ° |
| | | if (stockDetail.StockQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"ååºåæ°éåºç°è´æ°ï¼é置为0"); |
| | | stockDetail.StockQuantity = 0; |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ´æ°ååºåæç» - æ¡ç : {stockDetail.Barcode}, " + |
| | | $"åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}, " + |
| | | $"åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | |
| | | // å建æ°éå®ä¿¡æ¯ |
| | | var newLockInfo = new Dt_OutStockLockInfo |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | OrderDetailId = lockInfo.OrderDetailId, |
| | | OutboundBatchNo = lockInfo.OutboundBatchNo, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | MaterielName = lockInfo.MaterielName, |
| | | StockId = lockInfo.StockId, |
| | | OrderQuantity = splitQuantity, |
| | | AssignQuantity = splitQuantity, |
| | | PickedQty = 0, |
| | | LocationCode = lockInfo.LocationCode, |
| | | PalletCode = lockInfo.PalletCode, |
| | | TaskNum = lockInfo.TaskNum, |
| | | Status = (int)OutLockStockStatusEnum.åºåºä¸, |
| | | Unit = lockInfo.Unit, |
| | | SupplyCode = lockInfo.SupplyCode, |
| | | OrderType = lockInfo.OrderType, |
| | | CurrentBarcode = newBarcode, |
| | | IsSplitted = 1, |
| | | ParentLockId = lockInfo.Id, |
| | | Operator = App.User.UserName, |
| | | FactoryArea = lockInfo.FactoryArea, |
| | | lineNo = lockInfo.lineNo, |
| | | WarehouseCode = lockInfo.WarehouseCode, |
| | | BarcodeQty = lockInfo.BarcodeQty, |
| | | BarcodeUnit = lockInfo.BarcodeUnit, |
| | | }; |
| | | |
| | | await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æ°éå®ä¿¡æ¯æå - æ¡ç : {newBarcode}, åé
æ°é: {splitQuantity}"); |
| | | |
| | | // æ´æ°åéå®ä¿¡æ¯ |
| | | lockInfo.AssignQuantity = remainQty; |
| | | lockInfo.OrderQuantity = remainQty; |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ´æ°åéå®ä¿¡æ¯ - åé
æ°é: {originalLockAssignQty} -> {lockInfo.AssignQuantity}, " + |
| | | $"è®¢åæ°é: {originalLockOrderQty} -> {lockInfo.OrderQuantity}"); |
| | | |
| | | // éªè¯æå
åæ°æ®ä¸è´æ§ |
| | | await ValidateDataConsistencyAfterSplit(lockInfo.OrderDetailId, originalLockAssignQty, originalLockOrderQty); |
| | | |
| | | // è®°å½æå
åå² |
| | | await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, false, originalStockQty); |
| | | |
| | | // å建æå
ç»æå表 |
| | | var splitResults = CreateSplitResults(lockInfo, splitQuantity, remainQty, newBarcode, stockDetail.Barcode); |
| | | |
| | | _logger.LogInformation($"æå¨æå
é»è¾æ§è¡å®æ - åæ¡ç : {stockDetail.Barcode}, æ°æ¡ç : {newBarcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | return splitResults; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æå¨æå
é»è¾æ§è¡å¤±è´¥ - åæ¡ç : {stockDetail.Barcode}, æå
æ°é: {splitQuantity}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯æå
åæ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidateDataConsistencyAfterSplit(long orderDetailId, decimal expectedAllocatedQty, decimal expectedLockQty) |
| | | { |
| | | // éæ°è·å订åæç»æ°æ® |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | // è®¡ç®ææéå®ä¿¡æ¯çæ»åé
æ°é |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderDetailId == orderDetailId) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalLockAssignQty = allLocks.Sum(x => x.AssignQuantity); |
| | | |
| | | _logger.LogInformation($"æ°æ®ä¸è´æ§éªè¯ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQty}"); |
| | | |
| | | // å¦ææ°æ®ä¸ä¸è´ï¼è®°å½è¦å |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - totalLockAssignQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"æ°æ®ä¸ä¸è´ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQty}"); |
| | | } |
| | | |
| | | // éªè¯åé
æ°é没æå¼å¸¸åå |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - expectedAllocatedQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éå¼å¸¸åå - ææ: {expectedAllocatedQty}, å®é
: {orderDetail.AllocatedQuantity}"); |
| | | } |
| | | |
| | | if (Math.Abs(orderDetail.LockQuantity - expectedLockQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"é宿°éå¼å¸¸åå - ææ: {expectedLockQty}, å®é
: {orderDetail.LockQuantity}"); |
| | | } |
| | | } |
| | | #endregion |
| | | |
| | | #region åæ¶æå
|
| | | |
| | | /// <summary> |
| | | /// åæ¶æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string palletCode, string newBarcode) |
| | | { |
| | |
| | | |
| | | var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data; |
| | | |
| | | // æ§è¡åæ¶æå
é»è¾ |
| | | await ExecuteCancelSplitLogic(splitRecord, newLockInfo, newStockDetail); |
| | | // æ¥æ¾åå§éå®ä¿¡æ¯ |
| | | var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); |
| | | |
| | | // æ£æ¥è¯¥æ¡ç æ¯å¦è¢«å次æå
|
| | | var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted) |
| | | .ToListAsync(); |
| | | |
| | | if (childSplitRecords.Any()) |
| | | { |
| | | return WebResponseContent.Instance.Error("该æ¡ç å·²è¢«åæ¬¡æå
ï¼è¯·å
åæ¶åç»çæå
æä½"); |
| | | } |
| | | |
| | | // æ§è¡åæ¶æå
é»è¾ |
| | | await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¶æå
失败 - OrderNo: {orderNo}, Barcode: {newBarcode}, Error: {ex.Message}"); |
| | | _logger.LogError($"åæ¶æå
失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {newBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¶æå
失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | /// <summary> |
| | | /// æ§è¡åæ¶æå
é»è¾ |
| | | /// </summary> |
| | | private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, |
| | | Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo, |
| | | Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ§è¡åæ¶æå
é»è¾ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}, æå
æ°é: {splitRecord.SplitQty}"); |
| | | |
| | | #region åæ¹ååº |
| | | try |
| | | { |
| | | // æ ¹æ®æå
ç±»åè°ç¨ä¸åçå¤çæ¹æ³ |
| | | if (splitRecord.IsAutoSplit) |
| | | { |
| | | await HandleAutoSplitCancel(splitRecord, originalLockInfo, newLockInfo, newStockDetail); |
| | | } |
| | | else |
| | | { |
| | | await HandleManualSplitCancel(splitRecord, originalLockInfo, newLockInfo, newStockDetail); |
| | | } |
| | | |
| | | // éªè¯åæ¶æå
åæ°æ®ä¸è´æ§ |
| | | await ValidateDataConsistencyAfterCancelSplit(originalLockInfo.OrderDetailId, |
| | | originalLockInfo.AssignQuantity, originalLockInfo.OrderQuantity, |
| | | splitRecord.IsAutoSplit, splitRecord.SplitQty); |
| | | |
| | | // æ£æ¥å¹¶æ´æ°æ¹æ¬¡å订åç¶æ |
| | | await CheckAndUpdateBatchStatus(originalLockInfo.BatchNo); |
| | | await CheckAndUpdateOrderStatus(originalLockInfo.OrderNo); |
| | | |
| | | _logger.LogInformation($"åæ¶æå
é»è¾æ§è¡å®æ"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"åæ¶æå
é»è¾æ§è¡å¤±è´¥: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¹ååº - éæ¾æªæ£éçåºå |
| | | /// å¤çèªå¨æå
åæ¶ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode) |
| | | private async Task HandleAutoSplitCancel(Dt_SplitPackageRecord splitRecord, |
| | | Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo, |
| | | Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | _logger.LogInformation($"å¤çèªå¨æå
åæ¶ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | |
| | | // è·å订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == originalLockInfo.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | throw new InvalidOperationException("æªæ¾å°è®¢åæç»"); |
| | | |
| | | // æ¢å¤è®¢åæç»çåé
æ°é |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | |
| | | orderDetail.AllocatedQuantity -= splitRecord.SplitQty; |
| | | orderDetail.LockQuantity -= splitRecord.SplitQty; |
| | | |
| | | // è¾¹çæ£æ¥ |
| | | if (orderDetail.AllocatedQuantity < 0) orderDetail.AllocatedQuantity = 0; |
| | | if (orderDetail.LockQuantity < 0) orderDetail.LockQuantity = 0; |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"èªå¨æå
åæ¶æ¢å¤è®¢åæç» - åé
æ°é: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}"); |
| | | |
| | | // æ¢å¤ååºå |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | | |
| | | if (originalStock != null) |
| | | { |
| | | decimal originalStockQty = originalStock.StockQuantity; |
| | | originalStock.StockQuantity += splitRecord.SplitQty; |
| | | |
| | | // 妿ååºåç¶ææ¯åºåºå®æï¼æ¢å¤ä¸ºåºåºéå® |
| | | if (originalStock.Status == (int)StockStatusEmun.åºåºå®æ) |
| | | { |
| | | originalStock.Status = (int)StockStatusEmun.åºåºéå®; |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"èªå¨æå
åæ¶æ¢å¤ååºå - æ¡ç : {originalStock.Barcode}, æ°é: {originalStockQty} -> {originalStock.StockQuantity}"); |
| | | } |
| | | |
| | | // å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | await DeleteNewSplitRecords(newLockInfo, newStockDetail); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæå¨æå
åæ¶ |
| | | /// </summary> |
| | | private async Task HandleManualSplitCancel(Dt_SplitPackageRecord splitRecord, |
| | | Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo, |
| | | Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | _logger.LogInformation($"å¤çæå¨æå
åæ¶ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | |
| | | // æ¢å¤åéå®ä¿¡æ¯çåé
æ°é |
| | | decimal originalAssignQty = originalLockInfo.AssignQuantity; |
| | | decimal originalOrderQty = originalLockInfo.OrderQuantity; |
| | | |
| | | originalLockInfo.AssignQuantity += splitRecord.SplitQty; |
| | | originalLockInfo.OrderQuantity += splitRecord.SplitQty; |
| | | |
| | | // æ¢å¤ç¶æ |
| | | if (originalLockInfo.Status == (int)OutLockStockStatusEnum.æ£é宿) |
| | | { |
| | | originalLockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; |
| | | } |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æå¨æå
åæ¶æ¢å¤åéå®ä¿¡æ¯ - åé
æ°é: {originalAssignQty} -> {originalLockInfo.AssignQuantity}"); |
| | | |
| | | // æ¢å¤ååºåæç» |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | | |
| | | if (originalStock != null) |
| | | { |
| | | decimal originalStockQty = originalStock.StockQuantity; |
| | | originalStock.StockQuantity += splitRecord.SplitQty; |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | if (originalStock.Status == (int)StockStatusEmun.åºåºå®æ) |
| | | { |
| | | originalStock.Status = (int)StockStatusEmun.åºåºéå®; |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æå¨æå
åæ¶æ¢å¤ååºå - æ¡ç : {originalStock.Barcode}, æ°é: {originalStockQty} -> {originalStock.StockQuantity}"); |
| | | } |
| | | |
| | | // å 餿°éå®ä¿¡æ¯ååºåæç» |
| | | await DeleteNewSplitRecords(newLockInfo, newStockDetail); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å 餿°æå
è®°å½ç¸å
³çæ°æ® |
| | | /// </summary> |
| | | private async Task DeleteNewSplitRecords(Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | // å 餿°éå®ä¿¡æ¯ |
| | | if (newLockInfo != null) |
| | | { |
| | | _logger.LogInformation($"å 餿°éå®ä¿¡æ¯ - æ¡ç : {newLockInfo.CurrentBarcode}, åé
æ°é: {newLockInfo.AssignQuantity}"); |
| | | await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.Id == newLockInfo.Id) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // å 餿°åºåæç» |
| | | if (newStockDetail != null) |
| | | { |
| | | _logger.LogInformation($"å 餿°åºåæç» - æ¡ç : {newStockDetail.Barcode}, åºåæ°é: {newStockDetail.StockQuantity}"); |
| | | await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() |
| | | .Where(x => x.Barcode == newStockDetail.Barcode) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯åæ¶æå
åæ°æ®ä¸è´æ§ - ææ°çæ¬ |
| | | /// </summary> |
| | | private async Task ValidateDataConsistencyAfterCancelSplit(long orderDetailId, decimal originalAllocatedQty, decimal originalLockQty, bool isAutoSplit, decimal splitQuantity) |
| | | { |
| | | // éæ°è·å订åæç»æ°æ® |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return; |
| | | |
| | | // è®¡ç®ææéå®ä¿¡æ¯çæ»åé
æ°é |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderDetailId == orderDetailId) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalLockAssignQty = allLocks.Sum(x => x.AssignQuantity); |
| | | |
| | | _logger.LogInformation($"åæ¶æå
åæ°æ®ä¸è´æ§éªè¯ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQty}"); |
| | | |
| | | // æ ¹æ®æå
ç±»åè®¡ç®ææå¼ |
| | | decimal expectedAllocatedQty; |
| | | decimal expectedLockQty; |
| | | |
| | | if (isAutoSplit) |
| | | { |
| | | // èªå¨æå
åæ¶ï¼åé
æ°éåºè¯¥åå°æå
æ°é |
| | | expectedAllocatedQty = originalAllocatedQty - splitQuantity; |
| | | expectedLockQty = originalLockQty - splitQuantity; |
| | | |
| | | _logger.LogInformation($"åæ¶èªå¨æå
ææå¼ - åé
æ°é: {expectedAllocatedQty}, é宿°é: {expectedLockQty}"); |
| | | } |
| | | else |
| | | { |
| | | // æå¨æå
åæ¶ï¼åé
æ°éåºè¯¥ä¿æä¸å |
| | | expectedAllocatedQty = originalAllocatedQty; |
| | | expectedLockQty = originalLockQty; |
| | | |
| | | _logger.LogInformation($"åæ¶æå¨æå
ææå¼ - åé
æ°é: {expectedAllocatedQty}, é宿°é: {expectedLockQty}"); |
| | | } |
| | | |
| | | // è¾¹çæ£æ¥ï¼ç¡®ä¿ææå¼ä¸ä¸ºè´æ° |
| | | if (expectedAllocatedQty < 0) |
| | | { |
| | | _logger.LogWarning($"ææåé
æ°éä¸ºè´æ°ï¼é置为0ã计ç®å¼: {expectedAllocatedQty}"); |
| | | expectedAllocatedQty = 0; |
| | | } |
| | | |
| | | if (expectedLockQty < 0) |
| | | { |
| | | _logger.LogWarning($"ææé宿°éä¸ºè´æ°ï¼é置为0ã计ç®å¼: {expectedLockQty}"); |
| | | expectedLockQty = 0; |
| | | } |
| | | |
| | | // éªè¯åé
æ°é |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - expectedAllocatedQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"åæ¶æå
ååé
æ°éå¼å¸¸ - ææ: {expectedAllocatedQty}, å®é
: {orderDetail.AllocatedQuantity}"); |
| | | } |
| | | |
| | | // éªè¯é宿°é |
| | | if (Math.Abs(orderDetail.LockQuantity - expectedLockQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"åæ¶æå
åé宿°éå¼å¸¸ - ææ: {expectedLockQty}, å®é
: {orderDetail.LockQuantity}"); |
| | | } |
| | | |
| | | // éªè¯è®¢åæç»åé
æ°éä¸éå®ä¿¡æ¯æ»åé
æ°éçä¸è´æ§ |
| | | if (Math.Abs(orderDetail.AllocatedQuantity - totalLockAssignQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"åæ¶æå
åæ°æ®ä¸ä¸è´ - 订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQty}"); |
| | | } |
| | | |
| | | // è®°å½è¯¦ç»çä¸è´æ§æ¥å |
| | | _logger.LogInformation($"åæ¶æå
æ°æ®ä¸è´æ§æ¥å - " + |
| | | $"订åæç»åé
æ°é: {orderDetail.AllocatedQuantity}, " + |
| | | $"订åæç»é宿°é: {orderDetail.LockQuantity}, " + |
| | | $"éå®ä¿¡æ¯æ»åé
æ°é: {totalLockAssignQty}, " + |
| | | $"æå
ç±»å: {(isAutoSplit ? "èªå¨" : "æå¨")}, " + |
| | | $"æå
æ°é: {splitQuantity}"); |
| | | } |
| | | /// <summary> |
| | | /// éªè¯åæ¶æå
è¯·æ± |
| | | /// </summary> |
| | | private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest( |
| | | string orderNo, string palletCode, string newBarcode) |
| | | { |
| | | _logger.LogInformation($"å¼å§éªè¯åæ¶æå
è¯·æ± - 订å: {orderNo}, æç: {palletCode}, æ¡ç : {newBarcode}"); |
| | | |
| | | // æ¥æ¾æå
è®°å½ |
| | | var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.NewBarcode == newBarcode && |
| | | x.OrderNo == orderNo && |
| | | !x.IsReverted) |
| | | .FirstAsync(); |
| | | |
| | | if (splitRecord == null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æå
è®°å½"); |
| | | |
| | | _logger.LogInformation($"æ¾å°æå
è®°å½ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}, æå
æ°é: {splitRecord.SplitQty}"); |
| | | |
| | | // æ¥æ¾æ°éå®ä¿¡æ¯ |
| | | var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == newBarcode && |
| | | x.PalletCode == palletCode && |
| | | x.OrderNo == orderNo) |
| | | .FirstAsync(); |
| | | |
| | | if (newLockInfo == null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); |
| | | |
| | | _logger.LogInformation($"æ¾å°æ°éå®ä¿¡æ¯ - ç¶æ: {newLockInfo.Status}, å·²æ£é: {newLockInfo.PickedQty}, åé
æ°é: {newLockInfo.AssignQuantity}"); |
| | | |
| | | // æ£æ¥æ°æ¡ç æ¯å¦å·²è¢«åæ£ |
| | | var newBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == newBarcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | if (newBarcodePickingRecords.Any()) |
| | | { |
| | | var totalPickedQty = newBarcodePickingRecords.Sum(x => x.PickQuantity); |
| | | _logger.LogWarning($"æ°æ¡ç {newBarcode} å·²è¢«åæ£ï¼æ»æ£éæ°é: {totalPickedQty}"); |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error( |
| | | $"æ°æ¡ç å·²è¢«åæ£ï¼å·²æ£éæ°éï¼{totalPickedQty}ï¼ï¼è¯·å
忶忣ï¼ç¶åååæ¶æå
"); |
| | | } |
| | | |
| | | // æ£æ¥åæ¡ç æ¯å¦å·²è¢«åæ£ |
| | | var originalBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == splitRecord.OriginalBarcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | if (originalBarcodePickingRecords.Any()) |
| | | { |
| | | var totalPickedQty = originalBarcodePickingRecords.Sum(x => x.PickQuantity); |
| | | _logger.LogWarning($"åæ¡ç {splitRecord.OriginalBarcode} å·²è¢«åæ£ï¼æ»æ£éæ°é: {totalPickedQty}"); |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error( |
| | | $"åæ¡ç å·²è¢«åæ£ï¼å·²æ£éæ°éï¼{totalPickedQty}ï¼ï¼è¯·å
忶忣ï¼ç¶åååæ¶æå
"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ°æ§æ¡ç åæªè¢«åæ£ï¼å¯ä»¥åæ¶æå
"); |
| | | |
| | | // æ£æ¥æ°æ¡ç æ¯å¦è¢«å次æå
|
| | | var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted) |
| | | .ToListAsync(); |
| | | |
| | | if (childSplitRecords.Any()) |
| | | { |
| | | var childBarcodes = string.Join(", ", childSplitRecords.Select(x => x.NewBarcode)); |
| | | _logger.LogWarning($"æ¡ç {newBarcode} å·²è¢«åæ¬¡æå
ï¼çæçæ°æ¡ç : {childBarcodes}"); |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error( |
| | | $"该æ¡ç å·²è¢«åæ¬¡æå
ï¼çæçæ°æ¡ç ï¼{childBarcodes}ï¼è¯·å
åæ¶åç»æå
"); |
| | | } |
| | | |
| | | var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == newBarcode); |
| | | |
| | | if (newStockDetail == null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æ°åºåæç»"); |
| | | |
| | | _logger.LogInformation($"åæ¶æå
éªè¯éè¿ - æ¡ç : {newBarcode}"); |
| | | |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail)); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æ¹éåæ¶æå
é¾ |
| | | |
| | | /// <summary> |
| | | /// æ¹éåæ¶æå
é¾ - åæ¶æä¸ªæ¡ç åå
¶ææåç»æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> CancelSplitPackageChain(string orderNo, string palletCode, string startBarcode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // æ¥æ¾æç䏿ªå®æçéå®è®°å½ |
| | | var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToListAsync(); |
| | | // æ¥æ¾ææç¸å
³çæå
è®°å½ï¼å½¢ææå
é¾ï¼ |
| | | var splitChain = await GetSplitPackageChain(orderNo, startBarcode); |
| | | |
| | | if (!unfinishedLocks.Any()) |
| | | return WebResponseContent.Instance.Error("该æçæ²¡ææªå®æçéå®è®°å½"); |
| | | if (!splitChain.Any()) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°æå
è®°å½"); |
| | | |
| | | // ææ¹æ¬¡åç»å¤ç |
| | | var batchGroups = unfinishedLocks.GroupBy(x => x.BatchNo); |
| | | _logger.LogInformation($"æ¾å°æå
é¾ï¼å
± {splitChain.Count} æ¡è®°å½"); |
| | | |
| | | foreach (var batchGroup in batchGroups) |
| | | // æ¶éæå
é¾ä¸æ¶åçæææ¡ç ï¼å
æ¬åæ¡ç åæ°æ¡ç ï¼ |
| | | var allBarcodesInChain = new List<string> { startBarcode }; |
| | | allBarcodesInChain.AddRange(splitChain.Select(x => x.NewBarcode)); |
| | | |
| | | // æ£æ¥æå
é¾ä¸æ¯å¦æå·²è¢«åæ£çæ¡ç |
| | | var pickedBarcodesInfo = await GetPickedBarcodesInfo(orderNo, allBarcodesInChain); |
| | | |
| | | if (pickedBarcodesInfo.Any()) |
| | | { |
| | | var batchNo = batchGroup.Key; |
| | | var batchLocks = batchGroup.ToList(); |
| | | var pickedBarcodes = string.Join(", ", pickedBarcodesInfo.Select(x => $"{x.Barcode}(å·²æ£é{x.PickedQty})")); |
| | | return WebResponseContent.Instance.Error( |
| | | $"以䏿¡ç å·²è¢«åæ£ï¼è¯·å
忶忣ï¼{pickedBarcodes}"); |
| | | } |
| | | |
| | | // éæ¾åºååéå®è®°å½ |
| | | foreach (var lockInfo in batchLocks) |
| | | { |
| | | await ReleaseLockAndStock(lockInfo); |
| | | } |
| | | // ææå
顺åºååºåæ¶ï¼ä»ææ°çå¼å§åæ¶ï¼ |
| | | var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList(); |
| | | |
| | | // æ´æ°æ¹æ¬¡ç¶æ |
| | | await UpdateBatchStatusForReturn(batchNo, batchLocks); |
| | | |
| | | // æ´æ°è®¢åæç»çå·²åé
æ°é |
| | | await UpdateOrderDetailAfterReturn(batchLocks); |
| | | foreach (var splitRecord in reversedChain) |
| | | { |
| | | _logger.LogInformation($"åæ¶æå
è®°å½ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | await CancelSingleSplitPackage(splitRecord, palletCode); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("åæ¹ååºæå"); |
| | | return WebResponseContent.Instance.OK($"æååæ¶æå
é¾ï¼å
±{reversedChain.Count}次æå
æä½"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¹ååºå¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¹ååºå¤±è´¥ï¼{ex.Message}"); |
| | | _logger.LogError($"åæ¶æå
é¾å¤±è´¥ - OrderNo: {orderNo}, StartBarcode: {startBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¶æå
é¾å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// è·åå·²è¢«åæ£çæ¡ç ä¿¡æ¯ |
| | | /// </summary> |
| | | private async Task<List<PickedBarcodeInfo>> GetPickedBarcodesInfo(string orderNo, List<string> barcodes) |
| | | { |
| | | var pickedBarcodes = new List<PickedBarcodeInfo>(); |
| | | |
| | | foreach (var barcode in barcodes) |
| | | { |
| | | var pickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | if (pickingRecords.Any()) |
| | | { |
| | | var totalPickedQty = pickingRecords.Sum(x => x.PickQuantity); |
| | | pickedBarcodes.Add(new PickedBarcodeInfo |
| | | { |
| | | Barcode = barcode, |
| | | PickedQty = totalPickedQty, |
| | | PickRecordCount = pickingRecords.Count |
| | | }); |
| | | } |
| | | } |
| | | |
| | | return pickedBarcodes; |
| | | } |
| | | /// <summary> |
| | | /// è·åæå
é¾ - æ¥æ¾æä¸ªæ¡ç çæææå
è®°å½ï¼å
æ¬åç»æå
ï¼ |
| | | /// </summary> |
| | | public async Task<List<Dt_SplitPackageRecord>> GetSplitPackageChain(string orderNo, string startBarcode) |
| | | { |
| | | var allSplitRecords = new List<Dt_SplitPackageRecord>(); |
| | | var visitedBarcodes = new HashSet<string>(); // 鲿¢å¾ªç¯å¼ç¨ |
| | | |
| | | // 使ç¨éåè¿è¡å¹¿åº¦ä¼å
æç´¢ |
| | | var queue = new Queue<string>(); |
| | | queue.Enqueue(startBarcode); |
| | | visitedBarcodes.Add(startBarcode); |
| | | |
| | | while (queue.Count > 0) |
| | | { |
| | | var currentBarcode = queue.Dequeue(); |
| | | |
| | | // æ¥æ¾ä»¥å½åæ¡ç ä¸ºåæ¡ç çæææå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OriginalBarcode == currentBarcode && |
| | | x.OrderNo == orderNo && |
| | | !x.IsReverted) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var record in splitRecords) |
| | | { |
| | | // é¿å
éå¤å¤ç |
| | | if (!visitedBarcodes.Contains(record.NewBarcode)) |
| | | { |
| | | allSplitRecords.Add(record); |
| | | queue.Enqueue(record.NewBarcode); |
| | | visitedBarcodes.Add(record.NewBarcode); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return allSplitRecords; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¶å个æå
è®°å½ |
| | | /// </summary> |
| | | private async Task CancelSingleSplitPackage(Dt_SplitPackageRecord splitRecord, string palletCode) |
| | | { |
| | | _logger.LogInformation($"å¼å§åæ¶å个æå
è®°å½ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | |
| | | // 忬¡éªè¯åæ£ç¶æï¼é²æ¢å¹¶åæä½ï¼ |
| | | var newBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == splitRecord.NewBarcode && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | if (newBarcodePickingRecords.Any()) |
| | | { |
| | | throw new InvalidOperationException($"æ°æ¡ç {splitRecord.NewBarcode} å¨éªè¯åè¢«åæ£ï¼æ æ³åæ¶æå
"); |
| | | } |
| | | |
| | | var originalBarcodePickingRecords = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == splitRecord.OriginalBarcode && !x.IsCancelled) |
| | | .ToListAsync(); |
| | | |
| | | if (originalBarcodePickingRecords.Any()) |
| | | { |
| | | throw new InvalidOperationException($"åæ¡ç {splitRecord.OriginalBarcode} å¨éªè¯åè¢«åæ£ï¼æ æ³åæ¶æå
"); |
| | | } |
| | | |
| | | // æ¥æ¾ç¸å
³æ°æ® |
| | | var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == splitRecord.NewBarcode && |
| | | x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.NewBarcode); |
| | | |
| | | var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); |
| | | |
| | | // æ§è¡åæ¶é»è¾ |
| | | await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail); |
| | | |
| | | _logger.LogInformation($"åæ¶å个æå
è®°å½å®æ - åæ¡ç : {splitRecord.OriginalBarcode}, æ°æ¡ç : {splitRecord.NewBarcode}"); |
| | | } |
| | | #endregion |
| | | |
| | | #region æå
ä¿¡æ¯æ¥è¯¢å¢å¼º |
| | | |
| | | /// <summary> |
| | | /// è·åæå
é¾ä¿¡æ¯ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> GetSplitPackageChainInfo(string orderNo, string barcode) |
| | | { |
| | | try |
| | | { |
| | | var splitChain = await GetSplitPackageChain(orderNo, barcode); |
| | | |
| | | var chainInfo = new SplitPackageChainInfoDto |
| | | { |
| | | OriginalBarcode = barcode, |
| | | TotalSplitTimes = splitChain.Count, |
| | | SplitChain = splitChain.Select(x => new SplitChainItemDto |
| | | { |
| | | SplitTime = x.SplitTime, |
| | | OriginalBarcode = x.OriginalBarcode, |
| | | NewBarcode = x.NewBarcode, |
| | | SplitQuantity = x.SplitQty, |
| | | Operator = x.Operator, |
| | | IsReverted = x.IsReverted, |
| | | IsAutoSplit = x.IsAutoSplit |
| | | }).ToList() |
| | | }; |
| | | |
| | | return WebResponseContent.Instance.OK("è·åæå", chainInfo); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"è·åæå
é¾ä¿¡æ¯å¤±è´¥ - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error("è·åæå
é¾ä¿¡æ¯å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ¥æ¾æ ¹æ¡ç |
| | | /// </summary> |
| | | public async Task<string> FindRootBarcode(string orderNo, string startBarcode) |
| | | { |
| | | var currentBarcode = startBarcode; |
| | | var visited = new HashSet<string>(); |
| | | |
| | | while (!string.IsNullOrEmpty(currentBarcode) && !visited.Contains(currentBarcode)) |
| | | { |
| | | visited.Add(currentBarcode); |
| | | |
| | | // æ¥æ¾å½åæ¡ç æ¯å¦æ¯ç±å
¶ä»æ¡ç æå
èæ¥ |
| | | var parentRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.NewBarcode == currentBarcode && |
| | | x.OrderNo == orderNo && |
| | | !x.IsReverted) |
| | | .FirstAsync(); |
| | | |
| | | if (parentRecord == null) |
| | | { |
| | | // 没æç¶çº§æå
è®°å½ï¼è¯´æè¿æ¯æ ¹æ¡ç |
| | | return currentBarcode; |
| | | } |
| | | |
| | | currentBarcode = parentRecord.OriginalBarcode; |
| | | } |
| | | |
| | | // 妿åºç°å¾ªç¯å¼ç¨ï¼è¿åèµ·å§æ¡ç |
| | | return startBarcode; |
| | | } |
| | | #endregion |
| | | |
| | | #region æ´æ°æ¹æ¬¡ç¶ææ£æ¥ |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥å¹¶æ´æ°æ¹æ¬¡ç¶æ |
| | | /// </summary> |
| | | private async Task CheckAndUpdateBatchStatus(string batchNo) |
| | | { |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == batchNo); |
| | | |
| | | if (batch != null) |
| | | { |
| | | // éæ°è®¡ç®æ¹æ¬¡å®ææ°é |
| | | var batchLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.BatchNo == batchNo) |
| | | .ToListAsync(); |
| | | |
| | | var completedQuantity = batchLocks.Where(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿) |
| | | .Sum(x => x.PickedQty); |
| | | |
| | | batch.CompletedQuantity = completedQuantity; |
| | | |
| | | // æ´æ°æ¹æ¬¡ç¶æ |
| | | if (batch.CompletedQuantity >= batch.BatchQuantity) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | } |
| | | else if (batch.CompletedQuantity > 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | } |
| | | else |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.åé
ä¸; |
| | | } |
| | | |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region ç»ä¸ååºé»è¾ |
| | | |
| | | /// <summary> |
| | | /// ç»ä¸ååºæ¹æ³ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> ExecutePalletReturn(string orderNo, string palletCode, string returnReason = "åæ¹ååº") |
| | | { |
| | | ReturnTaskInfo returnTaskInfo = null; |
| | | try |
| | | { |
| | | _logger.LogInformation($"ãå¢å¼ºååºå¼å§ã订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // åºç¡éªè¯ |
| | | if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode)) |
| | | return WebResponseContent.Instance.Error("订åå·åæçç ä¸è½ä¸ºç©º"); |
| | | |
| | | // è·ååºåä¿¡æ¯ |
| | | var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode); |
| | | |
| | | if (stockInfo == null) |
| | | return WebResponseContent.Instance.Error($"æªæ¾å°æç {palletCode} 对åºçåºåä¿¡æ¯"); |
| | | |
| | | int stockId = stockInfo.Id; |
| | | |
| | | // æ§è¡ååºåæ°æ®éªè¯ |
| | | var validationResult = await ValidateDataBeforeReturn(orderNo, palletCode, stockId); |
| | | if (!validationResult.IsValid) |
| | | { |
| | | _logger.LogWarning($"ååºåæ°æ®éªè¯å¤±è´¥: {validationResult.ErrorMessage}"); |
| | | } |
| | | |
| | | // åææçç¶æ |
| | | var statusAnalysis = await AnalyzePalletStatusForReturn(orderNo, palletCode, stockInfo.Id); |
| | | |
| | | if (!statusAnalysis.HasItemsToReturn) |
| | | { |
| | | try |
| | | { |
| | | _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($"空箱ååºExecutePalletReturn失败: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空箱ååºExecutePalletReturn失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ãå¼å§ååºãæ»ååºæ°é: {statusAnalysis.TotalReturnQty}, æ¡ç æ°: {statusAnalysis.AllBarcodes.Count}"); |
| | | |
| | | // æ§è¡ååºæä½ |
| | | try |
| | | { |
| | | await ExecuteEnhancedReturnOperations(statusAnalysis); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"主ååºæ¹æ³å¤±è´¥: {ex.Message}"); |
| | | // å°è¯ç®åæ¹æ³ |
| | | await ExecuteSimpleReturnDataOperations(statusAnalysis); |
| | | } |
| | | |
| | | // æ´æ°è®¢åç¶æ |
| | | await UpdateOrderStatusAfterReturn(orderNo); |
| | | |
| | | // å建ååºä»»å¡ |
| | | try |
| | | { |
| | | // å建ååºä»»å¡ï¼ä½ä¸åéESSå½ä»¤ |
| | | returnTaskInfo = await CreateReturnTaskWithoutESS(orderNo, palletCode, stockInfo); |
| | | |
| | | } |
| | | catch (Exception taskEx) |
| | | { |
| | | _logger.LogError($"ååºä»»å¡CreateReturnTaskWithoutESSå建失败: {taskEx.Message}"); |
| | | // ä»»å¡å建失败ä¸å½±åæ°æ®ååº |
| | | } |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | // å¨äºå¡æäº¤åå¤çESSå½ä»¤ |
| | | if (returnTaskInfo != null && returnTaskInfo.ShouldSendESS) |
| | | { |
| | | await ProcessESSAfterTransaction(palletCode, returnTaskInfo); |
| | | } |
| | | // ååºåéªè¯ |
| | | await ValidateDataAfterReturn(orderNo, palletCode, stockId); |
| | | |
| | | return WebResponseContent.Instance.OK($"ååºæåï¼ååºæ°éï¼{statusAnalysis.TotalReturnQty}", new |
| | | { |
| | | ReturnQuantity = statusAnalysis.TotalReturnQty, |
| | | ReturnBarcodes = statusAnalysis.AllBarcodes, |
| | | Reason = returnReason, |
| | | PalletCode = palletCode, |
| | | OrderNo = orderNo |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _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> |
| | | private async Task<ValidationResult<bool>> ValidateDataBeforeReturn(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var errors = new List<string>(); |
| | | |
| | | try |
| | | { |
| | | // éªè¯åºåæ°æ® |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .ToListAsync(); |
| | | |
| | | // æ£æ¥åºåæ°éæ¯å¦ä¸ºè´æ° |
| | | var negativeStocks = stockDetails.Where(x => x.StockQuantity < 0).ToList(); |
| | | if (negativeStocks.Any()) |
| | | { |
| | | errors.Add($"åç°è´æ°åºå: {string.Join(", ", negativeStocks.Select(x => $"{x.Barcode}:{x.StockQuantity}"))}"); |
| | | } |
| | | |
| | | // éªè¯éå®è®°å½ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | // æ£æ¥å·²æ£éæ°éæ¯å¦å¤§äºåé
æ°é |
| | | var invalidLocks = lockInfos.Where(x => x.PickedQty > x.AssignQuantity).ToList(); |
| | | if (invalidLocks.Any()) |
| | | { |
| | | errors.Add($"åç°å·²æ£éæ°é大äºåé
æ°éçéå®è®°å½"); |
| | | } |
| | | |
| | | // éªè¯æå
è®°å½ |
| | | var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | // æ£æ¥å¾ªç¯æå
|
| | | var barcodeMap = new Dictionary<string, string>(); |
| | | foreach (var record in splitRecords.Where(x => !x.IsReverted)) |
| | | { |
| | | if (!barcodeMap.ContainsKey(record.OriginalBarcode)) |
| | | barcodeMap[record.OriginalBarcode] = record.NewBarcode; |
| | | } |
| | | |
| | | // æ£æ¥å¾ªç¯å¼ç¨ |
| | | foreach (var record in splitRecords) |
| | | { |
| | | var current = record.OriginalBarcode; |
| | | var visited = new HashSet<string>(); |
| | | |
| | | while (barcodeMap.ContainsKey(current)) |
| | | { |
| | | if (visited.Contains(current)) |
| | | { |
| | | errors.Add($"åç°å¾ªç¯æå
å¼ç¨: {record.OriginalBarcode}"); |
| | | break; |
| | | } |
| | | visited.Add(current); |
| | | current = barcodeMap[current]; |
| | | } |
| | | } |
| | | |
| | | if (errors.Any()) |
| | | { |
| | | return ValidationResult<bool>.Error($"ååºåæ°æ®éªè¯åç°é®é¢: {string.Join("; ", errors)}"); |
| | | } |
| | | |
| | | return ValidationResult<bool>.Success(true); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"æ°æ®éªè¯å¤±è´¥: {ex.Message}"); |
| | | return ValidationResult<bool>.Error($"æ°æ®éªè¯å¼å¸¸: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | // <summary> |
| | | /// æ§è¡å¢å¼ºçååºæä½ - ä¿®æ£çï¼é¿å
éå¤å¤çï¼ |
| | | /// </summary> |
| | | private async Task ExecuteEnhancedReturnOperations(PalletStatusAnalysis statusAnalysis) |
| | | { |
| | | _logger.LogInformation($"ãå¼å§æ§è¡ååºæä½ã订å: {statusAnalysis.OrderNo}, æç: {statusAnalysis.PalletCode}"); |
| | | |
| | | // ä½¿ç¨æ¬å°å·²å¤çéåï¼é¿å
éå¤ |
| | | var locallyProcessedBarcodes = new HashSet<string>(); |
| | | decimal totalProcessedQty = 0; |
| | | |
| | | // å¤çå·²åé
çéå®è®°å½ |
| | | if (statusAnalysis.HasRemainingLocks) |
| | | { |
| | | _logger.LogInformation($"å¤çå·²åé
éå®è®°å½ - {statusAnalysis.RemainingLocks.Count} æ¡"); |
| | | foreach (var lockInfo in statusAnalysis.RemainingLocks) |
| | | { |
| | | if (string.IsNullOrEmpty(lockInfo.CurrentBarcode) || |
| | | locallyProcessedBarcodes.Contains(lockInfo.CurrentBarcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²å¤çæ¡ç çéå®è®°å½: {lockInfo.CurrentBarcode}"); |
| | | continue; |
| | | } |
| | | |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | if (returnQty > 0) |
| | | { |
| | | // ãä¿®å¤ãä¼ é isUnallocated = false |
| | | await ProcessSingleBarcodeReturn(lockInfo.CurrentBarcode, statusAnalysis.StockId, returnQty, false); |
| | | locallyProcessedBarcodes.Add(lockInfo.CurrentBarcode); |
| | | totalProcessedQty += returnQty; |
| | | _logger.LogInformation($"å·²å¤çéå®è®°å½ - æ¡ç : {lockInfo.CurrentBarcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¤çæªåé
çéå®è®°å½ |
| | | if (statusAnalysis.HasUnallocatedLocks) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
éå®è®°å½ - {statusAnalysis.UnallocatedLocks.Count} æ¡"); |
| | | foreach (var lockInfo in statusAnalysis.UnallocatedLocks) |
| | | { |
| | | if (string.IsNullOrEmpty(lockInfo.CurrentBarcode) || |
| | | locallyProcessedBarcodes.Contains(lockInfo.CurrentBarcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²å¤çæ¡ç çæªåé
éå®: {lockInfo.CurrentBarcode}"); |
| | | continue; |
| | | } |
| | | |
| | | decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | if (returnQty > 0) |
| | | { |
| | | // ãä¿®å¤ã使ç¨ä¸é¨çæªåé
éå®å¤çæ¹æ³ |
| | | await ProcessUnallocatedLockReturn(lockInfo, returnQty); |
| | | locallyProcessedBarcodes.Add(lockInfo.CurrentBarcode); |
| | | totalProcessedQty += returnQty; |
| | | _logger.LogInformation($"å·²å¤çæªåé
éå® - æ¡ç : {lockInfo.CurrentBarcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¤çæªåé
çåºåè´§ç© |
| | | if (statusAnalysis.HasPalletStockGoods) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
åºåè´§ç© - {statusAnalysis.PalletStockGoods.Count} 个"); |
| | | foreach (var stockDetail in statusAnalysis.PalletStockGoods) |
| | | { |
| | | if (string.IsNullOrEmpty(stockDetail.Barcode) || |
| | | locallyProcessedBarcodes.Contains(stockDetail.Barcode)) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²å¤çæ¡ç çåºå: {stockDetail.Barcode}"); |
| | | continue; |
| | | } |
| | | |
| | | decimal returnQty = stockDetail.StockQuantity; |
| | | if (returnQty > 0) |
| | | { |
| | | await ProcessUnallocatedStockReturn(stockDetail); |
| | | locallyProcessedBarcodes.Add(stockDetail.Barcode); |
| | | totalProcessedQty += returnQty; |
| | | _logger.LogInformation($"å·²å¤çæªåé
åºå - æ¡ç : {stockDetail.Barcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // å¤çæå
è®°å½ - åªå¤çæªè¢«å
¶ä»é»è¾è¦ççæ¡ç |
| | | if (statusAnalysis.HasSplitRecords && statusAnalysis.SplitReturnQty > 0) |
| | | { |
| | | _logger.LogInformation($"å¤çæå
è®°å½ç¸å
³åºå - æ°å¢æ°é: {statusAnalysis.SplitReturnQty}"); |
| | | |
| | | // åªå¤çå¨SplitRecordsä¸ä½ä¸å¨å·²å¤çéåä¸çæ¡ç |
| | | var splitBarcodes = statusAnalysis.SplitRecords |
| | | .SelectMany(r => new[] { r.OriginalBarcode, r.NewBarcode }) |
| | | .Where(b => !string.IsNullOrEmpty(b)) |
| | | .Distinct() |
| | | .ToList(); |
| | | |
| | | foreach (var barcode in splitBarcodes) |
| | | { |
| | | if (locallyProcessedBarcodes.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; |
| | | await ProcessSingleBarcodeReturn(barcode, statusAnalysis.StockId, returnQty); |
| | | locallyProcessedBarcodes.Add(barcode); |
| | | totalProcessedQty += returnQty; |
| | | _logger.LogInformation($"å¤çæå
æ¡ç - {barcode}, æ°é: {returnQty}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ãååºæä½å®æãæ»å¤çæ°é: {totalProcessedQty}, å¤çæ¡ç æ°: {locallyProcessedBarcodes.Count}"); |
| | | |
| | | // éªè¯å¤çæ°éä¸é¢æä¸è´ |
| | | if (Math.Abs(totalProcessedQty - statusAnalysis.TotalReturnQty) > 0.01m) |
| | | { |
| | | _logger.LogWarning($"å¤çæ°é({totalProcessedQty})ä¸é¢æ({statusAnalysis.TotalReturnQty})ä¸ä¸è´ï¼ä½ç»§ç»æ§è¡"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// å¤çå个æ¡ç ååºï¼éç¨æ¹æ³ï¼- åºåå·²åé
åæªåé
|
| | | /// </summary> |
| | | private async Task ProcessSingleBarcodeReturn(string barcode, int stockId, decimal returnQty, bool isUnallocated = false) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¤çå个æ¡ç ååº - {barcode}, æ°é: {returnQty}, æ¯å¦æªåé
: {isUnallocated}"); |
| | | |
| | | 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)}"); |
| | | |
| | | // æ ¹æ®æ¯å¦æªåé
å³å®å¤çé»è¾ |
| | | if (isUnallocated) |
| | | { |
| | | // æªåé
éå®ï¼åªæ¢å¤ç¶æï¼ä¸æ¹ååºåæ°é |
| | | // åºåºæ°éåºä¸º0 |
| | | if (stockDetail.OutboundQuantity > 0) |
| | | { |
| | | _logger.LogWarning($"æªåé
éå®çåºååºåºæ°éä¸ä¸º0ï¼é置为0 - æ¡ç : {stockDetail.Barcode}, å½ååºåº: {stockDetail.OutboundQuantity}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // åºåæ°éä¿æä¸å |
| | | _logger.LogInformation($"æªåé
éå®ååº - åºåæ°éä¿æä¸å: {stockDetail.StockQuantity}"); |
| | | } |
| | | else |
| | | { |
| | | // å·²åé
éå®ï¼æ¢å¤åºåæ°é |
| | | stockDetail.StockQuantity += returnQty; |
| | | |
| | | // åå°åºåºæ°éï¼å¦æåºåºæ°é大äº0ï¼ |
| | | if (stockDetail.OutboundQuantity >= returnQty) |
| | | { |
| | | stockDetail.OutboundQuantity -= returnQty; |
| | | } |
| | | else |
| | | { |
| | | // 妿åºåºæ°éå°äºååºæ°éï¼è¯´ææ°æ®å¼å¸¸ï¼åºåºæ°éæ¸
é¶ |
| | | _logger.LogWarning($"åºåºæ°é({stockDetail.OutboundQuantity})å°äºååºæ°é({returnQty})ï¼æ¸
é¶"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | _logger.LogInformation($"å·²åé
éå®ååº - åºåæ°éå¢å : {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | } |
| | | |
| | | // æ´æ°ç¶æ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¡ç ååºå®æ - {barcode}: åºå {originalStockQty}->{stockDetail.StockQuantity}, åºåº {originalOutboundQty}->{stockDetail.OutboundQuantity}"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"å¤çæ¡ç ååºå¤±è´¥ - {barcode}: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// å¤çæªåé
éå®è®°å½ååº - ä¿®å¤çæ¬ |
| | | /// æªåé
éå®è®°å½ï¼åªæ¢å¤ç¶æï¼ä¸å¢å åºåæ°é |
| | | /// </summary> |
| | | private async Task ProcessUnallocatedLockReturn(Dt_OutStockLockInfo lockInfo, decimal returnQty) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
éå®ååº - éå®ID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, æ°é: {returnQty}"); |
| | | |
| | | try |
| | | { |
| | | // éªè¯éå®è®°å½ç¶æ |
| | | if (lockInfo.Status != (int)OutLockStockStatusEnum.åºåºä¸ && |
| | | lockInfo.Status != (int)OutLockStockStatusEnum.ååºä¸) |
| | | { |
| | | _logger.LogWarning($"éå®è®°å½ç¶æä¸æ¯åºåºä¸æååºä¸ï¼è·³è¿å¤ç - ç¶æ: {lockInfo.Status}"); |
| | | return; |
| | | } |
| | | |
| | | // è·ååºåæç» |
| | | 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}"); |
| | | } |
| | | |
| | | // è®°å½åå§ç¶æ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | _logger.LogInformation($"æªåé
éå®ååºåç¶æ:"); |
| | | _logger.LogInformation($" åºå - æ¡ç : {stockDetail.Barcode}, æ°é: {originalStockQty}, åºåº: {originalOutboundQty}, ç¶æ: {GetStockStatusName(originalStatus)}"); |
| | | |
| | | // å¯¹äºæªåé
éå®è®°å½ï¼åºåæ°éä¸åºæ¹åï¼ |
| | | // å 为åºåæ¬æ¥å°±åå¨ï¼åªæ¯ç¶æè¢«éå® |
| | | // stockDetail.StockQuantity ä¿æä¸å |
| | | |
| | | // åºåºæ°éåºä¸º0ï¼æªåé
éå®ä¸åºè¯¥æåºåºï¼ |
| | | if (stockDetail.OutboundQuantity > 0) |
| | | { |
| | | _logger.LogWarning($"æªåé
éå®çåºååºåºæ°éä¸ä¸º0ï¼é置为0 - æ¡ç : {stockDetail.Barcode}, å½ååºåº: {stockDetail.OutboundQuantity}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | // æ´æ°åºåç¶æä¸ºå
¥åºå®æï¼æ¢å¤ä¸ºå¯ç¨ç¶æï¼ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°æªåé
åºåç¶æ - æ¡ç : {stockDetail.Barcode}"); |
| | | _logger.LogInformation($" åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity} (ä¿æä¸å)"); |
| | | _logger.LogInformation($" åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | _logger.LogInformation($" ç¶æ: {GetStockStatusName(originalStatus)} -> {GetStockStatusName(stockDetail.Status)}"); |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååº |
| | | 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($"å¤çæªåé
éå®ååºå¤±è´¥ - éå®ID: {lockInfo.Id}, Error: {ex.Message}"); |
| | | throw new InvalidOperationException($"å¤çæªåé
éå®ååºå¤±è´¥: {ex.Message}", ex); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤çæªåé
åºåååº |
| | | /// </summary> |
| | | private async Task ProcessUnallocatedStockReturn(Dt_StockInfoDetail stockDetail) |
| | | { |
| | | _logger.LogInformation($"å¤çæªåé
åºåååº - æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | |
| | | // ç´æ¥æ´æ°åºåç¶æ |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// ååºåæ°æ®éªè¯ - å¢å¼ºç |
| | | /// </summary> |
| | | private async Task ValidateDataAfterReturn(string orderNo, string palletCode, int stockId) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"å¼å§ååºåæ°æ®éªè¯"); |
| | | |
| | | // éªè¯åºåç¶æåæ°é |
| | | var stockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId) |
| | | .ToListAsync(); |
| | | |
| | | decimal totalStock = stockDetails.Sum(x => x.StockQuantity); |
| | | |
| | | // éªè¯åºåæ°éæ¯å¦åç |
| | | var unreasonableStocks = stockDetails.Where(x => x.StockQuantity < 0).ToList(); |
| | | if (unreasonableStocks.Any()) |
| | | { |
| | | _logger.LogError($"åç°è´æ°åºåæ°éï¼æ¡ç : {string.Join(", ", unreasonableStocks.Select(x => x.Barcode))}"); |
| | | } |
| | | |
| | | // éªè¯éå®è®°å½ç¶æ |
| | | var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | var notReturnedLocks = lockInfos.Where(x => |
| | | x.Status != (int)OutLockStockStatusEnum.å·²ååº && |
| | | x.Status != (int)OutLockStockStatusEnum.å·²åèµ° && |
| | | x.Status != (int)OutLockStockStatusEnum.æ£é宿).ToList(); |
| | | |
| | | if (notReturnedLocks.Any()) |
| | | { |
| | | _logger.LogWarning($"ååºå仿æªååºç¶æçéå®è®°å½: {notReturnedLocks.Count}æ¡"); |
| | | foreach (var lockInfo in notReturnedLocks) |
| | | { |
| | | _logger.LogWarning($"æªååºéå® - ID: {lockInfo.Id}, ç¶æ: {GetLockStatusName(lockInfo.Status)}, æ¡ç : {lockInfo.CurrentBarcode}"); |
| | | } |
| | | } |
| | | |
| | | // æ°æ®ä¸è´æ§éªè¯ |
| | | decimal totalExpectedReturnQty = lockInfos |
| | | .Where(x => x.Status == (int)OutLockStockStatusEnum.å·²ååº) |
| | | .Sum(x => x.AssignQuantity - x.PickedQty); |
| | | |
| | | _logger.LogInformation($"ååºéªè¯æ±æ»:"); |
| | | _logger.LogInformation($" ååºååºåæ»é: {totalStock}"); |
| | | _logger.LogInformation($" å·²ååºéå®è®°å½æ°é: {lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.å·²ååº)}"); |
| | | _logger.LogInformation($" æ»ååºæ°éï¼éå®è®°å½è®¡ç®ï¼: {totalExpectedReturnQty}"); |
| | | |
| | | // éªè¯åºåæ°éä¸éå®è®°å½çä¸è´æ§ |
| | | foreach (var lockInfo in lockInfos.Where(x => !string.IsNullOrEmpty(x.CurrentBarcode))) |
| | | { |
| | | var stock = stockDetails.FirstOrDefault(x => x.Barcode == lockInfo.CurrentBarcode); |
| | | if (stock != null) |
| | | { |
| | | // 妿éå®è®°å½æ¯å·²ååºç¶æï¼å¯¹åºçåºååºè¯¥æ¯å
¥åºå®æç¶æ |
| | | if (lockInfo.Status == (int)OutLockStockStatusEnum.å·²ååº && |
| | | stock.Status != (int)StockStatusEmun.å
¥åºå®æ) |
| | | { |
| | | _logger.LogWarning($"éå®è®°å½å·²ååºä½åºåç¶æä¸æ£ç¡® - æ¡ç : {lockInfo.CurrentBarcode}, åºåç¶æ: {GetStockStatusName(stock.Status)}"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"ååºåæ°æ®éªè¯å®æ"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ååºåéªè¯å¤±è´¥: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | |
| | | private string GetLockStatusName(int status) |
| | | { |
| | | return status switch |
| | | { |
| | | 1 => "åºåºä¸", |
| | | 2 => "æ£é宿", |
| | | 3 => "å·²ååº", |
| | | 4 => "å·²åèµ°", |
| | | _ => $"æªç¥({status})" |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ç®åååºæ°æ®æä½ï¼å½ä¸»æ¹æ³å¤±è´¥æ¶ä½¿ç¨ï¼ |
| | | /// </summary> |
| | | private async Task ExecuteSimpleReturnDataOperations(PalletStatusAnalysis statusAnalysis) |
| | | { |
| | | _logger.LogInformation($"ãç®åååºãå¼å§æ§è¡ç®åååºæä½"); |
| | | |
| | | try |
| | | { |
| | | // è·å该æççæææ¡ç ï¼å
æ¬ææç¶æçåºåï¼ |
| | | var allStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == statusAnalysis.StockId) |
| | | .ToListAsync(); |
| | | |
| | | _logger.LogInformation($"æ¾å° {allStockDetails.Count} 个åºåæç»è®°å½"); |
| | | |
| | | foreach (var stockDetail in allStockDetails) |
| | | { |
| | | // è®°å½åå§ç¶æ |
| | | int originalStatus = stockDetail.Status; |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | |
| | | // å°ææåºåºç¸å
³çç¶ææ¢å¤ä¸ºå
¥åºå®æ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå® || |
| | | //stockDetail.Status == (int)StockStatusEmun.åºåºä¸ || |
| | | stockDetail.Status == (int)StockStatusEmun.åºåºå®æ) |
| | | { |
| | | // 妿æ¯åºåºå®æç¶æä¸åºå为0ï¼å¯è½éè¦ç¹æ®å¤ç |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºå®æ && stockDetail.StockQuantity == 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿å·²åºåºå®æçé¶åºåæ¡ç : {stockDetail.Barcode}"); |
| | | continue; |
| | | } |
| | | |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | |
| | | // 妿æ¯åºåºéå®ç¶æä½åºå为0ï¼éç½®åºå为1ï¼é¿å
é¶åºåé®é¢ï¼ |
| | | if (stockDetail.Status == (int)StockStatusEmun.åºåºéå® && stockDetail.StockQuantity == 0) |
| | | { |
| | | // æ¥æ¾æ¯å¦æå¯¹åºçéå®è®°å½æ¥ç¡®å®åºè¯¥æ¢å¤çæ°é |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == stockDetail.Barcode && |
| | | x.StockId == stockDetail.StockId) |
| | | .FirstAsync(); |
| | | |
| | | if (lockInfo != null) |
| | | { |
| | | stockDetail.StockQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | _logger.LogInformation($"æ¢å¤é¶åºåæ¡ç çæ°é - æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"ç®åååº - æ¡ç : {stockDetail.Barcode}, ç¶æ: {GetStockStatusName(originalStatus)} -> å
¥åºå®æ, æ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | } |
| | | } |
| | | |
| | | // æ´æ°ææéå®è®°å½ä¸ºå·²ååº |
| | | var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == statusAnalysis.OrderNo && |
| | | x.PalletCode == statusAnalysis.PalletCode && |
| | | (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.æ£é宿)) |
| | | .ToListAsync(); |
| | | |
| | | foreach (var lockInfo in allLocks) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | lockInfo.Operator = App.User.UserName; |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"ç®åååº - éå®è®°å½: {lockInfo.Id}, ç¶æ: å·²ååº"); |
| | | } |
| | | |
| | | _logger.LogInformation($"ãç®åååºã宿 - å¤ç {allStockDetails.Count} 个æ¡ç , {allLocks.Count} æ¡éå®è®°å½"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ç®åååºå¤±è´¥: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// è·ååºåç¶æåç§° |
| | | /// </summary> |
| | | private string GetStockStatusName(int status) |
| | | { |
| | | return status switch |
| | | { |
| | | 1 => "å
¥åºç¡®è®¤", |
| | | 2 => "å
¥åºå®æ", |
| | | 3 => "åºåºéå®", |
| | | 4 => "åºåºä¸", |
| | | 5 => "åºåºå®æ", |
| | | 6 => "å·²æ¸
ç", |
| | | _ => $"æªç¥({status})" |
| | | }; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// å建ååºä»»å¡ |
| | | /// </summary> |
| | | private async Task CreateReturnTask(string orderNo, string palletCode, Dt_StockInfo stockInfo) |
| | | { |
| | | // è·åå½åä»»å¡ä¿¡æ¯ |
| | | var currentTask = await _taskRepository.Db.Queryable<Dt_Task>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .FirstAsync(); |
| | | |
| | | if (currentTask != 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 |
| | | }; |
| | | var targetAddress = currentTask.TargetAddress; |
| | | |
| | | try |
| | | { |
| | | await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"CreateReturnTaskAndHandleESS åæ¹å é¤åå²ä»»å¡: {orderNo} ï¼ {currentTask.TaskNum}"); |
| | | // å é¤åå§åºåºä»»å¡ |
| | | //_taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.èªå¨å®æ); |
| | | var result = _task_HtyService.DeleteAndMoveIntoHty(currentTask, OperateTypeEnum.人工å é¤); |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | |
| | | if (!result) |
| | | { |
| | | await _taskRepository.Db.Deleteable(currentTask).ExecuteCommandAsync(); |
| | | } |
| | | _logger.LogInformation($"CreateReturnTaskAndHandleESS åæ¹å é¤åå²ä»»å¡: {orderNo} ï¼ {currentTask.TaskNum},å½±åè¡ {result}"); |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogInformation($"å建ååºä»»å¡å¤±è´¥ - 订å: {orderNo}, æç: {palletCode}"); |
| | | throw new Exception($"å建ååºä»»å¡å¤±è´¥ - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | } |
| | | // åéESSå½ä»¤ |
| | | await SendESSCommands(palletCode, targetAddress, returnTask); |
| | | |
| | | _logger.LogInformation($"å建ååºä»»å¡æå - 订å: {orderNo}, æç: {palletCode}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// æ´æ°ååºåç订åç¶æ |
| | | /// </summary> |
| | | private async Task UpdateOrderStatusAfterReturn(string orderNo) |
| | | { |
| | | // æ£æ¥è®¢åæ¯å¦è¿ææªå®æçéå®è®°å½ |
| | | var activeLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸)) |
| | | .ToListAsync(); |
| | | |
| | | 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}"); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// å¤ç空æçååº |
| | | /// </summary> |
| | | private async Task<WebResponseContent> HandleEmptyPalletReturn(string orderNo, string palletCode, Dt_StockInfo stockInfo) |
| | | { |
| | | _logger.LogInformation($"å¤ç空æçååº - 订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | try |
| | | { |
| | | // æ¸
çé¶åºåæ°æ® |
| | | await CleanupZeroStockData(stockInfo.Id); |
| | | |
| | | // è·åå½åä»»å¡ |
| | | var currentTask = await GetCurrentTask(orderNo, palletCode); |
| | | if (currentTask == null) |
| | | { |
| | | return WebResponseContent.Instance.Error("æªæ¾å°å½åä»»å¡"); |
| | | } |
| | | // å建空æçåºåè®°å½ |
| | | var emptyStockInfo = new Dt_StockInfo() |
| | | { |
| | | PalletType = PalletTypeEnum.Empty.ObjToInt(), |
| | | StockStatus = StockStatusEmun.ç»çæå.ObjToInt(), |
| | | PalletCode = palletCode, |
| | | LocationType = stockInfo.LocationType |
| | | }; |
| | | emptyStockInfo.Details = new List<Dt_StockInfoDetail>(); |
| | | _stockInfoService.AddMaterielGroup(emptyStockInfo); |
| | | |
| | | // å建ååºä»»å¡ï¼ä¸åéESSå½ä»¤ï¼ |
| | | var returnTaskInfo = await CreateEmptyPalletReturnTask(orderNo, palletCode, emptyStockInfo, currentTask); |
| | | |
| | | return WebResponseContent.Instance.OK("空æçååºæå", returnTaskInfo); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"空æçååºå¤±è´¥ HandleEmptyPalletReturn: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"空æçååºå¤±è´¥ HandleEmptyPalletReturn: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | 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> |
| | | /// åææçç¶æç¨äºååº - ä¿®æ£çï¼è§£å³æå
è®°å½éå¤è®¡ç®é®é¢ï¼ |
| | | /// </summary> |
| | | private async Task<PalletStatusAnalysis> AnalyzePalletStatusForReturn(string orderNo, string palletCode, int stockId) |
| | | { |
| | | var result = new PalletStatusAnalysis |
| | | { |
| | | OrderNo = orderNo, |
| | | PalletCode = palletCode, |
| | | StockId = stockId, |
| | | AllBarcodes = new List<string>(), |
| | | ProcessedBarcodes = new HashSet<string>() |
| | | }; |
| | | |
| | | _logger.LogInformation($"ãååºåæãå¼å§åææçç¶æ - 订å: {orderNo}, æç: {palletCode}, StockId: {stockId}"); |
| | | |
| | | try |
| | | { |
| | | // é¦å
è·åæç䏿æçåºåæç»ï¼åºç¡æ°æ®ï¼ |
| | | var allStockDetails = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity > 0) |
| | | .ToListAsync(); |
| | | |
| | | _logger.LogInformation($"æ¾å° {allStockDetails.Count} 个æåºåçæç»è®°å½"); |
| | | |
| | | // åæææéå®è®°å½ï¼å·²åé
åæªåé
ï¼ |
| | | var allLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | // åºåå·²åé
åæªåé
éå®è®°å½ |
| | | var allocatedLocks = allLockInfos |
| | | .Where(x => x.IsUnallocated != 1 && x.OrderDetailId > 0 && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToList(); |
| | | |
| | | var unallocatedLocks = allLockInfos |
| | | .Where(x => (x.IsUnallocated == 1 || x.OrderDetailId == 0) && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToList(); |
| | | |
| | | // å¤çå·²åé
éå®è®°å½ |
| | | if (allocatedLocks.Any()) |
| | | { |
| | | result.HasRemainingLocks = true; |
| | | result.RemainingLocks = allocatedLocks; |
| | | |
| | | // 对äºå·²åé
éå®è®°å½ï¼ååºæ°éæ¯æªæ£éçé¨å |
| | | result.RemainingLocksReturnQty = allocatedLocks.Sum(x => |
| | | { |
| | | var returnQty = x.AssignQuantity - x.PickedQty; |
| | | return returnQty > 0 ? returnQty : 0; |
| | | }); |
| | | |
| | | foreach (var lockInfo in allocatedLocks) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) |
| | | { |
| | | result.AllBarcodes.Add(lockInfo.CurrentBarcode); |
| | | result.ProcessedBarcodes.Add(lockInfo.CurrentBarcode); |
| | | } |
| | | } |
| | | _logger.LogInformation($"åç° {allocatedLocks.Count} æ¡å·²åé
éå®è®°å½ï¼ååºæ°é: {result.RemainingLocksReturnQty}"); |
| | | } |
| | | |
| | | // å¤çæªåé
éå®è®°å½ï¼èªå¨æå
产ççï¼ |
| | | if (unallocatedLocks.Any()) |
| | | { |
| | | result.HasUnallocatedLocks = true; |
| | | result.UnallocatedLocks = unallocatedLocks; |
| | | |
| | | // å¯¹äºæªåé
éå®è®°å½ï¼ååºæ°éæ¯å®çåé
æ°éï¼å ä¸ºæªæ£éè¿ï¼ |
| | | // ä½å®é
ä¸ï¼åºåæ¬æ¥å°±åå¨ï¼åªæ¯ç¶æéè¦æ¢å¤ |
| | | result.UnallocatedLocksReturnQty = unallocatedLocks.Sum(x => x.AssignQuantity); |
| | | |
| | | foreach (var lockInfo in unallocatedLocks) |
| | | { |
| | | if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode) && |
| | | !result.ProcessedBarcodes.Contains(lockInfo.CurrentBarcode)) |
| | | { |
| | | result.AllBarcodes.Add(lockInfo.CurrentBarcode); |
| | | result.ProcessedBarcodes.Add(lockInfo.CurrentBarcode); |
| | | } |
| | | } |
| | | _logger.LogInformation($"åç° {unallocatedLocks.Count} æ¡æªåé
éå®è®°å½ï¼ååºæ°éï¼ç¶ææ¢å¤ï¼: {result.UnallocatedLocksReturnQty}"); |
| | | } |
| | | |
| | | // éæ°è®¡ç®æ»ååºæ°é |
| | | // 对äºå·²åé
éå®ï¼ååºæ°é = æªæ£éæ°é |
| | | // å¯¹äºæªåé
éå®ï¼æ²¡æå®é
çåºåæ°éååï¼åªæ¯ç¶ææ¢å¤ |
| | | result.TotalReturnQty = result.RemainingLocksReturnQty; // åªè®¡ç®å·²åé
éå®çååºæ°é |
| | | |
| | | // è®°å½åºåæ°éï¼ç¨äºéªè¯ï¼ |
| | | decimal totalStockOnPallet = allStockDetails.Sum(x => x.StockQuantity); |
| | | |
| | | _logger.LogInformation($"ååºåæå®æ:"); |
| | | _logger.LogInformation($" æçæ»åºå: {totalStockOnPallet}"); |
| | | _logger.LogInformation($" å·²åé
éå®ååºæ°é: {result.RemainingLocksReturnQty}"); |
| | | _logger.LogInformation($" æªåé
éå®ç¶ææ¢å¤æ°é: {result.UnallocatedLocksReturnQty}"); |
| | | _logger.LogInformation($" å®é
ç©çååºæ°é: {result.TotalReturnQty}"); |
| | | |
| | | result.HasItemsToReturn = result.TotalReturnQty > 0 || result.UnallocatedLocksReturnQty > 0; |
| | | result.IsEmptyPallet = !result.HasItemsToReturn; |
| | | |
| | | return result; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ååºåæå¤±è´¥ - 订å: {orderNo}, æç: {palletCode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// åæ¹ååº - è°ç¨ç»ä¸ååºæ¹æ³ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode) |
| | | { |
| | | return await ExecutePalletReturn(orderNo, palletCode, "åæ¹ååº"); |
| | | } |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// å走空箱 -æ£ç¡®å¤çæªåé
éå®è®°å½ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | _logger.LogInformation($"ãå走空箱å¼å§ã订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // å
å°è¯æ§è¡ååºæä½ï¼ç¡®ä¿ææç©åé½ååº |
| | | _logger.LogInformation($"æ¥éª¤1: å
æ§è¡ååºæä½"); |
| | | var returnResult = await ExecutePalletReturn(orderNo, palletCode, "å走空箱åååº"); |
| | | |
| | | // å³ä½¿ååºå¤±è´¥ï¼ç»§ç»éªè¯ç©ºç®±æ¡ä»¶ï¼å¯è½æ¯çç空æçï¼ |
| | | if (!returnResult.Status) |
| | | { |
| | | _logger.LogWarning($"ååºæä½å¯è½å¤±è´¥ææ ç©å: {returnResult.Message}"); |
| | | } |
| | | |
| | | // éªè¯ç©ºç®±åèµ°æ¡ä»¶ï¼å¿
é¡»å
¨é¨å®ææ£éæå·²ååºï¼ |
| | | _logger.LogInformation($"æ¥éª¤2: éªè¯ç©ºç®±åèµ°æ¡ä»¶"); |
| | | |
| | | // è·åæççææéå®è®°å½ï¼å
æ¬å·²ååºåå·²åèµ°çï¼ |
| | | var allLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) |
| | | .ToListAsync(); |
| | | |
| | | if (!allLockInfos.Any()) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error("该æç没æéå®è®°å½"); |
| | | } |
| | | |
| | | // æ£æ¥æ¯å¦ææªå®æçéå®è®°å½ |
| | | var unfinishedLocks = allLockInfos.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || |
| | | x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); |
| | | |
| | | if (unfinishedLocks.Any()) |
| | | { |
| | | var unfinishedCount = unfinishedLocks.Count; |
| | | // åºåå·²åé
åæªåé
|
| | | var allocatedUnfinished = unfinishedLocks.Where(x => x.IsUnallocated != 1).ToList(); |
| | | var unallocatedUnfinished = unfinishedLocks.Where(x => x.IsUnallocated == 1).ToList(); |
| | | |
| | | string errorMsg = $"æçè¿æ{unfinishedCount}æ¡æªå®æè®°å½"; |
| | | if (allocatedUnfinished.Any()) errorMsg += $"ï¼å
¶ä¸å·²åé
{allocatedUnfinished.Count}æ¡"; |
| | | if (unallocatedUnfinished.Any()) errorMsg += $"ï¼æªåé
{unallocatedUnfinished.Count}æ¡"; |
| | | |
| | | errorMsg += "ï¼ä¸è½å走空箱"; |
| | | |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error(errorMsg); |
| | | } |
| | | |
| | | // è·å已宿çéå®è®°å½ï¼ç¶æä¸ºæ£é宿æå·²åèµ°ï¼ |
| | | var completedLocks = allLockInfos.Where(x => |
| | | x.Status == (int)OutLockStockStatusEnum.æ£é宿 || |
| | | x.Status == (int)OutLockStockStatusEnum.å·²åèµ°).ToList(); |
| | | |
| | | if (!completedLocks.Any()) |
| | | { |
| | | // æ£æ¥æ¯å¦é½æ¯å·²ååºç¶æ |
| | | var returnedLocks = allLockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.å·²ååº).ToList(); |
| | | if (returnedLocks.Any()) |
| | | { |
| | | _logger.LogInformation($"ææéå®è®°å½é½å·²ååºï¼å¯ä»¥å走空箱"); |
| | | completedLocks = returnedLocks; |
| | | } |
| | | else |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | return WebResponseContent.Instance.Error("该æç没æå·²å®ææ£éæå·²ååºçè®°å½"); |
| | | } |
| | | } |
| | | |
| | | _logger.LogInformation($"éªè¯éè¿ï¼æ¾å° {completedLocks.Count} æ¡å·²å®æè®°å½"); |
| | | |
| | | // æ¸
ç已宿çéå®è®°å½ï¼æ 记为已åèµ°ï¼ |
| | | _logger.LogInformation($"æ¥éª¤3: æ¸
çéå®è®°å½"); |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | | // åªå¤çç¶æä¸æ¯å·²åèµ°çè®°å½ |
| | | if (lockInfo.Status != (int)OutLockStockStatusEnum.å·²åèµ°) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²åèµ°; |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"éå®è®°å½æ 记为已åèµ° - ID: {lockInfo.Id}"); |
| | | } |
| | | } |
| | | |
| | | // æ¸
ç对åºçåºåè®°å½ç¶æ |
| | | _logger.LogInformation($"æ¥éª¤4: æ¸
çåºåè®°å½"); |
| | | foreach (var lockInfo in completedLocks) |
| | | { |
| | | await CleanupStockInfo(lockInfo); |
| | | } |
| | | |
| | | // æ´æ°ç¸å
³è®¢åç¶æ |
| | | _logger.LogInformation($"æ¥éª¤5: æ´æ°è®¢åç¶æ"); |
| | | await UpdateOrderStatusAfterPalletRemoval(orderNo); |
| | | |
| | | // è®°å½æä½åå² |
| | | //_logger.LogInformation($"æ¥éª¤6: è®°å½æä½åå²"); |
| | | //await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | _logger.LogInformation($"ãå走空箱æåã订å: {orderNo}, æç: {palletCode}"); |
| | | |
| | | return WebResponseContent.Instance.OK("å走空箱æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"å走空箱失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"å走空箱失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region è¾
婿¹æ³ |
| | | |
| | | private async Task CleanupZeroStockData(int stockId) |
| | | { |
| | | try |
| | | { |
| | | // å é¤åºåæ°é为0çæç»è®°å½ |
| | | var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() |
| | | .Where(x => x.StockId == stockId && x.StockQuantity == 0) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | await _stockInfoService.Db.Deleteable<Dt_StockInfo>() |
| | | .Where(x => x.Id == stockId).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¸
çé¶åºåæç»è®°å½ - StockId: {stockId}, å é¤è®°å½æ°: {deleteDetailCount}"); |
| | | |
| | | |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogWarning($"æ¸
çé¶åºåæ°æ®å¤±è´¥ - StockId: {stockId}, Error: {ex.Message}"); |
| | | |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region éªè¯æ¹æ³ |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>> ValidatePickingRequest( |
| | | string orderNo, string palletCode, string barcode) |
| | | string orderNo, string palletCode, string barcode) |
| | | { |
| | | _logger.LogInformation($"å¼å§éªè¯åæ£è¯·æ± - 订å: {orderNo}, æç: {palletCode}, æ¡ç : {barcode}"); |
| | | |
| | | // æ¥æ¾éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | |
| | | if (lockInfo.IsUnallocated == 1 || lockInfo.OrderDetailId == 0) |
| | | { |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("该æ¡ç æ¯æªåé
éå®è®°å½ï¼ä¸è½ç´æ¥åæ£"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¾å°éå®ä¿¡æ¯ - åé
æ°é: {lockInfo.AssignQuantity}, å·²æ£é: {lockInfo.PickedQty}"); |
| | | |
| | | // æ£æ¥æ¯å¦å·²ç»åæ£å®æ |
| | | if (lockInfo.PickedQty >= lockInfo.AssignQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("该æ¡ç 已忣宿"); |
| | |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == lockInfo.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("æªæ¾å°è®¢åæç»"); |
| | | |
| | | _logger.LogInformation($"æ¾å°è®¢åæç» - å·²åé
æ°é: {orderDetail.AllocatedQuantity}, é宿°é: {orderDetail.LockQuantity}"); |
| | | |
| | | // æ£æ¥è®¢åæç»çå·²åé
æ°éæ¯å¦è¶³å¤ |
| | | decimal remainingToPick = lockInfo.AssignQuantity - lockInfo.PickedQty; |
| | | |
| | | // è¿éåºè¯¥æ£æ¥éå®ä¿¡æ¯çåé
æ°éï¼è䏿¯è®¢åæç»çåé
æ°é |
| | | // å 为æå
åï¼éå®ä¿¡æ¯çåé
æ°éå¯è½å°äºè®¢åæç»çåé
æ°é |
| | | if (lockInfo.AssignQuantity < remainingToPick) |
| | | { |
| | | _logger.LogWarning($"éå®ä¿¡æ¯åé
æ°éä¸è¶³ - éè¦æ£é: {remainingToPick}, éå®ä¿¡æ¯åé
æ°é: {lockInfo.AssignQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"éå®ä¿¡æ¯åé
æ°éä¸è¶³ï¼éè¦æ£éï¼{remainingToPick}ï¼éå®ä¿¡æ¯åé
æ°éï¼{lockInfo.AssignQuantity}"); |
| | | } |
| | | |
| | | // æ£æ¥é宿°éæ¯å¦è¶³å¤ |
| | | if (orderDetail.LockQuantity < remainingToPick) |
| | | { |
| | | _logger.LogWarning($"订åæç»é宿°éä¸è¶³ - éè¦æ£é: {remainingToPick}, å¯ç¨é宿°é: {orderDetail.LockQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"订åæç»é宿°éä¸è¶³ï¼éè¦æ£éï¼{remainingToPick}ï¼å¯ç¨é宿°éï¼{orderDetail.LockQuantity}"); |
| | | } |
| | | |
| | | // æ£æ¥è®¢åæç»åé
æ°éæ¯å¦ä¸ºè´æ° |
| | | if (orderDetail.AllocatedQuantity < 0) |
| | | { |
| | | _logger.LogError($"订åæç»åé
æ°éä¸ºè´æ° - å½åå¼: {orderDetail.AllocatedQuantity}"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"订åæç»åé
æ°éå¼å¸¸ï¼è´æ°ï¼ï¼è¯·è系管çåå¤ç"); |
| | | } |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == lockInfo.StockId); |
| | | |
| | | // éªè¯åºåæ°é |
| | | if (stockDetail.StockQuantity < lockInfo.AssignQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"åºåæ°éä¸è¶³ï¼éè¦ï¼{lockInfo.AssignQuantity}ï¼å®é
ï¼{stockDetail.StockQuantity}"); |
| | | if (stockDetail == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("æªæ¾å°å¯¹åºçåºåä¿¡æ¯"); |
| | | |
| | | // éªè¯åºåæ°é |
| | | if (stockDetail.StockQuantity < remainingToPick) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"åºåæ°éä¸è¶³ï¼éè¦æ£éï¼{remainingToPick}ï¼å®é
åºåï¼{stockDetail.StockQuantity}"); |
| | | |
| | | // éªè¯åºåç¶æ |
| | | //if (stockDetail.Status != (int)StockStatusEmun.åºåºéå®) |
| | | //{ |
| | | // return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | // $"åºåç¶æå¼å¸¸ï¼å½åç¶æï¼{stockDetail.Status}ï¼ææç¶æï¼åºåºéå®"); |
| | | //} |
| | | |
| | | // ä½¿ç¨ OutboundBatchNo æ¥æ¾æ¹æ¬¡ |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == lockInfo.BatchNo); |
| | | .FirstAsync(x => x.BatchNo == lockInfo.OutboundBatchNo); |
| | | |
| | | _logger.LogInformation($"忣éªè¯éè¿ - æ¡ç : {barcode}, å©ä½éæ£é: {remainingToPick}, å¯ç¨åºå: {stockDetail.StockQuantity}"); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Success((lockInfo, orderDetail, stockDetail, batch)); |
| | | } |
| | | |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest( |
| | | string orderNo, string palletCode, string originalBarcode, decimal splitQuantity) |
| | | |
| | | |
| | | /// <summary> |
| | | /// æ£æ¥å¹¶æ§è¡èªå¨æå
ï¼å¦æéè¦ï¼ |
| | | /// </summary> |
| | | private async Task<List<SplitResult>> CheckAndAutoSplitIfNeeded(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, string palletCode) |
| | | { |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.CurrentBarcode == originalBarcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | if (lockInfo.IsUnallocated == 1 || lockInfo.OrderDetailId == 0) |
| | | { |
| | | _logger.LogInformation($"è·³è¿æªåé
éå®è®°å½çèªå¨æå
æ£æ¥ - éå®ID: {lockInfo.Id}"); |
| | | return null; |
| | | } |
| | | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | // æ£æ¥æ¯å¦éè¦èªå¨æå
çæ¡ä»¶ï¼ |
| | | // 1. åºåæ°é大äºåé
æ°é |
| | | // 2. éå®ä¿¡æ¯ç¶æä¸ºåºåºä¸ |
| | | // 3. æªæ£éæ°éçäºåé
æ°éï¼è¡¨ç¤ºè¿æªå¼å§æ£éï¼ |
| | | bool needAutoSplit = stockDetail.StockQuantity > lockInfo.AssignQuantity && |
| | | lockInfo.Status == (int)OutLockStockStatusEnum.åºåºä¸ && |
| | | lockInfo.PickedQty == 0; |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId); |
| | | if (!needAutoSplit) |
| | | return null; |
| | | |
| | | if (stockDetail.StockQuantity < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºåºåæ°é"); |
| | | // è®¡ç®æå
æ°é = åºåæ°é - åé
æ°é |
| | | decimal splitQuantity = stockDetail.StockQuantity - lockInfo.AssignQuantity; |
| | | |
| | | if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºæªæ£éæ°é"); |
| | | _logger.LogInformation($"éè¦èªå¨æå
- åºå: {stockDetail.StockQuantity}, åé
: {lockInfo.AssignQuantity}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); |
| | | // æ§è¡èªå¨æå
|
| | | var splitResult = await ExecuteAutoSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); |
| | | |
| | | // å°æå
æ°éä¼ éç»è°ç¨æ¹ï¼ç¨äºéªè¯ |
| | | if (splitResult != null && splitResult.Any()) |
| | | { |
| | | // å¨è¿åç»æä¸æºå¸¦æå
æ°éä¿¡æ¯ |
| | | foreach (var result in splitResult) |
| | | { |
| | | result.quantityTotal = splitQuantity.ToString("F2"); |
| | | } |
| | | } |
| | | |
| | | return splitResult; |
| | | } |
| | | |
| | | private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest( |
| | | string orderNo, string palletCode, string newBarcode) |
| | | /// <summary> |
| | | /// æ§è¡èªå¨æå
é»è¾ - å®å
¨ä¿®æ£ç |
| | | /// ååï¼åªå离ç©çåºåï¼ä¸æ¹åå订åçä»»ä½åé
ååºåºæ°é |
| | | /// </summary> |
| | | private async Task<List<SplitResult>> ExecuteAutoSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | | { |
| | | var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.NewBarcode == newBarcode && |
| | | x.OrderNo == orderNo && |
| | | !x.IsReverted) |
| | | .FirstAsync(); |
| | | _logger.LogInformation($"å¼å§æ§è¡èªå¨æå
é»è¾ - åæ¡ç : {stockDetail.Barcode}, æå
æ°é: {splitQuantity}"); |
| | | |
| | | if (splitRecord == null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æå
è®°å½"); |
| | | try |
| | | { |
| | | // éªè¯æå
æ°éåçæ§ |
| | | if (splitQuantity <= 0) |
| | | throw new InvalidOperationException($"æå
æ°éå¿
须大äº0ï¼å½åå¼: {splitQuantity}"); |
| | | |
| | | var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == newBarcode && |
| | | x.PalletCode == palletCode && |
| | | x.OrderNo == orderNo) |
| | | .FirstAsync(); |
| | | if (stockDetail.StockQuantity < lockInfo.AssignQuantity + splitQuantity) |
| | | throw new InvalidOperationException($"åºåæ°éä¸è¶³ä»¥è¿è¡èªå¨æå
ï¼åºå: {stockDetail.StockQuantity}, éè¦: {lockInfo.AssignQuantity + splitQuantity}"); |
| | | |
| | | if (newLockInfo == null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | | _logger.LogInformation($"çææ°æ¡ç : {newBarcode}"); |
| | | |
| | | // æ£æ¥æ°æ¡ç æ¯å¦å·²è¢«åæ£ |
| | | var pickingRecord = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == newBarcode && !x.IsCancelled) |
| | | .FirstAsync(); |
| | | // ãæ ¸å¿ä¿®æ£ãæ´æ°ååºåæç»ï¼åªåå°ç©çåºåï¼ä¸å½±ååºåºæ°é |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | stockDetail.StockQuantity -= splitQuantity; // ä»
åºååå° |
| | | // stockDetail.OutboundQuantity ä¿æä¸åï¼ |
| | | |
| | | if (pickingRecord != null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该æ¡ç å·²è¢«åæ£ï¼æ æ³åæ¶æå
"); |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"æ´æ°ååºåæç»ï¼æ¡ç {stockDetail.Barcode} åºå {originalStockQty} -> {stockDetail.StockQuantity}ï¼åºåºæ°éä¸å({stockDetail.OutboundQuantity})"); |
| | | |
| | | var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == newBarcode); |
| | | // å建æ°åºåæç»ï¼å¤ä½é¨åï¼- åºåºæ°é为0 |
| | | var newStockDetail = new Dt_StockInfoDetail |
| | | { |
| | | StockId = stockDetail.StockId, |
| | | MaterielCode = stockDetail.MaterielCode, |
| | | OrderNo = stockDetail.OrderNo, |
| | | BatchNo = stockDetail.BatchNo, |
| | | StockQuantity = splitQuantity, // æ°åºåæ°é |
| | | OutboundQuantity = 0, // åå§åºåºæ°é为0 |
| | | Barcode = newBarcode, |
| | | Status = (int)StockStatusEmun.åºåºéå®, // ä»ä¸ºéå®ç¶æï¼ä½æªç»å®è®¢å |
| | | SupplyCode = stockDetail.SupplyCode, |
| | | Unit = stockDetail.Unit, |
| | | BarcodeQty = stockDetail.BarcodeQty, |
| | | BarcodeUnit = stockDetail.BarcodeUnit, |
| | | BusinessType = stockDetail.BusinessType, |
| | | InboundOrderRowNo = stockDetail.InboundOrderRowNo, |
| | | }; |
| | | |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail)); |
| | | await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æ°åºåæç»ï¼æ¡ç {newBarcode}ï¼åºå {splitQuantity}ï¼åºåº 0"); |
| | | |
| | | // å建æ°éå®ä¿¡æ¯ - æ 记为æªåé
|
| | | var newLockInfo = new Dt_OutStockLockInfo |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | OrderDetailId = 0, // ä¸ç»å®å°å
·ä½è®¢åæç»ï¼è¡¨ç¤ºæªåé
|
| | | OutboundBatchNo = lockInfo.OutboundBatchNo, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | MaterielName = lockInfo.MaterielName, |
| | | StockId = lockInfo.StockId, |
| | | OrderQuantity = splitQuantity, |
| | | AssignQuantity = splitQuantity, |
| | | PickedQty = 0, |
| | | LocationCode = lockInfo.LocationCode, |
| | | PalletCode = lockInfo.PalletCode, |
| | | TaskNum = lockInfo.TaskNum, |
| | | Status = (int)OutLockStockStatusEnum.åºåºä¸, |
| | | Unit = lockInfo.Unit, |
| | | SupplyCode = lockInfo.SupplyCode, |
| | | OrderType = lockInfo.OrderType, |
| | | CurrentBarcode = newBarcode, |
| | | IsSplitted = 1, |
| | | ParentLockId = lockInfo.Id, |
| | | Operator = App.User.UserName, |
| | | FactoryArea = lockInfo.FactoryArea, |
| | | lineNo = lockInfo.lineNo, |
| | | WarehouseCode = lockInfo.WarehouseCode, |
| | | BarcodeQty = lockInfo.BarcodeQty, |
| | | BarcodeUnit = lockInfo.BarcodeUnit, |
| | | IsUnallocated = 1 // æç¡®æ 记为"æªåé
"çéå®è®°å½ |
| | | }; |
| | | |
| | | await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); |
| | | _logger.LogInformation($"å建æªåé
éå®è®°å½ï¼ID {newLockInfo.Id}ï¼æ¡ç {newBarcode}ï¼æ°é {splitQuantity}"); |
| | | |
| | | //åéå®è®°å½åå订åæç»æ°æ®å®å
¨ä¿æä¸åï¼ |
| | | // - ä¸ä¿®æ¹ lockInfo çä»»ä½å段 |
| | | // - ä¸ä¿®æ¹å
³èç Dt_OutboundOrderDetail ç AllocatedQuantity å LockQuantity |
| | | |
| | | // è®°å½æå
åå² |
| | | await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, true, originalStockQty); |
| | | |
| | | // å建æå
ç»æå表 |
| | | var splitResults = CreateSplitResults(lockInfo, splitQuantity, lockInfo.AssignQuantity, newBarcode, stockDetail.Barcode); |
| | | |
| | | _logger.LogInformation($"èªå¨æå
é»è¾æ§è¡å®æ - åå»ºäºæªåé
çåºååéå®è®°å½"); |
| | | |
| | | return splitResults; |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"èªå¨æå
é»è¾æ§è¡å¤±è´¥ - åæ¡ç : {stockDetail.Barcode}, Error: {ex.Message}"); |
| | | throw; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æ ¸å¿é»è¾æ¹æ³ |
| | | |
| | | /// <summary> |
| | | /// æ§è¡åæ£é»è¾ - å®å
¨ä¿®æ£ç |
| | | /// ç¡®ä¿OutboundQuantityå确累å ï¼ä¸å
嫿å
æ°é |
| | | /// </summary> |
| | | private async Task<PickingResult> ExecutePickingLogic( |
| | | Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, |
| | | Dt_StockInfoDetail stockDetail, decimal actualPickedQty) |
| | | { |
| | | // æ´æ°éå®ä¿¡æ¯ |
| | | lockInfo.PickedQty += actualPickedQty; |
| | | if (lockInfo.PickedQty >= lockInfo.AssignQuantity) |
| | | _logger.LogInformation($"å¼å§æ§è¡åæ£é»è¾ - æ¡ç : {stockDetail.Barcode}, åé
æ°é: {lockInfo.AssignQuantity}, å®é
æ£é: {actualPickedQty}"); |
| | | |
| | | // 忬¡éªè¯åºåæ°é |
| | | if (stockDetail.StockQuantity < actualPickedQty) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.æ£é宿; |
| | | throw new InvalidOperationException($"åºåæ°éä¸è¶³ï¼éè¦æ£é {actualPickedQty}ï¼å®é
åºå {stockDetail.StockQuantity}"); |
| | | } |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // æ´æ°åºå |
| | | // è®°å½æ£éåçå¼ |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | int originalStatus = stockDetail.Status; |
| | | |
| | | // ç¡®ä¿OutboundQuantityåªå¢å æ¬æ¬¡æ£éæ°éï¼ä¸å
å«å
¶ä» |
| | | stockDetail.StockQuantity -= actualPickedQty; |
| | | stockDetail.OutboundQuantity += actualPickedQty; |
| | | stockDetail.OutboundQuantity += actualPickedQty; // åªå¢å æ¬æ¬¡æ£éæ°é |
| | | |
| | | _logger.LogInformation($"æ´æ°åºåä¿¡æ¯ - æ¡ç : {stockDetail.Barcode}"); |
| | | _logger.LogInformation($" åºåæ°é: {originalStockQty} -> {stockDetail.StockQuantity}"); |
| | | _logger.LogInformation($" åºåºæ°é: {originalOutboundQty} -> {stockDetail.OutboundQuantity}"); |
| | | |
| | | // æ´æ°åºåç¶æ |
| | | if (stockDetail.StockQuantity <= 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºå®æ; |
| | | _logger.LogInformation($"åºåç¶ææ´æ°ä¸ºåºåºå®æ"); |
| | | } |
| | | else |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | _logger.LogInformation($"åºåç¶æä¿æä¸ºåºåºéå®"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | // æ´æ°éå®ä¿¡æ¯ |
| | | decimal originalPickedQty = lockInfo.PickedQty; |
| | | lockInfo.PickedQty += actualPickedQty; |
| | | _logger.LogInformation($"æ´æ°éå®ä¿¡æ¯ - å·²æ£éæ°éä» {originalPickedQty} å¢å å° {lockInfo.PickedQty}"); |
| | | |
| | | // 夿æ£éå®æç¶æ |
| | | if (Math.Abs(lockInfo.PickedQty - lockInfo.AssignQuantity) < 0.001m) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.æ£é宿; |
| | | _logger.LogInformation($"éå®ä¿¡æ¯ç¶ææ´æ°ä¸ºæ£é宿"); |
| | | } |
| | | else if (lockInfo.PickedQty > 0) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; |
| | | _logger.LogInformation($"éå®ä¿¡æ¯ç¶æä¿æä¸ºåºåºä¸ï¼é¨åæ£éï¼"); |
| | | } |
| | | |
| | | lockInfo.Operator = App.User.UserName; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // éªè¯æ£éåçæ°æ®ä¸è´æ§ |
| | | await ValidatePickingDataConsistency(lockInfo, stockDetail, actualPickedQty); |
| | | |
| | | _logger.LogInformation($"忣é»è¾æ§è¡å®æ - æ¡ç : {stockDetail.Barcode}"); |
| | | |
| | | return new PickingResult |
| | | { |
| | |
| | | }; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// éªè¯æ£éåæ°æ®ä¸è´æ§ |
| | | /// </summary> |
| | | private async Task ValidatePickingDataConsistency(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal pickedQty) |
| | | { |
| | | _logger.LogInformation($"éªè¯æ£éæ°æ®ä¸è´æ§ - æ¡ç : {stockDetail.Barcode}"); |
| | | |
| | | // éªè¯åºåæç»çOutboundQuantityå¢å éçäºæ£éæ°é |
| | | var refreshedStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Id == stockDetail.Id); |
| | | |
| | | decimal outboundIncrease = refreshedStockDetail.OutboundQuantity - stockDetail.OutboundQuantity; |
| | | |
| | | if (Math.Abs(outboundIncrease - pickedQty) > 0.01m) |
| | | { |
| | | _logger.LogError($"æ£éæ°æ®ä¸ä¸è´ï¼åºåºæ°éå¢å {outboundIncrease}ï¼ä½æ£éæ°éæ¯ {pickedQty}"); |
| | | // ç¡®ä¿OutboundQuantityæ£ç¡® |
| | | refreshedStockDetail.OutboundQuantity = stockDetail.OutboundQuantity + pickedQty; |
| | | await _stockInfoDetailService.Db.Updateable(refreshedStockDetail).ExecuteCommandAsync(); |
| | | _logger.LogWarning($"已修å¤åºåºæ°éï¼{stockDetail.OutboundQuantity} -> {refreshedStockDetail.OutboundQuantity}"); |
| | | } |
| | | |
| | | // éªè¯éå®è®°å½çå·²æ£éæ°é |
| | | var refreshedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == lockInfo.Id); |
| | | |
| | | if (Math.Abs(refreshedLockInfo.PickedQty - (lockInfo.PickedQty - pickedQty) - pickedQty) > 0.01m) |
| | | { |
| | | _logger.LogError($"éå®è®°å½å·²æ£éæ°éä¸ä¸è´"); |
| | | } |
| | | |
| | | _logger.LogInformation($"æ£éæ°æ®ä¸è´æ§éªè¯éè¿"); |
| | | } |
| | | |
| | | private async Task<RevertPickingResult> RevertPickingData(Dt_PickingRecord pickingRecord) |
| | | { |
| | | _logger.LogInformation($"å¼å§æ¢å¤æ£éæ°æ® - æ£éè®°å½ID: {pickingRecord.Id}, æ¡ç : {pickingRecord.Barcode}, æ£éæ°é: {pickingRecord.PickQuantity}"); |
| | | |
| | | // æ¢å¤éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == pickingRecord.OutStockLockId); |
| | | |
| | | if (lockInfo == null) |
| | | { |
| | | throw new InvalidOperationException($"æªæ¾å°å¯¹åºçéå®ä¿¡æ¯ï¼ID: {pickingRecord.OutStockLockId}"); |
| | | } |
| | | |
| | | decimal originalPickedQty = lockInfo.PickedQty; |
| | | decimal originalAssignQty = lockInfo.AssignQuantity; // è®°å½åå§åé
æ°é |
| | | |
| | | // åªæ¢å¤å·²æ£éæ°éï¼ä¸ä¿®æ¹åé
æ°é |
| | | lockInfo.PickedQty -= pickingRecord.PickQuantity; |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; |
| | | |
| | | // ç¡®ä¿å·²æ£éæ°éä¸ä¼ä¸ºè´æ° |
| | | if (lockInfo.PickedQty < 0) |
| | | { |
| | | _logger.LogWarning($"å·²æ£éæ°éåºç°è´æ°ï¼é置为0ãåå¼: {lockInfo.PickedQty + pickingRecord.PickQuantity}, æ¢å¤æ°é: {pickingRecord.PickQuantity}"); |
| | | lockInfo.PickedQty = 0; |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¢å¤éå®ä¿¡æ¯ - å·²æ£éæ°éä» {originalPickedQty} åå°å° {lockInfo.PickedQty}"); |
| | | _logger.LogInformation($"éå®ä¿¡æ¯åé
æ°éä¿æä¸å: {originalAssignQty}"); |
| | | |
| | | // æ¢å¤éå®ç¶æ |
| | | if (lockInfo.PickedQty <= 0) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; |
| | | _logger.LogInformation($"éå®ä¿¡æ¯ç¶ææ¢å¤ä¸ºåºåºä¸"); |
| | | } |
| | | else if (lockInfo.PickedQty < lockInfo.AssignQuantity) |
| | | { |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; // é¨åæ£éç¶æ |
| | | _logger.LogInformation($"éå®ä¿¡æ¯ç¶ææ¢å¤ä¸ºåºåºä¸ï¼é¨åæ£éï¼"); |
| | | } |
| | | |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // æ¢å¤åºå |
| | | // æ¢å¤åºåä¿¡æ¯ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == pickingRecord.Barcode); |
| | | |
| | | if (stockDetail == null) |
| | | { |
| | | throw new InvalidOperationException($"æªæ¾å°å¯¹åºçåºåä¿¡æ¯ï¼æ¡ç : {pickingRecord.Barcode}"); |
| | | } |
| | | |
| | | decimal originalStockQty = stockDetail.StockQuantity; |
| | | decimal originalOutboundQty = stockDetail.OutboundQuantity; |
| | | |
| | | stockDetail.StockQuantity += pickingRecord.PickQuantity; |
| | | stockDetail.OutboundQuantity -= pickingRecord.PickQuantity; |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | |
| | | // ç¡®ä¿åºåºæ°éä¸ä¼ä¸ºè´æ° |
| | | if (stockDetail.OutboundQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åºåºæ°éåºç°è´æ°ï¼é置为0ãåå¼: {stockDetail.OutboundQuantity + pickingRecord.PickQuantity}, æ¢å¤æ°é: {pickingRecord.PickQuantity}"); |
| | | stockDetail.OutboundQuantity = 0; |
| | | } |
| | | |
| | | _logger.LogInformation($"æ¢å¤åºåä¿¡æ¯ - åºåæ°éä» {originalStockQty} å¢å å° {stockDetail.StockQuantity}"); |
| | | _logger.LogInformation($"æ¢å¤åºåä¿¡æ¯ - åºåºæ°éä» {originalOutboundQty} åå°å° {stockDetail.OutboundQuantity}"); |
| | | |
| | | // æ¢å¤åºåç¶æ |
| | | if (stockDetail.StockQuantity > 0) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | _logger.LogInformation($"åºåç¶ææ¢å¤ä¸ºåºåºéå®"); |
| | | } |
| | | |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ¢å¤æ£éæ°æ®å®æ - æ¡ç : {pickingRecord.Barcode}"); |
| | | |
| | | return new RevertPickingResult |
| | | { |
| | |
| | | }; |
| | | } |
| | | |
| | | private async Task<SplitResultDto> ExecuteSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | | { |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | | |
| | | // å建æ°åºåæç» |
| | | var newStockDetail = new Dt_StockInfoDetail |
| | | { |
| | | StockId = stockDetail.StockId, |
| | | MaterielCode = stockDetail.MaterielCode, |
| | | OrderNo = stockDetail.OrderNo, |
| | | BatchNo = stockDetail.BatchNo, |
| | | StockQuantity = splitQuantity, |
| | | OutboundQuantity = 0, |
| | | Barcode = newBarcode, |
| | | Status = (int)StockStatusEmun.åºåºéå®, |
| | | SupplyCode = stockDetail.SupplyCode, |
| | | Unit = stockDetail.Unit |
| | | }; |
| | | await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); |
| | | |
| | | // æ´æ°ååºåæç» |
| | | stockDetail.StockQuantity -= splitQuantity; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | // å建æ°éå®ä¿¡æ¯ |
| | | var newLockInfo = new Dt_OutStockLockInfo |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | OrderDetailId = lockInfo.OrderDetailId, |
| | | OutboundBatchNo = lockInfo.OutboundBatchNo, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | StockId = lockInfo.StockId, |
| | | OrderQuantity = splitQuantity, |
| | | AssignQuantity = splitQuantity, |
| | | PickedQty = 0, |
| | | LocationCode = lockInfo.LocationCode, |
| | | PalletCode = palletCode, |
| | | Status = (int)OutLockStockStatusEnum.åºåºä¸, |
| | | CurrentBarcode = newBarcode, |
| | | Operator = App.User.UserName, |
| | | }; |
| | | await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); |
| | | |
| | | // æ´æ°åéå®ä¿¡æ¯ |
| | | lockInfo.AssignQuantity -= splitQuantity; |
| | | lockInfo.OrderQuantity -= splitQuantity; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // è®°å½æå
åå² |
| | | await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode); |
| | | |
| | | return new SplitResultDto { NewBarcode = newBarcode }; |
| | | } |
| | | |
| | | private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | // æ¢å¤ååºå |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | | |
| | | originalStock.StockQuantity += splitRecord.SplitQty; |
| | | await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); |
| | | |
| | | // æ¢å¤åéå®ä¿¡æ¯ |
| | | var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); |
| | | |
| | | originalLockInfo.AssignQuantity += splitRecord.SplitQty; |
| | | originalLockInfo.OrderQuantity += splitRecord.SplitQty; |
| | | await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); |
| | | |
| | | // å 餿°åºåæç» |
| | | await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() |
| | | .Where(x => x.Barcode == newLockInfo.CurrentBarcode) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // å 餿°éå®ä¿¡æ¯ |
| | | await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.Id == newLockInfo.Id) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // æ è®°æå
è®°å½ä¸ºå·²æ¤é |
| | | splitRecord.IsReverted = true; |
| | | splitRecord.RevertTime = DateTime.Now; |
| | | splitRecord.RevertOperator = App.User.UserName; |
| | | await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æ°æ®æ´æ°æ¹æ³ |
| | | |
| | | /// <summary> |
| | | /// æ´æ°æ¹æ¬¡åè®¢åæ°æ® - ä¿®æ£ç |
| | | /// ç¡®ä¿åªæ´æ°å®é
çæ£éæ°é |
| | | /// </summary> |
| | | private async Task UpdateBatchAndOrderData(Dt_OutboundBatch batch, Dt_OutboundOrderDetail orderDetail, decimal pickedQty, string orderNo) |
| | | { |
| | | // æ´æ°æ¹æ¬¡å®ææ°é |
| | | batch.CompletedQuantity += pickedQty; |
| | | if (batch.CompletedQuantity >= batch.BatchQuantity) |
| | | _logger.LogInformation($"å¼å§æ´æ°æ¹æ¬¡åè®¢åæ°æ® - æ£éæ°é: {pickedQty}"); |
| | | |
| | | // éæ°è·åææ°æ°æ®ï¼é²æ¢å¹¶åé®é¢ï¼ |
| | | var latestOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetail.Id); |
| | | |
| | | if (latestOrderDetail == null) |
| | | throw new InvalidOperationException("æªæ¾å°è®¢åæç»"); |
| | | |
| | | orderDetail = latestOrderDetail; |
| | | |
| | | // éªè¯æ£éæ°éåçæ§ |
| | | if (pickedQty <= 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | _logger.LogWarning($"æ£éæ°éæ æ: {pickedQty}"); |
| | | return; |
| | | } |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | |
| | | // è®°å½åå§å¼ |
| | | decimal originalOverOutQty = orderDetail.OverOutQuantity; |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | |
| | | // æ´æ°è®¢åæç» |
| | | orderDetail.OverOutQuantity += pickedQty; |
| | | orderDetail.AllocatedQuantity -= pickedQty; |
| | | orderDetail.OverOutQuantity += pickedQty; // å·²åºåºæ°éå¢å |
| | | orderDetail.AllocatedQuantity -= pickedQty; // å·²åé
æ°éåå° |
| | | orderDetail.LockQuantity -= pickedQty; // é宿°éåå° |
| | | |
| | | // ç¡®ä¿æ°éä¸ä¼ä¸ºè´æ° |
| | | if (orderDetail.AllocatedQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"åé
æ°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.AllocatedQuantity + pickedQty}"); |
| | | orderDetail.AllocatedQuantity = 0; |
| | | } |
| | | |
| | | if (orderDetail.LockQuantity < 0) |
| | | { |
| | | _logger.LogWarning($"é宿°éåºç°è´æ°ï¼é置为0ãåå¼: {orderDetail.LockQuantity + pickedQty}"); |
| | | orderDetail.LockQuantity = 0; |
| | | } |
| | | |
| | | // æ´æ°æ¹æ¬¡åé
ç¶æ |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | |
| | | _logger.LogInformation($"æ´æ°è®¢åæç»æå"); |
| | | _logger.LogInformation($" å·²åºåºæ°é: {originalOverOutQty} -> {orderDetail.OverOutQuantity}"); |
| | | _logger.LogInformation($" å·²åé
æ°é: {originalAllocatedQty} -> {orderDetail.AllocatedQuantity}"); |
| | | _logger.LogInformation($" é宿°é: {originalLockQty} -> {orderDetail.LockQuantity}"); |
| | | |
| | | // æ´æ°æ¹æ¬¡å®ææ°é |
| | | if (batch != null) |
| | | { |
| | | decimal originalBatchCompletedQty = batch.CompletedQuantity; |
| | | batch.CompletedQuantity += pickedQty; |
| | | |
| | | _logger.LogInformation($"æ´æ°æ¹æ¬¡å®ææ°é - ä» {originalBatchCompletedQty} å¢å å° {batch.CompletedQuantity}"); |
| | | |
| | | // æ´æ°æ¹æ¬¡ç¶æ |
| | | if (batch.CompletedQuantity >= batch.BatchQuantity) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | _logger.LogInformation($"æ¹æ¬¡ç¶ææ´æ°ä¸ºå·²å®æ"); |
| | | } |
| | | else if (batch.CompletedQuantity > 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | _logger.LogInformation($"æ¹æ¬¡ç¶ææ´æ°ä¸ºæ§è¡ä¸"); |
| | | } |
| | | |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(orderNo); |
| | | |
| | | _logger.LogInformation($"æ¹æ¬¡åè®¢åæ°æ®æ´æ°å®æ"); |
| | | } |
| | | |
| | | private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord) |
| | | private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord, RevertPickingResult revertResult) |
| | | { |
| | | // æ¢å¤æ¹æ¬¡å®ææ°é |
| | | _logger.LogInformation($"å¼å§æ¢å¤æ¹æ¬¡åè®¢åæ°æ®"); |
| | | |
| | | // æ¢å¤æ¹æ¬¡å®ææ°é |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == pickingRecord.BatchNo); |
| | | .FirstAsync(x => x.BatchNo == revertResult.LockInfo.OutboundBatchNo); |
| | | |
| | | batch.CompletedQuantity -= pickingRecord.PickQuantity; |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | if (batch != null) |
| | | { |
| | | decimal originalCompletedQty = batch.CompletedQuantity; |
| | | batch.CompletedQuantity -= pickingRecord.PickQuantity; |
| | | if (batch.CompletedQuantity < 0) |
| | | { |
| | | batch.CompletedQuantity = 0; |
| | | _logger.LogWarning($"æ¹æ¬¡å®ææ°éåºç°è´æ°ï¼é置为0"); |
| | | } |
| | | _logger.LogInformation($"æ¢å¤æ¹æ¬¡å®ææ°é - ä» {originalCompletedQty} åå°å° {batch.CompletedQuantity}"); |
| | | |
| | | // æ¢å¤è®¢åæç» |
| | | // éæ°è®¡ç®æ¹æ¬¡ç¶æ |
| | | if (batch.CompletedQuantity <= 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.åé
ä¸; |
| | | _logger.LogInformation($"æ¹æ¬¡ç¶ææ¢å¤ä¸ºåé
ä¸"); |
| | | } |
| | | else if (batch.CompletedQuantity < batch.BatchQuantity) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | _logger.LogInformation($"æ¹æ¬¡ç¶ææ¢å¤ä¸ºæ§è¡ä¸"); |
| | | } |
| | | |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // æ¢å¤è®¢åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == pickingRecord.OrderDetailId); |
| | | |
| | | orderDetail.OverOutQuantity -= pickingRecord.PickQuantity; |
| | | orderDetail.AllocatedQuantity += pickingRecord.PickQuantity; |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | if (orderDetail != null) |
| | | { |
| | | decimal originalOverOutQty = orderDetail.OverOutQuantity; |
| | | decimal originalAllocatedQty = orderDetail.AllocatedQuantity; |
| | | decimal originalLockQty = orderDetail.LockQuantity; |
| | | |
| | | // åªæ¢å¤ç¸å
³æ°éï¼åé
æ°éä¿æä¸å |
| | | orderDetail.OverOutQuantity -= pickingRecord.PickQuantity; |
| | | orderDetail.AllocatedQuantity += pickingRecord.PickQuantity; |
| | | orderDetail.LockQuantity += pickingRecord.PickQuantity; |
| | | if (orderDetail.OverOutQuantity < 0) orderDetail.OverOutQuantity = 0; |
| | | if (orderDetail.AllocatedQuantity < 0) orderDetail.AllocatedQuantity = 0; |
| | | if (orderDetail.LockQuantity < 0) orderDetail.LockQuantity = 0; |
| | | |
| | | _logger.LogInformation($"æ¢å¤è®¢åæç» - å·²åºåºæ°éä» {originalOverOutQty} åå°å° {orderDetail.OverOutQuantity}"); |
| | | _logger.LogInformation($"订åæç»åé
æ°éä¿æä¸å: {originalAllocatedQty}"); |
| | | _logger.LogInformation($"订åæç»é宿°éä¿æä¸å: {originalLockQty}"); |
| | | |
| | | await UpdateBatchAllocateStatus(orderDetail); |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | // éæ°æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(pickingRecord.OrderNo); |
| | | |
| | | _logger.LogInformation($"æ¢å¤æ¹æ¬¡åè®¢åæ°æ®å®æ"); |
| | | } |
| | | |
| | | private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo) |
| | | private async Task UpdateBatchAllocateStatus(Dt_OutboundOrderDetail orderDetail) |
| | | { |
| | | // æ¢å¤åºåç¶æ |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | if (orderDetail.AllocatedQuantity >= orderDetail.NeedOutQuantity) |
| | | { |
| | | stockDetail.Status = (int)StockStatusEmun.å
¥åºå®æ; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.AssignOver.ObjToInt(); |
| | | } |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private async Task UpdateBatchStatusForReturn(string batchNo, List<Dt_OutStockLockInfo> returnedLocks) |
| | | { |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == batchNo); |
| | | |
| | | // 计ç®ååºæ°é |
| | | var returnedQty = returnedLocks.Sum(x => x.AssignQuantity - x.PickedQty); |
| | | batch.CompletedQuantity -= returnedQty; |
| | | |
| | | if (batch.CompletedQuantity <= 0) |
| | | else if (orderDetail.AllocatedQuantity > 0) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.å·²ååº; |
| | | orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.AssignOverPartial.ObjToInt(); |
| | | } |
| | | else |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | } |
| | | |
| | | batch.Operator = App.User.UserName; |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | 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); |
| | | |
| | | await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() |
| | | .SetColumns(x => x.AllocatedQuantity == x.AllocatedQuantity - returnedQty) |
| | | .Where(x => x.Id == orderDetailId) |
| | | .ExecuteCommandAsync(); |
| | | orderDetail.BatchAllocateStatus = OrderDetailStatusEnum.New.ObjToInt(); |
| | | } |
| | | } |
| | | |
| | | |
| | | #endregion |
| | | |
| | | /// <summary> |
| | | /// ç»ESSä¸ä»»å¡ |
| | | /// </summary> |
| | | /// <param name="palletCode"></param> |
| | | /// <param name="targetAddress"></param> |
| | | /// <param name="returnTask"></param> |
| | | /// <returns></returns> |
| | | /// <exception cref="Exception"></exception> |
| | | private async Task SendESSCommands(string palletCode, string targetAddress, Dt_Task returnTask) |
| | | { |
| | | try |
| | | { |
| | | // åéæµå¨ä¿¡å· |
| | | var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest |
| | | { |
| | | slotCode = movestations[targetAddress], |
| | | containerCode = palletCode |
| | | }); |
| | | |
| | | |
| | | var essTask = new TaskModel() |
| | | { |
| | | taskType = "putaway", |
| | | taskGroupCode = "", |
| | | groupPriority = 0, |
| | | tasks = new List<TasksType>{ new() { |
| | | taskCode = returnTask.TaskNum.ToString(), |
| | | taskPriority = 0, |
| | | taskDescribe = new TaskDescribeType |
| | | { |
| | | containerCode = palletCode, |
| | | containerType = "CT_KUBOT_STANDARD", |
| | | fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "", |
| | | toStationCode = "", |
| | | toLocationCode = returnTask.TargetAddress, |
| | | deadline = 0, |
| | | storageTag = "" |
| | | } |
| | | } } |
| | | }; |
| | | |
| | | var resultTask = await _eSSApiService.CreateTaskAsync(essTask); |
| | | _logger.LogInformation($"ReturnRemaining åå»ºä»»å¡æå: {resultTask}"); |
| | | |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _logger.LogError($"ReturnRemaining ESSå½ä»¤åé失败: {ex.Message}"); |
| | | throw new Exception($"ESSç³»ç»é信失败: {ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #region è¾
婿¹æ³ |
| | | |
| | |
| | | return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString().PadLeft(5, '0'); |
| | | } |
| | | |
| | | private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQty, string newBarcode) |
| | | private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQty, string newBarcode, bool isAutoSplit, decimal? originalStockQuantity = null) |
| | | { |
| | | var splitHistory = new Dt_SplitPackageRecord |
| | | { |
| | |
| | | NewBarcode = newBarcode, |
| | | SplitQty = splitQty, |
| | | SplitTime = DateTime.Now, |
| | | Status = (int)SplitPackageStatusEnum.å·²æå
|
| | | Status = (int)SplitPackageStatusEnum.å·²æå
, |
| | | IsAutoSplit = isAutoSplit, |
| | | // SplitType = isAutoSplit ? "èªå¨æå
" : "æå¨æå
" |
| | | OriginalStockQuantity = originalStockQuantity ?? stockDetail.StockQuantity, |
| | | //RemainingStockQuantity = stockDetail.StockQuantity - splitQty |
| | | TaskNum = lockInfo.TaskNum |
| | | }; |
| | | |
| | | await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync(); |
| | |
| | | PickTime = DateTime.Now, |
| | | Operator = App.User.UserName, |
| | | OutStockLockId = result.FinalLockInfo.Id, |
| | | |
| | | BarcodeUnit = result.FinalLockInfo.BarcodeUnit, |
| | | BarcodeQty = result.FinalLockInfo.BarcodeQty, |
| | | BatchNo = result.FinalLockInfo.BatchNo, |
| | | lineNo = result.FinalLockInfo.lineNo, |
| | | SupplyCode = result.FinalLockInfo.SupplyCode, |
| | | WarehouseCode = result.FinalLockInfo.WarehouseCode, |
| | | // IsCancelled = false |
| | | }; |
| | | |
| | |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private string GetPalletStatusText(PalletStatusEnum status) |
| | | { |
| | | return status switch |
| | | { |
| | | PalletStatusEnum.æªå¼å§ => "æªå¼å§", |
| | | PalletStatusEnum.æ£éä¸ => "æ£éä¸", |
| | | PalletStatusEnum.已宿 => "已宿", |
| | | PalletStatusEnum.æ ä»»å¡ => "æ ä»»å¡", |
| | | _ => "æªç¥" |
| | | }; |
| | | } |
| | | #endregion |
| | | |
| | | #region DTOç±» |
| | | |
| | | /// <summary> |
| | | /// ååºä»»å¡ä¿¡æ¯ç±» |
| | | /// </summary> |
| | | public class ReturnTaskInfo |
| | | { |
| | | /// <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 string Barcode { get; set; } |
| | | public decimal PickedQty { get; set; } |
| | | public int PickRecordCount { get; set; } |
| | | } |
| | | |
| | | |
| | | public class PickingResult |
| | | { |
| | |
| | | public Dt_StockInfoDetail StockDetail { get; set; } |
| | | } |
| | | |
| | | public class SplitResultDto |
| | | { |
| | | public string NewBarcode { get; set; } |
| | | } |
| | | |
| | | public class ValidationResult<T> |
| | | { |
| | | public bool IsValid { get; set; } |
| | |
| | | } |
| | | |
| | | #endregion |
| | | } |
| | | |
| | | |
| | | // æ¯æç±» |
| | | public class SplitResultDto |
| | | { |
| | | public string NewBarcode { get; set; } |
| | | } |
| | | } |