using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Org.BouncyCastle.Asn1.Ocsp; 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; namespace WIDESEA_OutboundService { internal class SplitPackageService : ServiceBase>, ISplitPackageService { private readonly IUnitOfWorkManage _unitOfWorkManage; public IRepository Repository => BaseDal; private readonly IStockInfoService _stockInfoService; private readonly IStockInfoDetailService _stockInfoDetailService; private readonly IOutStockLockInfoService _outStockLockInfoService; private readonly IDailySequenceService _dailySequenceService; private readonly IInvokeMESService _invokeMESService; private readonly ILogger _logger; public SplitPackageService(IRepository BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, IDailySequenceService dailySequenceService, IInvokeMESService invokeMESService, ILogger logger) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; _outStockLockInfoService = outStockLockInfoService; _stockInfoDetailService = stockInfoDetailService; _dailySequenceService = dailySequenceService; _invokeMESService = invokeMESService; _logger = logger; } /// /// 拆包拆箱操作 /// public async Task SplitPackage(SplitPackageDto request) { try { _unitOfWorkManage.BeginTran(); // 1. 验证出库锁定信息 var lockInfo = await _outStockLockInfoService.Db.Queryable() .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}"); var stockDetail = await _stockInfoDetailService.Db.Queryable() .Where(x => x.Barcode == request.OriginalBarcode && x.StockId == lockInfo.StockId) .FirstAsync(); if (stockDetail == null) throw new Exception($"未找到条码{request.OriginalBarcode}对应的库存记录"); stockDetail.StockQuantity = request.SplitQuantity; stockDetail.OutboundQuantity = request.SplitQuantity; _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommand(); var seq = await _dailySequenceService.GetNextSequenceAsync(); // 3. 生成新条码 string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0'); decimal remainingQty = remainingLockQuantity - request.SplitQuantity; // 为拆包产生的新条码创建库存记录 var newStockDetail = new Dt_StockInfoDetail { SupplyCode = stockDetail.SupplyCode, WarehouseCode = stockDetail.WarehouseCode, BarcodeQty = stockDetail.BarcodeQty, BarcodeUnit = stockDetail.BarcodeUnit, BusinessType = stockDetail.BusinessType, Unit = stockDetail.Unit, StockId = lockInfo.StockId, MaterielCode = stockDetail.MaterielCode, OrderNo = stockDetail.OrderNo, BatchNo = stockDetail.BatchNo, StockQuantity = remainingQty, OutboundQuantity = remainingQty, // 锁定全部数量 Barcode = newBarcode, InboundOrderRowNo = stockDetail.InboundOrderRowNo, }; await _outStockLockInfoService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); // 4. 创建新的出库锁定信息(新条码) var newLockInfo = new Dt_OutStockLockInfo { OrderNo = lockInfo.OrderNo, OrderDetailId = lockInfo.OrderDetailId, BatchNo = lockInfo.BatchNo, MaterielCode = lockInfo.MaterielCode, MaterielName = lockInfo.MaterielName, StockId = lockInfo.StockId, OrderQuantity = remainingQty, OriginalQuantity = remainingQty, AssignQuantity = remainingQty, // 新条码分配数量 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 // 记录父级锁定ID }; await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); // 5. 更新原锁定信息的分配数量(减少拆包数量) lockInfo.AssignQuantity = request.SplitQuantity; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // 6. 记录拆包历史(用于追踪) var splitHistory = new Dt_SplitPackageRecord { FactoryArea = lockInfo.FactoryArea, TaskNum = lockInfo.TaskNum, OutStockLockInfoId = lockInfo.Id, StockId = stockDetail?.StockId ?? 0, Operator = App.User.UserName, IsReverted = false, OriginalBarcode = lockInfo.CurrentBarcode, NewBarcode = newBarcode, SplitQty = request.SplitQuantity, RemainQuantity = lockInfo.OriginalQuantity - request.SplitQuantity, MaterielCode = lockInfo.MaterielCode, SplitTime = DateTime.Now, OrderNo = request.OrderNo, PalletCode = request.PalletCode, Status = (int)SplitPackageStatusEnum.已拆包 }; await Db.Insertable(splitHistory).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); try { MaterielToMesDTO dto = new MaterielToMesDTO { batchNo = stockDetail.BatchNo, factoryArea = stockDetail.FactoryArea, materialCode = stockDetail.MaterielCode, newmaterialCode = newBarcode, oldmaterialCode = request.OriginalBarcode, operationType = 1, qty = remainingQty, supplyCode = stockDetail.SupplyCode, unit = stockDetail.BarcodeUnit, warehouseCode = stockDetail.WarehouseCode, reqCode = Guid.NewGuid().ToString(), reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; _invokeMESService.NewMaterielToMes(dto); } catch(Exception ex) { _logger.LogError("SplitPackage 回传MES: " + ex.Message); } return WebResponseContent.Instance.OK("拆包成功", new { NewBarcode = newBarcode, NewLockInfoId = newLockInfo.Id }); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error($"拆包失败: {ex.Message}"); } } //public async Task RevertSplitPackage(string originalBarcode) //{ // try // { // _unitOfWorkManage.BeginTran(); // // 1. 查找最近的未撤销拆包记录 // var splitRecord = await Db.Queryable() // .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() // .Where(x => x.CurrentBarcode == splitRecord.NewBarcode) // .FirstAsync(); // if (newLockInfo?.Status ==6) // 假设状态2表示已拣选 // return WebResponseContent.Instance.Error("新条码已拣选,无法撤销拆包"); // // 3. 获取原条码的锁定信息 // var originalLockInfo = await _outStockLockInfoService.Db.Queryable() // .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() // .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() // .Where(x => x.CurrentBarcode == splitRecord.NewBarcode) // .ExecuteCommandAsync(); // } // // 7. 删除新条码的库存记录 // var newStockDetail = await _stockInfoDetailService.Db.Queryable() // .Where(x => x.Barcode == splitRecord.NewBarcode) // .FirstAsync(); // if (newStockDetail != null) // { // await _stockInfoDetailService.Db.Deleteable() // .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 async Task RevertSplitPackage(string originalBarcode) { try { _unitOfWorkManage.BeginTran(); // 1. 查找所有未撤销的拆包记录 var splitRecords = await Db.Queryable() .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted) .OrderBy(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() .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() .Where(x => x.CurrentBarcode == originalBarcode) .FirstAsync(); if (originalLockInfo == null) return WebResponseContent.Instance.Error("未找到原条码锁定信息"); var originalStockDetail = await _stockInfoDetailService.Db.Queryable() .Where(x => x.Barcode == originalBarcode && x.StockId == originalLockInfo.StockId) .FirstAsync(); // 4. 获取所有新条码的库存记录 var newStockDetails = await _stockInfoDetailService.Db.Queryable() .Where(x => newBarcodes.Contains(x.Barcode)) .ToListAsync(); // 5. 计算总还原数量 decimal totalRevertQty = newStockDetails.Sum(x => x.StockQuantity); // 6. 还原原条码库存记录 if (originalStockDetail != null) { // 原条码当前数量加上所有新条码的数量 originalStockDetail.StockQuantity += totalRevertQty; originalStockDetail.OutboundQuantity += totalRevertQty; await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync(); } // 7. 还原原条码锁定信息 decimal totalAssignQty = newLockInfos.Sum(x => x.AssignQuantity); originalLockInfo.AssignQuantity += totalAssignQty; if (originalLockInfo.OrderQuantity < originalLockInfo.AssignQuantity) { originalLockInfo.AssignQuantity=originalLockInfo.OrderQuantity; } originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中; originalLockInfo.IsSplitted = 0; await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); // 8. 删除所有新条码的锁定信息 await _outStockLockInfoService.Db.Deleteable() .Where(x => newBarcodes.Contains(x.CurrentBarcode)) .ExecuteCommandAsync(); // 9. 删除所有新条码的库存记录 await _stockInfoDetailService.Db.Deleteable() .Where(x => newBarcodes.Contains(x.Barcode)) .ExecuteCommandAsync(); // 10. 标记所有拆包记录为已撤销 foreach (var record in splitRecords) { record.IsReverted = true; } await Db.Updateable(splitRecords).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK($"撤销拆包成功,共还原{splitRecords.Count}次拆包,总数量:{totalRevertQty}"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error($"撤销拆包失败:{ex.Message}"); } } // 获取拆包信息 public async Task GetSplitPackageInfo(string orderNo, string palletCode, string barcode) { var outStockInfo = await _outStockLockInfoService.Db.Queryable() .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() // .Where(x => x.Barcode == barcode) // .FirstAsync(); return WebResponseContent.Instance.OK("", new { MaterielCode = outStockInfo.MaterielCode, RemainQuantity = outStockInfo.RemainQuantity, Unit = outStockInfo.Unit }); } /// /// 获取可拆包的出库锁定信息 /// public async Task GetSplitableLockInfos(int orderDetailId) { var lockInfos = await _outStockLockInfoService.Db.Queryable() .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); } } }