| | |
| | | |
| | | namespace WIDESEA_OutboundService |
| | | { |
| | | public class OutboundBatchPickingService : ServiceBase<Dt_PickingRecord, IRepository<Dt_PickingRecord>> |
| | | public class OutboundBatchPickingService : ServiceBase<Dt_PickingRecord, IRepository<Dt_PickingRecord>>, IOutboundBatchPickingService |
| | | { |
| | | |
| | | |
| | |
| | | /// <summary> |
| | | /// åæ¹åæ£ç¡®è®¤ |
| | | /// </summary> |
| | | public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty) |
| | | public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string palletCode, string barcode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. éªè¯åæ£è¯·æ± |
| | | var validationResult = await ValidateBatchPickingRequest(orderNo, batchNo, palletCode, barcode, actualPickedQty); |
| | | var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (lockInfo, orderDetail, stockDetail) = validationResult.Data; |
| | | var (lockInfo, orderDetail, stockDetail, batch) = validationResult.Data; |
| | | |
| | | // 使ç¨éå®ä¿¡æ¯çåé
æ°éä½ä¸ºå®é
忣æ°é |
| | | var actualPickedQty = lockInfo.AssignQuantity; |
| | | |
| | | // 2. æ§è¡åæ£é»è¾ |
| | | var pickingResult = await ExecuteBatchPickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); |
| | | var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); |
| | | |
| | | // 3. æ´æ°æ¹æ¬¡å®ææ°é |
| | | await UpdateBatchCompletedQuantity(batchNo, actualPickedQty); |
| | | // 3. æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo); |
| | | |
| | | // 4. æ´æ°è®¢åç¸å
³æ°æ® |
| | | await UpdateOrderRelatedData(orderDetail.Id, actualPickedQty, orderNo); |
| | | |
| | | // 5. è®°å½æ£éåå² |
| | | await RecordPickingHistory(pickingResult, orderNo, palletCode, batchNo); |
| | | // 4. è®°å½æ£éåå² |
| | | await RecordPickingHistory(pickingResult, orderNo, palletCode); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("忹忣æå"); |
| | | return WebResponseContent.Instance.OK("忣æå", new |
| | | { |
| | | PickedQuantity = actualPickedQty, |
| | | Barcode = barcode, |
| | | MaterialCode = lockInfo.MaterielCode |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¹åæ£å¤±è´¥ - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¹åæ£å¤±è´¥ï¼{ex.Message}"); |
| | | _logger.LogError($"åæ£å¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ£å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | /// <summary> |
| | | /// 忶忣 |
| | | /// </summary> |
| | | public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // æ¥æ¾åæ£è®°å½ |
| | | var pickingRecord = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.Barcode == barcode && |
| | | !x.IsCancelled) |
| | | .OrderByDescending(x => x.PickTime) |
| | | .FirstAsync(); |
| | | |
| | | if (pickingRecord == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°åæ£è®°å½"); |
| | | |
| | | // æ¢å¤éå®ä¿¡æ¯ååºå |
| | | await RevertPickingData(pickingRecord); |
| | | |
| | | //æ´æ°æ¹æ¬¡åè®¢åæ°æ® |
| | | await RevertBatchAndOrderData(pickingRecord); |
| | | |
| | | // æ è®°åæ£è®°å½ä¸ºå·²åæ¶ |
| | | pickingRecord.IsCancelled = true; |
| | | pickingRecord.CancelTime = DateTime.Now; |
| | | pickingRecord.CancelOperator = App.User.UserName; |
| | | await Db.Updateable(pickingRecord).ExecuteCommandAsync(); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("忶忣æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¶åæ£å¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¶åæ£å¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>> ValidateBatchPickingRequest( |
| | | string orderNo, string batchNo, string palletCode, string barcode, decimal actualPickedQty) |
| | | #endregion |
| | | |
| | | #region æå¨æå
|
| | | |
| | | /// <summary> |
| | | /// æå¨æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> ManualSplitPackage(string orderNo, string palletCode, string originalBarcode, decimal splitQuantity) |
| | | { |
| | | // æ¥æ¾æ¹æ¬¡éå®ä¿¡æ¯ |
| | | try |
| | | { |
| | | _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); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("æå¨æå
æå", new |
| | | { |
| | | NewBarcode = splitResult.NewBarcode, |
| | | OriginalBarcode = originalBarcode, |
| | | SplitQuantity = splitQuantity |
| | | }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"æå¨æå
失败 - OrderNo: {orderNo}, Barcode: {originalBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"æå¨æå
失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region åæ¶æå
|
| | | |
| | | /// <summary> |
| | | /// åæ¶æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string palletCode, string newBarcode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // æ¥æ¾æå
è®°å½å¹¶éªè¯ |
| | | var validationResult = await ValidateCancelSplitRequest(orderNo, palletCode, newBarcode); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data; |
| | | |
| | | // æ§è¡åæ¶æå
é»è¾ |
| | | await ExecuteCancelSplitLogic(splitRecord, newLockInfo, newStockDetail); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("åæ¶æå
æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¶æå
失败 - OrderNo: {orderNo}, Barcode: {newBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¶æå
失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region åæ¹ååº |
| | | |
| | | /// <summary> |
| | | /// åæ¹ååº - éæ¾æªæ£éçåºå |
| | | /// </summary> |
| | | public async Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // æ¥æ¾æç䏿ªå®æçéå®è®°å½ |
| | | var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.PalletCode == palletCode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToListAsync(); |
| | | |
| | | if (!unfinishedLocks.Any()) |
| | | return WebResponseContent.Instance.Error("该æçæ²¡ææªå®æçéå®è®°å½"); |
| | | |
| | | // ææ¹æ¬¡åç»å¤ç |
| | | var batchGroups = unfinishedLocks.GroupBy(x => x.BatchNo); |
| | | |
| | | foreach (var batchGroup in batchGroups) |
| | | { |
| | | var batchNo = batchGroup.Key; |
| | | var batchLocks = batchGroup.ToList(); |
| | | |
| | | // éæ¾åºååéå®è®°å½ |
| | | foreach (var lockInfo in batchLocks) |
| | | { |
| | | await ReleaseLockAndStock(lockInfo); |
| | | } |
| | | |
| | | // æ´æ°æ¹æ¬¡ç¶æ |
| | | await UpdateBatchStatusForReturn(batchNo, batchLocks); |
| | | |
| | | // æ´æ°è®¢åæç»çå·²åé
æ°é |
| | | await UpdateOrderDetailAfterReturn(batchLocks); |
| | | } |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | 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<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>> ValidatePickingRequest( |
| | | string orderNo, string palletCode, string barcode) |
| | | { |
| | | // æ¥æ¾éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.BatchNo == batchNo && |
| | | x.PalletCode == palletCode && |
| | | x.CurrentBarcode == barcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçæ¹æ¬¡éå®ä¿¡æ¯"); |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | |
| | | if (actualPickedQty <= 0 || actualPickedQty > lockInfo.AssignQuantity - lockInfo.PickedQty) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("忣æ°éæ æ"); |
| | | // æ£æ¥æ¯å¦å·²ç»åæ£å®æ |
| | | 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); |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == barcode && x.StockId == lockInfo.StockId); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Success((lockInfo, orderDetail, stockDetail)); |
| | | // éªè¯åºåæ°é |
| | | if (stockDetail.StockQuantity < lockInfo.AssignQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error( |
| | | $"åºåæ°éä¸è¶³ï¼éè¦ï¼{lockInfo.AssignQuantity}ï¼å®é
ï¼{stockDetail.StockQuantity}"); |
| | | |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == lockInfo.BatchNo); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Success((lockInfo, orderDetail, stockDetail, batch)); |
| | | } |
| | | |
| | | private async Task<PickingResult> ExecuteBatchPickingLogic( |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest( |
| | | string orderNo, string palletCode, string originalBarcode, decimal splitQuantity) |
| | | { |
| | | 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) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId); |
| | | |
| | | if (stockDetail.StockQuantity < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºåºåæ°é"); |
| | | |
| | | if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºæªæ£éæ°é"); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); |
| | | } |
| | | |
| | | private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest( |
| | | string orderNo, string palletCode, string 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("æªæ¾å°æå
è®°å½"); |
| | | |
| | | 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("æªæ¾å°æ°éå®ä¿¡æ¯"); |
| | | |
| | | // æ£æ¥æ°æ¡ç æ¯å¦å·²è¢«åæ£ |
| | | var pickingRecord = await Db.Queryable<Dt_PickingRecord>() |
| | | .Where(x => x.Barcode == newBarcode && !x.IsCancelled) |
| | | .FirstAsync(); |
| | | |
| | | if (pickingRecord != null) |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该æ¡ç å·²è¢«åæ£ï¼æ æ³åæ¶æå
"); |
| | | |
| | | var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == newBarcode); |
| | | |
| | | return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail)); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æ ¸å¿é»è¾æ¹æ³ |
| | | |
| | | private async Task<PickingResult> ExecutePickingLogic( |
| | | Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, |
| | | Dt_StockInfoDetail stockDetail, decimal actualPickedQty) |
| | | { |
| | |
| | | }; |
| | | } |
| | | |
| | | private async Task UpdateBatchCompletedQuantity(string batchNo, decimal pickedQty) |
| | | private async Task<RevertPickingResult> RevertPickingData(Dt_PickingRecord pickingRecord) |
| | | { |
| | | await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>() |
| | | .SetColumns(x => x.CompletedQuantity == x.CompletedQuantity + pickedQty) |
| | | .Where(x => x.BatchNo == batchNo) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // æ£æ¥æ¹æ¬¡æ¯å¦å®æ |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == batchNo); |
| | | |
| | | if (batch.CompletedQuantity >= batch.BatchQuantity) |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region æå¨æå
|
| | | |
| | | /// <summary> |
| | | /// æå¨æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> ManualSplitPackage(string orderNo, string batchNo, string originalBarcode, decimal splitQuantity) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. éªè¯æå
è¯·æ± |
| | | var validationResult = await ValidateManualSplitRequest(orderNo, batchNo, originalBarcode, splitQuantity); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (lockInfo, stockDetail) = validationResult.Data; |
| | | |
| | | // 2. æ§è¡æå
é»è¾ |
| | | var splitResult = await ExecuteManualSplit(lockInfo, stockDetail, splitQuantity, batchNo); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("æå¨æå
æå", new { NewBarcode = splitResult.NewBarcode }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"æå¨æå
失败 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {originalBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"æå¨æå
失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateManualSplitRequest( |
| | | string orderNo, string batchNo, string originalBarcode, decimal splitQuantity) |
| | | { |
| | | // æ¢å¤éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.BatchNo == batchNo && |
| | | x.CurrentBarcode == originalBarcode && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .FirstAsync(); |
| | | .FirstAsync(x => x.Id == pickingRecord.OutStockLockId); |
| | | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°ææçéå®ä¿¡æ¯"); |
| | | lockInfo.PickedQty -= pickingRecord.PickQuantity; |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // æ¢å¤åºå |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == originalBarcode && x.StockId == lockInfo.StockId); |
| | | .FirstAsync(x => x.Barcode == pickingRecord.Barcode); |
| | | |
| | | if (stockDetail.StockQuantity < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºåºåæ°é"); |
| | | stockDetail.StockQuantity += pickingRecord.PickQuantity; |
| | | stockDetail.OutboundQuantity -= pickingRecord.PickQuantity; |
| | | stockDetail.Status = (int)StockStatusEmun.åºåºéå®; |
| | | await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); |
| | | |
| | | if (lockInfo.AssignQuantity - lockInfo.PickedQty < splitQuantity) |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æå
æ°éä¸è½å¤§äºæªæ£éæ°é"); |
| | | |
| | | return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); |
| | | return new RevertPickingResult |
| | | { |
| | | LockInfo = lockInfo, |
| | | StockDetail = stockDetail |
| | | }; |
| | | } |
| | | |
| | | private async Task<SplitResultDto> ExecuteManualSplit(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string batchNo) |
| | | private async Task<SplitResultDto> ExecuteSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, |
| | | decimal splitQuantity, string palletCode) |
| | | { |
| | | // çææ°æ¡ç |
| | | string newBarcode = await GenerateNewBarcode(); |
| | |
| | | { |
| | | OrderNo = lockInfo.OrderNo, |
| | | OrderDetailId = lockInfo.OrderDetailId, |
| | | BatchNo = batchNo, |
| | | OutboundBatchNo = lockInfo.OutboundBatchNo, |
| | | MaterielCode = lockInfo.MaterielCode, |
| | | StockId = lockInfo.StockId, |
| | | OrderQuantity = splitQuantity, |
| | | AssignQuantity = splitQuantity, |
| | | PickedQty = 0, |
| | | LocationCode = lockInfo.LocationCode, |
| | | PalletCode = lockInfo.PalletCode, |
| | | PalletCode = palletCode, |
| | | Status = (int)OutLockStockStatusEnum.åºåºä¸, |
| | | CurrentBarcode = newBarcode, |
| | | Operator = App.User.UserName, |
| | |
| | | |
| | | // æ´æ°åéå®ä¿¡æ¯ |
| | | lockInfo.AssignQuantity -= splitQuantity; |
| | | lockInfo.OrderQuantity -= splitQuantity; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | |
| | | // è®°å½æå
åå² |
| | |
| | | return new SplitResultDto { NewBarcode = newBarcode }; |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region åæ¶æå
|
| | | |
| | | /// <summary> |
| | | /// åæ¶æå
|
| | | /// </summary> |
| | | public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string batchNo, string newBarcode) |
| | | private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail) |
| | | { |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // æ¥æ¾æå
è®°å½åæ°éå®ä¿¡æ¯ |
| | | var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() |
| | | .Where(x => x.NewBarcode == newBarcode && x.OrderNo == orderNo && !x.IsReverted) |
| | | .FirstAsync(); |
| | | |
| | | if (splitRecord == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°æå
è®°å½"); |
| | | |
| | | var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.CurrentBarcode == newBarcode && x.BatchNo == batchNo) |
| | | .FirstAsync(); |
| | | |
| | | if (newLockInfo == null) |
| | | return WebResponseContent.Instance.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); |
| | | |
| | | // æ¢å¤ååºå |
| | | var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); |
| | |
| | | .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 == newBarcode) |
| | | .Where(x => x.Barcode == newLockInfo.CurrentBarcode) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // å 餿°éå®ä¿¡æ¯ |
| | |
| | | // æ è®°æå
è®°å½ä¸ºå·²æ¤é |
| | | splitRecord.IsReverted = true; |
| | | splitRecord.RevertTime = DateTime.Now; |
| | | splitRecord.Operator = App.User.UserName; |
| | | splitRecord.RevertOperator = App.User.UserName; |
| | | await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("åæ¶æå
æå"); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¶æå
失败 - OrderNo: {orderNo}, BatchNo: {batchNo}, Barcode: {newBarcode}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¶æå
失败ï¼{ex.Message}"); |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region åæ¹ååº |
| | | #region æ°æ®æ´æ°æ¹æ³ |
| | | |
| | | /// <summary> |
| | | /// åæ¹ååº - éæ¾æªæ£éçåºå |
| | | /// </summary> |
| | | public async Task<WebResponseContent> BatchReturnStock(string orderNo, string batchNo) |
| | | private async Task UpdateBatchAndOrderData(Dt_OutboundBatch batch, Dt_OutboundOrderDetail orderDetail, decimal pickedQty, string orderNo) |
| | | { |
| | | try |
| | | // æ´æ°æ¹æ¬¡å®ææ°é |
| | | batch.CompletedQuantity += pickedQty; |
| | | if (batch.CompletedQuantity >= batch.BatchQuantity) |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | batch.BatchStatus = (int)BatchStatusEnum.已宿; |
| | | } |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | |
| | | // 1. æ¥æ¾æ¹æ¬¡æªå®æçéå®è®°å½ |
| | | var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(x => x.OrderNo == orderNo && |
| | | x.BatchNo == batchNo && |
| | | x.Status == (int)OutLockStockStatusEnum.åºåºä¸) |
| | | .ToListAsync(); |
| | | // æ´æ°è®¢åæç» |
| | | orderDetail.OverOutQuantity += pickedQty; |
| | | orderDetail.AllocatedQuantity -= pickedQty; |
| | | await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); |
| | | |
| | | if (!unfinishedLocks.Any()) |
| | | return WebResponseContent.Instance.Error("è¯¥æ¹æ¬¡æ²¡ææªå®æçéå®è®°å½"); |
| | | |
| | | // 2. éæ¾åºååéå®è®°å½ |
| | | foreach (var lockInfo in unfinishedLocks) |
| | | { |
| | | await ReleaseLockAndStock(lockInfo); |
| | | // æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(orderNo); |
| | | } |
| | | |
| | | // 3. æ´æ°æ¹æ¬¡ç¶æ |
| | | await UpdateBatchStatusForReturn(batchNo); |
| | | |
| | | // 4. æ´æ°è®¢åæç»çå·²åé
æ°é |
| | | await UpdateOrderDetailAfterReturn(unfinishedLocks); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | | |
| | | return WebResponseContent.Instance.OK("åæ¹ååºæå"); |
| | | } |
| | | catch (Exception ex) |
| | | private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord) |
| | | { |
| | | _unitOfWorkManage.RollbackTran(); |
| | | _logger.LogError($"åæ¹ååºå¤±è´¥ - OrderNo: {orderNo}, BatchNo: {batchNo}, Error: {ex.Message}"); |
| | | return WebResponseContent.Instance.Error($"åæ¹ååºå¤±è´¥ï¼{ex.Message}"); |
| | | } |
| | | // æ¢å¤æ¹æ¬¡å®ææ°é |
| | | var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() |
| | | .FirstAsync(x => x.BatchNo == pickingRecord.BatchNo); |
| | | |
| | | batch.CompletedQuantity -= pickingRecord.PickQuantity; |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | 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(); |
| | | |
| | | // éæ°æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(pickingRecord.OrderNo); |
| | | } |
| | | |
| | | private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo) |
| | |
| | | } |
| | | |
| | | // æ´æ°éå®è®°å½ç¶æä¸ºååº |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.ååºä¸; |
| | | lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; |
| | | await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private async Task UpdateBatchStatusForReturn(string batchNo) |
| | | private async Task UpdateBatchStatusForReturn(string batchNo, List<Dt_OutStockLockInfo> returnedLocks) |
| | | { |
| | | await _outboundBatchRepository.Db.Updateable<Dt_OutboundBatch>() |
| | | .SetColumns(x => new Dt_OutboundBatch |
| | | 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) |
| | | { |
| | | BatchStatus = (int)BatchStatusEnum.å·²ååº, |
| | | Operator = App.User.UserName |
| | | }) |
| | | .Where(x => x.BatchNo == batchNo) |
| | | .ExecuteCommandAsync(); |
| | | batch.BatchStatus = (int)BatchStatusEnum.å·²ååº; |
| | | } |
| | | else |
| | | { |
| | | batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; |
| | | } |
| | | |
| | | batch.Operator = App.User.UserName; |
| | | await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks) |
| | |
| | | private async Task<string> GenerateNewBarcode() |
| | | { |
| | | var seq = await _dailySequenceService.GetNextSequenceAsync(); |
| | | return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0'); |
| | | 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) |
| | |
| | | await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode, string batchNo) |
| | | private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode) |
| | | { |
| | | var pickingRecord = new Dt_PickingRecord |
| | | { |
| | | OrderNo = orderNo, |
| | | // BatchNo = batchNo, |
| | | // BatchNo = result.FinalLockInfo.BatchNo, |
| | | OrderDetailId = result.FinalLockInfo.OrderDetailId, |
| | | PalletCode = palletCode, |
| | | Barcode = result.FinalLockInfo.CurrentBarcode, |
| | |
| | | PickQuantity = result.ActualPickedQty, |
| | | PickTime = DateTime.Now, |
| | | Operator = App.User.UserName, |
| | | OutStockLockId = result.FinalLockInfo.Id |
| | | OutStockLockId = result.FinalLockInfo.Id, |
| | | // IsCancelled = false |
| | | }; |
| | | |
| | | await Db.Insertable(pickingRecord).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | private async Task UpdateOrderRelatedData(int orderDetailId, decimal pickedQty, string orderNo) |
| | | { |
| | | // æ´æ°è®¢åæç»çå·²åºåºæ°é |
| | | await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() |
| | | .SetColumns(x => new Dt_OutboundOrderDetail |
| | | { |
| | | OverOutQuantity = x.OverOutQuantity + pickedQty, |
| | | AllocatedQuantity = x.AllocatedQuantity - pickedQty |
| | | }) |
| | | .Where(x => x.Id == orderDetailId) |
| | | .ExecuteCommandAsync(); |
| | | |
| | | // æ£æ¥è®¢åç¶æ |
| | | await CheckAndUpdateOrderStatus(orderNo); |
| | | } |
| | | |
| | | private async Task CheckAndUpdateOrderStatus(string orderNo) |
| | | { |
| | | var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) |
| | | .Where((o, item) => item.OrderNo == orderNo) |
| | | .Select((o, item) => o) |
| | | .LeftJoin<Dt_OutboundOrder>((detail, order) => detail.OrderId == order.Id) |
| | | .Where((detail, order) => order.OrderNo == orderNo) |
| | | .Select((detail, order) => detail) |
| | | .ToListAsync(); |
| | | |
| | | |
| | | |
| | | bool allCompleted = orderDetails.All(x => x.OverOutQuantity >= x.NeedOutQuantity); |
| | | |
| | | if (allCompleted) |
| | | { |
| | | var orderStatus = allCompleted ? (int)OutOrderStatusEnum.åºåºå®æ : (int)OutOrderStatusEnum.åºåºä¸; |
| | | |
| | | await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() |
| | | .SetColumns(x => new Dt_OutboundOrder { OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ }) |
| | | .SetColumns(x => x.OrderStatus == orderStatus) |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .ExecuteCommandAsync(); |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region DTOç±» |
| | | |
| | | public class PickingResult |
| | | { |
| | | public Dt_OutStockLockInfo FinalLockInfo { get; set; } |
| | | public decimal ActualPickedQty { get; set; } |
| | | } |
| | | |
| | | public class RevertPickingResult |
| | | { |
| | | public Dt_OutStockLockInfo LockInfo { get; set; } |
| | | public Dt_StockInfoDetail StockDetail { get; set; } |
| | | } |
| | | |
| | | public class SplitResultDto |
| | | { |
| | | public string NewBarcode { get; set; } |
| | | } |
| | | |
| | | public class ValidationResult<T> |
| | | { |
| | | public bool IsValid { get; set; } |
| | | public string ErrorMessage { get; set; } |
| | | public T Data { get; set; } |
| | | |
| | | public static ValidationResult<T> Success(T data) => new ValidationResult<T> { IsValid = true, Data = data }; |
| | | public static ValidationResult<T> Error(string message) => new ValidationResult<T> { IsValid = false, ErrorMessage = message }; |
| | | } |
| | | |
| | | #endregion |