using Microsoft.AspNetCore.Http;
|
using Microsoft.Extensions.Logging;
|
using Org.BouncyCastle.Asn1.Ocsp;
|
using SqlSugar.Extensions;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using WIDESEA_Common.StockEnum;
|
using WIDESEA_Core;
|
using WIDESEA_Core.BaseRepository;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_DTO.Basic;
|
using WIDESEA_DTO.Outbound;
|
using WIDESEA_IBasicService;
|
using WIDESEA_IOutboundService;
|
using WIDESEA_IStockService;
|
using WIDESEA_Model.Models;
|
using WIDESEA_Model.Models.Basic;
|
|
namespace WIDESEA_OutboundService
|
{
|
internal class SplitPackageService : ServiceBase<Dt_SplitPackageRecord, IRepository<Dt_SplitPackageRecord>>, ISplitPackageService
|
{
|
private readonly IUnitOfWorkManage _unitOfWorkManage;
|
public IRepository<Dt_SplitPackageRecord> Repository => BaseDal;
|
|
private readonly IStockInfoService _stockInfoService;
|
private readonly IStockInfoDetailService _stockInfoDetailService;
|
private readonly IOutStockLockInfoService _outStockLockInfoService;
|
private readonly IDailySequenceService _dailySequenceService;
|
private readonly IInvokeMESService _invokeMESService;
|
private readonly IMaterielToMesService _materielToMesService;
|
private readonly ILogger<SplitPackageService> _logger;
|
public SplitPackageService(IRepository<Dt_SplitPackageRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, IDailySequenceService dailySequenceService, IInvokeMESService invokeMESService, ILogger<SplitPackageService> logger, IMaterielToMesService materielToMesService) : base(BaseDal)
|
{
|
_unitOfWorkManage = unitOfWorkManage;
|
_stockInfoService = stockInfoService;
|
_outStockLockInfoService = outStockLockInfoService;
|
_stockInfoDetailService = stockInfoDetailService;
|
_dailySequenceService = dailySequenceService;
|
_invokeMESService = invokeMESService;
|
_logger = logger;
|
_materielToMesService = materielToMesService;
|
}
|
|
public async Task<WebResponseContent> SplitPackage(SplitPackageDto request)
|
{
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
|
// 1. 验证出库锁定信息
|
var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.Where(x => x.OrderNo == request.OrderNo &&
|
x.PalletCode == request.PalletCode &&
|
x.CurrentBarcode == request.OriginalBarcode &&
|
x.Status == 1)
|
.FirstAsync();
|
|
if (lockInfo == null)
|
return WebResponseContent.Instance.Error("未找到有效的出库锁定信息");
|
|
// 2. 检查剩余锁定数量
|
decimal remainingLockQuantity = lockInfo.OriginalQuantity - lockInfo.PickedQty;
|
if (request.SplitQuantity > remainingLockQuantity)
|
return WebResponseContent.Instance.Error($"拆包数量不能大于剩余锁定数量,剩余:{remainingLockQuantity}");
|
|
// 3. 查找当前条码的库存信息
|
var baseStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
.Where(x => x.Barcode == request.OriginalBarcode && x.StockId == lockInfo.StockId)
|
.FirstAsync();
|
|
if (baseStockDetail == null)
|
throw new Exception($"未找到条码{request.OriginalBarcode}对应的库存记录");
|
|
// 4. 记录拆包前的库存数量和锁定信息
|
decimal stockBeforeSplit = baseStockDetail.StockQuantity;
|
decimal assignBeforeSplit = lockInfo.AssignQuantity;
|
|
// 5. 计算拆分后的剩余数量
|
decimal remainingQty = baseStockDetail.StockQuantity - request.SplitQuantity;
|
|
// 更新基础条码的库存数量为剩余数量
|
baseStockDetail.StockQuantity = remainingQty;
|
baseStockDetail.OutboundQuantity = remainingQty;
|
await _stockInfoDetailService.Db.Updateable(baseStockDetail).ExecuteCommandAsync();
|
|
// 6. 生成新条码
|
var seq = await _dailySequenceService.GetNextSequenceAsync();
|
string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
|
|
// 7. 为新条码创建库存记录(拆分出的数量)
|
var newStockDetail = new Dt_StockInfoDetail
|
{
|
SupplyCode = baseStockDetail.SupplyCode,
|
WarehouseCode = baseStockDetail.WarehouseCode,
|
BarcodeQty = baseStockDetail.BarcodeQty,
|
BarcodeUnit = baseStockDetail.BarcodeUnit,
|
BusinessType = baseStockDetail.BusinessType,
|
Unit = baseStockDetail.Unit,
|
StockId = lockInfo.StockId,
|
MaterielCode = baseStockDetail.MaterielCode,
|
OrderNo = baseStockDetail.OrderNo,
|
BatchNo = baseStockDetail.BatchNo,
|
StockQuantity = request.SplitQuantity,
|
OutboundQuantity = request.SplitQuantity,
|
Barcode = newBarcode,
|
InboundOrderRowNo = baseStockDetail.InboundOrderRowNo,
|
};
|
await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
|
|
// 8. 创建新的出库锁定信息(新条码)
|
var newLockInfo = new Dt_OutStockLockInfo
|
{
|
OrderNo = lockInfo.OrderNo,
|
OrderDetailId = lockInfo.OrderDetailId,
|
BatchNo = lockInfo.BatchNo,
|
MaterielCode = lockInfo.MaterielCode,
|
MaterielName = lockInfo.MaterielName,
|
StockId = lockInfo.StockId,
|
OrderQuantity = request.SplitQuantity,
|
OriginalQuantity = request.SplitQuantity,
|
AssignQuantity = request.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,
|
OriginalLockQuantity = request.SplitQuantity,
|
IsSplitted = 1,
|
ParentLockId = lockInfo.Id,
|
Operator = App.User.UserName,
|
FactoryArea = lockInfo.FactoryArea,
|
lineNo = lockInfo.lineNo,
|
WarehouseCode = lockInfo.WarehouseCode,
|
};
|
await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
|
|
// 9. 更新原锁定信息的分配数量(减少拆包数量)
|
lockInfo.AssignQuantity = remainingQty;
|
lockInfo.IsSplitted = 1;
|
await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
|
|
// 10. 记录拆包历史
|
var splitHistory = new Dt_SplitPackageRecord
|
{
|
FactoryArea = lockInfo.FactoryArea,
|
TaskNum = lockInfo.TaskNum,
|
OutStockLockInfoId = lockInfo.Id,
|
StockId = baseStockDetail.StockId,
|
Operator = App.User.UserName,
|
IsReverted = false,
|
OriginalBarcode = request.OriginalBarcode,
|
NewBarcode = newBarcode,
|
SplitQty = request.SplitQuantity,
|
RemainQuantity = remainingQty,
|
StockBeforeSplit = stockBeforeSplit, // 记录拆包前的库存数量
|
AssignBeforeSplit = assignBeforeSplit, // 记录拆包前的分配数量
|
MaterielCode = lockInfo.MaterielCode,
|
SplitTime = DateTime.Now,
|
OrderNo = request.OrderNo,
|
PalletCode = request.PalletCode,
|
Status = (int)SplitPackageStatusEnum.已拆包
|
};
|
await Db.Insertable(splitHistory).ExecuteCommandAsync();
|
|
_unitOfWorkManage.CommitTran();
|
|
try
|
{
|
|
var dt_MaterielToMes = new Dt_MaterielToMes
|
{
|
OldMaterialBarCode = request.OriginalBarcode,
|
NewMaterialBarCode = newBarcode,
|
Unit = baseStockDetail.BarcodeUnit,
|
factoryArea = baseStockDetail.FactoryArea,
|
Qty = remainingQty,
|
supplyCode = baseStockDetail.SupplyCode,
|
warehouseCode = baseStockDetail.WarehouseCode,
|
BatchNo = baseStockDetail.BatchNo,
|
MaterielCode = baseStockDetail.MaterielCode,
|
|
};
|
_materielToMesService.AddData(dt_MaterielToMes);
|
|
}
|
catch (Exception ex)
|
{
|
_logger.LogError("SplitPackage 回传MES: " + ex.Message);
|
}
|
|
return WebResponseContent.Instance.OK("拆包成功", new SplitPackageChainDto
|
{
|
NewBarcode = newBarcode,
|
NewLockInfoId = newLockInfo.Id,
|
RemainQuantity = remainingQty
|
});
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return WebResponseContent.Instance.Error($"拆包失败: {ex.Message}");
|
}
|
}
|
|
public async Task<WebResponseContent> RevertSplitPackage(string originalBarcode)
|
{
|
try
|
{
|
_unitOfWorkManage.BeginTran();
|
|
// 1. 查找该条码作为原始条码的所有未撤销拆包记录,按时间倒序
|
var splitRecords = await Db.Queryable<Dt_SplitPackageRecord>()
|
.Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
|
.OrderByDescending(x => x.SplitTime)
|
.ToListAsync();
|
|
if (splitRecords == null || !splitRecords.Any())
|
return WebResponseContent.Instance.Error("未找到可撤销的拆包记录");
|
|
// 2. 检查所有新条码是否已拣选
|
var newBarcodes = splitRecords.Select(x => x.NewBarcode).ToList();
|
var newLockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.Where(x => newBarcodes.Contains(x.CurrentBarcode))
|
.ToListAsync();
|
|
var pickedBarcodes = newLockInfos.Where(x => x.Status == 2).Select(x => x.CurrentBarcode).ToList();
|
if (pickedBarcodes.Any())
|
return WebResponseContent.Instance.Error($"以下条码已拣选,无法撤销拆包:{string.Join(",", pickedBarcodes)}");
|
|
// 3. 获取原条码的锁定信息和库存信息
|
var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.Where(x => x.CurrentBarcode == originalBarcode)
|
.FirstAsync();
|
|
if (originalLockInfo == null)
|
return WebResponseContent.Instance.Error("未找到原条码锁定信息");
|
|
var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
.Where(x => x.Barcode == originalBarcode && x.StockId == originalLockInfo.StockId)
|
.FirstAsync();
|
|
if (originalStockDetail == null)
|
return WebResponseContent.Instance.Error("未找到原条码库存信息");
|
|
// 4. 查找第一次拆包记录,获取拆包前的状态
|
var firstSplitRecord = splitRecords.OrderBy(x => x.SplitTime).FirstOrDefault();
|
if (firstSplitRecord == null)
|
return WebResponseContent.Instance.Error("未找到有效的拆包记录");
|
|
// 5. 恢复原条码到拆包前的状态
|
originalStockDetail.StockQuantity = firstSplitRecord.StockBeforeSplit;
|
originalStockDetail.OutboundQuantity = firstSplitRecord.StockBeforeSplit;
|
await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
|
|
// 6. 恢复原条码锁定信息到拆包前的状态
|
originalLockInfo.AssignQuantity = firstSplitRecord.AssignBeforeSplit;
|
originalLockInfo.OriginalQuantity = firstSplitRecord.AssignBeforeSplit;
|
originalLockInfo.OrderQuantity = firstSplitRecord.AssignBeforeSplit;
|
originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
|
originalLockInfo.IsSplitted = 0; // 恢复为未拆包状态
|
await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
|
|
// 7. 删除所有新条码的锁定信息
|
await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
|
.Where(x => newBarcodes.Contains(x.CurrentBarcode))
|
.ExecuteCommandAsync();
|
|
// 8. 删除所有新条码的库存记录
|
await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
|
.Where(x => newBarcodes.Contains(x.Barcode))
|
.ExecuteCommandAsync();
|
|
// 9. 标记所有拆包记录为已撤销
|
foreach (var record in splitRecords)
|
{
|
record.IsReverted = true;
|
record.RevertTime = DateTime.Now;
|
|
record.Status = (int)SplitPackageStatusEnum.已撤销;
|
}
|
await Db.Updateable(splitRecords).ExecuteCommandAsync();
|
|
_unitOfWorkManage.CommitTran();
|
|
return WebResponseContent.Instance.OK($"撤销拆包成功,共撤销{splitRecords.Count}次拆包,{originalBarcode}恢复为拆包前数量:{firstSplitRecord.StockBeforeSplit}");
|
|
}
|
catch (Exception ex)
|
{
|
_unitOfWorkManage.RollbackTran();
|
return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}");
|
}
|
}
|
|
//public async Task<WebResponseContent> RevertSplitPackage(string originalBarcode)
|
//{
|
// try
|
// {
|
// _unitOfWorkManage.BeginTran();
|
|
// // 1. 查找最近的未撤销拆包记录
|
// var splitRecord = await Db.Queryable<Dt_SplitPackageRecord>()
|
// .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
|
// .OrderByDescending(x => x.SplitTime)
|
// .FirstAsync();
|
|
// if (splitRecord == null)
|
// return WebResponseContent.Instance.Error("未找到可撤销的拆包记录");
|
|
// // 2. 检查新条码是否已拣选
|
// var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
// .Where(x => x.CurrentBarcode == splitRecord.NewBarcode)
|
// .FirstAsync();
|
|
// if (newLockInfo?.Status ==6) // 假设状态2表示已拣选
|
// return WebResponseContent.Instance.Error("新条码已拣选,无法撤销拆包");
|
|
// // 3. 获取原条码的锁定信息
|
// var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
// .Where(x => x.CurrentBarcode == originalBarcode)
|
// .FirstAsync();
|
|
// if (originalLockInfo == null)
|
// return WebResponseContent.Instance.Error("未找到原条码锁定信息");
|
|
|
// originalLockInfo.AssignQuantity += splitRecord.RemainQuantity;
|
// // originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
|
// originalLockInfo.IsSplitted = 0; // 标记为未拆包
|
// await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
|
|
// // 5. 还原原条码库存记录
|
// var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
// .Where(x => x.Barcode == originalBarcode && x.StockId == splitRecord.StockId)
|
// .FirstAsync();
|
|
// if (originalStockDetail != null)
|
// {
|
// // 将拆出的数量加回到原条码库存
|
// originalStockDetail.StockQuantity += splitRecord.RemainQuantity;
|
// originalStockDetail.OutboundQuantity += splitRecord.RemainQuantity;
|
// await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
|
// }
|
|
// // 6. 删除新条码的锁定信息
|
// if (newLockInfo != null)
|
// {
|
// await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
|
// .Where(x => x.CurrentBarcode == splitRecord.NewBarcode)
|
// .ExecuteCommandAsync();
|
// }
|
|
// // 7. 删除新条码的库存记录
|
// var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
// .Where(x => x.Barcode == splitRecord.NewBarcode)
|
// .FirstAsync();
|
|
// if (newStockDetail != null)
|
// {
|
// await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
|
// .Where(x => x.Barcode == splitRecord.NewBarcode)
|
// .ExecuteCommandAsync();
|
// }
|
|
// // 8. 更新拆包记录为已撤销
|
// splitRecord.IsReverted = true;
|
|
// await Db.Updateable(splitRecord).ExecuteCommandAsync();
|
|
// _unitOfWorkManage.CommitTran();
|
|
// return WebResponseContent.Instance.OK($"撤销拆包成功,还原数量:{splitRecord.SplitQty}");
|
|
// }
|
// catch (Exception ex)
|
// {
|
// _unitOfWorkManage.RollbackTran();
|
// return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}");
|
// }
|
//}
|
|
|
// 获取可撤销的拆包记录列表
|
public Dt_SplitPackageRecord GetRevertableSplitRecords(string originalBarcode)
|
{
|
var revertableRecords = Db.Queryable<Dt_SplitPackageRecord>()
|
.Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted)
|
.OrderBy(x => x.SplitTime)
|
.First();
|
|
return revertableRecords;
|
}
|
|
// 获取拆包信息
|
public async Task<WebResponseContent> GetSplitPackageInfo(string orderNo, string palletCode, string barcode)
|
{
|
var outStockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.Where(x => x.OrderNo == orderNo &&
|
x.PalletCode == palletCode &&
|
x.CurrentBarcode == barcode &&
|
x.Status == 1)
|
.FirstAsync();
|
|
if (outStockInfo == null)
|
return WebResponseContent.Instance.Error("未找到对应的出库信息");
|
|
//var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
|
// .Where(x => x.Barcode == barcode)
|
// .FirstAsync();
|
|
return WebResponseContent.Instance.OK("", new
|
{
|
MaterielCode = outStockInfo.MaterielCode,
|
RemainQuantity = outStockInfo.RemainQuantity,
|
Unit = outStockInfo.Unit
|
});
|
}
|
/// <summary>
|
/// 获取可拆包的出库锁定信息
|
/// </summary>
|
public async Task<WebResponseContent> GetSplitableLockInfos(int orderDetailId)
|
{
|
var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
|
.Includes(x => x.StockInfo)
|
.Where(x => x.OrderDetailId == orderDetailId &&
|
x.Status == (int)OutLockStockStatusEnum.出库中 &&
|
x.AssignQuantity > x.PickedQty) // 还有未拣选数量
|
.Select(x => new
|
{
|
x.Id,
|
x.PalletCode,
|
x.LocationCode,
|
x.MaterielCode,
|
LockQuantity = x.AssignQuantity - x.PickedQty,
|
x.CurrentBarcode,
|
x.IsSplitted,
|
StockDetails = x.StockInfo.Details.Where(d => d.MaterielCode == x.MaterielCode)
|
.Select(d => new
|
{
|
d.Barcode,
|
AvailableQuantity = d.StockQuantity - d.OutboundQuantity
|
})
|
.ToList()
|
})
|
.ToListAsync();
|
|
return WebResponseContent.Instance.OK(null, lockInfos);
|
}
|
}
|
}
|