using System.Linq; using System.Linq.Expressions; using Aop.Api.Domain; using JNPF.Common.Core.Manager; using JNPF.Common.Dtos.VisualDev; using JNPF.Common.Extension; using JNPF.Common.Security; using JNPF.EventBus; using JNPF.FriendlyException; using JNPF.Systems.Interfaces.System; using JNPF.VisualDev; using JNPF.VisualDev.Entitys; using JNPF.VisualDev.Interfaces; using Mapster; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NPOI.OpenXmlFormats.Dml.Spreadsheet; using NPOI.SS.Formula.PTG; using SqlSugar; using Tnb.BasicData.Entities; using Tnb.WarehouseMgr.Entities; using Tnb.WarehouseMgr.Entities.Consts; using Tnb.WarehouseMgr.Entities.Dto; using Tnb.WarehouseMgr.Entities.Dto.Inputs; using Tnb.WarehouseMgr.Entities.Dto.Outputs; using Tnb.WarehouseMgr.Entities.Dto.Queries; using Tnb.WarehouseMgr.Entities.Enums; using Tnb.WarehouseMgr.Interfaces; namespace Tnb.WarehouseMgr { /// /// 盘点任务 /// [OverideVisualDev(ModuleConsts.MODULE_WMSCHECKTASK_ID)] public class WmsCheckTaskService : ServiceLoggerBase { private readonly ISqlSugarClient _db; private readonly IWareHouseService _warehouseService; private readonly IUserManager _userManager; private readonly IVisualDevService _visualDevService; private readonly IRunService _runService; private readonly IBillRullService _billRullService; private readonly IDictionaryDataService _dataService; private static Dictionary _carryMap = new(); public WmsCheckTaskService( ISqlSugarRepository repository, IWareHouseService wareHouseService, IVisualDevService visualDevService, IRunService runService, IBillRullService billRullService, IUserManager userManager, IDictionaryDataService dataService, IEventPublisher eventPublisher ) { _db = repository.AsSugarClient(); _warehouseService = wareHouseService; _visualDevService = visualDevService; _runService = runService; _billRullService = billRullService; _dataService = dataService; _userManager = userManager; OverideFuncs.CreateAsync = Create; } private async Task Create(VisualDevModelDataCrInput input) { int row = 1; if (input == null) { throw new ArgumentNullException(nameof(input)); } try { await _db.Ado.BeginTranAsync(); if (input.data?.Count > 0) { string? checkType = input.data[nameof(WmsCheckstockH.checkstock_type)]?.ToString(); if (!checkType.IsNullOrEmpty()) { List billStatus = new() { WmsWareHouseConst.TASK_BILL_STATUS_CANCEL_ID, WmsWareHouseConst.TASK_BILL_STATUS_COMPLE_ID, }; Expression> filter = Expressionable.Create() .And((a, b) => b.wh_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString()) .And((a, b) => b.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b) => !billStatus.Contains(a.status)) .ToExpression(); List>> queryTasks = new() { Task.Run(() => FetchDisTasks((a, b) => a.startlocation_id == b.id, filter)), Task.Run(() => FetchDisTasks((a, b) => a.endlocation_id == b.id, filter)) }; List[] disTasks = await Task.WhenAll(queryTasks); if (disTasks?.Length > 0 && disTasks.Any(list => list?.Count > 0)) { throw new AppFriendlyException("该仓库还有未完成的任务,不允许盘点!", 500); } input.data["tablefield114"] = null; VisualDevEntity? templateEntity = await _visualDevService.GetInfoById(ModuleConsts.MODULE_WMSCHECKTASK_ID, true); await _runService.Create(templateEntity, input); List details = new(); var filterExpable = Expressionable.Create() .And((a, b, c) => a.wh_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString()) .And((a, b, c) => a.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b, c) => c.is_lock == 0); var areaIds = new string[] { }; if (input.data.ContainsKey(nameof(WmsCheckstockH.area_id)) && input.data[nameof(WmsCheckstockH.area_id)] != null) { areaIds = input.data[nameof(WmsCheckstockH.area_id)].ToObject(); } Expression> filterExp = (a, b, c) => false; var filerExpable = Expressionable.Create() .And((a, b, c) => a.wh_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString()) .And((a, b, c) => a.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b, c) => c.is_lock == 0); switch (checkType?.ToEnum()) { case EnumCheckType.全库盘点: { filterExp = filerExpable.ToExpression(); } break; case EnumCheckType.物料盘点: { if (!input.data.ContainsKey(nameof(WmsCarryCode.material_id)) && input.data[nameof(WmsCarryCode.material_id)] != null) { filterExp = (a, b, c) => b.material_id == input.data[nameof(WmsCarryCode.material_id)].ToString(); } } break; case EnumCheckType.区域盘点: { if (areaIds?.Length > 0) { filterExp = (a, b, c) => areaIds.Contains(a.region_id); } } break; } var carryCodes = await _db.Queryable().InnerJoin((a, b) => a.id == b.location_id) .InnerJoin((a, b, c) => b.carry_id == c.id) .Where(filterExpable.ToExpression()) .Select((a, b, c) => new WmsCarryCode { carry_code = c.carry_code }, true) .ToListAsync(); carryCodes ??= Enumerable.Empty().ToList(); List? checkStockDs = carryCodes.Adapt>(); var prQtyDic = checkStockDs.GroupBy(g => $"{g.carry_id}{g.material_id}{g.code_batch}").ToDictionary(x => x.Key, x => x.Sum(d => d.codeqty)); foreach ((object k, decimal v) in prQtyDic) { WmsCheckstockD? checkstockD = checkStockDs?.Find(x => string.Equals(k, $"{x.carry_id}{x.material_id}{x.code_batch}")); if (checkstockD != null) { checkstockD.id = SnowflakeIdHelper.NextId(); checkstockD.checkstock_id = input.data["ReturnIdentity"].ToString(); checkstockD.create_id = _userManager.UserId; checkstockD.create_time = DateTime.Now; checkstockD.pr_qty = v; details.Add(checkstockD); } } var r = await _db.Insertable(details).ExecuteCommandAsync(); //生成预任务信息 if (details.Count > 0 && carryCodes.Count > 0) { // 判断当前仓库是否有盘点签收配置 var wcsConf = await _db.Queryable().Where(it => it.warehouse_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString() && it.enabled == 1) .FirstAsync(); BasLocation[] endLocs = Array.Empty(); int randomIndex = 0; if (wcsConf != null) { if (wcsConf.location_id.IsNullOrWhiteSpace()) { throw new AppFriendlyException("盘点签收配置库位为空,请查看配置!", 500); } JArray? jsonArr = null; jsonArr = JArray.Parse(wcsConf.location_id); string?[] locArr = jsonArr.Select(x => x.ToObject()).ToArray(); if (locArr != null) { endLocs = await _db.Queryable().Where(it => it.wh_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString() && locArr.Contains(it.id)).ToArrayAsync(); randomIndex = Random.Shared.Next(0, endLocs.Length); } else { throw new AppFriendlyException("盘点签收配置有误,请查看配置!", 500); } } else { string[] locTypes = new[] { ((int)EnumLocationType.出库库位).ToString(), ((int)EnumLocationType.出入库位).ToString() }; endLocs = await _db.Queryable().Where(it => it.wh_id == input.data[nameof(WmsCheckstockH.warehouse_id)].ToString() && locTypes.Contains(it.is_type)).ToArrayAsync(); randomIndex = Random.Shared.Next(0, endLocs.GetUpperBound(0)); } List carrys = await _db.Queryable().Where(it => carryCodes.Select(x => x.carry_id).Distinct().Contains(it.id)).ToListAsync(); List curDetails = details.DistinctBy(x => x.carry_id).ToList(); List curCarryCodes = carryCodes.FindAll(x => curDetails.Select(d => d.carry_id).Contains(x.carry_id)); if (curCarryCodes.Count > 0) { List preTasks = new(); List locIds = new(); foreach (WmsCarryH item in carrys) { WmsPointH? sPoint = await _db.Queryable().FirstAsync(it => it.location_id == item.location_id); WmsPointH? ePoint = await _db.Queryable().FirstAsync(it => it.location_id == endLocs[randomIndex].id); if (sPoint != null && ePoint != null) { List points = await _warehouseService.PathAlgorithms(sPoint.id, ePoint.id); if (points.Count <= 2) { throw new AppFriendlyException("该路径不存在", 500); } if (points?.Count > 0) { locIds.AddRange(points.Select(x => x.location_id).ToList()!); List curPreTasks = points.Where(it => !it.location_id.IsNullOrEmpty()).GroupBy(g => g.area_code).Select(it => { WmsPointH? sPoint = it.FirstOrDefault(); WmsPointH? ePoint = it.LastOrDefault(); WmsPretaskH preTask = new() { org_id = _userManager.User.OrganizeId, startlocation_id = sPoint?.location_id!, startlocation_code = sPoint?.location_code!, endlocation_id = ePoint?.location_id!, endlocation_code = ePoint?.location_code!, startpoint_id = sPoint?.id!, startpoint_code = sPoint?.point_code!, endpoint_id = ePoint?.id!, endpoint_code = ePoint?.point_code!, start_floor = sPoint?.floor.ToString(), end_floor = ePoint?.floor.ToString(), bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_PRETASK_H_ENCODE).GetAwaiter().GetResult(), status = WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID, biz_type = WmsWareHouseConst.BIZTYPE_WMSCHECKOUTSTOCK_ID, task_type = WmsWareHouseConst.WMS_PRETASK_OUTSTOCK_TYPE_ID, carry_id = item.id, carry_code = item.carry_code!, area_id = sPoint?.area_id!, area_code = it.Key, require_id = input.data["ReturnIdentity"].ToString(), require_code = input.data[nameof(WmsCheckstockH.checkstock_code)]?.ToString(), create_id = _userManager.UserId, create_time = DateTime.Now }; return preTask; }).ToList(); if (endLocs[randomIndex].is_sign == 0) { curPreTasks[^1].is_sign = 0; } preTasks.AddRange(curPreTasks); } } } List pretaskCodes = new(); foreach (WmsPretaskH pt in preTasks) { List partCodes = carryCodes.FindAll(x => x.carry_id == pt.carry_id).Distinct().ToList(); List curPreTaskCodes = partCodes.Adapt>(); curPreTaskCodes.ForEach(x => { x.id = SnowflakeIdHelper.NextId(); x.bill_id = pt.id; x.create_time = DateTime.Now; }); pretaskCodes.AddRange(curPreTaskCodes); } bool isOk = await _warehouseService.GenPreTask(preTasks, pretaskCodes); if (isOk) { _ = await _db.Updateable().SetColumns(it => it.status == ((int)EnumCheckStatus.盘点中).ToString()).Where(it => it.id == input.data["ReturnIdentity"].ToString()).ExecuteCommandAsync(); GenPreTaskUpInput genPreTaskAfterUpInput = new() { CarryIds = preTasks.Select(x => x.carry_id).ToList(), LocationIds = new HashSet(locIds).ToList() }; await _warehouseService.GenInStockTaskHandleAfter(genPreTaskAfterUpInput, it => new WmsCarryH { is_lock = 1 }, it => new BasLocation { is_lock = 1 }); } } } } } await _db.Ado.CommitTranAsync(); } catch (Exception ex) { row = 0; Logger.Error("盘点任务新增失败", ex); await _db.Ado.RollbackTranAsync(); throw; } finally { await InvokeGenPretaskExcute(); } return Task.FromResult(row); } private Task> FetchDisTasks(Expression> joinExp, Expression> whereExp) { var details = _db.CopyNew().Queryable().InnerJoin(joinExp).Where(whereExp).Select().ToListAsync(); return details; } /// /// 根据盘点任务ID获取盘点任务明细 /// /// 盘点任务主表ID /// 盘点任务明细列表 [HttpGet("{checkStockId}")] public async Task> GetCheckStockDList(string checkStockId) { var details = await _db.Queryable().Where(it => it.checkstock_id == checkStockId) .Select() .Mapper(it => it.delayeqty = Math.Max(0, it.pr_qty.Value - it.qty.Value)) .ToListAsync(); return details; } /// /// 根据盘点类型获取任务明细 /// /// ///
{ ///
warehouse_id:仓库ID ///
CheckType:盘点类型 0 全库盘点、1 物料盘点 2 区域盘点 ///
material_id:物料ID ///
regionIds: 区域ID列表 ///
} /// /// [HttpPost] public async Task GetTaskDetailByCheckType(CheckDetailQuery input) { if (_carryMap.Count == 0) { _carryMap = await _db.Queryable().ToDictionaryAsync(x => x.id, x => x.carry_code); } Expression> filterExp = (a, b, c) => false; var filerExpable = Expressionable.Create() .And((a, b, c) => a.wh_id == input.warehouse_id) .And((a, b, c) => a.is_type == ((int)EnumLocationType.存储库位).ToString()) .And((a, b, c) => c.is_lock == 0); switch (input.CheckType) { case EnumCheckType.全库盘点: { filterExp = filerExpable.ToExpression(); } break; case EnumCheckType.物料盘点: { if (!input.material_id.IsNullOrWhiteSpace()) { filterExp = filerExpable.And((a, b, c) => b.material_id == input.material_id).ToExpression(); } } break; case EnumCheckType.区域盘点: { if (input.regionIds?.Count > 0) { filterExp = filerExpable.And((a, b, c) => input.regionIds.Contains(a.region_id)).ToExpression(); } } break; } var carryCodes = await _db.Queryable().InnerJoin((a, b) => a.id == b.location_id) .InnerJoin((a, b, c) => b.carry_id == c.id) .Where(filterExp) .Select() .ToListAsync(); List outputs = carryCodes.GroupBy(g => new { g.material_code, g.code_batch, g.location_code, g.carry_id }).Select(x => new WmsCheckstockD { material_code = x.Key.material_code, code_batch = x.Key.code_batch, carry_id = x.Key.carry_id, carry_code = _carryMap[x.Key.carry_id]?.ToString() ?? string.Empty, location_id = x.FirstOrDefault(y => y.location_code == x.Key.location_code)?.location_id ?? string.Empty, location_code = x.Key.location_code, pr_qty = x.Sum(d => d.codeqty), closing_status = WmsWareHouseConst.CLOSINGSTATUS_WJS_ID, qty = 0, }) .ToList(); return outputs; } /// /// 获取盘点任务信息 /// /// 载具编号 /// /// /// /// [HttpGet("{carryCode}")] public async Task GetCheckTaskInfo(string carryCode) { var checkTypeMap = await _dataService.GetDicByKey("CheckType"); var checkSpeciesMap = await _dataService.GetDicByKey("CheckSpecies"); var result = await _db.Queryable().InnerJoin((a, b) => a.require_id == b.id) .InnerJoin((a, b, c) => b.warehouse_id == c.id) .Where(a => a.carry_code == carryCode) .Select((a, b, c) => new WmsCheckstockH { warehouse_name = c.whname }, true) .Mapper(it => { it.checkstock_type = checkTypeMap.ContainsKey(it.checkstock_type!) ? checkTypeMap[it.checkstock_type!]?.ToString() ?? "" : ""; it.handle_kinds = checkTypeMap.ContainsKey(it.handle_kinds!) ? checkTypeMap[it.handle_kinds!]?.ToString() ?? "" : ""; }) .ToListAsync(); return result; } /// /// 根据条码编号和载具id获取对应载具条码信息 /// /// /// public async Task GetCarryCodeInfoByBarCode([FromQuery] CarryCodeInfoQuery q) { var items = await _db.Queryable().InnerJoin((a, b) => a.id == b.carry_id) .Where((a, b) => b.barcode == q.barcode && b.carry_id == q.carryId) .Select((a, b) => new WmsCarryCode { carry_code = a.carry_code }, true) .ToListAsync(); return items; } /// /// 盘点签收 /// /// 盘点签收输入参数 /// 有异常信息为失败,无异常为正常 /// /// [HttpPost] public async Task TakeStockSign(TakeStockSignInput input) { if (input == null) throw new ArgumentNullException("input"); if (input.details == null) throw new ArgumentNullException(nameof(input.details)); var checkStockCodes = input.details.Adapt>(); var disTask = await _db.Queryable().Where(it => it.carry_id == checkStockCodes[0].carry_id && it.status == WmsWareHouseConst.BILLSTATUS_COMPLETE_ID && it.is_sign == 0).FirstAsync(); try { await _db.Ado.BeginTranAsync(); if (disTask != null) { disTask.is_sign = 1; _ = await _db.Updateable(disTask).UpdateColumns(it => it.is_sign).ExecuteCommandAsync(); } var checkStock = await _db.Queryable().SingleAsync(it => it.id == checkStockCodes[0].checkstock_id); if (checkStock != null) { InStockStrategyQuery q = new() { warehouse_id = checkStock!.warehouse_id! }; var endLocs = await _warehouseService.InStockStrategy(q); WmsPointH? sPoint = null, ePoint = null; var carry = await _db.Queryable().SingleAsync(it => it.id == checkStockCodes[0].carry_id); sPoint = await _db.Queryable().FirstAsync(it => it.location_id == carry.location_id); if (endLocs?.Count > 0) { ePoint = await _db.Queryable().FirstAsync(it => it.location_id == endLocs[0].id); } if (sPoint == null || ePoint == null) throw new AppFriendlyException("起点或终点不能为空", 500); List points = await _warehouseService.PathAlgorithms(sPoint.id, ePoint.id); //根据获取的路径点生成预任务,生成顺序必须预路径算法返回的起终点的顺序一致(预任务顺序) if (points?.Count > 0) { if (points.Count <= 2) { throw new AppFriendlyException("该路径不存在", 500); } /* var genPreTask = BuildPreTaskHelper.GenPretaskCurried(null, WmsWareHouseConst.BIZTYPE_CARRYMOVEINSTOCK_ID, WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID); var genPreTaskUpInput = await genPreTask(carry, sPoint, ePoint); */ List preTasks = points.Where(it => !it.location_id.IsNullOrEmpty()).GroupBy(g => g.area_code).Select(it => { WmsPointH? sPoint = it.FirstOrDefault(); WmsPointH? ePoint = it.LastOrDefault(); WmsPretaskH preTask = new() { org_id = _userManager.User.OrganizeId!, startlocation_id = sPoint?.location_id!, startlocation_code = sPoint?.location_code!, endlocation_id = ePoint?.location_id!, endlocation_code = ePoint?.location_code!, start_floor = sPoint?.floor.ToString(), end_floor = ePoint?.floor.ToString(), startpoint_id = sPoint?.id!, startpoint_code = sPoint?.point_code!, endpoint_id = ePoint?.id!, endpoint_code = ePoint?.point_code!, bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_PRETASK_H_ENCODE).GetAwaiter().GetResult(), status = WmsWareHouseConst.PRETASK_BILL_STATUS_DXF_ID, biz_type = WmsWareHouseConst.BIZTYPE_CARRYMOVEINSTOCK_ID, task_type = WmsWareHouseConst.WMS_PRETASK_INSTOCK_TYPE_ID, carry_id = carry?.id ?? string.Empty, carry_code = carry?.carry_code ?? string.Empty, area_id = sPoint?.area_id!, area_code = it.Key, create_id = _userManager.UserId, create_time = DateTime.Now }; return preTask; }).ToList(); bool isOk = await _warehouseService.GenPreTask(preTasks, null!); if (isOk) { GenPreTaskUpInput preTaskUpInput = new() { //RquireId = input.data["ReturnIdentity"].ToString()!, CarryId = checkStockCodes[0].carry_id, CarryStartLocationId = points.FirstOrDefault()!.location_id!, CarryStartLocationCode = points.FirstOrDefault()!.location_code!, LocationIds = points.Select(x => x.location_id).ToList()! }; WmsHandleH handleH = new() { org_id = _userManager.User.OrganizeId, startlocation_id = carry?.location_id ?? string.Empty, endlocation_id = endLocs?[0].id ?? string.Empty, bill_code = _billRullService.GetBillNumber(WmsWareHouseConst.WMS_CARRYMOINSTK_ENCODE).GetAwaiter().GetResult(), biz_type = WmsWareHouseConst.BIZTYPE_CARRYMOVEINSTOCK_ID, carry_id = checkStockCodes?[0].carry_id ?? string.Empty, carry_code = carry?.carry_code ?? string.Empty, create_id = _userManager.UserId, create_time = DateTime.Now }; preTaskUpInput.PreTaskRecord = handleH; //根据载具移入Id,回更单据状态 _ = await _db.Updateable().SetColumns(it => new WmsMoveInstock { status = WmsWareHouseConst.BILLSTATUS_ON_ID }).Where(it => it.id == preTaskUpInput.RquireId).ExecuteCommandAsync(); await _warehouseService.GenInStockTaskHandleAfter(preTaskUpInput, it => new WmsCarryH { is_lock = 1, location_id = preTaskUpInput.CarryStartLocationId, location_code = preTaskUpInput.CarryStartLocationCode }, it => new BasLocation { is_lock = 1 }); } } } foreach (var csCode in checkStockCodes!) { csCode.id = SnowflakeIdHelper.NextId(); var checkStockPartDs = await _db.Queryable().Where(it => it.checkstock_id == csCode.checkstock_id).ToListAsync(); var checkStockDMap = checkStockPartDs.GroupBy(g => $"{g.carry_id}{g.material_id}{g.code_batch}").ToDictionary(x => x.Key, x => x.First().id); csCode.checkstock_d_id = checkStockDMap.ContainsKey($"{csCode.carry_id}{csCode.material_id}{csCode.code_batch}") ? checkStockDMap[$"{csCode.carry_id}{csCode.material_id}{csCode.code_batch}"] : ""; } //插入 盘点code表 _ = await _db.Insertable(checkStockCodes).ExecuteCommandAsync(); var detailIds = checkStockCodes.Select(x => x.checkstock_d_id).ToList(); var checkStockDs = await _db.Queryable().Where(it => detailIds.Contains(it.id)).ToListAsync(); foreach (var csd in checkStockDs) { var csCode = checkStockCodes.Find(x => x.checkstock_d_id == csd.id); if (csCode != null) { csd.qty += csCode.codeqty; var residueQty = csd.qty - csd.pr_qty; if (residueQty == 0) { csd.closing_status = WmsWareHouseConst.CLOSINGSTATUS_NORMAL_ID; } else if (residueQty > 0) { csd.closing_status = WmsWareHouseConst.CLOSINGSTATUS_PY_ID; } else if (residueQty < 0) { csd.closing_status = WmsWareHouseConst.CLOSINGSTATUS_PK_ID; } } } _ = await _db.Updateable(checkStockDs).UpdateColumns(it => new { it.qty, it.closing_status }).ExecuteCommandAsync(); var checkStockDsGrp = checkStockDs.GroupBy(g => g.checkstock_id); var checkStocks = await _db.Queryable().Where(it => checkStockDsGrp.Select(g => g.Key).Contains(it.id)).ToListAsync(); var tasks = new List>(checkStockDsGrp.Count()); foreach (var grp in checkStockDsGrp) { if (grp.All(x => x.closing_status != WmsWareHouseConst.CLOSINGSTATUS_WJS_ID)) { var cs = checkStocks.Find(x => x.id == grp.Key); if (cs != null) { cs.status = WmsWareHouseConst.CHECKSTATUS_PDJZ_ID; tasks.Add(_db.Updateable(cs).UpdateColumns(it => it.status).ExecuteCommandAsync()); } } } _ = await Task.WhenAll(tasks); await _db.Ado.CommitTranAsync(); } catch (Exception ex) { Logger.Error("盘点签收失败", ex); await _db.Ado.RollbackTranAsync(); throw; } } } }