| | |
| | | try |
| | | { |
| | | _unitOfWorkManage.BeginTran(); |
| | | |
| | | // 1. åç½®éªè¯åä¸å¡æ£æ¥ |
| | | |
| | | var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode); |
| | | if (!validationResult.IsValid) |
| | | return WebResponseContent.Instance.Error(validationResult.ErrorMessage); |
| | | |
| | | var (lockInfo, orderDetail, stockDetail) = validationResult.Data; |
| | | |
| | | // 2. 计ç®å®é
æ£éæ°é |
| | | // 计ç®å®é
æ£éæ°é |
| | | var quantityResult = await CalculateActualPickingQuantity(lockInfo, orderDetail, stockDetail); |
| | | if (!quantityResult.IsValid) |
| | | return WebResponseContent.Instance.Error(quantityResult.ErrorMessage); |
| | | |
| | | var (actualQty, adjustedReason) = quantityResult.Data; |
| | | |
| | | // 3. æ§è¡åæ£é»è¾ |
| | | var overPickingValidation = await ValidateOverPicking(orderDetail.Id, actualQty); |
| | | if (!overPickingValidation.IsValid) |
| | | { |
| | | return WebResponseContent.Instance.Error(overPickingValidation.ErrorMessage); |
| | | } |
| | | |
| | | // æ§è¡åæ£é»è¾ |
| | | var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, orderNo, palletCode, barcode, actualQty); |
| | | |
| | | // 4. æ´æ°ç¸å
³æ°æ® |
| | | // æ´æ°ç¸å
³æ°æ® |
| | | await UpdateOrderRelatedData(orderDetail.Id, pickingResult.ActualPickedQty, orderNo); |
| | | |
| | | // 5. è®°å½æä½åå² |
| | | // è®°å½æä½åå² |
| | | await RecordPickingHistory(pickingResult, orderNo, palletCode); |
| | | |
| | | _unitOfWorkManage.CommitTran(); |
| | |
| | | decimal remainingOrderQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity; |
| | | decimal stockQuantity = stockDetail.StockQuantity; |
| | | |
| | | if (plannedQty <= 0) |
| | | { |
| | | return ValidationResult<(decimal, string)>.Error($"è®¡åæ£éæ°éå¿
须大äº0ï¼å½å: {plannedQty}"); |
| | | } |
| | | |
| | | if (remainingOrderQty <= 0) |
| | | { |
| | | return ValidationResult<(decimal, string)>.Error($"订åå©ä½éæ±æ°éå¿
须大äº0ï¼å½å: {remainingOrderQty}"); |
| | | } |
| | | |
| | | if (stockQuantity <= 0) |
| | | { |
| | | return ValidationResult<(decimal, string)>.Error($"åºåæ°éå¿
须大äº0ï¼å½å: {stockQuantity}"); |
| | | } |
| | | // ä¸éæ£æ¥ï¼åæå°å¼ |
| | | decimal actualQty = plannedQty; |
| | | string adjustedReason = null; |
| | | |
| | | // æ£æ¥1ï¼è®¢åæ°ééå¶ |
| | | if (plannedQty > remainingOrderQty && remainingOrderQty > 0) |
| | | if (plannedQty > remainingOrderQty) |
| | | { |
| | | actualQty = remainingOrderQty; |
| | | adjustedReason = $"è®¢åæ°ééå¶ï¼ä»{plannedQty}è°æ´ä¸º{actualQty}"; |
| | | } |
| | | |
| | | // æ£æ¥2ï¼åºåæ°ééå¶ |
| | | if (actualQty > stockQuantity) |
| | | { |
| | | actualQty = stockQuantity; |
| | |
| | | ? $"{adjustedReason}ï¼åºåæ°ééå¶ï¼è¿ä¸æ¥è°æ´ä¸º{actualQty}" |
| | | : $"åºåæ°ééå¶ï¼ä»{plannedQty}è°æ´ä¸º{actualQty}"; |
| | | } |
| | | |
| | | // æ£æ¥3ï¼å®é
坿£éæ°éå¿
须大äº0 |
| | | if (actualQty <= 0) |
| | | { |
| | | return ValidationResult<(decimal, string)>.Error($"æ æ³åæ£ï¼è®¡åæ°é{plannedQty}ï¼å©ä½è®¢å{remainingOrderQty}ï¼åºå{stockQuantity}"); |
| | | return ValidationResult<(decimal, string)>.Error($"æ æ³åæ£ï¼è®¡ç®åçå®é
æ°é为{actualQty}"); |
| | | } |
| | | decimal projectedOverOut = orderDetail.OverOutQuantity + actualQty; |
| | | if (projectedOverOut > orderDetail.NeedOutQuantity) |
| | | { |
| | | // 妿ä¼è¶
æ£ï¼è°æ´ä¸ºåå¥½æ»¡è¶³éæ±çæ°é |
| | | actualQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity; |
| | | adjustedReason = adjustedReason != null |
| | | ? $"{adjustedReason}ï¼é²è¶
æ£éå¶ï¼æç»è°æ´ä¸º{actualQty}" |
| | | : $"é²è¶
æ£éå¶ï¼ä»{plannedQty}è°æ´ä¸º{actualQty}"; |
| | | } |
| | | |
| | | if (adjustedReason != null) |
| | | { |
| | |
| | | return ValidationResult<(decimal, string)>.Success((actualQty, adjustedReason)); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// ä¸é¨éªè¯æ¯å¦ä¼åçè¶
æ£ |
| | | /// </summary> |
| | | private async Task<ValidationResult<bool>> ValidateOverPicking(int orderDetailId, decimal pickingQty) |
| | | { |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == orderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return ValidationResult<bool>.Error("æªæ¾å°è®¢åæç»"); |
| | | |
| | | decimal projectedOverOut = orderDetail.OverOutQuantity + pickingQty; |
| | | |
| | | if (projectedOverOut > orderDetail.NeedOutQuantity) |
| | | { |
| | | return ValidationResult<bool>.Error( |
| | | $"忣åå°å¯¼è´è¶
æ£ï¼å½åå·²åºåº{orderDetail.OverOutQuantity}ï¼æ¬æ¬¡åæ£{pickingQty}ï¼å计{projectedOverOut}ï¼è¶
è¿éæ±{orderDetail.NeedOutQuantity}"); |
| | | } |
| | | |
| | | return ValidationResult<bool>.Success(true); |
| | | } |
| | | private async Task<PickingResult> ExecutePickingLogic( |
| | | Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail, |
| | | string orderNo, string palletCode, string barcode, decimal actualQty) |
| | |
| | | decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity + pickedQty; |
| | | decimal newPickedQty = currentOrderDetail.PickedQty + pickedQty; |
| | | |
| | | // æç»æ£æ¥ï¼ç¡®ä¿ä¸ä¼è¶
è¿è®¢åéæ±æ°é |
| | | if (newOverOutQuantity > currentOrderDetail.NeedOutQuantity) |
| | | { |
| | | throw new Exception($"忣åå°å¯¼è´å·²åºåºæ°é({newOverOutQuantity})è¶
è¿è®¢åéæ±æ°é({currentOrderDetail.NeedOutQuantity})"); |
| | | |
| | | _logger.LogError($"é²è¶
æ£æ£æ¥å¤±è´¥ - OrderDetailId: {orderDetailId}, å·²åºåº: {newOverOutQuantity}, éæ±: {currentOrderDetail.NeedOutQuantity}, æ¬æ¬¡åæ£: {pickedQty}"); |
| | | |
| | | |
| | | decimal adjustedQty = currentOrderDetail.NeedOutQuantity - currentOrderDetail.OverOutQuantity; |
| | | |
| | | if (adjustedQty > 0) |
| | | { |
| | | _logger.LogWarning($"èªå¨è°æ´åæ£æ°é鲿¢è¶
æ£ï¼ä»{pickedQty}è°æ´ä¸º{adjustedQty}"); |
| | | newOverOutQuantity = currentOrderDetail.NeedOutQuantity; |
| | | newPickedQty = currentOrderDetail.PickedQty + adjustedQty; |
| | | } |
| | | else |
| | | { |
| | | throw new Exception($"忣åå°å¯¼è´å·²åºåºæ°é({newOverOutQuantity})è¶
è¿è®¢åéæ±æ°é({currentOrderDetail.NeedOutQuantity})ï¼ä¸æ æ³èªå¨è°æ´"); |
| | | } |
| | | } |
| | | |
| | | // æ´æ°è®¢åæç» |
| | |
| | | if (pickingRecord == null) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("æªæ¾å°å¯¹åºçæ£éè®°å½"); |
| | | |
| | | if (pickingRecord.PickQuantity <= 0) |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"æ£éè®°å½æ°éæ æ: {pickingRecord.PickQuantity}"); |
| | | } |
| | | // æ¥æ¾éå®ä¿¡æ¯ |
| | | var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() |
| | | .Where(it => it.Id == pickingRecord.OutStockLockId) |
| | |
| | | |
| | | if (lockInfo == null) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("æªæ¾å°å¯¹åºçåºåºéå®ä¿¡æ¯"); |
| | | |
| | | |
| | | if (lockInfo.PickedQty < pickingRecord.PickQuantity) |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error( |
| | | $"åæ¶æ°é({pickingRecord.PickQuantity})è¶
è¿éå®ä¿¡æ¯çå·²æ£éæ°é({lockInfo.PickedQty})"); |
| | | } |
| | | // æ£æ¥ç¶ææ¯å¦å
è®¸åæ¶ |
| | | if (lockInfo.Status != (int)OutLockStockStatusEnum.æ£é宿) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("å½åç¶æä¸å
è®¸åæ¶åæ£"); |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() |
| | | .Where(it => it.Barcode == barcode && it.StockId == pickingRecord.StockId) |
| | | .FirstAsync(); |
| | | var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo); |
| | | |
| | | if (order?.OrderStatus == (int)OutOrderStatusEnum.åºåºå®æ) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("订åå·²åºåºå®æï¼ä¸å
è®¸åæ¶åæ£"); |
| | | |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == pickingRecord.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"æªæ¾å°è®¢åæç»ï¼ID: {pickingRecord.OrderDetailId}"); |
| | | |
| | | // æ£æ¥è®¢åæç»çå·²æ£éæ°éæ¯å¦è¶³å¤åæ¶ |
| | | if (orderDetail.PickedQty < pickingRecord.PickQuantity) |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"åæ¶æ°é({pickingRecord.PickQuantity})è¶
è¿è®¢åæç»çå·²æ£éæ°é({orderDetail.PickedQty})"); |
| | | } |
| | | |
| | | // æ£æ¥è®¢åæç»çå·²åºåºæ°éæ¯å¦è¶³å¤åæ¶ |
| | | if (orderDetail.OverOutQuantity < pickingRecord.PickQuantity) |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"åæ¶æ°é({pickingRecord.PickQuantity})è¶
è¿è®¢åæç»çå·²åºåºæ°é({orderDetail.OverOutQuantity})"); |
| | | } |
| | | |
| | | var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>().FirstAsync(it => it.Barcode == barcode && it.StockId == pickingRecord.StockId); |
| | | |
| | | if (stockDetail != null) |
| | | { |
| | |
| | | { |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"æ¡ç {barcode}å·²ç»ååºï¼ä¸è½åæ¶åæ£"); |
| | | } |
| | | } |
| | | |
| | | // æ£æ¥è®¢åç¶æ |
| | | var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>() |
| | | .Where(x => x.OrderNo == orderNo) |
| | | .FirstAsync(); |
| | | |
| | | if (order?.OrderStatus == (int)OutOrderStatusEnum.åºåºå®æ) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("订åå·²åºåºå®æï¼ä¸å
è®¸åæ¶åæ£"); |
| | | |
| | | // è·å订åæç» |
| | | var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() |
| | | .FirstAsync(x => x.Id == pickingRecord.OrderDetailId); |
| | | |
| | | if (orderDetail == null) |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"æªæ¾å°è®¢åæç»ï¼ID: {pickingRecord.OrderDetailId}"); |
| | | |
| | | } |
| | | |
| | | return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Success((pickingRecord, lockInfo, orderDetail)); |
| | | } |
| | | /// <summary> |
| | |
| | | await UpdateOrderDetailsOnReturn(analysis.RemainingLocks); |
| | | } |
| | | |
| | | // æ
åµ2ï¼å¤çæçä¸å
¶ä»åºåè´§ç© |
| | | // å¤çæçä¸å
¶ä»åºåè´§ç© |
| | | if (analysis.HasPalletStockGoods) |
| | | { |
| | | await HandlePalletStockGoodsReturn(analysis.PalletStockGoods); |
| | | } |
| | | |
| | | // æ
åµ3ï¼å¤çæå
è®°å½ |
| | | // å¤çæå
è®°å½ |
| | | if (analysis.HasSplitRecords) |
| | | { |
| | | await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode); |