using Dm.filter;
using MailKit.Search;
using Microsoft.AspNetCore.Http;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Outbound;
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
namespace WIDESEA_OutboundService
{
///
///
///
public class OutboundPickingService : ServiceBase>, IOutboundPickingService
{
private readonly IUnitOfWorkManage _unitOfWorkManage;
public IRepository 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 _taskRepository;
public OutboundPickingService(IRepository BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, IRepository taskRepository) : base(BaseDal)
{
_unitOfWorkManage = unitOfWorkManage;
_stockInfoService = stockInfoService;
_stockService = stockService;
_outStockLockInfoService = outStockLockInfoService;
_stockInfoDetailService = stockInfoDetailService;
_locationInfoService = locationInfoService;
_outboundOrderDetailService = outboundOrderDetailService;
_splitPackageService = splitPackageService;
_outboundOrderService = outboundOrderService;
_taskRepository = taskRepository;
}
#region 查询出库详情列表
public async Task> GetOutStockLockListAsync(string orderNo)
{
var locks = await _outStockLockInfoService.Db.Queryable()
.Where(t => t.OrderNo == orderNo)
.ToListAsync();
return locks.Select(t => new OutStockLockListResp
{
Id = t.Id,
// TaskNum = t.TaskNum,
PalletCode = t.PalletCode,
CurrentBarcode = t.CurrentBarcode,
AssignQuantity = t.AssignQuantity,
PickedQty = t.PickedQty,
Status = t.Status,
// IsSplitted = t.IsSplitted
}).ToList();
}
#endregion
public async Task ValidateBarcode(string barcode)
{
try
{
if (string.IsNullOrEmpty(barcode))
{
return WebResponseContent.Instance.Error("条码不能为空");
}
// 根据条码查询库存明细
var stockDetail = await _stockInfoDetailService.Db.Queryable()
.Includes(x => x.StockInfo)
.Where(x => x.Barcode == barcode)
.FirstAsync();
if (stockDetail == null)
{
return WebResponseContent.Instance.Error("条码不存在");
}
var result = new
{
Barcode = barcode,
MaterielCode = stockDetail.MaterielCode,
BatchNo = stockDetail.BatchNo,
AvailableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity,
LocationCode = stockDetail.StockInfo?.LocationCode,
PalletCode = stockDetail.StockInfo?.PalletCode
};
return WebResponseContent.Instance.OK(null, result);
}
catch (Exception ex)
{
return WebResponseContent.Instance.Error($"条码验证失败: {ex.Message}");
}
}
///
/// 扫码拣选确认
///
public async Task ConfirmPicking(PickingConfirmRequest request)
{
try
{
_unitOfWorkManage.BeginTran();
// 1. 验证条码有效性
var stockDetail = await _stockInfoDetailService.Db.Queryable()
.Where(x => x.Barcode == request.Barcode && x.MaterielCode == request.MaterielCode)
.FirstAsync();
if (stockDetail == null)
return WebResponseContent.Instance.Error("无效的条码或物料编码");
// 2. 检查库存可用数量
decimal availableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity;
if (request.PickQuantity > availableQuantity)
return WebResponseContent.Instance.Error($"拣选数量超过可用库存,可用数量:{availableQuantity}");
// 3. 查找相关的出库锁定信息(支持拆包后的新条码)
var lockInfo = await FindLockInfoByBarcode(request.OrderDetailId, request.Barcode, request.MaterielCode);
if (lockInfo == null)
return WebResponseContent.Instance.Error("未找到相关的出库锁定信息");
// 4. 检查锁定数量
decimal remainingLockQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty;
if (request.PickQuantity > remainingLockQuantity)
return WebResponseContent.Instance.Error($"拣选数量超过锁定数量,剩余可拣选:{remainingLockQuantity}");
// 5. 更新锁定信息的已拣选数量
lockInfo.PickedQty += request.PickQuantity;
await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
// 6. 更新库存出库数量 - 实际减少库存
stockDetail.OutboundQuantity += request.PickQuantity;
await _stockInfoService.Db.Updateable(stockDetail).ExecuteCommandAsync();
// 7. 更新出库单明细
var orderDetail = await _outboundOrderDetailService.Db.Queryable()
.Where(x => x.Id == request.OrderDetailId)
.FirstAsync();
orderDetail.OverOutQuantity += request.PickQuantity;
orderDetail.LockQuantity -= request.PickQuantity;
// 检查是否完成出库
if (Math.Abs(orderDetail.OverOutQuantity - orderDetail.OrderQuantity) < 0.001m)
{
orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
orderDetail.LockQuantity = 0;
// 更新相关的锁定信息状态为已出库
var relatedLockInfos = await _outStockLockInfoService.Db.Queryable()
.Where(x => x.OrderDetailId == request.OrderDetailId &&
x.Status == (int)OutLockStockStatusEnum.出库中)
.ToListAsync();
foreach (var relatedLock in relatedLockInfos)
{
relatedLock.Status = (int)OutLockStockStatusEnum.已出库;
}
await _outStockLockInfoService.Db.Updateable(relatedLockInfos).ExecuteCommandAsync();
}
await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
// 8. 记录拣选历史
var pickHistory = new Dt_PickingRecord
{
OrderDetailId = request.OrderDetailId,
Barcode = request.Barcode,
PickQuantity = request.PickQuantity,
PickTime = DateTime.Now,
LocationCode = request.LocationCode,
StockId = stockDetail.StockId
};
await Db.Insertable(pickHistory).ExecuteCommandAsync();
_unitOfWorkManage.CommitTran();
return WebResponseContent.Instance.OK("拣选确认成功");
}
catch (Exception ex)
{
_unitOfWorkManage.RollbackTran();
return WebResponseContent.Instance.Error($"拣选确认失败: {ex.Message}");
}
}
public async Task ConfirmPicking(string orderNo, string palletCode, string barcode)
{
try
{
_unitOfWorkManage.BeginTran();
var lockInfo = await _outStockLockInfoService.Db.Queryable()
.Where(it => it.OrderNo == orderNo &&
it.Status == (int)OutLockStockStatusEnum.出库中 &&
it.PalletCode == palletCode &&
it.CurrentBarcode == barcode)
.FirstAsync();
if (lockInfo == null)
throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
var stockDetail = await _stockInfoDetailService.Db.Queryable()
.Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
.FirstAsync();
if (stockDetail == null)
return WebResponseContent.Instance.Error("无效的条码或物料编码");
decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
// 4. 更新库存
stockDetail.StockQuantity -= actualQty;
stockDetail.OutboundQuantity -= actualQty;
await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
lockInfo.PickedQty += actualQty;
lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
await _outboundOrderDetailService.Db.Updateable()
.SetColumns(it => it.PickedQty == it.PickedQty + actualQty)
.Where(it => it.Id == lockInfo.OrderDetailId)
.ExecuteCommandAsync();
await CheckAndUpdateOrderStatus(orderNo);
//查询任务表
var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
// 9. 记录拣选历史
var pickingHistory = new Dt_PickingRecord
{
FactoryArea = lockInfo.FactoryArea,
TaskNo = task?.TaskNum ?? 0,
LocationCode = task?.SourceAddress ?? "",
StockId = stockDetail.Id,
OrderNo = orderNo,
OrderDetailId = lockInfo.OrderDetailId,
PalletCode = palletCode,
Barcode = barcode,
MaterielCode = lockInfo.MaterielCode,
PickQuantity = lockInfo.AssignQuantity,
PickTime = DateTime.Now,
Operator = App.User.UserName,
OutStockLockId = lockInfo.Id
};
await Db.Insertable(pickingHistory).ExecuteCommandAsync();
_unitOfWorkManage.CommitTran();
return WebResponseContent.Instance.OK("拣选确认成功");
}
catch (Exception ex)
{
return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
}
}
// 检查并更新订单状态
private async Task CheckAndUpdateOrderStatus(string orderNo)
{
var orderDetails = await _stockInfoDetailService.Db.Queryable()
.Where(x => x.OrderId == orderNo.ObjToInt())
.ToListAsync();
bool allCompleted = true;
foreach (var detail in orderDetails)
{
if (detail.OverOutQuantity < detail.NeedOutQuantity)
{
allCompleted = false;
break;
}
}
if (allCompleted)
{
await _outboundOrderService.Db.Updateable()
.SetColumns(x => x.OrderStatus == 2) // 已完成
.Where(x => x.OrderNo == orderNo)
.ExecuteCommandAsync();
}
}
// 取消拣选功能
public async Task CancelPicking(string orderNo, string palletCode, string barcode)
{
try
{
_unitOfWorkManage.BeginTran();
// 查找拣选记录
var outStockInfo = await _outStockLockInfoService.Db.Queryable()
.Where(x => x.OrderNo == orderNo &&
x.PalletCode == palletCode &&
x.CurrentBarcode == barcode &&
x.Status == 2)
.FirstAsync();
if (outStockInfo == null)
return WebResponseContent.Instance.Error("未找到已拣选记录");
// 还原出库详情状态
outStockInfo.PickedQty = 0;
outStockInfo.Status = 1;
await _outStockLockInfoService.Db.Updateable(outStockInfo).ExecuteCommandAsync();
//// 还原库存出库数量
//await _stockInfoDetailService.Db.Updateable()
// .SetColumns(x => x.OutboundQuantity == x.OutboundQuantity - outStockInfo.AssignQuantity)
// .Where(x => x.Barcode == barcode)
// .ExecuteCommandAsync();
// 还原出库单明细
var orderDetail = await _outboundOrderDetailService.Db.Queryable()
.Where(x => x.Id == outStockInfo.OrderDetailId)
.FirstAsync();
orderDetail.OverOutQuantity -= outStockInfo.AssignQuantity;
await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
// 删除拣选历史
await Db.Deleteable()
.Where(x => x.OutStockLockId == outStockInfo.Id)
.ExecuteCommandAsync();
_unitOfWorkManage.CommitTran();
return WebResponseContent.Instance.OK("取消拣选成功");
}
catch (Exception ex)
{
return WebResponseContent.Instance.Error($"取消拣选失败:{ex.Message}");
}
}
///
/// 根据条码查找锁定信息
///
private async Task FindLockInfoByBarcode(int orderDetailId, string barcode, string materielCode)
{
return await _outStockLockInfoService.Db.Queryable()
.Where(x => x.OrderDetailId == orderDetailId &&
x.MaterielCode == materielCode &&
x.CurrentBarcode == barcode &&
x.Status == (int)OutLockStockStatusEnum.出库中 &&
x.AssignQuantity > x.PickedQty)
.FirstAsync();
}
// 获取未拣选列表
public async Task> GetUnpickedList(string orderNo, string palletCode)
{
var list = await _outStockLockInfoService.Db.Queryable()
.Where(x => x.OrderNo == orderNo &&
x.PalletCode == palletCode &&
x.Status == 1)
.ToListAsync();
return list.Where(x => x.RemainQuantity > 0).ToList();
}
// 获取已拣选列表
public async Task> GetPickedList(string orderNo, string palletCode)
{
var list = await _outStockLockInfoService.Db.Queryable()
.Where(x => x.OrderNo == orderNo &&
x.PalletCode == palletCode &&
x.Status == 2)
.ToListAsync();
return list;
}
public async Task