using AngleSharp.Dom; using Dm; using Mapster; using Masuit.Tools; using NewLife.Reflection; using OfficeOpenXml.FormulaParsing.Excel.Functions.Math; using SqlSugar; using System.Collections; using System.Collections.Generic; using System.Drawing.Printing; using System.Linq.Expressions; using System.Threading.Tasks; using WIDESEA_Cache; using WIDESEA_Common; using WIDESEA_Core; using WIDESEA_Core.Enums; using WIDESEA_DTO; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Stock; using WIDESEA_DTO.WMS; using WIDESEA_IOrderRepository; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Order; using WIDESEA_OrderRepository; using WIDESEA_StorageTaskRepository; namespace WIDESEA_StorageBasicService; public class StockInfoService : ServiceBase, IStockInfoService { private readonly ILocationStatusChangeRecordRepository _locationStatusChangeRecordRepository; private readonly IDt_InboundOrderRepository _inboundOrderRepository; private readonly IUnitOfWorkManage _unitOfWorkManage; private readonly IDt_OutboundOrderRepository _outorderRepository; private readonly IDt_OutboundOrderDetailRepository _outorderdetailRepository; private readonly IStockInfoDetailRepository _stockInfoDetailRepository; private readonly IDt_TaskService _taskService; private readonly ILocationInfoRepository _locationRepository; private readonly IDt_TaskRepository _taskRepository; public StockInfoService(IStockInfoRepository BaseDal, ILocationStatusChangeRecordRepository locationStatusChangeRecordRepository, IDt_InboundOrderRepository inboundOrderRepository, IUnitOfWorkManage unitOfWorkManage, IDt_OutboundOrderRepository outorderRepository, IDt_OutboundOrderDetailRepository outorderdetailRepository, IStockInfoDetailRepository stockInfoDetailRepository, IDt_TaskService taskService, ILocationInfoRepository locationRepository, IDt_TaskRepository taskRepository) : base(BaseDal) { _locationStatusChangeRecordRepository = locationStatusChangeRecordRepository; _inboundOrderRepository = inboundOrderRepository; _unitOfWorkManage = unitOfWorkManage; _outorderRepository = outorderRepository; _outorderdetailRepository = outorderdetailRepository; _stockInfoDetailRepository = stockInfoDetailRepository; _taskService = taskService; _locationRepository = locationRepository; _taskRepository = taskRepository; } /// /// 分页 /// /// /// public override PageGridData GetPageData(PageDataOptions options) { string wheres = ValidatePageOptions(options); //获取排序字段 Dictionary orderbyDic = GetPageDataSort(options, TProperties); List orderByModels = new List(); foreach (var item in orderbyDic) { OrderByModel orderByModel = new() { FieldName = item.Key, OrderByType = item.Value }; orderByModels.Add(orderByModel); } int totalCount = 0; List searchParametersList = new List(); if (!string.IsNullOrEmpty(options.Wheres)) { try { searchParametersList = options.Wheres.DeserializeObject>(); options.Filter = searchParametersList; } catch { } } Expression> locationStatus = null; Expression> floor = null; Expression> areaId = null; Expression> materielCode = null; foreach (var item in searchParametersList) { if (item.Name.Contains("locationStatus")) { locationStatus = x => x.LocationInfo.LocationStatus == Convert.ToInt32(item.Value); } else if (item.Name.Contains("floor")) { floor = x => x.LocationInfo.Floor.Contains(item.Value); } else if (item.Name.Contains("areaId")) { areaId = x => x.LocationInfo.AreaId== Convert.ToInt32(item.Value); } else if (item.Name.Contains("materielCode")) { materielCode = x => x.StockInfoDetails.Any(d => d.MaterielCode == item.Value); } } //.IncludesAllFirstLayer() var data = BaseDal.Db.Queryable() .Includes(x => x.StockInfoDetails) .Includes(x => x.LocationInfo) .WhereIF(!wheres.IsNullOrEmpty(), wheres) .WhereIF(locationStatus != null, locationStatus) .WhereIF(floor != null, floor) .WhereIF(areaId != null, areaId) .WhereIF(materielCode != null, materielCode) .OrderBy(orderByModels) .ToPageList(options.Page, options.Rows, ref totalCount); new PageGridData(totalCount, data); return new PageGridData(totalCount, data); } #region 库存视图 public List GetStockSelectViews(string materielCode) { var stock = BaseDal.Db.Queryable() .Includes(x => x.StockInfoDetails) .Includes(x => x.LocationInfo) .Where(x => x.LocationInfo.LocationStatus == (int)LocationEnum.InStock && x.LocationInfo.EnalbeStatus == (int)EnableEnum.Enable) .Where(x => x.StockInfoDetails.Any(d => d.MaterielCode == materielCode && d.Quantity > 0 && d.Quantity > d.OutboundQuantity)).ToList().OrderBy(x => x.CreateDate); var result = stock.Select(s => new StockSelectViewDTO { MaterielCode = s.StockInfoDetails .FirstOrDefault(d => d.MaterielCode == materielCode)?.MaterielCode ?? string.Empty, MaterielName = s.StockInfoDetails .FirstOrDefault(d => d.MaterielCode == materielCode)?.MaterielName ?? string.Empty, UseableQuantity = s.StockInfoDetails .Where(d => d.MaterielCode == materielCode && d.Quantity > 0 && d.Quantity > d.OutboundQuantity) .Sum(d => (decimal?)d.Quantity - d.OutboundQuantity) ?? 0, // 处理空值情况 PalletCode = s.PalletCode ?? string.Empty, LocationCode = s.LocationInfo?.LocationCode ?? string.Empty }).ToList(); return result; } #endregion #region 根据单据明细生成出库任务 public async Task GenerateOutboundTask(GenerateOutTaskDto requestOut) { WebResponseContent content = new WebResponseContent(); try { foreach (var orderId in requestOut.orderIds) { List taskDtos = new List(); Dt_OutboundOrder outboundOrder = _outorderRepository.QueryFirst(x => x.Id == orderId); (bool, string) checkResult = CheckSelectStockDeital(outboundOrder, requestOut.stockViews, requestOut); if (!checkResult.Item1) throw new Exception(checkResult.Item2); else { var result = AssignStockOutbound(outboundOrder, requestOut.stockViews); if (result.Item1.Count <= 0) { return content.Error("未找到库存分配,请确认是否存在库存或单据数量已出完"); } result.Item1.Distinct().ForEach(item => { RequestTaskDto task = new RequestTaskDto { TaskType = outboundOrder.OrderType, OrderNo = outboundOrder.OrderNo, MaterielCode = outboundOrder.MaterielCode, Position = item.LocationCode, PalletCode = item.PalletCode, AreaId = Convert.ToInt32(requestOut.AreaId), }; taskDtos.Add(task); item.StockInfoDetails.Where(x => x.MaterielCode == outboundOrder.MaterielCode).FirstOrDefault().Status = (int)StockStateEmun.出库锁定; item.StockStatus = (int)StockStateEmun.出库锁定; }); List taskNews = new List(); List taskDTOs = new List(); foreach (var item in taskDtos) { taskNews.Add(await _taskService.RequestOutboundTaskAsync(item)); } List locationStatusChangeRecords = new List(); List locations = new List(); foreach (var item in taskNews) { taskDTOs.Add(CreateTaskDTO(item)); var result1 = _taskService.UpdateLocationStatus(item.SourceAddress, LocationEnum.InStockDisable, item.TaskNum.Value, (int)StatusChangeTypeEnum.AutomaticOutbound); var result2 = _taskService.UpdateLocationStatus(item.TargetAddress, LocationEnum.Lock, item.TaskNum.Value, (int)StatusChangeTypeEnum.AutomaticOutbound); locationStatusChangeRecords.AddRange(result1.Item1); locationStatusChangeRecords.AddRange(result2.Item1); locations.AddRange(result1.Item2); locations.AddRange(result2.Item2); } await _unitOfWorkManage.UseTranAsync(async () => { _taskRepository.AddData(taskNews); BaseDal.UpdateDataNav(result.Item1); await _locationStatusChangeRecordRepository.AddDataAsync(locationStatusChangeRecords); await _locationRepository.UpdateDataAsync(locations); _outorderRepository.UpdateData(result.Item2); }); content = await _taskService.SendWCSTask(taskDTOs); } } return content; } catch (Exception ex) { return content.Error(ex.Message); } } private WMSTaskDTO CreateTaskDTO(Dt_Task task) { return new WMSTaskDTO { TaskNum = task.TaskNum.Value, Grade = task.Grade.Value, PalletCode = task.PalletCode, RoadWay = task.Roadway, SourceAddress = task.SourceAddress, TargetAddress = task.TargetAddress, TaskState = task.TaskState, Id = 0, TaskType = task.TaskType, AGVTaskNum = task.AGVTaskNum, Remark = task.Remark }; } public (List, Dt_OutboundOrder) AssignStockOutbound(Dt_OutboundOrder outboundOrder, List stockSelectViews) { decimal originalNeedQuantity = outboundOrder.OrderQuantity - outboundOrder.OverOutQuantity; decimal needQuantity = originalNeedQuantity; List outStocks = new List(); if (stockSelectViews != null && stockSelectViews.Count > 0) { outStocks = BaseDal.Db.Queryable().Where(x => stockSelectViews.Select(x => x.PalletCode).ToList().Contains(x.PalletCode)).Includes(x => x.StockInfoDetails).ToList(); } else { outStocks = GetStockInfos(outboundOrder.MaterielCode); } List outStockNews=new List(); foreach (var item in outStocks) { var stockDetail = item.StockInfoDetails .Where(d => d.MaterielCode == outboundOrder.MaterielCode && d.Quantity > 0); if (needQuantity <= 0) { continue; } foreach (var detail in stockDetail) { if (detail != null) { if (needQuantity >= detail.Quantity) { if (detail.Quantity <= detail.OutboundQuantity) { continue; } needQuantity -= detail.Quantity; detail.OutboundQuantity = detail.Quantity; } else { if (detail.Quantity <= detail.OutboundQuantity) { continue; } detail.OutboundQuantity = needQuantity; needQuantity = 0; } outStockNews.Add(item); } } } outboundOrder.OverOutQuantity = outboundOrder.OrderQuantity - needQuantity; outboundOrder.OrderStatus = (int)OrderStateEmun.开始; return (outStockNews, outboundOrder); } private (bool, string) CheckSelectStockDeital(Dt_OutboundOrder outboundOrder, List stockSelectViews, GenerateOutTaskDto requestOut) { if (outboundOrder == null) { return (false, "未找到出库单信息"); } if (outboundOrder.OrderStatus == (int)OrderStateEmun.已完成) { return (false, "该明细不可操作"); } if (stockSelectViews != null && stockSelectViews.Sum(x => x.UseableQuantity) > outboundOrder.OrderQuantity - outboundOrder.OverOutQuantity) { return (false, "选择数量超出单据数量"); } if (outboundOrder.OrderQuantity <= outboundOrder.OverOutQuantity) { return (false, "单据数量已出库完成"); } if (stockSelectViews != null && stockSelectViews.Count > 1 && requestOut.AreaId == null) { return (false, "出库库存大于一条不能出库至指定库位!"); } if ( requestOut.TargetAddress == null && requestOut.AreaId == null) { return (false, "请选择出库地址"); } if (outboundOrder.OrderStatus == (int)OrderStateEmun.已完成) { return (false, "该出库单已完成,无法生成出库任务"); } return (true, "成功"); } public List GetStockInfos(string materielCode) { return BaseDal.Db.Queryable() .Includes(x => x.StockInfoDetails) .Includes(x => x.LocationInfo) .Where(x => x.LocationInfo.LocationStatus == (int)LocationEnum.InStock && x.LocationInfo.EnalbeStatus == (int)EnableEnum.Enable) .Where(x => x.StockInfoDetails.Any(d => d.MaterielCode == materielCode && d.Quantity > 0)).ToList().OrderBy(x => x.CreateDate).ToList(); } #endregion 根据单据明细生成出库任务 #region 组盘 public async Task AddGroupPlateAsync(GroupPlate groupPlate) { WebResponseContent content = new WebResponseContent(); try { if (groupPlate == null || groupPlate.OrderNos.IsNullOrEmpty() || groupPlate.palletCode.IsNullOrEmpty()) { return content.Error("参数错误"); } var info = await BaseDal.QueryFirstAsync(x => x.PalletCode == groupPlate.palletCode); if (!info.IsNullOrEmpty()) { content.Error("该托盘已存在组盘"); } else { List details = new List(); foreach (var item in groupPlate.OrderNos) { var InboundOrder = _inboundOrderRepository.QueryFirst(x => x.OrderNo == item); if (InboundOrder != null) { var x = details.Where(x => x.MaterielCode == InboundOrder.MaterialNo && x.Warehouse == InboundOrder.WarehouseName && x.DrawingNumber == InboundOrder.ProductDrawingNumber && x.DemandClassification == InboundOrder.DemandClassification).FirstOrDefault(); if (x != null) { details.Remove(x); x.MaterielCode = InboundOrder.MaterialNo; x.MaterielName = InboundOrder.MaterialName; x.DemandClassification = InboundOrder.DemandClassification; x.Warehouse = InboundOrder.WarehouseName; x.OrderNo = InboundOrder.UpperOrderNo; x.Unit = InboundOrder.Unit; x.Specs = InboundOrder.Specs; x.Weight = InboundOrder.Weight; x.Quantity = x.Quantity + InboundOrder.Quantity; x.DrawingNumber = InboundOrder.ProductDrawingNumber; x.Date = InboundOrder.Datetime; details.Add(x); } else { DtStockInfoDetail detail = new DtStockInfoDetail() { MaterielCode = InboundOrder.MaterialNo, MaterielName = InboundOrder.MaterialName, DemandClassification = InboundOrder.DemandClassification, Warehouse = InboundOrder.WarehouseName, OrderNo = InboundOrder.UpperOrderNo, Unit = InboundOrder.Unit, Specs = InboundOrder.Specs, Weight = InboundOrder.Weight, Quantity = InboundOrder.Quantity, DrawingNumber = InboundOrder.ProductDrawingNumber, Date = InboundOrder.Datetime, }; details.Add(detail); } } else { return content.Error("未找到入库单据信息"); } } DtStockInfo boxing = new DtStockInfo() { PalletCode = groupPlate.palletCode, StockStatus = (int)StockStateEmun.组盘暂存, StockInfoDetails = details }; await BaseDal.AddDataNavAsync(boxing); content.OK("组盘成功"); } return content; } catch (Exception ex) { return content.Error(ex.Message); } } #endregion #region 解盘 public async Task DeleteGroupPlateAsync(GroupPlate groupPlate) { WebResponseContent content = new WebResponseContent(); try { if (groupPlate == null || groupPlate.palletCode.IsNullOrEmpty()) { return content.Error("参数错误"); } var stock = await BaseDal.QueryFirstNavAsync(x => x.PalletCode == groupPlate.palletCode && x.StockStatus == (int)StockStateEmun.组盘暂存); if (!stock.IsNullOrEmpty()) { stock.StockStatus = (int)StockStateEmun.组盘撤销; DtStockInfo_Hty stockhty = stock.Adapt(); stockhty.ModifyDate = DateTime.Now; await _unitOfWorkManage.UseTranAsync(async () => { await BaseDal.Db.DeleteNav(x => x.Id == stock.Id) .Include(x => x.StockInfoDetails) .ExecuteCommandAsync(); await AddStockHtyAsync(stockhty); }); content.OK("解盘成功"); } else { content.Error("未找到组盘数据"); } return content; } catch (Exception ex) { return content.Error(ex.Message); } } private async Task AddStockHtyAsync(DtStockInfo_Hty stockhty) { var isStockAdd = await SqlSugarHelper.DbWMS.InsertNav(stockhty).IncludesAllFirstLayer().ExecuteCommandAsync(); if (!isStockAdd) { throw new Exception("库存历史信息添加失败"); } } /// /// 批量删除 /// /// /// public override WebResponseContent DeleteData(object[] keys) { try { List stockInfos = new List(); List locationInfos = new List(); foreach (var item in keys) { var stock = BaseDal.QueryFirstNavAsync(x => x.Id == item.ObjToInt()).Result; var stockHty = stock.Adapt(); stockInfos.Add(stockHty); var location = SqlSugarHelper.DbWMS.Queryable().FirstAsync(x => x.LocationCode == stock.LocationCode).Result; var lastStatus = location.LocationStatus; location.LocationStatus = (int)LocationEnum.Free; locationInfos.Add(location); _locationStatusChangeRecordRepository.AddLocationStatusChangeRecord(location, lastStatus, (int)StatusChangeTypeEnum.ManualOperation, 0); } //var hty = BaseDal.Db.InsertNav(stockInfos) // .Include(x => x.StockInfoDetails) // .ExecuteCommand(); var isStockAdd = SqlSugarHelper.DbWMS.InsertNav(stockInfos).IncludesAllFirstLayer().ExecuteCommandAsync(); var locationd = SqlSugarHelper.DbWMS.Updateable(locationInfos).ExecuteCommandHasChange(); return base.DeleteData(keys); } catch (Exception ex) { return WebResponseContent.Instance.Error(ex.Message); } } #endregion }