using Microsoft.AspNetCore.Http; 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.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; public SplitPackageService(IRepository BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, IDailySequenceService dailySequenceService) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; _outStockLockInfoService = outStockLockInfoService; _stockInfoDetailService = stockInfoDetailService; _dailySequenceService = dailySequenceService; } /// /// 拆包拆箱操作 /// 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.AssignQuantity - 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}对应的库存记录"); 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 { StockId = lockInfo.StockId, MaterielCode = lockInfo.MaterielCode, OrderNo = lockInfo.OrderNo, BatchNo = lockInfo.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.RemainQuantity - request.SplitQuantity, MaterielCode = lockInfo.MaterielCode, SplitTime = DateTime.Now, OrderNo = request.OrderNo, PalletCode = request.PalletCode, Status = (int)SplitPackageStatusEnum.已拆包 }; await Db.Insertable(splitHistory).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); // 7. 回传新条码给MES // await SendBarcodeToMES(newBarcode, request.MaterielCode, request.SplitQuantity); 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(); // 查找最近的未撤销拆包记录 var splitPackage = await Db.Queryable() .Where(x => x.OriginalBarcode == originalBarcode && !x.IsReverted) .OrderByDescending(x => x.CreateDate) .FirstAsync(); if (splitPackage == null) return WebResponseContent.Instance.Error("未找到拆包记录"); // 检查新条码是否已拣选 var newOutStockInfo = await _outStockLockInfoService.Db.Queryable() .Where(x => x.CurrentBarcode == splitPackage.NewBarcode) .FirstAsync(); if (newOutStockInfo.Status == 2) return WebResponseContent.Instance.Error("新条码已拣选,无法撤销拆包"); // 还原原出库详情数量 var originalOutStockInfo = await _outStockLockInfoService.Db.Queryable() .Where(x => x.CurrentBarcode == originalBarcode) .FirstAsync(); originalOutStockInfo.AssignQuantity += splitPackage.SplitQty; await _outStockLockInfoService.Db.Updateable(originalOutStockInfo).ExecuteCommandAsync(); // 删除新出库详情记录 await _outStockLockInfoService.Db.Deleteable() .Where(x => x.CurrentBarcode == splitPackage.NewBarcode) .ExecuteCommandAsync(); // 标记拆包记录为已撤销 splitPackage.IsReverted = true; await Db.Updateable(splitPackage).ExecuteCommandAsync(); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("撤销拆包成功"); } 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); } } }