using Microsoft.Extensions.Logging;
|
using SqlSugar;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_BasicService;
|
using WIDESEA_Common.OrderEnum;
|
using WIDESEA_Common.StockEnum;
|
using WIDESEA_Core;
|
using WIDESEA_Core.BaseRepository;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_IAllocateService;
|
using WIDESEA_IBasicService;
|
using WIDESEA_IOutboundService;
|
using WIDESEA_IStockService;
|
using WIDESEA_Model.Models;
|
using WIDESEA_Model.Models.Basic;
|
using WIDESEA_Model.Models.Outbound;
|
|
namespace WIDESEA_OutboundService
|
{
|
public class OutboundBatchPickingService : ServiceBase<Dt_PickingRecord, IRepository<Dt_PickingRecord>>, IOutboundBatchPickingService
|
{
|
|
|
private readonly IUnitOfWorkManage _unitOfWorkManage;
|
public IRepository<Dt_PickingRecord> Repository => BaseDal;
|
|
private readonly IStockInfoService _stockInfoService;
|
private readonly IStockService _stockService;
|
private readonly IOutStockLockInfoService _outStockLockInfoService;
|
private readonly IStockInfoDetailService _stockInfoDetailService;
|
private readonly ILocationInfoService _locationInfoService;
|
private readonly IOutboundOrderDetailService _outboundOrderDetailService;
|
private readonly IOutboundOrderService _outboundOrderService;
|
private readonly ISplitPackageService _splitPackageService;
|
private readonly IRepository<Dt_Task> _taskRepository;
|
private readonly IESSApiService _eSSApiService;
|
private readonly IInvokeMESService _invokeMESService;
|
private readonly IDailySequenceService _dailySequenceService;
|
private readonly IAllocateService _allocateService;
|
private readonly IRepository<Dt_OutboundBatch> _outboundBatchRepository;
|
private readonly ILogger<OutboundPickingService> _logger;
|
|
private Dictionary<string, string> stations = new Dictionary<string, string>
|
{
|
{"2-1","2-9" },
|
{"3-1","3-9" },
|
|
};
|
|
private Dictionary<string, string> movestations = new Dictionary<string, string>
|
{
|
{"2-1","2-5" },
|
{"3-1","3-5" },
|
|
};
|
|
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)
|
{
|
_unitOfWorkManage = unitOfWorkManage;
|
_stockInfoService = stockInfoService;
|
_stockService = stockService;
|
_outStockLockInfoService = outStockLockInfoService;
|
_stockInfoDetailService = stockInfoDetailService;
|
_locationInfoService = locationInfoService;
|
_outboundOrderDetailService = outboundOrderDetailService;
|
_splitPackageService = splitPackageService;
|
_outboundOrderService = outboundOrderService;
|
_taskRepository = taskRepository;
|
_eSSApiService = eSSApiService;
|
_logger = logger;
|
_invokeMESService = invokeMESService;
|
_dailySequenceService = dailySequenceService;
|
_allocateService = allocateService;
|
}
|
|
|
#region 分批分拣
|
|
/// <summary>
|
/// 分批分拣确认
|
/// </summary>
|
public async Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string palletCode, string barcode)
|
{
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
|
// 1. 验证分拣请求
|
var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode);
|
if (!validationResult.IsValid)
|
return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
|
|
var (lockInfo, orderDetail, stockDetail, batch) = validationResult.Data;
|
|
// 使用锁定信息的分配数量作为实际分拣数量
|
var actualPickedQty = lockInfo.AssignQuantity;
|
|
// 2. 执行分拣逻辑
|
var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty);
|
|
// 3. 更新批次和订单数据
|
await UpdateBatchAndOrderData(batch, orderDetail, actualPickedQty, orderNo);
|
|
// 4. 记录拣选历史
|
await RecordPickingHistory(pickingResult, orderNo, palletCode);
|
|
_unitOfWorkManage.CommitTran();
|
|
return WebResponseContent.Instance.OK("分拣成功", new
|
{
|
PickedQuantity = actualPickedQty,
|
Barcode = barcode,
|
MaterialCode = lockInfo.MaterielCode
|
});
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
_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}");
|
}
|
}
|
|
#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.PalletCode == palletCode &&
|
x.CurrentBarcode == barcode &&
|
x.Status == (int)OutLockStockStatusEnum.出库中)
|
.FirstAsync();
|
|
if (lockInfo == null)
|
return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.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);
|
|
// 验证库存数量
|
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<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)
|
{
|
// 更新锁定信息
|
lockInfo.PickedQty += actualPickedQty;
|
if (lockInfo.PickedQty >= lockInfo.AssignQuantity)
|
{
|
lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
|
}
|
await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
|
|
// 更新库存
|
stockDetail.StockQuantity -= actualPickedQty;
|
stockDetail.OutboundQuantity += actualPickedQty;
|
|
if (stockDetail.StockQuantity <= 0)
|
{
|
stockDetail.Status = (int)StockStatusEmun.出库完成;
|
}
|
await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
|
|
return new PickingResult
|
{
|
FinalLockInfo = lockInfo,
|
ActualPickedQty = actualPickedQty
|
};
|
}
|
|
private async Task<RevertPickingResult> RevertPickingData(Dt_PickingRecord pickingRecord)
|
{
|
// 恢复锁定信息
|
var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.FirstAsync(x => x.Id == pickingRecord.OutStockLockId);
|
|
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 == pickingRecord.Barcode);
|
|
stockDetail.StockQuantity += pickingRecord.PickQuantity;
|
stockDetail.OutboundQuantity -= pickingRecord.PickQuantity;
|
stockDetail.Status = (int)StockStatusEmun.出库锁定;
|
await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
|
|
return new RevertPickingResult
|
{
|
LockInfo = lockInfo,
|
StockDetail = stockDetail
|
};
|
}
|
|
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 数据更新方法
|
|
private async Task UpdateBatchAndOrderData(Dt_OutboundBatch batch, Dt_OutboundOrderDetail orderDetail, decimal pickedQty, string orderNo)
|
{
|
// 更新批次完成数量
|
batch.CompletedQuantity += pickedQty;
|
if (batch.CompletedQuantity >= batch.BatchQuantity)
|
{
|
batch.BatchStatus = (int)BatchStatusEnum.已完成;
|
}
|
await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
|
|
// 更新订单明细
|
orderDetail.OverOutQuantity += pickedQty;
|
orderDetail.AllocatedQuantity -= pickedQty;
|
await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
|
|
// 检查订单状态
|
await CheckAndUpdateOrderStatus(orderNo);
|
}
|
|
private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord)
|
{
|
// 恢复批次完成数量
|
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)
|
{
|
// 恢复库存状态
|
var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
.FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
|
|
if (stockDetail != null)
|
{
|
stockDetail.Status = (int)StockStatusEmun.入库完成;
|
await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
|
}
|
|
// 更新锁定记录状态为回库
|
lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
|
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)
|
{
|
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)
|
{
|
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();
|
}
|
}
|
|
#endregion
|
|
#region 辅助方法
|
|
private async Task<string> GenerateNewBarcode()
|
{
|
var seq = await _dailySequenceService.GetNextSequenceAsync();
|
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)
|
{
|
var splitHistory = new Dt_SplitPackageRecord
|
{
|
OrderNo = lockInfo.OrderNo,
|
OutStockLockInfoId = lockInfo.Id,
|
StockId = stockDetail.StockId,
|
Operator = App.User.UserName,
|
OriginalBarcode = stockDetail.Barcode,
|
NewBarcode = newBarcode,
|
SplitQty = splitQty,
|
SplitTime = DateTime.Now,
|
Status = (int)SplitPackageStatusEnum.已拆包
|
};
|
|
await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
|
}
|
|
private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode)
|
{
|
var pickingRecord = new Dt_PickingRecord
|
{
|
OrderNo = orderNo,
|
// BatchNo = result.FinalLockInfo.BatchNo,
|
OrderDetailId = result.FinalLockInfo.OrderDetailId,
|
PalletCode = palletCode,
|
Barcode = result.FinalLockInfo.CurrentBarcode,
|
MaterielCode = result.FinalLockInfo.MaterielCode,
|
PickQuantity = result.ActualPickedQty,
|
PickTime = DateTime.Now,
|
Operator = App.User.UserName,
|
OutStockLockId = result.FinalLockInfo.Id,
|
// IsCancelled = false
|
};
|
|
await Db.Insertable(pickingRecord).ExecuteCommandAsync();
|
}
|
|
private async Task CheckAndUpdateOrderStatus(string orderNo)
|
{
|
var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
|
.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);
|
|
var orderStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
|
|
await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
|
.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
|
}
|
|
|
// 支持类
|
public class SplitResultDto
|
{
|
public string NewBarcode { get; set; }
|
}
|
}
|