using Business.Core.Utilities; using Business.Domain; using Business.Dto; using Business.EntityFrameworkCore; using Business.EntityFrameworkCore.SqlRepositories; using Business.PriorityManagement; using Business.ReplenishmentManagement; using Business.ResourceExamineManagement.Dto; using Business.ResourceExamineManagement; using Business.StructuredDB.SaleFcst; using Business.StructuredDB.WMS; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Volo.Abp.Application.Services; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; using Volo.Abp.MultiTenancy; using Volo.Abp.Uow; using Newtonsoft.Json; using EFCore.BulkExtensions; using Business.Core.Enum; using Org.BouncyCastle.Crypto; using Business.StructuredDB.Replenishment; using System.ComponentModel.Design; using Newtonsoft.Json.Linq; using MathNet.Numerics.RootFinding; using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.Collections; using MongoDB.Driver.Linq; using System.Reflection; using MongoDB.Driver; namespace Business.Replenishment { /// /// 产能分析 /// public class ReplenishmentAppService : ApplicationService, IReplenishmentAppService, ITransientDependency { #region 服务 /// /// 雪花算法 /// SnowFlake help = new SnowFlake(); private readonly IRepository _replenishmentROP; private readonly IRepository _domesticTerminalFcst; private readonly ISqlRepository _ASNBOLShipperDetail; private readonly IRepository _standardItemModelSet; private readonly ISqlRepository _invTransHist; private readonly ISqlRepository _SAPInv; private readonly IRepository _srmPurchase; private readonly ISqlRepository _monthlyShipmentPlan; private readonly ISqlRepository _generalizedCodeMaster; private readonly ISqlRepository _rf_serialnumber; private readonly ISqlRepository _locationDetail; private readonly IRepository _ic_item; private readonly IRepository _mysql_ic_item; private readonly ISqlRepository _itemMaster; private readonly IRepository _srm_pr_main; private readonly IRepository _mysql_srm_pr_main; private readonly IRepository _srm_po_occupy; private readonly IRepository _mysql_srm_po_occupy; private readonly IRepository _mysql_srm_pr_main_occupy; private readonly IRepository _mes_morder; private readonly IRepository _mes_moentry; private readonly IRepository _mes_mooccupy; private readonly IRepository _mysql_mes_mooccupy; private readonly IRepository _mysql_b_bom_pretreatment; private readonly ISqlRepository _workOrdMaster; private readonly ISqlRepository _prodLineDetail; private readonly IRepository _ic_bom; private readonly IRepository _mysql_ic_bom; private readonly IRepository _ic_bom_child; private readonly IRepository _mysql_ic_bom_child; private readonly ISqlRepository _holidayMaster; private readonly IRepository _ic_item_stockoccupy; private readonly IRepository _mysql_ic_item_stockoccupy; private readonly IRepository _ic_item_stock; private readonly IRepository _mysql_ic_item_stock; private readonly IRepository _ic_substitute; private readonly IRepository _ic_substitute_group; private readonly IRepository _ic_substitute_group_detail; private readonly IRepository _mysql_ic_substitute; private readonly IRepository _mysql_ic_substitute_group; private readonly IRepository _mysql_ic_substitute_group_detail; private readonly IRepository _mysql_mes_moentry; private readonly ISqlRepository _workOrdDetail; private readonly IRepository _srm_po_list; private readonly IRepository _mysql_srm_po_list; private readonly ISqlRepository _routingOpDetail; private readonly ISqlRepository _productStructureMaster; private readonly ISqlRepository _shopCalendarWorkCtr; private readonly ISqlRepository _periodSequenceDet; private readonly ISqlRepository _qualityLineWorkDetail; private readonly ISqlRepository _custMaster; private readonly IRepository _srm_po_main; private readonly IRepository _mysql_srm_po_main; private readonly IRepository _srm_purchase; private readonly IRepository _mysql_srm_purchase; private readonly IRepository _mysql_mes_morder; private readonly IRepository _mysql_crm_seorder; private readonly IRepository _mysql_crm_seorderentry; private readonly IRepository _mes_oorder; private readonly ISqlRepository _scheduleResultOpMaster; private readonly IRepository _productionMasterPlan; private readonly IRepository _replenishmentWeekPlan; private readonly IRepository _mysql_examine_result; private readonly IRepository _mysql_bom_child_examine; private readonly IRepository _replenishmentServiceLevel; /// /// 年度销售预测 /// private IRepository _yearDemandManagement; /// /// 年度销售预测历史记录 /// private IRepository _yearDemandManagementHistory; /// /// 年度生产大纲 /// private IRepository _replenishmentAnnualProduction; /// /// 平台库存表 /// private readonly IRepository _PlatformInventory; /// /// 工单工艺路线明细 /// private ISqlRepository _workOrdRouting; /// /// 月度产能共识表 /// private IRepository _monthlyProdCapacity; /// /// 月度产能共识明细表 /// private IRepository _monthlyProdCapacityDtl; /// /// 计划订单表 /// private IRepository _crm_planorder; /// /// 平台库存表 /// private IRepository _platformInventory; /// /// 在途检查 /// private readonly PurchaseOrderAppService _purchaseOrderAppService; /// /// 计算BOM平铺物料情况 /// private readonly CalcBomViewAppService _CalcBomViewAppService; /// /// 预处理 /// private readonly PretreatmentAppService _pretreatmentAppService; private readonly SerialNumberAppService _serialNumberAppService; /// /// 产能检查 /// private readonly ProductExamineAppService _productExamineAppService; /// /// 资源检查入参 /// private readonly SeorderentryDto param = new SeorderentryDto(); /// /// 生产线明细表 /// public List prodLines = new List(); /// /// 标准工艺路径表 /// public List routingOps = new List(); /// /// 排产记录表 /// public List periodSequences = new List(); /// /// 工作日历 /// public List calendarWorks = new List(); /// /// 休息时间段 /// public List qualityLineWorks = new List(); /// /// 节假日 /// public List holidays = new List(); private List leadTimeList; private List supplierList; private readonly BusinessBangDbContext _businessBangDbContext; private readonly BusinessDbContext _businessDbContext; private readonly ICurrentTenant _currentTenant; private readonly IUnitOfWorkManager _unitOfWorkManager; #endregion #region 构造函数 /// /// 构造函数 /// public ReplenishmentAppService( IRepository replenishmentROP, IRepository domesticTerminalFcst, ISqlRepository ASNBOLShipperDetail, IRepository standardItemModelSet, ISqlRepository invTransHist, ISqlRepository SAPInv, IRepository srmPurchase, ISqlRepository monthlyShipmentPlan, ISqlRepository generalizedCodeMaster, ISqlRepository rf_serialnumber, ISqlRepository locationDetail, IRepository ic_item, IRepository mysql_ic_item, ISqlRepository itemMaster, IRepository srm_pr_main, IRepository mysql_srm_pr_main, IRepository srm_po_occupy, IRepository mysql_srm_po_occupy, IRepository mysql_srm_pr_main_occupy, IRepository mes_morder, IRepository mes_moentry, IRepository mes_mooccupy, IRepository mysql_mes_mooccupy, IRepository mysql_b_bom_pretreatment, ISqlRepository workOrdMaster, ISqlRepository prodLineDetail, IRepository ic_bom, IRepository mysql_ic_bom, IRepository ic_bom_child, IRepository mysql_ic_bom_child, ISqlRepository holidayMaster, IRepository ic_item_stockoccupy, IRepository mysql_ic_item_stockoccupy, IRepository ic_item_stock, IRepository mysql_ic_item_stock, IRepository ic_substitute, IRepository ic_substitute_group, IRepository ic_substitute_group_detail, IRepository mysql_ic_substitute, IRepository mysql_ic_substitute_group, IRepository mysql_ic_substitute_group_detail, IRepository mysql_mes_moentry, ISqlRepository workOrdDetail, IRepository srm_po_list, IRepository mysql_srm_po_list, ISqlRepository routingOpDetail, ISqlRepository productStructureMaster, ISqlRepository shopCalendarWorkCtr, ISqlRepository periodSequenceDet, ISqlRepository qualityLineWorkDetail, IRepository srm_po_main, IRepository mysql_srm_po_main, IRepository srm_purchase, IRepository mysql_srm_purchase, IRepository mysql_mes_morder, IRepository mysql_crm_seorder, IRepository mysql_crm_seorderentry, IRepository mes_oorder, ISqlRepository scheduleResultOpMaster, IRepository productionMasterPlan, IRepository yearDemandManagement, IRepository replenishmentAnnualProduction, IRepository yearDemandManagementHistory, IRepository PlatformInventory, ISqlRepository workOrdRouting, ISqlRepository custMaster, IRepository monthlyProdCapacity, IRepository monthlyProdCapacityDtl, IRepository crm_planorder, IRepository platformInventory, IRepository replenishmentWeekPlan, IRepository mysql_examine_result, IRepository mysql_bom_child_examine, IRepository replenishmentServiceLevel, SerialNumberAppService serialNumberAppService, PretreatmentAppService pretreatmentAppService, PurchaseOrderAppService purchaseOrderAppService, CalcBomViewAppService CalcBomViewAppService, ProductExamineAppService productExamineAppService, BusinessBangDbContext businessBangDbContext, BusinessDbContext businessDbContext, ICurrentTenant currentTenant, IUnitOfWorkManager unitOfWorkManager) { _replenishmentROP = replenishmentROP; _domesticTerminalFcst = domesticTerminalFcst; _ASNBOLShipperDetail = ASNBOLShipperDetail; _standardItemModelSet = standardItemModelSet; _invTransHist = invTransHist; _SAPInv = SAPInv; _srmPurchase = srmPurchase; _monthlyShipmentPlan = monthlyShipmentPlan; _generalizedCodeMaster = generalizedCodeMaster; _rf_serialnumber=rf_serialnumber; _locationDetail = locationDetail; _ic_item = ic_item; _mysql_ic_item = mysql_ic_item; _itemMaster = itemMaster; _srm_pr_main= srm_pr_main; _mysql_srm_pr_main= mysql_srm_pr_main; _srm_po_occupy = srm_po_occupy; _mysql_srm_po_occupy = mysql_srm_po_occupy; _mysql_srm_pr_main_occupy = mysql_srm_pr_main_occupy; _mes_morder = mes_morder; _mes_moentry = mes_moentry; _mes_mooccupy = mes_mooccupy; _mysql_mes_mooccupy = mysql_mes_mooccupy; _mysql_b_bom_pretreatment = mysql_b_bom_pretreatment; _workOrdMaster = workOrdMaster; _custMaster=custMaster; _prodLineDetail = prodLineDetail; _ic_bom = ic_bom; _mysql_ic_bom = mysql_ic_bom; _ic_bom_child = ic_bom_child; _mysql_ic_bom_child = mysql_ic_bom_child; _holidayMaster = holidayMaster; _ic_item_stockoccupy = ic_item_stockoccupy; _mysql_ic_item_stockoccupy = mysql_ic_item_stockoccupy; _ic_item_stock = ic_item_stock; _mysql_ic_item_stock = mysql_ic_item_stock; _ic_substitute = ic_substitute; _ic_substitute_group= ic_substitute_group; _ic_substitute_group_detail= ic_substitute_group_detail; _mysql_ic_substitute= mysql_ic_substitute; _mysql_ic_substitute_group= mysql_ic_substitute_group; _mysql_ic_substitute_group_detail= mysql_ic_substitute_group_detail; _mysql_mes_moentry = mysql_mes_moentry; _workOrdDetail = workOrdDetail; _srm_po_list = srm_po_list; _mysql_srm_po_list = mysql_srm_po_list; _routingOpDetail = routingOpDetail; _productStructureMaster = productStructureMaster; _shopCalendarWorkCtr=shopCalendarWorkCtr; _periodSequenceDet = periodSequenceDet; _qualityLineWorkDetail= qualityLineWorkDetail; _srm_po_main= srm_po_main; _mysql_srm_po_main= mysql_srm_po_main; _srm_purchase= srm_purchase; _mysql_srm_purchase = mysql_srm_purchase; _mysql_mes_morder = mysql_mes_morder; _mysql_crm_seorder = mysql_crm_seorder; _mysql_crm_seorderentry = mysql_crm_seorderentry; _mes_oorder = mes_oorder; _scheduleResultOpMaster = scheduleResultOpMaster; _productionMasterPlan=productionMasterPlan; _replenishmentWeekPlan=replenishmentWeekPlan; _mysql_examine_result = mysql_examine_result; _mysql_bom_child_examine= mysql_bom_child_examine; _replenishmentServiceLevel = replenishmentServiceLevel; _PlatformInventory = PlatformInventory; _workOrdRouting = workOrdRouting; _ASNBOLShipperDetail = ASNBOLShipperDetail; _yearDemandManagement = yearDemandManagement; _yearDemandManagementHistory = yearDemandManagementHistory; _replenishmentAnnualProduction = replenishmentAnnualProduction; _holidayMaster = holidayMaster; _monthlyProdCapacity = monthlyProdCapacity; _monthlyProdCapacityDtl = monthlyProdCapacityDtl; _crm_planorder = crm_planorder; _platformInventory = platformInventory; _serialNumberAppService= serialNumberAppService; _pretreatmentAppService = pretreatmentAppService; _CalcBomViewAppService = CalcBomViewAppService; _purchaseOrderAppService = purchaseOrderAppService; _productExamineAppService = productExamineAppService; _currentTenant =currentTenant; _businessBangDbContext= businessBangDbContext; _businessDbContext = businessDbContext; _unitOfWorkManager = unitOfWorkManager; } #endregion /// /// 生成年度生产大纲 /// /// /// /// public async Task SaveAnnualProductionOutline(InputDto input) { //获取当前导入或修改数据 List yearDemands = _yearDemandManagement.GetListAsync(p => p.Year == input.year && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p => p.OrderNum).ThenBy(o => o.PlanMonth).ToList(); //标准工艺路径表 var sapItemList = yearDemands.Select(m => m.SAPItemNumber).Distinct().ToList(); List routingOps = _routingOpDetail.Select(p => sapItemList.Contains(p.RoutingCode) && p.Domain == input.factory_id.ToString() && p.IsActive); var routingOpList = routingOps.Where(x => x.Descr == "去溢料").ToList(); //生产线明细表 List prodLines = _prodLineDetail.Select(p => sapItemList.Contains(p.Part) && p.Domain == input.factory_id.ToString() && p.IsActive && routingOpList.Select(m => m.Op).Contains(p.Op)).OrderBy(x => x.Sequence).ToList(); List locations = _locationDetail.Select(x => sapItemList.Contains(x.ItemNum) && x.Domain == input.factory_id.ToString() && x.IsActive).ToList(); //平台库存 var platformInvList = _PlatformInventory.GetListAsync(a => sapItemList.Contains(a.SAPItemNumber) && a.factory_id == input.factory_id && !a.IsDeleted).Result; var replenishmentModels = _replenishmentROP.GetListAsync(x => !x.IsDeleted && x.isparam && sapItemList.Contains(x.number)).Result.ToList(); // 获取某年某月的起始日期和结束日期 int year = input.year; int month = DateTime.Now.Month; DateTime start = new DateTime(year, month, 1); DateTime end = start.AddMonths(1).AddDays(-1); //取当月发货出库记录 var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && yearDemands.Select(p => p.SAPItemNumber).Contains(a.ContainerItem)).Where(s => s.ShipDate >= start && s.ShipDate <= end); #region 在制数量 //获取在制数量 获取工单数取每年4月到12月底的工单 DateTime startYear = new DateTime(year, 4, 1); DateTime endYear = new DateTime(year, 12, 31); List workOrdRoutings = _workOrdRouting.Select(x => x.IsActive && x.QtyComplete > 0 && x.Domain == input.factory_id.ToString()).Where(p => p.DueDate >= startYear && p.DueDate <= endYear).ToList(); decimal? InProductionQty = 0.00m; var workOrds = workOrdRoutings.GroupBy(x => x.WorkOrd).ToList(); Dictionary dictInProduction = new Dictionary(); //按照工单循环 //某工单10-90工序 Max(10-80工序QtyComplete)-90工序QtyComplete =在制数量 foreach (var item in workOrds) { var workOrdRoutingList = workOrdRoutings.Where(x => x.WorkOrd == item.Key).OrderByDescending(o => o.OP).ToList(); //找出最大工序 var MaxOp = workOrdRoutingList.FirstOrDefault(); //查询出其他工序最大值 var MaxQtyComplete = workOrdRoutingList.Where(x => x.RecID != MaxOp.RecID).ToList().Max(o => o.QtyComplete); InProductionQty += MaxQtyComplete - MaxOp.QtyComplete; if (dictInProduction.ContainsKey(MaxOp.ItemNum)) { dictInProduction[MaxOp.ItemNum] += InProductionQty.Value; } else { dictInProduction.Add(MaxOp.ItemNum, InProductionQty.Value); } } #endregion //年度生产大纲实体 List annualProductionOutlines = new List(); List frontYearDemand = new List(); foreach (var item in yearDemands) { var routingOp = routingOps.Where(x => x.RoutingCode == item.SAPItemNumber).ToList(); //组装标准工时 var Assembly = routingOp.Where(x => x.Descr == "组装").FirstOrDefault(); //热封标准工时 var HeatSealing = routingOp.Where(x => x.Descr == "热封").FirstOrDefault(); //包装标准工时 var Packaging = routingOp.Where(x => x.Descr == "包装").FirstOrDefault(); var prodLine = prodLines.Where(x => x.Part == item.SAPItemNumber).OrderBy(x => x.Sequence).FirstOrDefault(); //不同库位库存数量 var locationList = locations.Where(x => x.ItemNum == item.SAPItemNumber).ToList(); //平台数据 var platformInvs = platformInvList.Where(x => x.SAPItemNumber == item.SAPItemNumber).ToList(); //销售预测 对应 Excel中公式 AVERAGE 如果预测为0不参与计算排产批量 var QtySum = yearDemands.Where(x => x.SAPItemNumber == item.SAPItemNumber && x.Qty > 0).ToList(); //排产批量:(AVG(1 - 12月销售预测)/ 100 )=(0.45 = 1 小数向上取整) *100 = 100 var pcpl = Math.Ceiling(QtySum.Sum(p => p.Qty) / (QtySum.Count()*100)) * 100; //库存合计 + 在制+已发货 + 灭菌中 TODO: 灭菌中取值待确定 var ship = shipList.Where(x => x.ContainerItem == item.SAPItemNumber).ToList(); decimal itemInProduct = 0; if(dictInProduction.ContainsKey(item.SAPItemNumber)) { itemInProduct = dictInProduction[item.SAPItemNumber]; } var locationSum = (locationList.Count == 0 ? 0 : locationList.Sum(x => x.QtyOnHand)) + (platformInvs.Count == 0 ? 0 : platformInvs.Sum(x => x.InventoryQuantity)) + (ship.Count == 0 ? 0 : ship.Sum(x => x.RealQty)) + itemInProduct + 0; //前面N个月的生产数量 var frontQtySum = annualProductionOutlines.Sum(x => x.Qty); //生产数量:3月为例子,if((库存合计和前2月生产数量)-(前2个月销售预测数据+安全库存)-当月销售预测数据 / 2 < 0) // { 排产批量 * ((-(库存合计+前2个月生产数量)) + (安全库存+前2个月销售预测) + 当月销售预测 / 2 ) / 排产批量 ) } else {0} decimal ProduceQty = 0.00m; //判断库存是否满足需要,满足则不用生产 var num = (locationSum.Value + frontQtySum) - frontYearDemand.Sum(m => m.Qty) + 0 - item.Qty / 2; if (num < 0) { ProduceQty = pcpl * Math.Ceiling((-(locationSum.Value + frontQtySum)) + (frontYearDemand.Sum(m => m.Qty) + 0) + item.Qty / 2); } else { ProduceQty = 0; } //生成年度生产大纲 ReplenishmentAnnualProduction annualProductionOutline = new ReplenishmentAnnualProduction(); annualProductionOutline.Year = item.Year; annualProductionOutline.Area = item.Area; annualProductionOutline.ProdLine = item.ProdLine; annualProductionOutline.ProdRange = item.ProdRange; annualProductionOutline.WorkshopLine = prodLine == null ? "" : prodLine.Line; annualProductionOutline.SAPItemNumber = item.SAPItemNumber; annualProductionOutline.Model = item.Model; annualProductionOutline.Languages = item.Languages; annualProductionOutline.PlanMonth = item.PlanMonth; annualProductionOutline.Qty = ProduceQty; annualProductionOutline.StandardHours = (Assembly == null ? 0 : Assembly.RunTime) + (HeatSealing == null ? 0 : HeatSealing.RunTime) + (Packaging == null ? 0 : Packaging.RunTime); //组装热封包装工时乘以数量 =单月工时 annualProductionOutline.AssemblyHours = Assembly == null ? 0 : Assembly.RunTime * ProduceQty; annualProductionOutline.HeatSealingHours = HeatSealing == null ? 0 : HeatSealing.RunTime * ProduceQty; annualProductionOutline.PackagingHours = Packaging == null ? 0 : Packaging.RunTime * ProduceQty; annualProductionOutline.Totalhours = annualProductionOutline.AssemblyHours + annualProductionOutline.HeatSealingHours + annualProductionOutline.Totalhours; annualProductionOutline.OrderNum = item.OrderNum; annualProductionOutline.create_time= DateTime.Now; annualProductionOutline.tenant_id = input.tenant_id; annualProductionOutline.company_id= input.company_id; annualProductionOutline.factory_id= input.factory_id; annualProductionOutline.org_id= input.org_id; annualProductionOutlines.Add(annualProductionOutline); frontYearDemand.Add(item); } //保存数据 using (var unitOfWork = _unitOfWorkManager.Begin(false, true)) { try { await _replenishmentAnnualProduction.HardDeleteAsync(a => a.Year == input.year && a.factory_id == input.factory_id); await _replenishmentAnnualProduction.InsertManyAsync(annualProductionOutlines); await CalcLongPeriodItemPR(input); await CalcROP(input); await SaveProductionMasterPlan(input, annualProductionOutlines); await unitOfWork.CompleteAsync(); } catch (Exception e) { unitOfWork.Dispose(); new NLogHelper("AnnualProductionOutlineAppService").WriteLog("SaveAnnualProductionOutline", "【" + input.year + "年" + "】年度生成大纲生成失败:" + e.Message, _currentTenant.Id.ToString()); return "NO|" + e.Message; }; } return "OK"; } /// /// 生成主计划 /// /// /// public async Task SaveProductionMasterPlan(InputDto input,List annual) { //计算当前年月的N0,N+1,N+2 List planMons = GetPlanMonth(4); List productionMasters= new List(); List pre3MonthPlan = _productionMasterPlan.GetListAsync(a => GetPlanMonth(-3).Contains(a.PlanMonth) && a.factory_id == input.factory_id).Result; //N0,N+1,N+2月度发货计划 var annualProduction = _monthlyShipmentPlan.Select(x => planMons.Contains(x.PlanMonth) && !x.IsDeleted && x.factory_id == input.factory_id).OrderBy(p => p.PlanMonth).ThenBy(o => o.OrderNum).ToList(); var productionMasterPlan = _productionMasterPlan.GetListAsync(x => planMons.Contains(x.PlanMonth) && !x.IsDeleted && x.factory_id == input.factory_id).Result.OrderBy(p => p.PlanMonth).ThenBy(o => o.OrderNum).ToList(); List locations = _locationDetail.Select(x => annualProduction.Select(m => m.SAPItemNumber).Contains(x.ItemNum) && x.Domain == input.factory_id.ToString() && x.IsActive).ToList(); var InProdcutQty=GetInProdcutQty(annualProduction.Select(m => m.SAPItemNumber).ToList(),input); var itemStock = CalcStock(annualProduction.Select(m => m.SAPItemNumber).ToList(),input); foreach (var item in annualProduction.Where(a=> GetPlanMonth(4).Contains(a.PlanMonth))) { ProductionMasterPlan plan = new ProductionMasterPlan(); plan.Area = item.Area; plan.ProdLine = item.ProdLine; plan.ProdRange = item.ProdRange; plan.WorkshopLine = annual.Find(a=>a.PlanMonth==item.PlanMonth && a.SAPItemNumber==item.SAPItemNumber).WorkshopLine; plan.ItemNumber = item.SAPItemNumber; plan.Model = item.Model; plan.Languages = item.Languages; plan.PlanMonth = item.PlanMonth; plan.PlanShipQty = item.Qty; //计划生产数量取下个月计划发货数量的一半 plan.PlanProductQty = item.Qty- itemStock.Find(a => a.ItemNumber == item.SAPItemNumber).Qty- InProdcutQty.Find(a => a.ItemNumber == item.SAPItemNumber).Qty;//发货计划-(库存+在制) plan.PlanStockQty= item.Qty;//TODO:期末库存怎么算? if(locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "8001")!=null) { plan.Inventory = locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "8001").QtyOnHand; }else { plan.Inventory = 0; } if (locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "8000") != null) { plan.Inventory1 = locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "8000").QtyOnHand; } else { plan.Inventory1 = 0; } if (locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "5008") != null) { plan.Inventory2 = locations.FirstOrDefault(a => a.ItemNum == item.SAPItemNumber && a.Location == "5008").QtyOnHand; } else { plan.Inventory2 = 0; } plan.DuringSterilization = 0;//TODO:灭菌中需要从委外订单接口获取 plan.InProduction = InProdcutQty.Find(a=>a.ItemNumber==item.SAPItemNumber).Qty; if(pre3MonthPlan.Where(a => a.ItemNumber == item.SAPItemNumber).Count()>0) { plan.EconomicLotSize = pre3MonthPlan.Where(a => a.ItemNumber == item.SAPItemNumber).Average(a => a.PlanProductQty); }else { plan.EconomicLotSize = plan.PlanProductQty; } plan.tenant_id = input.tenant_id; plan.factory_id = input.factory_id; plan.company_id=input.company_id; plan.org_id = input.org_id; plan.create_time = DateTime.Now; plan.OrderNum =item.OrderNum; productionMasters.Add(plan); } //保存数据 using (var unitOfWork = _unitOfWorkManager.Begin(false, true)) { try { var planList = productionMasters.Select(a => a.PlanMonth).ToList(); await _productionMasterPlan.DeleteAsync(a => planList.Contains(a.PlanMonth) && a.factory_id == input.factory_id); await _productionMasterPlan.InsertManyAsync(productionMasters); await unitOfWork.CompleteAsync(); } catch (Exception e) { unitOfWork.Dispose(); new NLogHelper("AnnualProductionOutlineAppService").WriteLog("SaveProductionMasterPlan", "【" + input.year + "年" + "】主计划生成失败:" + e.Message, _currentTenant.Id.ToString()); return "NO|" + e.Message; }; } return "OK"; } /// /// 获取物料在制数量 /// /// /// /// public List GetInProdcutQty(List itemNumbers,InputDto input) { List itemQty = new List(); DateTime startYear = new DateTime(DateTime.Now.AddYears(-1).Year, 1, 1); DateTime endYear = new DateTime(DateTime.Now.Year, 12, 31); List workOrdRoutings = _workOrdRouting.Select(x => x.IsActive && x.QtyComplete > 0 && x.Domain == input.factory_id.ToString()).Where(p => p.DueDate >= startYear && p.DueDate <= endYear).ToList(); decimal? InProductionQty = 0.00m; var workOrds = workOrdRoutings.GroupBy(x => x.WorkOrd).ToList(); Dictionary dictInProduction = new Dictionary(); //按照工单循环 //某工单10-90工序 Max(10-80工序QtyComplete)-90工序QtyComplete =在制数量 foreach (var item in workOrds) { var workOrdRoutingList = workOrdRoutings.Where(x => x.WorkOrd == item.Key).OrderByDescending(o => o.OP).ToList(); //找出最大工序 var MaxOp = workOrdRoutingList.FirstOrDefault(); //查询出其他工序最大值 var MaxQtyComplete = workOrdRoutingList.Where(x => x.RecID != MaxOp.RecID).ToList().Max(o => o.QtyComplete); InProductionQty += MaxQtyComplete - MaxOp.QtyComplete; if (dictInProduction.ContainsKey(MaxOp.ItemNum)) { dictInProduction[MaxOp.ItemNum] += InProductionQty.Value; } else { dictInProduction.Add(MaxOp.ItemNum, InProductionQty.Value); } } itemNumbers.ForEach(a => { EOPDto eOP = new EOPDto(); eOP.ItemNumber = a; eOP.Qty = 0; if(dictInProduction.ContainsKey(a)) eOP.Qty = dictInProduction[a]; itemQty.Add(eOP); }); return itemQty; } /// /// 更新年度生产大纲 /// /// /// /// public async Task DemandAnalysis(InputDto input) { string productResult=await SaveAnnualProductionOutline(input); if(productResult!="OK") return productResult; return "OK|刷新成功!"; } /// /// 计算当月有多少个周末 /// /// /// /// private int CalcWeekDays(int days, DateTime startDay) { int sumDays = 0; for (int i = 0; i < days; i++) { int weekDays = (int)startDay.AddDays(i).DayOfWeek; if (weekDays == 0 || weekDays == 6) { sumDays++; } } return sumDays; } /// /// 调整ROP和最高库存水位(按照成品展开到原材料,原材料用领料算出库) /// /// /// public async Task CalcROP(InputDto input) { //1.获取补货模型全局参数 ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString()); //获取月度发货计划(一次导入三个月的销售预测),因为是固定格式yyyy-MM所以可以用字符串比较,避免写很多字符串判断相等 string planMonth = $"{DateTime.Now.Year}-{DateTime.Now.Month.ToString("00")}"; string planMonthMax = $"{DateTime.Now.AddMonths(replenishmentDto.SaleFcstMonth).Year}-{DateTime.Now.AddMonths(replenishmentDto.SaleFcstMonth).Month.ToString("00")}"; var planList = _monthlyShipmentPlan.Select(a => a.PlanMonth.CompareTo(planMonth) >= 0 && a.PlanMonth.CompareTo(planMonthMax) <= 0 && !a.IsDeleted).ToList(); var boms = _ic_bom.GetListAsync(a => planList.Select(p => p.SAPItemNumber).ToList().Contains(a.item_number) && a.factory_id == input.factory_id).Result; var pretreatments = _mysql_b_bom_pretreatment.GetListAsync(s => boms.Select(c => c.mysql_id).ToList().Contains(s.sourceid)).Result; List autoCreates = new List(); boms.ForEach(p => { if (!pretreatments.Where(s => s.sourceid == p.mysql_id).Any()) { autoCreates.Add(p); } }); if (autoCreates.Any()) { AutoCreateBomBill(input.company_id.ToString(), autoCreates); pretreatments = _mysql_b_bom_pretreatment.GetListAsync(s => boms.Select(c => c.mysql_id).ToList().Contains(s.sourceid)).Result; } var itemList = _ic_item.GetListAsync(a => pretreatments.Select(p => p.item_number).Contains(a.number) && !a.IsDeleted).Result; var mesItemList = _itemMaster.Select(a => pretreatments.Select(p => p.item_number).Contains(a.ItemNum) && a.Domain == input.factory_id.ToString() && a.IsActive); var srm_purchaseList = _srmPurchase.GetListAsync(a => pretreatments.Select(p => p.item_number).Contains(a.number) && !a.IsDeleted).Result; //获取补货模型前H周期和未来F个周期的数据 var ropModelList = _replenishmentROP.GetListAsync(a => a.zero_based_seqno >= -1 * replenishmentDto.HistoryOutStockMonth && a.zero_based_seqno <= replenishmentDto.SaleFcstMonth).Result; //获取成品库存、灭菌库存、在制库存(会从SAP同步的库存表更新到LocationDetail、ic_item表中) //List sAPInvs = _SAPInv.Select(p => p.WERKS == input.factory_id.ToString() && itemNums.Contains(p.MATNR)); var locations = _locationDetail.Select(p => p.Domain == input.factory_id.ToString() && pretreatments.Select(p => p.item_number).Contains(p.ItemNum) && p.IsActive); var sapItemInv = _SAPInv.Select(a => pretreatments.Select(p => p.item_number).Contains(a.MATNR) && a.WERKS == input.factory_id.ToString()); //取过去6个月发货出库记录 var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && pretreatments.Select(p => p.item_number).Contains(a.ContainerItem) && a.ShipDate >= getMonthStartTime(-6) && a.ShipDate <= getMonthEndTime(-1)); //领料出库记录 //var pickbilllist = _NbrDetail.Select(a => a.Domain == input.factory_id.ToString() && a.Type == "SM" && a.IsActive && a.UpdateTime >= DateTime.Now.AddMonths(replenishmentDto.HistoryOutStockMonth * -1) && itemNumList.Contains(a.ItemNum)); var pickbilllist = _invTransHist.Select(a => a.Domain == input.factory_id.ToString() && a.TransType == "iss-wo" && a.IsActive && a.CreateTime >= DateTime.Now.AddMonths(-6) && pretreatments.Select(p => p.item_number).Contains(a.ItemNum)); //取本月发货出库记录 var shipMList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && pretreatments.Select(p => p.item_number).Contains(a.ContainerItem) && a.ShipDate >= getMonthStartTime(0) && a.ShipDate <= DateTime.Now); //按照物料分组统计出货金额 var itemGroup = shipList.GroupBy(p => p.ContainerItem) .Select(p => new ASNBOLShipperDetail { QtyToShip = p.Sum(a => a.QtyToShip), ContainerItem = p.Key }).ToList(); //按照物料分组统计出货金额 var itemMGroup = shipMList.GroupBy(p => p.ContainerItem) .Select(p => new ASNBOLShipperDetail { QtyToShip = p.Sum(a => a.QtyToShip), ContainerItem = p.Key }).ToList(); List addList = new List(); List updateList = new List();//更新上一个月的实际出库数量 var mathtool = new MathNet.Numerics.Distributions.Normal(); ropModelList?.Where(r => r.isparam && r.seqno == DateTime.Now.AddMonths(-1).Month && r.year == DateTime.Now.AddMonths(-1).Year).ToList()?.ForEach (m => { if (itemGroup.Any(a => a.ContainerItem == m.number)) { m.actual_out_qty = itemGroup.Find(a => a.ContainerItem == m.number)?.QtyToShip; updateList.Add(m); } }); var serviceList=_replenishmentServiceLevel.GetListAsync(a => a.factory_id == input.factory_id).Result; List addServiceLevelList = new List(); List updateServiceLevelList = new List(); //需要按照成品资源检查计算原材料 planList?.Where(s => s.PlanMonth == planMonth).ToList()?.ForEach(a => { var itemSourceId = boms.Find(b => b.item_number == a.SAPItemNumber)?.mysql_id; if (itemSourceId != null) { for (int i = 0; i < pretreatments.Count; i++) { if (pretreatments[i].sourceid == itemSourceId) { ReplenishmentROP rop = new ReplenishmentROP(); rop.number = pretreatments[i].item_number; var icItem = itemList.Find(s => s.number == pretreatments[i].item_number); if (icItem != null) { rop.name = icItem.name; rop.model = a.Model; rop.erp_cls = icItem.erp_cls; //物料属性: 0.配置类 1.自制 2.委外加工 3.外购 4.虚拟件 rop.fversion = icItem.fversion; rop.min_pack_qty = icItem.minpackqty; rop.moq = icItem.moq; } rop.actual_period_start_instock = 0; if (rop.distributionchannel == "海王" || rop.distributionchannel == "国科") { rop.actual_period_start_instock = locations.Find(l => l.ItemNum == pretreatments[i].item_number)?.QtyOnHand; } else { rop.actual_period_start_instock = sapItemInv.Find(s => s.MATNR == pretreatments[i].item_number)?.LABST.ToDecimal(); } rop.distributionchannel = a.DistributionChannel; rop.lifecycle = a.LifeCycle; rop.area = a.Area; rop.plan_out_qty = Math.Ceiling(a.Qty); rop.actual_out_qty = 0; if (itemMGroup.Any(m => m.ContainerItem == pretreatments[i].item_number)) { rop.actual_out_qty = itemMGroup.Find(m => m.ContainerItem == pretreatments[i].item_number)?.QtyToShip; } rop.year = DateTime.Now.Year; rop.long_period = "Y"; rop.short_period = "M"; rop.seqno = DateTime.Now.Month; rop.zero_based_seqno = 0; rop.period_start_date = getMonthStartTime(0); rop.period_end_date = getMonthEndTime(0); if (rop.erp_cls == 3) { rop.monthl_avg_demand = CalcAvgDemand(planList, pretreatments[i].item_number, rop.erp_cls, pickbilllist); } else { rop.monthl_avg_demand = CalcAvgDemand(planList, pretreatments[i].item_number, rop.erp_cls); } rop.monthl_avg_demand_variance = Math.Ceiling(Convert.ToDecimal(CalcVariance(planList, pretreatments[i].item_number, rop.erp_cls, rop.monthl_avg_demand, pickbilllist))); rop.monthl_avg_outstock = CalcAvgOutStock(ropModelList, replenishmentDto, rop.actual_out_qty.Value, pretreatments[i].item_number); rop.supply_leadtime = 1;//默认 if (mesItemList.Find(s => s.ItemNum == pretreatments[i].item_number) != null) { rop.stock_turnover = mesItemList.Find(s => s.ItemNum == pretreatments[i].item_number).StockTurnOver; rop.supply_leadtime = mesItemList.Find(s => s.ItemNum == pretreatments[i].item_number)?.PurLT; rop.stock_turnover = mesItemList.Find(s => s.ItemNum == pretreatments[i].item_number)?.StockTurnOver; } else { rop.stock_turnover = 4; } if (srm_purchaseList.Find(s => s.number == pretreatments[i].item_number) != null) { rop.supply_leadtime = srm_purchaseList.Find(s => s.number == pretreatments[i].item_number).lead_time; } CalcFMRAndABC(rop, replenishmentDto, shipList, pickbilllist, input); if(serviceList.Any(s=>s.number==rop.number)) { var rs = serviceList.Find(s => s.number == rop.number); //总的移库次数 int totalCount = shipList.Count() + pickbilllist.Count(); //平均出库次数 decimal avgOutStock = totalCount / replenishmentDto.HistoryOutStockMonth; //总出货金额 decimal? totalPrice = shipList.Sum(a => a.Price * a.QtyToShip); //按照物料分组统计出货金额 var itemGroup = shipList.GroupBy(p => p.ContainerItem) .Select(p => new ASNBOLShipperDetail { Price = p.Sum(a => a.Price * a.QtyToShip) * 100 / totalPrice, ContainerItem = p.Key }).ToList().OrderByDescending(c => c.Price).ThenByDescending(c => c.ContainerItem); rs.number = rop.number; rs.name = rop.name; rs.model = rop.model; rs.item_type = icItem.item_type; rs.fversion = rop.fversion; rs.totalCount = totalCount; rs.avgOutStock = avgOutStock; rs.totalPrice = totalPrice; if(itemGroup.Count()>0 && itemGroup.First(p => p.ContainerItem == rop.number)!=null) { rs.Price= itemGroup.First(p => p.ContainerItem == rop.number).Price; }else { rs.Price = 0; } rs.abc = rop.abc; rs.fmr = rop.fmr; rs.service_level_pct = rop.service_level_pct; rs.factory_id = input.factory_id; rs.tenant_id = input.tenant_id; rs.company_id = input.company_id; rs.update_time = DateTime.Now; updateServiceLevelList.Add(rs); } else { //总的移库次数 int totalCount = shipList.Count() + pickbilllist.Count(); //平均出库次数 decimal avgOutStock = totalCount / replenishmentDto.HistoryOutStockMonth; //总出货金额 decimal? totalPrice = shipList.Sum(a => a.Price * a.QtyToShip); //按照物料分组统计出货金额 var itemGroup = shipList.GroupBy(p => p.ContainerItem) .Select(p => new ASNBOLShipperDetail { Price = p.Sum(a => a.Price * a.QtyToShip) * 100 / totalPrice, ContainerItem = p.Key }).ToList().OrderByDescending(c => c.Price).ThenByDescending(c => c.ContainerItem); ReplenishmentServiceLevel rs = new ReplenishmentServiceLevel(); rs.number = rop.number; rs.name = rop.name; rs.model = rop.model; rs.item_type = icItem.item_type; rs.fversion = rop.fversion; rs.totalCount = totalCount; rs.avgOutStock = avgOutStock; rs.totalPrice = totalPrice; if (itemGroup.Count() > 0 && itemGroup.First(p => p.ContainerItem == rop.number) != null) { rs.Price = itemGroup.First(p => p.ContainerItem == rop.number).Price; } else { rs.Price = 0; } rs.abc = rop.abc; rs.fmr = rop.fmr; rs.service_level_pct = rop.service_level_pct; rs.factory_id = input.factory_id; rs.tenant_id = input.tenant_id; rs.company_id = input.company_id; rs.create_time = DateTime.Now; rs.GenerateNewId(help.NextId()); addServiceLevelList.Add(rs); } rop.security_stock = Math.Ceiling((decimal)(mathtool.InverseCumulativeDistribution((double)rop.service_level_pct.Value) * (double)rop.monthl_avg_demand_variance)); rop.eop = Math.Ceiling(rop.monthl_avg_demand.Value * rop.supply_leadtime.Value / DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)); rop.rop_computed = rop.security_stock + rop.eop; rop.max_stock_level = rop.monthl_avg_outstock * (12 / rop.stock_turnover); rop.rop_revised = Math.Min(rop.rop_computed.Value, rop.max_stock_level.Value); rop.isparam = true; rop.tenant_id = input.tenant_id; rop.factory_id = input.factory_id; rop.create_time = DateTime.Now; rop.org_id = input.org_id; addList.Add(rop); } } } }); addList?.ForEach(item => { item.GenerateNewId(help.NextId()); }); addServiceLevelList?.ForEach(item => { item.GenerateNewId(help.NextId()); }); await _replenishmentROP.InsertManyAsync(addList); await _replenishmentROP.UpdateManyAsync(updateList); await _replenishmentServiceLevel.InsertManyAsync(addServiceLevelList); await _replenishmentServiceLevel.UpdateManyAsync(updateServiceLevelList); var ropModeAllList = _replenishmentROP.GetListAsync(a => a.year != DateTime.Now.Year && a.seqno != DateTime.Now.Month && a.isparam).Result; ropModeAllList?.ForEach(item => { item.seqno = item.seqno - 1; }); await _replenishmentROP.UpdateManyAsync(ropModeAllList); return "OK"; } /// /// 根据月计划生产周计划 /// /// /// public async Task CalcWeekPlan(InputDto input) { //1.获取补货模型全局参数 ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString()); //获取月度发货计划(一次导入三个月的销售预测),因为是固定格式yyyy-MM所以可以用字符串比较,避免写很多字符串判断相等 string planMonth = $"{DateTime.Now.Year}-{DateTime.Now.Month.ToString("00")}"; string planMonthMax = $"{DateTime.Now.AddMonths(replenishmentDto.SaleFcstMonth).Year}-{DateTime.Now.AddMonths(replenishmentDto.SaleFcstMonth).Month.ToString("00")}"; var planList = _monthlyShipmentPlan.Select(a => a.PlanMonth.CompareTo(planMonth) >= 0 && a.PlanMonth.CompareTo(planMonthMax) <= 0 && !a.IsDeleted).ToList(); //发货计划物料列表 List planItemList = planList?.Select(a => a.SAPItemNumber).Distinct().ToList(); var itemList = _ic_item.GetListAsync(a => planList.Select(p => p.SAPItemNumber).Contains(a.number) && !a.IsDeleted).Result; var bomList = _ic_bom.GetListAsync(a => planItemList.Contains(a.item_number) && !a.IsDeleted && a.factory_id == input.factory_id).Result; var monthPlan = _productionMasterPlan.GetListAsync(a => planItemList.Contains(a.ItemNumber) && !a.IsDeleted && a.factory_id == input.factory_id).Result; //获取补货模型前H周期和未来F个周期的数据 var ropModelList = _replenishmentROP.GetListAsync(a => a.zero_based_seqno >= -1 * replenishmentDto.HistoryOutStockMonth && a.zero_based_seqno <= replenishmentDto.SaleFcstMonth && planItemList.Contains(a.number)).Result; planList = planList.OrderBy(a => a.OrderNum).ToList(); List moList = new List(); List moentryList = new List(); List weekPlan = new List(); long bang_id = help.NextId(); DateTime currentTime = DateTime.Now; int week = Convert.ToInt32(currentTime.DayOfWeek); week = week == 0 ? 7 : week; int count = planItemList.Count * 12; var list = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "MPO", count, "", 1); var nbrlistDto = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "M5", count, "", 1); int j = 0; for (int i=0;i<12;i++) { planList.Where(c=>c.PlanMonth== planMonth).ToList().ForEach(a => { var ic_item = itemList.Find(x => x.number == a.SAPItemNumber); var itemrop= ropModelList.Find(x=>x.number == a.SAPItemNumber); var monthPlanItem = monthPlan.Find(x => x.ItemNumber == a.SAPItemNumber); if (ic_item?.erp_cls == 1 && itemrop!=null) { var bom = bomList.Find(b => b.item_number == a.SAPItemNumber); mes_morder mes_Morder = new mes_morder(); mes_Morder.GenerateNewId(help.NextId()); mes_Morder.morder_type = MorderEnum.JhMorder; //mes_Morder.morder_icitem_type mes_Morder.work_order_type = MorderEnum.FhMorder; mes_Morder.morder_state = MorderEnum.Initial_state; mes_Morder.morder_no = list[j].NbrResult; mes_Morder.fms_number = ic_item.fms_number; mes_Morder.bom_number = bom.bom_number; mes_Morder.fmodel = ic_item.model; mes_Morder.urgent = 1; mes_Morder.moentry_startup_status = 0; mes_Morder.tenant_id = input.tenant_id; mes_Morder.factory_id = input.factory_id; mes_Morder.company_id = input.company_id; mes_Morder.org_id = input.org_id; mes_Morder.product_code = ic_item.number; mes_Morder.product_name = ic_item.name; mes_Morder.morder_date = DateTime.Now.Date.AddDays(1); //成品半成品取周一开始 + 取供应提前期为结束时间 mes_Morder.moentry_stime = currentTime.AddDays(1 - week + 7 * (i + 1)); mes_Morder.moentry_sys_etime = currentTime.AddDays(1 - week + 7*(i+1)+ Convert.ToDouble(itemrop.supply_leadtime)); //mes_Morder.morder_fstate = "计划"; //TODO:目前没有取值位置 mes_Morder.moentry_prd = null; mes_Morder.moentry_prdname = null; mes_Morder.moentry_wrkc = null; mes_Morder.moentry_wrkcname = null; mes_Morder.picking_qty = 0; //TODO:可删除主表字段 mes_Morder.unit = ic_item.unit; mes_Morder.morder_production_number = itemrop.eop/4; mes_Morder.need_number = itemrop.eop/4; mes_Morder.remaining_number = 0; mes_Morder.create_time = DateTime.Now; mes_Morder.bang_id = bang_id; //生成工单子表数据 mes_moentry mes_Moentry = new mes_moentry(); mes_Moentry.GenerateNewId(help.NextId()); mes_Moentry.moentry_moid = mes_Morder.Id; mes_Moentry.moentry_mono = mes_Morder.morder_no; mes_Moentry.unit = ic_item.unit; mes_Moentry.morder_production_number = itemrop.eop/4; mes_Moentry.need_number = itemrop.eop/4; mes_Moentry.remaining_number = 0; mes_Moentry.tenant_id = input.tenant_id; mes_Moentry.factory_id = input.factory_id; mes_Moentry.company_id = input.company_id; mes_Moentry.org_id = input.org_id; mes_Moentry.create_time = DateTime.Now; mes_Moentry.bang_id = bang_id; moList.Add(mes_Morder); moentryList.Add(mes_Moentry); ReplenishmentWeekPlan weekItem = new ReplenishmentWeekPlan(); weekItem.Priority = 1; weekItem.Area = itemrop.area; weekItem.PlanStartDate = mes_Morder.moentry_stime.Value; weekItem.Week =$"WK{GetWeekOfYear(mes_Morder.moentry_stime.Value).ToString("00")}"; weekItem.OrderNO = list[j].NbrResult; weekItem.ProductionOrder = nbrlistDto[j].NbrResult; weekItem.SAPOrderNO = ""; weekItem.OrderType = "量产"; weekItem.ProductionBatch = ""; weekItem.ProductionStatus = ""; weekItem.ProdLine =a.ProdLine; weekItem.ProdRange =a.ProdRange; weekItem.Line = monthPlanItem.WorkshopLine; weekItem.ItemNumber =a.SAPItemNumber; weekItem.Model =a.Model; weekItem.Languages =a.Languages; weekItem.Qty = mes_Morder.morder_production_number.Value; weekItem.ItemStatus = ""; weekItem.PlanKittingDate = DateTime.Now; weekItem.tenant_id = input.tenant_id; weekItem.factory_id = input.factory_id; weekItem.company_id = input.company_id; weekItem.org_id = input.org_id; weekItem.create_time = DateTime.Now; if (itemrop.area=="国内") { weekItem.SterilizationDate = mes_Morder.moentry_sys_etime.Value.AddDays(8); }else { weekItem.SterilizationDate = mes_Morder.moentry_sys_etime.Value.AddDays(15); } weekItem.InStockDate = mes_Morder.moentry_sys_etime; weekItem.AssembleHours = 0; weekItem.HeatSealHours = 0; weekItem.PackageHours = 0; weekItem.TotalHours = 0; weekPlan.Add(weekItem); j++; } }); } await _mysql_mes_morder.InsertManyAsync(moList); await _mysql_mes_moentry.InsertManyAsync(moentryList); await PlanOrderResourceCheck(moList, moentryList, bang_id, input); List routingOps = _routingOpDetail.Select(p => planItemList.Contains(p.RoutingCode) && p.Domain == input.factory_id.ToString() && p.IsActive); var examine_resultList= _mysql_examine_result.GetListAsync(a=>a.bangid == bang_id).Result; //批量保存 后期考虑子工单 List workOrds = new List(); List workOrdRoutings = new List(); List workOrdDetails = new List(); if (moList.Any()) { moList.ForEach(s => { s.create_time = DateTime.Now; }); List childExamineList = _mysql_bom_child_examine.GetListAsync(c => examine_resultList.Select(x => x.Id).Contains(c.examine_id.GetValueOrDefault())).Result; //同步工单 CreateWorkOrdDates(moList, routingOps, workOrds, workOrdRoutings, workOrdDetails, examine_resultList, childExamineList); } if (workOrds.Any()) { workOrds=CalcPriority(workOrds, input.factory_id.ToString()); _workOrdMaster.Insert(workOrds); } var DBworkOrdList = _workOrdMaster.Select(a => a.Domain == workOrds[0].Domain && workOrds.Select(c => c.WorkOrd).Contains(a.WorkOrd)); if (workOrdRoutings.Any()) { workOrdRoutings.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdRouting.Insert(workOrdRoutings); } if (workOrdDetails.Any()) { workOrdDetails.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdDetail.Insert(workOrdDetails); } weekPlan.ForEach(a => { var moItem = examine_resultList.Find(b => b.morder_no == a.OrderNO); var routingOp = routingOps.Where(x => x.RoutingCode == a.ItemNumber).ToList(); //组装标准工时 var Assembly = routingOp.Where(x => x.Descr == "组装").FirstOrDefault(); //热封标准工时 var HeatSealing = routingOp.Where(x => x.Descr == "热封").FirstOrDefault(); //包装标准工时 var Packaging = routingOp.Where(x => x.Descr == "包装").FirstOrDefault(); if (moItem!=null && moItem.kitting_times b.WorkOrd == a.OrderNO); if(workOrd!=null) { a.Priority = workOrd.Priority; } }); await _replenishmentWeekPlan.InsertManyAsync(weekPlan); return "OK"; } /// /// 发布周计划 /// /// /// public async Task PublishWeekPlan(InputDto input) { DateTime nextWeekMonday = getPreWeekStartTime().AddDays(14); DateTime next2WeekMonday = getPreWeekStartTime().AddDays(28); var weekPlan = _replenishmentWeekPlan.GetListAsync(a => a.PlanStartDate >= nextWeekMonday && a.PlanStartDate < next2WeekMonday).Result; var workMasters = _workOrdMaster.Select(a => weekPlan.Select(s=>s.ItemNumber).Contains(a.ItemNum) && weekPlan.Select(p=>p.OrderNO).Contains(a.WorkOrd) && a.Domain == input.factory_id.ToString()); weekPlan?.ForEach(a => { a.ProductionStatus = "已发布"; workMasters.ForEach(b => { if (b.ItemNum == a.ItemNumber && b.WorkOrd == a.OrderNO) { b.Priority = a.Priority; } }); }); _workOrdMaster.Update(workMasters); await _replenishmentWeekPlan.UpdateManyAsync(weekPlan); return "OK"; } /// /// 每天补货 /// /// /// public async Task CalcDayPlan(InputDto input) { ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString()); //DOP运行资源检查每个成品和半成品SKU的可用库存,当可用库存 <= 修正后ROP时,自动生成月度计划订单(MPlan order,MPO48年月日3位流水号)= EOP;可用库存为没有被其他SO和PO占用的(合格成品或半成品库存 + WIP); var ropModelList = _replenishmentROP.GetListAsync(a =>a.isparam && a.factory_id==input.factory_id && !a.IsDeleted).Result; var itemStock = CalcStock(ropModelList.Select(a => a.number).ToList(), input); var bomList = _ic_bom.GetListAsync(a => ropModelList.Select(a => a.number).ToList().Contains(a.item_number) && !a.IsDeleted && a.factory_id == input.factory_id).Result; List moList = new List(); List moentryList = new List(); var itemNeedPRList = ropModelList.Where(a => a.erp_cls == 1).ToList(); int count = 0; itemNeedPRList.ForEach(a => { if (itemStock.First(c => c.ItemNumber == a.number).Qty < a.rop_revised) { count++; } }); var list = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "MPO", count, "", 1); var nbrlistDto = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "M5", count, "", 1); int j = 0; DateTime currentTime = DateTime.Now; int week = Convert.ToInt32(currentTime.DayOfWeek); week = week == 0 ? 7 : week; long bang_id = help.NextId(); ropModelList.Where(a => a.erp_cls == 1)?.ToList().ForEach(b => { if(itemStock.First(c=>c.ItemNumber==b.number).Qty c.item_number ==b.number); mes_morder mes_Morder = new mes_morder(); mes_Morder.GenerateNewId(help.NextId()); mes_Morder.morder_type = MorderEnum.JhMorder; //mes_Morder.morder_icitem_type mes_Morder.work_order_type = MorderEnum.FhMorder; mes_Morder.morder_state = MorderEnum.Initial_state; mes_Morder.morder_no = list[j].NbrResult; mes_Morder.fms_number =b.number; mes_Morder.bom_number = bom.bom_number; mes_Morder.fmodel = b.model; mes_Morder.urgent = 1; mes_Morder.moentry_startup_status = 0; mes_Morder.tenant_id = input.tenant_id; mes_Morder.factory_id = input.factory_id; mes_Morder.company_id = input.company_id; mes_Morder.org_id = input.org_id; mes_Morder.product_code = b.number; mes_Morder.product_name = b.name; mes_Morder.morder_date = DateTime.Now.Date.AddDays(1); //成品半成品取周一开始 + 取供应提前期为结束时间 mes_Morder.moentry_stime = currentTime.AddDays(1 - week + 7); mes_Morder.moentry_sys_etime = currentTime.AddDays(1 - week + 7 + Convert.ToDouble(b.supply_leadtime)); //mes_Morder.morder_fstate = "计划"; //TODO:目前没有取值位置 mes_Morder.moentry_prd = null; mes_Morder.moentry_prdname = null; mes_Morder.moentry_wrkc = null; mes_Morder.moentry_wrkcname = null; mes_Morder.picking_qty = 0; //TODO:可删除主表字段 mes_Morder.unit = ""; mes_Morder.morder_production_number = b.eop; mes_Morder.need_number = b.eop; mes_Morder.remaining_number = 0; mes_Morder.create_time = DateTime.Now; mes_Morder.bang_id = bang_id; //生成工单子表数据 mes_moentry mes_Moentry = new mes_moentry(); mes_Moentry.GenerateNewId(help.NextId()); mes_Moentry.moentry_moid = mes_Morder.Id; mes_Moentry.moentry_mono = mes_Morder.morder_no; mes_Moentry.unit = ""; mes_Moentry.morder_production_number = b.eop; mes_Moentry.need_number = b.eop; mes_Moentry.remaining_number = 0; mes_Moentry.tenant_id = input.tenant_id; mes_Moentry.factory_id = input.factory_id; mes_Moentry.company_id = input.company_id; mes_Moentry.org_id = input.org_id; mes_Moentry.create_time = DateTime.Now; mes_Moentry.bang_id = bang_id; moList.Add(mes_Morder); moentryList.Add(mes_Moentry); j++; } }); await PlanOrderResourceCheck(moList, moentryList, bang_id, input); List routingOps = _routingOpDetail.Select(p => ropModelList.Where(a=>a.erp_cls==1).Select(a => a.number).ToList().Contains(p.RoutingCode) && p.Domain == input.factory_id.ToString() && p.IsActive); var examine_resultList = _mysql_examine_result.GetListAsync(a => a.bangid == bang_id).Result; //批量保存 后期考虑子工单 List workOrds = new List(); List workOrdRoutings = new List(); List workOrdDetails = new List(); if (moList.Any()) { moList.ForEach(s => { s.create_time = DateTime.Now; }); List childExamineList = _mysql_bom_child_examine.GetListAsync(c => examine_resultList.Select(x => x.Id).Contains(c.examine_id.GetValueOrDefault())).Result; //同步工单 CreateWorkOrdDates(moList, routingOps, workOrds, workOrdRoutings, workOrdDetails, examine_resultList, childExamineList); } if (workOrds.Any()) { workOrds = CalcPriority(workOrds, input.factory_id.ToString()); _workOrdMaster.Insert(workOrds); } var DBworkOrdList = _workOrdMaster.Select(a => a.Domain == workOrds[0].Domain && workOrds.Select(c => c.WorkOrd).Contains(a.WorkOrd)); if (workOrdRoutings.Any()) { workOrdRoutings.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdRouting.Insert(workOrdRoutings); } if (workOrdDetails.Any()) { workOrdDetails.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdDetail.Insert(workOrdDetails); } //成品半成品取下周一开始+取供应提前期为结束时间 //原材料取第二天开始+取供应提前期为结束时间 //DOP运行资源检查每个原材料SKU的可用库存,当可用库存<=修正后ROP时,自动生成月度采购申请(Purchase request,PR46年月日4位流水号)=EOP;可用库存为没有被其他SO和PO占用的(合格原材料库存+在途库存); var planList = _ic_item.GetListAsync().Result; List< srm_pr_main> prList = new List(); var numbers = ropModelList.Where(a => a.erp_cls == 3).Select(b => b.number).ToList(); var icItemList = _mysql_ic_item.GetListAsync(c => numbers.Contains(c.number)).Result; var srm_pr_mains = _srm_pr_main.GetListAsync(x => icItemList.Select(p => p.Id).ToList().Contains(x.icitem_id.Value) && x.state != 0 && !x.IsDeleted && x.state!=4).Result; var quarter_srm_pr_mains=srm_pr_mains.Where(s => s.pr_psend_date >= _CalcBomViewAppService.quarter_starttime && s.pr_psend_date < _CalcBomViewAppService.quarter_endtime).ToList(); ropModelList.Where(a => a.erp_cls ==3)?.ToList().ForEach(b => { if (itemStock.First(c => c.ItemNumber == b.number).Qty < b.rop_revised) { mo_srm_purchase supplier = null; var plan = planList.Find(x => x.number == b.number); var item = icItemList.Find(d => d.number == b.number); var supplist = supplierList.Where(s => s.number == b.number).ToList(); decimal day = 0; var plan_date = DateTime.Now.AddDays(1 + Convert.ToDouble(b.supply_leadtime)); //判断当前时间与交期还剩余多少天 var timesp = plan_date - DateTime.Now.Date; //交期-生产时长-物料前处理周期 day = timesp.Days; if (day > 0) { supplist = supplist.Where(s => s.lead_time.GetValueOrDefault() < day).ToList(); if (supplist.Any()) { //TODO:走配额管理 List itemPrlist = quarter_srm_pr_mains.Where(s => s.icitem_id == item.Id).ToList(); if (itemPrlist.Any()) { decimal qtyCount = itemPrlist.Sum(x => x.pr_aqty).GetValueOrDefault(); //按配额比例,计算使用哪个供应商。 List PrPurchaseDtoList = new List(); supplist.ForEach(s => { PrPurchaseDto purDto = new PrPurchaseDto(); purDto.pr_purchaseid = s.supplier_id; purDto.num = itemPrlist.Where(x => x.pr_purchaseid == s.supplier_id).Sum(c => c.pr_aqty.GetValueOrDefault()); purDto.ratio = Math.Round(purDto.num / qtyCount, 2, MidpointRounding.AwayFromZero); purDto.quota_priority = s.quota_priority; purDto.quota_rate = s.quota_rate.GetValueOrDefault(); PrPurchaseDtoList.Add(purDto); }); //首先取出占比还小于配额比的数据 var priorityPurlist = PrPurchaseDtoList.Where(s => s.quota_rate - s.ratio > 2).ToList(); if (!priorityPurlist.Any()) { //没有则取出当前集合中的供应商的数据,计算差额 priorityPurlist = PrPurchaseDtoList; } //还没有达到配额比的供应商,优先分配 decimal difference = 0; PrPurchaseDto dto1 = priorityPurlist[0]; priorityPurlist.ForEach(s => { decimal diff = s.quota_rate - s.ratio; if (diff > difference) { difference = diff; dto1 = s; } }); supplier = supplist.Find(s => s.supplier_id == dto1.pr_purchaseid); } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } srm_pr_main srm_Pr = new srm_pr_main(); srm_Pr.GenerateNewId(help.NextId()); srm_Pr.IsRequireGoods = supplier.IsRequireGoods; srm_Pr.pr_purchaseid = supplier.supplier_id;//供应商id srm_Pr.pr_purchasenumber = supplier.supplier_number;//供应商编码 srm_Pr.pr_purchasename = supplier.supplier_name;//供应商名称 srm_Pr.pr_purchaser = supplier.order_rector_name;//采购员 srm_Pr.pr_purchaser_num = supplier.order_rector_num;//采购员工号(采购信息表) //数量圆整 按最小包装量向上圆整 decimal qty =b.eop.Value; //判断最小起订量 qty = qty > supplier.qty_min ? qty : supplier.qty_min.GetValueOrDefault(); srm_Pr.pr_rqty = qty;//需求数量 srm_Pr.pr_aqty = qty;//申请数量 srm_Pr.pr_sqty = qty;//建议数量 srm_Pr.icitem_id = item.Id;//物料id srm_Pr.icitem_name = b.name;//物料名称 srm_Pr.pr_order_type = 1;//单据类型 srm_Pr.pr_ssend_date = DateTime.Now.Date.AddDays(1);//系统建议下单日期 srm_Pr.pr_sarrive_date = DateTime.Now.AddDays((double)supplier.lead_time.GetValueOrDefault());//系统建议到达日期(建议到货日期) srm_Pr.pr_sysprice = supplier.order_price;//系统价格(含税) srm_Pr.pr_orderprice = qty * supplier.order_price;//订单价格(含税) srm_Pr.pr_price = supplier.netpurchase_price;//采购净价(不含税) srm_Pr.pr_rate = supplier.taxrate;//税率 srm_Pr.pr_unit = item.unit;//单位 srm_Pr.state = 1;//状态 srm_Pr.old_apply_aqty = 0;//已申请数量 srm_Pr.pr_type = b.erp_cls;//申请类型 srm_Pr.currencytype = supplier.currency_type == null ? 1 : supplier.currency_type.Value;//币种 srm_Pr.secInv_ratio = plan.secinv_ratio;//安全库存触发采购比例 srm_Pr.tenant_id =input.tenant_id; srm_Pr.company_id = input.company_id; srm_Pr.factory_id = input.factory_id; srm_Pr.create_time = DateTime.Now; srm_Pr.create_by_name = "admin"; srm_Pr.update_time = DateTime.Now; srm_Pr.update_by_name = "admin"; prList.Add(srm_Pr); } }); prList.ForEach(a => { a.GenerateNewId(help.NextId()); }); await _mysql_srm_pr_main.InsertManyAsync(prList); await CalcDayPlanSafeStock(input); //每天DOP检查每个SKU的可用库存<=SS时,第二次触发PO或PR,每次补货数量=圆整后EOP;SKU可用库存=实际库存+WIP或在途库存-销售订单占用-计划订单占用;当M+1\M+2实际有销售订单产生时,相同SKU的销售订单和计划订单先进行冲销; //Step1:按照瑞奇、两大平台分别根据历史出库数据和预测出货数据计算ROP //Step2:计算瑞奇的M-M+2共12周补货(每次补EOP/4),计算海王的M-M+2共12周补货(每次补EOP/4),计算国科的M-M+2共12周补货(每次补EOP/4) //Step3:汇总Step2所有补货,为工厂的制造需求 //Step4:按照Step3制造需求做资源检查,按月统计各物料消耗预测 //Step5:按照各SKU的历史出库数据和Step4里计算的预测消耗数量计算参数 //Step6:计算各SKU的12周采购补货 return "OK"; } /// /// 每天补货 /// /// /// public async Task CalcDayPlanSafeStock(InputDto input) { ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString()); var ropModelList = _replenishmentROP.GetListAsync(a => a.isparam && a.factory_id == input.factory_id && !a.IsDeleted).Result; var itemStock = CalcStock(ropModelList.Select(a => a.number).ToList(), input); var bomList = _ic_bom.GetListAsync(a => ropModelList.Select(a => a.number).ToList().Contains(a.item_number) && !a.IsDeleted && a.factory_id == input.factory_id).Result; List moList = new List(); List moentryList = new List(); var itemNeedPRList = ropModelList.Where(a => a.erp_cls == 1).ToList(); int count = 0; itemNeedPRList.ForEach(a => { if (itemStock.First(c => c.ItemNumber == a.number).Qty < a.security_stock) { count++; } }); var list = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "MPO", count, "", 1); var nbrlistDto = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "M5", count, "", 1); int j = 0; DateTime currentTime = DateTime.Now; int week = Convert.ToInt32(currentTime.DayOfWeek); week = week == 0 ? 7 : week; long bang_id = help.NextId(); ropModelList.Where(a => a.erp_cls == 1)?.ToList().ForEach(b => { if (itemStock.First(c => c.ItemNumber == b.number).Qty < b.rop_revised) { var bom = bomList.Find(c => c.item_number == b.number); mes_morder mes_Morder = new mes_morder(); mes_Morder.GenerateNewId(help.NextId()); mes_Morder.morder_type = MorderEnum.JhMorder; //mes_Morder.morder_icitem_type mes_Morder.work_order_type = MorderEnum.FhMorder; mes_Morder.morder_state = MorderEnum.Initial_state; mes_Morder.morder_no = list[j].NbrResult; mes_Morder.fms_number = b.number; mes_Morder.bom_number = bom.bom_number; mes_Morder.fmodel = b.model; mes_Morder.urgent = 1; mes_Morder.moentry_startup_status = 0; mes_Morder.tenant_id = input.tenant_id; mes_Morder.factory_id = input.factory_id; mes_Morder.company_id = input.company_id; mes_Morder.org_id = input.org_id; mes_Morder.product_code = b.number; mes_Morder.product_name = b.name; mes_Morder.morder_date = DateTime.Now.Date.AddDays(1); //成品半成品取周一开始 + 取供应提前期为结束时间 mes_Morder.moentry_stime = currentTime.AddDays(1 - week + 7); mes_Morder.moentry_sys_etime = currentTime.AddDays(1 - week + 7 + Convert.ToDouble(b.supply_leadtime)); //mes_Morder.morder_fstate = "计划"; //TODO:目前没有取值位置 mes_Morder.moentry_prd = null; mes_Morder.moentry_prdname = null; mes_Morder.moentry_wrkc = null; mes_Morder.moentry_wrkcname = null; mes_Morder.picking_qty = 0; //TODO:可删除主表字段 mes_Morder.unit = ""; mes_Morder.morder_production_number = b.eop; mes_Morder.need_number = b.eop; mes_Morder.remaining_number = 0; mes_Morder.create_time = DateTime.Now; mes_Morder.bang_id = bang_id; //生成工单子表数据 mes_moentry mes_Moentry = new mes_moentry(); mes_Moentry.GenerateNewId(help.NextId()); mes_Moentry.moentry_moid = mes_Morder.Id; mes_Moentry.moentry_mono = mes_Morder.morder_no; mes_Moentry.unit = ""; mes_Moentry.morder_production_number = b.eop; mes_Moentry.need_number = b.eop; mes_Moentry.remaining_number = 0; mes_Moentry.tenant_id = input.tenant_id; mes_Moentry.factory_id = input.factory_id; mes_Moentry.company_id = input.company_id; mes_Moentry.org_id = input.org_id; mes_Moentry.create_time = DateTime.Now; mes_Moentry.bang_id = bang_id; moList.Add(mes_Morder); moentryList.Add(mes_Moentry); j++; } }); await PlanOrderResourceCheck(moList, moentryList, bang_id, input); List routingOps = _routingOpDetail.Select(p => ropModelList.Where(a => a.erp_cls == 1).Select(a => a.number).ToList().Contains(p.RoutingCode) && p.Domain == input.factory_id.ToString() && p.IsActive); var examine_resultList = _mysql_examine_result.GetListAsync(a => a.bangid == bang_id).Result; //批量保存 后期考虑子工单 List workOrds = new List(); List workOrdRoutings = new List(); List workOrdDetails = new List(); if (moList.Any()) { moList.ForEach(s => { s.create_time = DateTime.Now; }); List childExamineList = _mysql_bom_child_examine.GetListAsync(c => examine_resultList.Select(x => x.Id).Contains(c.examine_id.GetValueOrDefault())).Result; //同步工单 CreateWorkOrdDates(moList, routingOps, workOrds, workOrdRoutings, workOrdDetails, examine_resultList, childExamineList); } if (workOrds.Any()) { workOrds = CalcPriority(workOrds, input.factory_id.ToString()); _workOrdMaster.Insert(workOrds); } var DBworkOrdList = _workOrdMaster.Select(a => a.Domain == workOrds[0].Domain && workOrds.Select(c => c.WorkOrd).Contains(a.WorkOrd)); if (workOrdRoutings.Any()) { workOrdRoutings.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdRouting.Insert(workOrdRoutings); } if (workOrdDetails.Any()) { workOrdDetails.ForEach(c => { c.WorkOrdMasterRecID = DBworkOrdList.Where(a => a.WorkOrd == c.WorkOrd).First().RecID; }); _workOrdDetail.Insert(workOrdDetails); } //成品半成品取下周一开始+取供应提前期为结束时间 //原材料取第二天开始+取供应提前期为结束时间 //DOP运行资源检查每个原材料SKU的可用库存,当可用库存<=修正后ROP时,自动生成月度采购申请(Purchase request,PR46年月日4位流水号)=EOP;可用库存为没有被其他SO和PO占用的(合格原材料库存+在途库存); var planList = _ic_item.GetListAsync().Result; List prList = new List(); var numbers = ropModelList.Where(a => a.erp_cls == 3).Select(b => b.number).ToList(); var icItemList = _mysql_ic_item.GetListAsync(c => numbers.Contains(c.number)).Result; var srm_pr_mains = _srm_pr_main.GetListAsync(x => icItemList.Select(p => p.Id).ToList().Contains(x.icitem_id.Value) && x.state != 0 && !x.IsDeleted && x.state != 4).Result; var quarter_srm_pr_mains = srm_pr_mains.Where(s => s.pr_psend_date >= _CalcBomViewAppService.quarter_starttime && s.pr_psend_date < _CalcBomViewAppService.quarter_endtime).ToList(); ropModelList.Where(a => a.erp_cls == 3)?.ToList().ForEach(b => { if (itemStock.First(c => c.ItemNumber == b.number).Qty < b.security_stock) { mo_srm_purchase supplier = null; var plan = planList.Find(x => x.number == b.number); var item = icItemList.Find(d => d.number == b.number); var supplist = supplierList.Where(s => s.number == b.number).ToList(); decimal day = 0; var plan_date = DateTime.Now.AddDays(1 + Convert.ToDouble(b.supply_leadtime)); //判断当前时间与交期还剩余多少天 var timesp = plan_date - DateTime.Now.Date; //交期-生产时长-物料前处理周期 day = timesp.Days; if (day > 0) { supplist = supplist.Where(s => s.lead_time.GetValueOrDefault() < day).ToList(); if (supplist.Any()) { //TODO:走配额管理 List itemPrlist = quarter_srm_pr_mains.Where(s => s.icitem_id == item.Id).ToList(); if (itemPrlist.Any()) { decimal qtyCount = itemPrlist.Sum(x => x.pr_aqty).GetValueOrDefault(); //按配额比例,计算使用哪个供应商。 List PrPurchaseDtoList = new List(); supplist.ForEach(s => { PrPurchaseDto purDto = new PrPurchaseDto(); purDto.pr_purchaseid = s.supplier_id; purDto.num = itemPrlist.Where(x => x.pr_purchaseid == s.supplier_id).Sum(c => c.pr_aqty.GetValueOrDefault()); purDto.ratio = Math.Round(purDto.num / qtyCount, 2, MidpointRounding.AwayFromZero); purDto.quota_priority = s.quota_priority; purDto.quota_rate = s.quota_rate.GetValueOrDefault(); PrPurchaseDtoList.Add(purDto); }); //首先取出占比还小于配额比的数据 var priorityPurlist = PrPurchaseDtoList.Where(s => s.quota_rate - s.ratio > 2).ToList(); if (!priorityPurlist.Any()) { //没有则取出当前集合中的供应商的数据,计算差额 priorityPurlist = PrPurchaseDtoList; } //还没有达到配额比的供应商,优先分配 decimal difference = 0; PrPurchaseDto dto1 = priorityPurlist[0]; priorityPurlist.ForEach(s => { decimal diff = s.quota_rate - s.ratio; if (diff > difference) { difference = diff; dto1 = s; } }); supplier = supplist.Find(s => s.supplier_id == dto1.pr_purchaseid); } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } } else { supplier = supplist.OrderBy(s => s.quota_priority).FirstOrDefault();//默认取配额优先级 } srm_pr_main srm_Pr = new srm_pr_main(); srm_Pr.GenerateNewId(help.NextId()); srm_Pr.IsRequireGoods = supplier.IsRequireGoods; srm_Pr.pr_purchaseid = supplier.supplier_id;//供应商id srm_Pr.pr_purchasenumber = supplier.supplier_number;//供应商编码 srm_Pr.pr_purchasename = supplier.supplier_name;//供应商名称 srm_Pr.pr_purchaser = supplier.order_rector_name;//采购员 srm_Pr.pr_purchaser_num = supplier.order_rector_num;//采购员工号(采购信息表) //数量圆整 按最小包装量向上圆整 decimal qty = b.eop.Value; //判断最小起订量 qty = qty > supplier.qty_min ? qty : supplier.qty_min.GetValueOrDefault(); srm_Pr.pr_rqty = qty;//需求数量 srm_Pr.pr_aqty = qty;//申请数量 srm_Pr.pr_sqty = qty;//建议数量 srm_Pr.icitem_id = item.Id;//物料id srm_Pr.icitem_name = b.name;//物料名称 srm_Pr.pr_order_type = 1;//单据类型 srm_Pr.pr_ssend_date = DateTime.Now.Date.AddDays(1);//系统建议下单日期 srm_Pr.pr_sarrive_date = DateTime.Now.AddDays((double)supplier.lead_time.GetValueOrDefault());//系统建议到达日期(建议到货日期) srm_Pr.pr_sysprice = supplier.order_price;//系统价格(含税) srm_Pr.pr_orderprice = qty * supplier.order_price;//订单价格(含税) srm_Pr.pr_price = supplier.netpurchase_price;//采购净价(不含税) srm_Pr.pr_rate = supplier.taxrate;//税率 srm_Pr.pr_unit = item.unit;//单位 srm_Pr.state = 1;//状态 srm_Pr.old_apply_aqty = 0;//已申请数量 srm_Pr.pr_type = b.erp_cls;//申请类型 srm_Pr.currencytype = supplier.currency_type == null ? 1 : supplier.currency_type.Value;//币种 srm_Pr.secInv_ratio = plan.secinv_ratio;//安全库存触发采购比例 srm_Pr.tenant_id = input.tenant_id; srm_Pr.company_id = input.company_id; srm_Pr.factory_id = input.factory_id; srm_Pr.create_time = DateTime.Now; srm_Pr.create_by_name = "admin"; srm_Pr.update_time = DateTime.Now; srm_Pr.update_by_name = "admin"; prList.Add(srm_Pr); } }); prList.ForEach(a => { a.GenerateNewId(help.NextId()); }); await _mysql_srm_pr_main.InsertManyAsync(prList); return "OK"; } /// /// 同步工单等相关数据 /// /// /// 工艺路线数据 public void CreateWorkOrdDates(List morders, List allRoutings, List workOrds, List workOrdRoutings, List workOrdDetails, List exmResult, List childExamineList) { //工单主表 WorkOrdMaster workOrd; //工单工艺路线表 WorkOrdRouting woRouting; //工单物料表 WorkOrdDetail woDetail; foreach (var item in morders) { //添加工单数据 workOrd = new WorkOrdMaster(); workOrd.Domain = item.factory_id.ToString(); workOrd.OrdDate = item.moentry_sys_stime; workOrd.ReleaseDate = Convert.ToDateTime(item.moentry_sys_stime.GetValueOrDefault().ToString("u")); workOrd.WorkOrd = item.morder_no; workOrd.ItemNum = item.product_code;//物料编码 workOrd.QtyOrded = item.need_number.GetValueOrDefault(); workOrd.RoutingCode = item.product_code; workOrd.Period = 1; workOrd.Priority = item.urgent; workOrd.Status = " "; workOrd.IsActive = true; workOrd.IsConfirm = true; workOrd.CreateTime = DateTime.Now; workOrd.Batch = item.morder_no; workOrd.Typed = ""; workOrds.Add(workOrd); //添加工单工艺路线数据 var curRoutings = allRoutings.Where(p => p.RoutingCode == item.product_code).ToList(); foreach (var dtl in curRoutings) { woRouting = new WorkOrdRouting(); woRouting.Domain = item.factory_id.ToString(); woRouting.Descr = dtl.Descr; woRouting.MilestoneOp = dtl.MilestoneOp; woRouting.WorkOrd = item.morder_no; woRouting.OP = dtl.Op; woRouting.ParentOp = dtl.ParentOp; woRouting.RunTime = dtl.RunTime; woRouting.ItemNum = item.product_code; woRouting.QtyOrded = item.need_number.GetValueOrDefault(); woRouting.OverlapUnits = dtl.OverlapUnits; woRouting.Status = ""; woRouting.IsActive = true; woRouting.CommentIndex = dtl.CommentIndex; woRouting.CreateTime = DateTime.Now; workOrdRoutings.Add(woRouting); } var exm = exmResult.Find(s => s.morder_id == item.Id); if (exm != null) { var childs = childExamineList.Where(s => s.examine_id == exm.Id && s.is_use == true).ToList(); List returnList = new List(); GetWorkDetalis(childs.Where(s => s.level == 2).ToList(), returnList, childs); if (returnList.Any()) { foreach (var structure in returnList) { woDetail = workOrdDetails.Find(s => s.ItemNum == structure.item_number && s.WorkOrd == item.morder_no); if (woDetail == null) { //添加工单的物料信息 woDetail = new WorkOrdDetail(); woDetail.Domain = item.factory_id.ToString(); woDetail.WorkOrd = item.morder_no; woDetail.Op = structure.Op; woDetail.ItemNum = structure.item_number; woDetail.QtyRequired = structure.needCount.GetValueOrDefault(); woDetail.QtyPosted = 0m; woDetail.QtyReturned = 0m; woDetail.FrozenBOMQty = structure.needCount.GetValueOrDefault(); woDetail.Status = ""; woDetail.IsActive = true; woDetail.CreateTime = DateTime.Now; workOrdDetails.Add(woDetail); } else { woDetail.QtyRequired += structure.needCount.GetValueOrDefault(); woDetail.FrozenBOMQty += structure.needCount.GetValueOrDefault(); } } } } } } /// /// 获取虚拟件明细 /// /// /// /// public void GetWorkDetalis(List dtl, List returnList, List childs) { returnList.AddRange(dtl); var rst = childs.Where(s => dtl.Where(x => x.erp_cls == 4).Select(c => c.fid).Contains(s.parent_id.GetValueOrDefault())).ToList(); if (rst.Any()) { GetWorkDetalis(rst, returnList, childs); } return; } /// /// 根据工单编号排优先级 /// /// 工单编号列表 /// 工厂编码 /// //计算工单优先级 public List CalcPriority(List workOrdMasters,string domain) { var works= _workOrdMaster.Select(a => workOrdMasters.Contains(a.WorkOrd) && a.Domain == domain && a.IsActive); var saleWordOrd = works.Where(a => !string.IsNullOrEmpty(a.SalesJob)); Dictionary dict = new Dictionary(); var soList=_mysql_crm_seorder.GetListAsync(a=> saleWordOrd.Select(b=>b.SalesJob).Contains(a.bill_no)).Result; var custList = _custMaster.Select(a => soList.Select(b => b.custom_no).Contains(a.Cust) && a.Domain == domain); //T1海外客户 var T1CustList = custList.FindAll(a => a.Terms == "T1" || a.Terms == "海外").Select(a => a.Cust); //国科海王客户 var T2CustList = custList.FindAll(a => a.Terms == "国科" || a.Terms == "海王").Select(a => a.Cust); //T1海外订单 var T1SOList = soList.Where(a => T1CustList.Contains(a.custom_no)).Select(a => a.bill_no); //国科海王订单 var T2SOList = soList.Where(a => T2CustList.Contains(a.custom_no)).Select(a => a.bill_no); //T1海外工单 var T1WOList = saleWordOrd.Where(a => T1SOList.Contains(a.SalesJob)).ToList(); //国科海王工单 var T2WOList = saleWordOrd.Where(a => T2SOList.Contains(a.SalesJob)).ToList(); //在制工单 var inProductList = _mes_mooccupy.GetListAsync(a => works.Select(a => a.WorkOrd).Contains(a.moo_mo)).Result; //优先1:T1、海外销售订单新产生的生产指令; var T1WOListNew=T1WOList.Where(a=> !inProductList.Select(b=>b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a=>a.DueDate).ToList(); //优先2:T1、海外销售订单关联的在制生产指令; var T1WOListInProduct = T1WOList.Where(a => inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先3:平台(国科、海王)销售订单新产生的生产指令; var T2WOListNew = T2WOList.Where(a => !inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先4:平台(国科、海王)销售订单关联的在制生产指令; var T2WOListInProduct = T2WOList.Where(a => !inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先5:除销售订单以外,计划补货订单关联的生产指令; var PlanWOList = works.Except(T1WOListNew).ToList(); PlanWOList = works.Except(T1WOListInProduct).ToList(); PlanWOList = works.Except(T2WOListNew).ToList(); PlanWOList = works.Except(T2WOListInProduct).ToList(); PlanWOList= PlanWOList.OrderBy(a => a.DueDate).ToList(); //先按生产指令的类型,再按生产指令的完工日期顺序排产; List orderedList = new List(); decimal Priority = 1.0m; for (int i=0;i< T1WOListNew.Count;i++) { T1WOListNew[i].Priority = Priority; orderedList.Add(T1WOListNew[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T1WOListInProduct.Count; i++) { T1WOListInProduct[i].Priority = Priority; orderedList.Add(T1WOListInProduct[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T2WOListNew.Count; i++) { T2WOListNew[i].Priority = Priority; orderedList.Add(T2WOListNew[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T2WOListInProduct.Count; i++) { T2WOListInProduct[i].Priority = Priority; orderedList.Add(T2WOListInProduct[i]); Priority = Priority + 1.0m; } for (int i = 0; i < PlanWOList.Count; i++) { PlanWOList[i].Priority = Priority; orderedList.Add(PlanWOList[i]); Priority = Priority + 1.0m; } return orderedList; } /// /// 根据工单排优先级(用于先排优先级再写入的场景) /// /// 工单编号列表 /// 工厂编码 /// //计算工单优先级 public List CalcPriority(List works, string domain) { var saleWordOrd = works.Where(a => !string.IsNullOrEmpty(a.SalesJob)); Dictionary dict = new Dictionary(); var soList = _mysql_crm_seorder.GetListAsync(a => saleWordOrd.Select(b => b.SalesJob).Contains(a.bill_no)).Result; var custList = _custMaster.Select(a => soList.Select(b => b.custom_no).Contains(a.Cust) && a.Domain == domain); //T1海外客户 var T1CustList = custList.FindAll(a => a.Terms == "T1" || a.Terms == "海外").Select(a => a.Cust); //国科海王客户 var T2CustList = custList.FindAll(a => a.Terms == "国科" || a.Terms == "海王").Select(a => a.Cust); //T1海外订单 var T1SOList = soList.Where(a => T1CustList.Contains(a.custom_no)).Select(a => a.bill_no); //国科海王订单 var T2SOList = soList.Where(a => T2CustList.Contains(a.custom_no)).Select(a => a.bill_no); //T1海外工单 var T1WOList = saleWordOrd.Where(a => T1SOList.Contains(a.SalesJob)).ToList(); //国科海王工单 var T2WOList = saleWordOrd.Where(a => T2SOList.Contains(a.SalesJob)).ToList(); //在制工单 var inProductList = _mes_mooccupy.GetListAsync(a => works.Select(a => a.WorkOrd).Contains(a.moo_mo)).Result; //优先1:T1、海外销售订单新产生的生产指令; var T1WOListNew = T1WOList.Where(a => !inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先2:T1、海外销售订单关联的在制生产指令; var T1WOListInProduct = T1WOList.Where(a => inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先3:平台(国科、海王)销售订单新产生的生产指令; var T2WOListNew = T2WOList.Where(a => !inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先4:平台(国科、海王)销售订单关联的在制生产指令; var T2WOListInProduct = T2WOList.Where(a => !inProductList.Select(b => b.moo_mo).Contains(a.WorkOrd))?.OrderBy(a => a.DueDate).ToList(); //优先5:除销售订单以外,计划补货订单关联的生产指令; var PlanWOList = works.Except(T1WOListNew).ToList(); PlanWOList = works.Except(T1WOListInProduct).ToList(); PlanWOList = works.Except(T2WOListNew).ToList(); PlanWOList = works.Except(T2WOListInProduct).ToList(); PlanWOList = PlanWOList.OrderBy(a => a.DueDate).ToList(); //先按生产指令的类型,再按生产指令的完工日期顺序排产; List orderedList = new List(); decimal Priority = 1.0m; for (int i = 0; i < T1WOListNew.Count; i++) { T1WOListNew[i].Priority = Priority; orderedList.Add(T1WOListNew[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T1WOListInProduct.Count; i++) { T1WOListInProduct[i].Priority = Priority; orderedList.Add(T1WOListInProduct[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T2WOListNew.Count; i++) { T2WOListNew[i].Priority = Priority; orderedList.Add(T2WOListNew[i]); Priority = Priority + 1.0m; } for (int i = 0; i < T2WOListInProduct.Count; i++) { T2WOListInProduct[i].Priority = Priority; orderedList.Add(T2WOListInProduct[i]); Priority = Priority + 1.0m; } for (int i = 0; i < PlanWOList.Count; i++) { PlanWOList[i].Priority = Priority; orderedList.Add(PlanWOList[i]); Priority = Priority + 1.0m; } return orderedList; } /// /// 手动调整优先级 /// /// 工单编号列表 /// 工厂编码 /// //计算工单优先级 public async Task ChangePriority(string weekplanid, string tenant_id, string factory_id, string company_id, string itemNumber, string qty, string instockdate, string priority) { try { if (string.IsNullOrEmpty(qty)) return "数量不正确,请检查"; if (string.IsNullOrEmpty(instockdate)) return "成品入库日期不正确,请检查"; if (string.IsNullOrEmpty(priority)) return "优先级不正确,请检查"; decimal newpriority=Convert.ToDecimal(priority); DateTime newinstockdate=Convert.ToDateTime(instockdate); decimal newqty=Convert.ToDecimal(qty); long id= Convert.ToInt64(weekplanid); long tenantid=Convert.ToInt64(tenant_id); long factoryid=Convert.ToInt64(factory_id); long companyid=Convert.ToInt64(company_id); var weekPlan=_replenishmentWeekPlan.FindAsync(a => a.Id == id && a.tenant_id == tenantid && a.factory_id == factoryid && a.company_id == companyid).Result; var isExistSamePriority= _replenishmentWeekPlan.FindAsync(a => a.Id != id && a.Week == weekPlan.Week && a.Priority == newpriority).Result; if(isExistSamePriority!=null) return "优先级重复,请重新调整。"; weekPlan.Priority= newpriority; weekPlan.Qty= newqty; weekPlan.InStockDate= newinstockdate; await _replenishmentWeekPlan.UpdateAsync(weekPlan); } catch(Exception e) { return "调整失败,请联系管理员。"; } return "ok"; } /// /// 根据物料编码获取可用库存 /// 成品半成品可用库存为没有被其他SO和PO占用的(合格成品或半成品库存+WIP) /// 原材料可用库存为没有被其他SO和PO占用的(合格原材料库存+在途库存); /// /// 物料编码列表 /// public List CalcStock(List itemNumbers,InputDto input) { List itemQty = new List(); var itemList=_mysql_ic_item.GetListAsync(a => itemNumbers.Contains(a.number) && a.factory_id == input.factory_id && !a.IsDeleted).Result; var itemStockList=_mysql_ic_item_stock.GetListAsync(a => itemNumbers.Contains(a.icitem_number) && a.factory_id == input.factory_id && !a.IsDeleted).Result; var pooccupylist=_mysql_srm_po_occupy.GetListAsync(a => a.state == 1 && !a.IsDeleted && a.factory_id == input.factory_id ).Result; var poList = _mysql_srm_po_list.GetListAsync(a=> pooccupylist.Select(p=>p.polist_id).ToList().Contains(a.Id) && pooccupylist.Select(p => p.polist_row).ToList().Contains(a.polist_row) && a.factory_id == input.factory_id && !a.IsDeleted).Result; var molist=_mysql_mes_morder.GetListAsync(a => itemNumbers.Contains(a.product_code) && a.factory_id == input.factory_id && !a.IsDeleted).Result; var proccupylist=_mysql_srm_pr_main_occupy.GetListAsync(a=> molist.Select(p=>p.morder_no).ToList().Contains(a.morder_mo) && a.factory_id == input.factory_id && !a.IsDeleted).Result; itemNumbers.ForEach(a => { EOPDto eOPDto=new EOPDto(); eOPDto.ItemNumber = a; decimal stockqty = 0; if(itemStockList.Find(s => s.icitem_number == a)!=null) { stockqty = itemStockList.Find(s => s.icitem_number == a).sqty.Value; } decimal poOccupyQty = 0; for(int i=0;i /// 计划工单齐套检查 /// /// /// public async Task PlanOrderResourceCheck(List Mes_Morders, List moentryList,long bangid, InputDto input) { //先排除锁定期内的工单 //优先级排序 var sysSet = _generalizedCodeMaster.Select(s => s.FldName == "SystemConfig" && s.Val == "WorkOrderLockPeriod" && s.Domain == input.factory_id.ToString()).ToList(); decimal lookDay = 0; if (sysSet.Any()) { //锁定期 lookDay = sysSet[0].UDeci1; } lookDay = lookDay == 0 ? 7 : lookDay; DateTime lookTime = DateTime.Now.Date.AddDays((double)lookDay); Mes_Morders = Mes_Morders.Where(s => s.moentry_sys_stime > lookTime || s.moentry_sys_stime == null).OrderBy(s => s.moentry_sys_stime).ToList(); if (Mes_Morders.Any()) { Mes_Morders.ForEach(s => { s.moentry_sys_stime = DateTime.Now.Date.AddDays((double)lookDay + 1); }); List monolist = Mes_Morders.Select(c => c.morder_no).ToList(); //清理PR的占用。 //如果PR没有转PO,则PR没有合并,就删除,有合并,就减少合并后的PR的数量。 var prlist = _mysql_srm_pr_main.GetListAsync(s => monolist.Contains(s.pr_mono)).Result; List alllist = new List(); List updatelist = new List(); List dellist = new List(); RecursionGetDbPr2(prlist, alllist); foreach (var pr in prlist) { //找到没有关闭的PR,如果关联上的PR都是已关闭,则说明已经转了PO。 //TODO:解决Database operation expected to affect 1 row(s) but actually affected 0 row(s). 如果业务逻辑有问题自行修改 var getPr = RerunPr(pr, alllist); if (getPr != null) { if (getPr.pr_aqty - pr.pr_aqty <= 0) { //删除这个pr,没有小于0则是更新 dellist.Add(getPr); } else { getPr.pr_aqty = getPr.pr_aqty - pr.pr_aqty; updatelist.Add(getPr); } } } List occupy = await _mysql_srm_po_occupy.GetListAsync(s => monolist.Contains(s.morder_mo)); _businessDbContext.BulkDelete(occupy); if (updatelist.Any()) { _businessDbContext.BulkUpdate(updatelist); } if (dellist.Any()) { _businessDbContext.BulkDelete(dellist); } //清理掉库存占用 var itemstockoccupy = _mysql_ic_item_stockoccupy.GetListAsync(s => monolist.Contains(s.morder_mo)).Result; if (itemstockoccupy.Any()) { _businessDbContext.BulkDelete(itemstockoccupy); } //清理锁定期外的排程数据. var periodList = _periodSequenceDet.Select(s => monolist.Contains(s.WorkOrds)); if (periodList.Any()) { _periodSequenceDet.Delete(s => monolist.Contains(s.WorkOrds)); } var schedulList = _scheduleResultOpMaster.Select(s => monolist.Contains(s.WorkOrd)); if (schedulList.Any()) { _scheduleResultOpMaster.Delete(s => monolist.Contains(s.WorkOrd)); } //只走计划工单 var rtn = await OrderKittingCheck(Mes_Morders, moentryList, bangid, true); } else { return "未查找到对应的计划工单,请联系管理员。"; } return "ok"; } //找到没有关闭的PR,做数量的减少 public srm_pr_main RerunPr(srm_pr_main pr, List referlist) { //如果当前PR已经关闭,则检查合并数据 if (pr.state == 0) { //已关闭,并且没有关联数据,则代表转PO了。 var refPr = referlist.Find(s => s.pr_billno == pr.refer_pr_billno); //如果有关联,继续往下查 if (refPr != null) { return RerunPr(refPr, referlist); } else { return null; } } else { return pr; } } /// /// 递归寻找PR,找到合并后的PR /// public void RecursionGetDbPr2(List list, List referlist) { List prlist = _mysql_srm_pr_main.GetListAsync(s => list.Select(c => c.refer_pr_billno).Contains(s.pr_billno)).Result; if (prlist.Any()) { referlist.AddRange(prlist); RecursionGetDbPr2(prlist, referlist); } } /// /// 工单检查物料齐套 /// /// public async Task OrderKittingCheck(List mo_Mes_Morders, List moentryList,long bangid, bool planCheck = false) { //资源检查结果 PschedDto rtn = new PschedDto(); //资源检查明细list List examines = new List(); OrderCheckDto input = new OrderCheckDto(); if (mo_Mes_Morders.Any()) { input.company_id = mo_Mes_Morders[0].company_id; input.factoryId = mo_Mes_Morders[0].factory_id.GetValueOrDefault(); } //资源检查入参全局变量赋值 param.company_id = input.company_id; param.factoryId = input.factoryId; param.checkflag = false; param.checkPlan = planCheck; /*List mo_Mes_Morders = _mes_morder.GetManyByCondition(x => x.start_time > DateTime .Now.Date.AddDays(1) && x.start_time < DateTime .Now.Date.AddDays(input.Day) && x.morder_state == MorderEnum.Initial_state && !x.IsDeleted && x.tenant_id == param.tenantId && x.bang_id == bangid).Result; List mo_Mes_Moentries = _mes_moentry.GetManyByCondition(x=> mo_Mes_Morders.Select(s => s.id).Contains(x.moentry_moid.Value)).Result;*/ var morderIdList = mo_Mes_Morders.Select(s => s.Id).ToList(); //List mo_Mes_Moentries = _mysql_mes_moentry.GetListAsync(x => morderIdList.Contains(x.moentry_moid.Value)).Result; List mo_Mes_Moentries = moentryList; //获取订单行数据 List sentrys = _mysql_crm_seorderentry.GetListAsync(p => p.factory_id == input.factoryId && !p.IsDeleted && mo_Mes_Moentries.Select(x => x.soentry_id).Contains(p.Id)).Result; //删除同步Mysql后旧数据 await DeleteMySqlOldData(sentrys); List boms = _ic_bom.GetListAsync(p => mo_Mes_Morders.Select(m => m.bom_number).Contains(p.bom_number) && p.factory_id == input.factoryId && !p.IsDeleted).Result.ToList(); //物料bom List bomlist = new List(); //物料bom明细 List bomchildlist = new List(); //物料信息 List icitemlist = new List(); //替代关系 List sublist = new List(); List suballlist = new List(); List subdtllist = new List(); //物料库存表 List stocklist = new List(); //物料占用记录 List sklist = new List(); //sorder=null 是因为齐套检查不需要生成工单、采购、委外等信息,所以不需要关联的工单信息传递进去。 var pretreatments = _mysql_b_bom_pretreatment.GetListAsync(s => boms.Select(c => c.mysql_id).ToList().Contains(s.sourceid)).Result; List autoCreates = new List(); boms.ForEach(p => { if (!pretreatments.Where(s => s.sourceid == p.mysql_id).Any()) { autoCreates.Add(p); } }); if (autoCreates.Any()) { AutoCreateBomBill(param.company_id.ToString(), autoCreates); pretreatments = _mysql_b_bom_pretreatment.GetListAsync(s => boms.Select(c => c.mysql_id).ToList().Contains(s.sourceid)).Result; } //数据库快照-同步mysql库数据到mongoDB中 await DbSnapShot(input.company_id, input.factoryId, bangid, pretreatments); DataInitialization(boms, bangid, icitemlist, stocklist, pretreatments, sklist); if (!planCheck) { //如果是齐套检查,则不考虑其他数据的占用,只看原材料库存 sklist = new List(); } _CalcBomViewAppService.param = param; foreach (var item in mo_Mes_Morders) { var moentry = mo_Mes_Moentries.Find(s => s.moentry_moid == item.Id); if (moentry == null) { continue; } var sentry = sentrys.Find(s => s.Id == moentry.soentry_id); //工单资源检查信息 ExamineResult dtl = new ExamineResult(); dtl.morder_id = item.Id; dtl.morder_no = item.morder_no; dtl.bangid = bangid; dtl.order_statr_time = item.start_time; dtl.bom_number = item.bom_number; dtl.need_qty = item.need_number.GetValueOrDefault(); //获取当前物料bom数据 var childBom = boms.Where(p => p.bom_number == item.bom_number).FirstOrDefault(); var itemPrelist = pretreatments.Where(s => s.sourceid == childBom.mysql_id).ToList(); if (!itemPrelist.Any()) { continue; } var getBomList = ObjectMapper.Map, List>(itemPrelist); if (sentry != null) { dtl.sentry_id = sentry.Id; getBomList.ForEach(s => s.sentry_id = item.Id); } _CalcBomViewAppService.newStockOccList = new List(); _CalcBomViewAppService.mes_morder = item; //库存初始化 _CalcBomViewAppService.BomStock(getBomList, stocklist, bangid); //计算 _CalcBomViewAppService.CalcView(getBomList, bangid, item.need_number.GetValueOrDefault(), item.moentry_sys_stime, sklist, sentry, icitemlist); //TODO:最晚开始时间 var curFacDtl = leadTimeList.FirstOrDefault(p => p.item_id == childBom.icitem_id); //物料齐套时间 dtl.kitting_times = getBomList.Where(p => p.is_use && p.kitting_time != null).OrderByDescending(m => m.kitting_time).First().kitting_time.GetValueOrDefault(); //替代关系展开list dtl.BomChildExamineList = getBomList; //添加订单行开工信息 examines.Add(dtl); } rtn.examines = examines; if (planCheck) { //如果有计划工单,则需要生成pr po oo List prmainlist = new List(); if (_CalcBomViewAppService.SRMPRDtoList.Any()) { List> prlist = _CalcBomViewAppService.SRMPRDtoList.Where(f => f.srm_Pr_Main != null).Select(s => s.srm_Pr_Main).ToList(); List list = new List(); foreach (var pr in prlist) { foreach (var item in pr) { list.Add(item); prmainlist.Add(item); } } if (list.Any()) rtn.srm_pr_list = _CalcBomViewAppService.SRMPRDtoList; } //获取工单数据 var workOrdMasters = _workOrdMaster.Select(p => mo_Mes_Morders.Select(c => c.morder_no).Contains(p.WorkOrd)); workOrdMasters.ForEach(s => { var mo = mo_Mes_Morders.Find(m => m.morder_no == s.WorkOrd); if (mo != null) { //锁定期 s.OrdDate = mo.moentry_sys_stime; s.DueDate = mo.moentry_sys_stime; } }); //优先级排序 workOrdMasters = workOrdMasters.OrderBy(s => s.OrdDate).ToList(); //排产 //await _productionScheduleAppService.DoProductSchedule(workOrdMasters, param.factoryId.ToString()); foreach (var wod in workOrdMasters) { var morder = mo_Mes_Morders.Find(s => s.morder_no == wod.WorkOrd); morder.moentry_sys_stime = wod.OrdDate; morder.moentry_sys_etime = wod.DueDate; /*var exa = rtn.examines.Find(s => s.morder_no == wod.WorkOrd); exa.latest_times = morder.moentry_sys_etime.GetValueOrDefault().Date.AddDays(1); //根据排产后得日期,反推PR。 var wkordPrList = prmainlist.Where(s => s.pr_mono == wod.WorkOrd).ToList(); //反算所有的PR,根据时间减去提前期 //按最大预处理时间倒排 var moIcitems = icitemlist.Where(s => wkordPrList.Select(c => c.icitem_id).Contains(s.mysql_id)).ToList(); var maxTime = moIcitems.Max(s => s.clean_leadtime.GetValueOrDefault()); wkordPrList.ForEach(pr => { var ts = pr.pr_parrive_date - pr.pr_psend_date; //var icitem = icitemlist.Find(s => s.mysql_id == pr.icitem_id); //var bce = exa.BomChildExamineList.Find(s => s.item_id == pr.icitem_id && s.num == pr.num); pr.pr_sarrive_date = morder.moentry_sys_stime.Value.AddDays((double)(0 - maxTime - 1));//到货还需要向前推 物料的预处理时间。到货日期必须提前一天到 pr.pr_ssend_date = pr.pr_sarrive_date.GetValueOrDefault().AddDays(0 - ts.Value.Days); if (pr.pr_ssend_date < DateTime.Now.Date.AddDays(1)) { pr.pr_ssend_date = DateTime.Now.Date.AddDays(1); } });*/ } using (var unitOfWork = _unitOfWorkManager.Begin(false, true)) { try { _businessDbContext.BulkUpdate(mo_Mes_Morders); if (prmainlist.Any()) { var pr_mainlist = ObjectMapper.Map, List>(prmainlist); _businessDbContext.BulkInsert(pr_mainlist); } if (_CalcBomViewAppService.newStockOccList.Any()) { var stockoccupylist = ObjectMapper.Map, List>(_CalcBomViewAppService.newStockOccList); stockoccupylist.ForEach(s => { s.create_time = DateTime.Now; s.tenant_id = param.company_id.GetValueOrDefault(); s.company_id = param.company_id; s.factory_id = param.factoryId; }); _businessDbContext.BulkInsert(stockoccupylist); } if (_CalcBomViewAppService.srm_Po_OccupiesInsert.Any()) { _CalcBomViewAppService.srm_Po_OccupiesInsert.ForEach(s => { s.company_id = param.company_id; s.factory_id = param.factoryId; }); await _srm_po_occupy.InsertManyAsync(_CalcBomViewAppService.srm_Po_OccupiesInsert); } await unitOfWork.CompleteAsync(); } catch (Exception e) { unitOfWork.Dispose(); new NLogHelper("ResourceExamineAppService").WriteLog("OrderKittingCheck", "工单检查数据更新失败:" + e.Message, _currentTenant.Id.ToString()); } } } //检查结果写入数据库 await ExamineResultInsertDBAsync(examines); //清空快照数据 await ClearSnapShot(bangid); return rtn; } public async Task ExamineResultInsertDBAsync(List examines) { //检查结果写入数据库 List examineList = new List(); List bomExamineList = new List(); List mooccupyList = new List(); List moorderList = new List(); List ooderList = new List(); List purchaseList = new List(); List purchaseoccupyList = new List(); foreach (var ex in examines) { var b_ex = ObjectMapper.Map(ex); b_ex.GenerateNewId(help.NextId());// = help.NextId(); if (_CalcBomViewAppService.mordersInsertList.Any()) { var moentry = _CalcBomViewAppService.moentriesInsertList.Where(s => s.soentry_id == b_ex.sentry_id).FirstOrDefault(); if (moentry != null) { var sentryMo = _CalcBomViewAppService.mordersInsertList.Where(s => s.mysql_id == moentry.moentry_moid).FirstOrDefault(); if (sentryMo != null) { b_ex.morder_id = sentryMo.mysql_id; b_ex.morder_no = sentryMo.morder_no; } } } b_ex.create_time = DateTime.Now; b_ex.company_id = param.company_id; b_ex.tenant_id = param.company_id; b_ex.factory_id = param.factoryId; examineList.Add(b_ex); ex.BomChildExamineList.ForEach(s => { var bc_ex = ObjectMapper.Map(s); bc_ex.GenerateNewId(help.NextId()); bc_ex.examine_id = b_ex.Id; bc_ex.company_id = param.company_id; bc_ex.tenant_id = param.company_id; bc_ex.factory_id = param.factoryId; bc_ex.create_time = DateTime.Now; bomExamineList.Add(bc_ex); if (s.mo_occupy_list != null) { var olist = ObjectMapper.Map, List>(s.mo_occupy_list); olist.ForEach(o => { o.bom_child_examine_id = bc_ex.Id; o.company_id = param.company_id; o.tenant_id = param.company_id; o.factory_id = param.factoryId; }); mooccupyList.AddRange(olist); } if (s.make_list != null) { var mlist = ObjectMapper.Map, List>(s.make_list); mlist.ForEach(o => { o.bom_child_examine_id = bc_ex.Id; o.company_id = param.company_id; o.tenant_id = param.company_id; o.factory_id = param.factoryId; }); moorderList.AddRange(mlist); } if (s.subcontracting_list != null) { var slist = ObjectMapper.Map, List>(s.subcontracting_list); slist.ForEach(o => { o.bom_child_examine_id = bc_ex.Id; o.company_id = param.company_id; o.tenant_id = param.company_id; o.factory_id = param.factoryId; }); ooderList.AddRange(slist); } if (s.purchase_list != null) { var plist = ObjectMapper.Map, List>(s.purchase_list); plist.ForEach(o => { o.bom_child_examine_id = bc_ex.Id; o.company_id = param.company_id; o.tenant_id = param.company_id; o.factory_id = param.factoryId; }); purchaseList.AddRange(plist); } if (s.purchase_occupy_list != null) { var purlist = ObjectMapper.Map, List>(s.purchase_occupy_list); purlist.ForEach(o => { o.bom_child_examine_id = bc_ex.Id; o.company_id = param.company_id; o.tenant_id = param.company_id; o.factory_id = param.factoryId; }); purchaseoccupyList.AddRange(purlist); } }); } using (var unitOfWork = _unitOfWorkManager.Begin(false, true)) { try { if (examineList.Any()) { _businessBangDbContext.BulkInsert(examineList); } if (bomExamineList.Any()) { _businessBangDbContext.BulkInsert(bomExamineList.OrderBy(s => s.num_order).ToList()); } if (mooccupyList.Any()) { _businessBangDbContext.BulkInsert(mooccupyList); } if (moorderList.Any()) { _businessBangDbContext.BulkInsert(moorderList); } if (ooderList.Any()) { _businessBangDbContext.BulkInsert(ooderList); } if (purchaseList.Any()) { _businessBangDbContext.BulkInsert(purchaseList); } if (purchaseoccupyList.Any()) { _businessBangDbContext.BulkInsert(purchaseoccupyList); } await unitOfWork.CompleteAsync(); } catch (Exception e) { new NLogHelper("ResourceExamineAppService").WriteLog("ReceiveResult", "资源检查计算结果相关数据更新失败:" + e.Message, _currentTenant.Id.ToString()); unitOfWork.Dispose(); } } } /// /// 删除旧订单行数据 /// /// /// /// /// public async Task DeleteMySqlOldData(List soentry_id) { List sentryids = soentry_id.Select(p => p.Id).ToList(); //删除工单相关表数据 /*var mes_moentry = _mysql_mes_moentry.GetListAsync(x => sentryids.Contains(x.soentry_id.Value)).Result; var mes_morder = _mysql_mes_morder.GetListAsync(x => mes_moentry.Select(p => p.moentry_moid).ToList().Contains(x.Id)).Result; if (mes_moentry.Count > 0) { await _mysql_mes_moentry.DeleteManyAsync(mes_moentry); } if (mes_morder.Count > 0) { await _mysql_mes_morder.DeleteManyAsync(mes_morder); }*/ var mysql_mes_mooccupy = _mysql_mes_mooccupy.GetListAsync(x => soentry_id.Select(p => p.Id).Contains(x.moo_id_billid.Value)).Result; if (mysql_mes_mooccupy.Count > 0) { await _mysql_mes_mooccupy.DeleteManyAsync(mysql_mes_mooccupy); } /*var srm_pr_main = _mysql_srm_pr_main.GetListAsync(x => soentry_id.Select(p => p.Id).Contains(x.sentry_id.GetValueOrDefault())).Result; if (srm_pr_main.Count > 0) { await _mysql_srm_pr_main.DeleteManyAsync(srm_pr_main); }*/ /*var mes_oorders = _mysql_mes_oorder.GetListAsync(x => soentry_id.Select(p => p.Id).Contains(x.sentry_id.GetValueOrDefault())).Result; if (mes_oorders.Count > 0) { await _mysql_mes_oorder.DeleteManyAsync(mes_oorders); }*/ } /// /// 数据库快照 /// /// public async Task DbSnapShot(long? companyId, long factoryId, long bangid, List pretreatments) { //TODO:申老师明确后续需要调整 根据需要使用的字段,来同步表数据。 //同步物料库存数据 根据预处理,来只找出部分数据同步。 List itemIds = pretreatments.Select(s => s.item_id.GetValueOrDefault()).ToList(); //var icitemStokc = _mysql_ic_item_stock.GetListAsync(p => p.tenant_id == tenantId && p.factory_id == factoryId).Result; var icitemStokc = _mysql_ic_item_stock.GetListAsync(p => p.company_id == companyId && itemIds.Contains(p.icitem_id)).Result; if (icitemStokc.Count > 0) { List numbers = pretreatments.Select(s => s.item_number).ToList(); var locStock = _locationDetail.Select(a => numbers.Contains(a.ItemNum) && a.IsActive && a.Domain == factoryId.ToString()); //设置当前计算bangid icitemStokc.ForEach(item => { item.bang_id = bangid; var pret = pretreatments.Find(s => s.item_id == item.icitem_id); if (pret != null) { item.sqty = locStock.Where(s => s.ItemNum == pret.item_number).Sum(p => p.QtyOnHand); } }); var moIcitemStokc = ObjectMapper.Map, List>(icitemStokc); moIcitemStokc.ForEach(item => { item.GenerateNewId(help.NextId()); }); //插入数据 await _ic_item_stock.InsertManyAsync(moIcitemStokc); } var workordmsters = _workOrdMaster.Select(s => s.Domain == factoryId.ToString() && s.Status.ToLower() != "c"); if (workordmsters.Any()) { //根据工单表找到工单明细,然后根据DOP工单占用记录,对比工单明细发货数量,来做冲销。 var workdetails = _workOrdDetail.Select(s => workordmsters.Select(c => c.WorkOrd).Contains(s.WorkOrd)); //物料库存占用记录表 var item_occupy = _mysql_ic_item_stockoccupy.GetListAsync(p => p.company_id == companyId && itemIds.Contains(p.icitem_id) && workordmsters.Select(c => c.WorkOrd).Contains(p.morder_mo)).Result; if (item_occupy.Any()) { item_occupy.ForEach(s => { //TODO:对发布数量和占用数量进行对冲,因为目前没有处理不同层级下使用相同物料的占用的问题。dop占用有项次号,而wms的发布数量明细没有项次号 var wdtl = workdetails.Where(x => x.WorkOrd == s.morder_mo && x.ItemNum == s.icitem_number).ToList(); if (wdtl.Any()) { foreach (var dtl in wdtl) { if (s.quantity - dtl.QtyPosted > 0) { s.quantity -= dtl.QtyPosted; dtl.QtyPosted = 0; } else { dtl.QtyPosted -= s.quantity; s.quantity = 0; } } } }); item_occupy = item_occupy.Where(s => s.quantity > 0).ToList(); if (item_occupy.Count > 0) { var mo_item_occupy = ObjectMapper.Map, List>(item_occupy); mo_item_occupy.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _ic_item_stockoccupy.InsertManyAsync(mo_item_occupy); } } //工单主表 var mes_morder = _mysql_mes_morder.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId && workordmsters.Select(c => c.WorkOrd).Contains(x.morder_no)).Result; if (mes_morder.Count > 0) { mes_morder.ForEach(item => { item.bang_id = bangid; }); var moMes_morder = ObjectMapper.Map, List>(mes_morder); moMes_morder.ForEach(item => { item.GenerateNewId(help.NextId()); }); await _mes_morder.InsertManyAsync(moMes_morder); } //工单子表 var mes_moentry = _mysql_mes_moentry.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId).Result; if (mes_moentry.Count > 0) { mes_moentry.ForEach(item => { item.bang_id = bangid; }); var moMes_moentry = ObjectMapper.Map, List>(mes_moentry); moMes_moentry.ForEach(item => { item.GenerateNewId(help.NextId()); }); await _mes_moentry.InsertManyAsync(moMes_moentry); } //在制工单占用记录表 var mes_mooccupy = _mysql_mes_mooccupy.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId && mes_morder.Select(c => c.morder_no).Contains(x.moo_mo)).Result; if (mes_mooccupy.Count > 0) { var moMes_mooccupy = ObjectMapper.Map, List>(mes_mooccupy); moMes_mooccupy.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _mes_mooccupy.InsertManyAsync(moMes_mooccupy); } } //TODO:要不要根据某些条件只同步有效的数据 //采购订单 var srm_po_main = _mysql_srm_po_main.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId).Result; if (srm_po_main.Count > 0) { var moSrm_po_main = ObjectMapper.Map, List>(srm_po_main); moSrm_po_main.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _srm_po_main.InsertManyAsync(moSrm_po_main); } //采购订单明细 var srm_po_list = _mysql_srm_po_list.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId).Result; if (srm_po_list.Count > 0) { var moSrm_po_list = ObjectMapper.Map, List>(srm_po_list); moSrm_po_list.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _srm_po_list.InsertManyAsync(moSrm_po_list); } //采购订单占用详情 var srm_po_occupy = _mysql_srm_po_occupy.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId).Result; if (srm_po_occupy.Count > 0) { var moSrm_po_occupy = ObjectMapper.Map, List>(srm_po_occupy); moSrm_po_occupy.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _srm_po_occupy.InsertManyAsync(moSrm_po_occupy); } //pr var srm_pr_main = _mysql_srm_pr_main.GetListAsync(x => x.company_id == companyId && x.factory_id == factoryId && itemIds.Contains(x.icitem_id.GetValueOrDefault())).Result; if (srm_pr_main.Count > 0) { var moSrm_pr_main = ObjectMapper.Map, List>(srm_pr_main); moSrm_pr_main.ForEach(item => { item.GenerateNewId(help.NextId()); item.bang_id = bangid; }); await _srm_pr_main.InsertManyAsync(moSrm_pr_main); } } /// /// 清处数据库快照 /// /// public async Task ClearSnapShot(long bangid) { //清除物料库存数据 await _ic_item_stock.DeleteAsync(p => p.bang_id == bangid); //清除工单占用记录表 //await _mes_mooccupy.DeleteAsync(p => p.bang_id == bangid); //清除工单主表 //await _mes_morder.Delete(p => p.bang_id == bangid); //清除工单子表 //await _mes_moentry.Delete(p => p.bang_id == bangid); //清除采购订单 await _srm_po_main.DeleteAsync(p => p.bang_id == bangid); //清除采购订单明细 await _srm_po_list.DeleteAsync(p => p.bang_id == bangid); //清除采购订单占用详情 //await _srm_po_occupy.DeleteAsync(p => p.bang_id == bangid); //清除PR await _srm_pr_main.DeleteAsync(p => p.bang_id == bangid); } /// /// 物料数据计算前准备 /// /// /// /// /// /// /// /// /// /// /// /// /// public void DataInitialization(List boms, long bangid, List icitemlist, List stocklist, List pretreatments, List sklist) { List itemIds = pretreatments.Select(p => p.item_id.GetValueOrDefault()).ToList(); icitemlist.AddRange(_ic_item.GetListAsync(p => itemIds.Contains(p.mysql_id) && !p.IsDeleted).Result); #region 1、数据准备 //1.1、获取产能检查相关数据 //产品物料编码 List itemnums = pretreatments.Where(p => p.level == 1).Select(p => p.item_number).Distinct().ToList(); //产线明细 prodLines = _prodLineDetail.Select(p => itemnums.Contains(p.Part)).ToList(); //标准工序 routingOps = _routingOpDetail.Select(p => itemnums.Contains(p.RoutingCode)).ToList(); List lines = prodLines.Select(p => p.Line).Distinct().ToList(); //工作日历 calendarWorks = _shopCalendarWorkCtr.Select(p => lines.Contains(p.ProdLine)).ToList(); //休息日 qualityLineWorks = _qualityLineWorkDetail.Select(p => lines.Contains(p.ProdLine)).ToList(); //节假日 holidays = _holidayMaster.Select(p => p.Dated >= DateTime.Now.Date); //主工单 List mo_Mes_Morders = _mes_morder.GetListAsync(x => boms.Select(p => p.bom_number).Contains(x.bom_number) && (x.morder_state != MorderEnum.Accomplish_state || x.morder_state != MorderEnum.Close_state && x.morder_icitem_type != MorderEnum.XgwyMorder) && x.company_id == param.company_id && x.bang_id == bangid).Result; List moids = mo_Mes_Morders.Select(p => p.mysql_id).ToList(); List mo_Mes_Moentry = _mes_moentry.GetListAsync(x => moids.Contains(x.moentry_moid)).Result; //工单占用表 List mes_mooccupyList = _mes_mooccupy.GetListAsync(x => x.moo_state == 1 && !x.IsDeleted && x.company_id == param.company_id && boms.Select(p => p.item_number).Contains(x.fitem_number) && x.bang_id == bangid).Result; //物料采购订单明细 var poDetailList = _srm_po_list.GetListAsync(x => icitemlist.Select(p => p.mysql_id).ToList().Contains(x.icitem_id.Value) && x.bang_id == bangid && x.state == 1 && x.rstate == 1 && !x.IsDeleted).Result; //采购订单占用数据 var poOccupys = _srm_po_occupy.GetListAsync(x => poDetailList.Select(p => p.Id).ToList().Contains(x.polist_id) && x.bang_id == bangid && !x.IsDeleted).Result; var srm_pr_mains = _srm_pr_main.GetListAsync(x => icitemlist.Select(p => p.mysql_id).ToList().Contains(x.icitem_id.Value) && x.bang_id == bangid && x.state == 0 && !x.IsDeleted).Result; #endregion //处理一下已经插入得占用记录 //物料库存 stocklist.AddRange(_ic_item_stock.GetListAsync(p => p.factory_id == param.factoryId && p.bang_id == bangid && itemIds.Contains(p.icitem_id)).Result); //物料库存占用表 sklist.AddRange(_ic_item_stockoccupy.GetListAsync(x => x.bang_id == bangid && !x.IsDeleted).Result); //物料提前期 leadTimeList = GetLeadTime(icitemlist, param.company_id, param.factoryId);//提前期列表 supplierList = GetSupplier(itemIds, param.company_id, param.factoryId);//供应商列表 _CalcBomViewAppService.leadTimeList = leadTimeList; _CalcBomViewAppService.supplierList = supplierList; //资源检查添加产线工序等数据 _CalcBomViewAppService.prodLines = prodLines; _CalcBomViewAppService.routingOps = routingOps; _CalcBomViewAppService.calendarWorks = calendarWorks; _CalcBomViewAppService.qualityLineWorks = qualityLineWorks; _CalcBomViewAppService.holidays = holidays; _CalcBomViewAppService.mordersList = mo_Mes_Morders;//工单 _CalcBomViewAppService.moentriesList = mo_Mes_Moentry;//工单子表 _CalcBomViewAppService.mooccupyAllList = mes_mooccupyList;//工单占用表 _CalcBomViewAppService.srm_Po_Lists = poDetailList; //采购明细 _CalcBomViewAppService.srm_Po_Occupies = poOccupys; //采购占用表 _CalcBomViewAppService.ic_item_List = icitemlist; //物料表 _CalcBomViewAppService.srm_Pr_Mains = srm_pr_mains;//PR } public void GenerateSort(List returnlist) { int level = returnlist.Max(s => s.level); returnlist.ForEach(s => { int num = level - s.level; if (num > 0) { s.num_order = long.Parse((s.num_order * Math.Pow(1000, num)).ToString()); } }); } /// /// 根据物料id获取物料4个提前期 /// /// 物料id /// 企业id /// 工厂id /// private List GetLeadTime(List icItemIds, long? companyId, long factoryid) { return icItemIds.Where(p => p.factory_id == factoryid && p.company_id == companyId && !p.IsDeleted). Select(x => new ICItemLeadTimeDto { item_id = x.mysql_id, transportation_leadtime = x.transportation_leadtime, stock_leadtime = x.stock_leadtime, production_leadtime = x.production_leadtime, order_leadtime = x.order_leadtime, minorderqty = x.minorderqty, put_integer = x.put_integer, minpackqty = x.minpackqty, ordissu_days = x.ordissu_days }).AsQueryable().ToList(); } //根据物料id获取物料供应商 private List GetSupplier(List icItemIds, long? companyId, long factoryid) { return _srm_purchase.GetListAsync(p => icItemIds.Contains(p.icitem_id) && p.company_id == companyId && p.factory_id == factoryid && !p.IsDeleted).Result; } /// /// 物料和BOM前置数据准备 /// /// /// /// /// /// /// /// public void IcItemPretreatment(List boms, List bomlist, List bomchildlist, List icitemlist, List sublist, List suballlist, List subdtllist) { //获取物料bom,物料bom明细 GetIcBomData(boms, bomlist, bomchildlist); //根据明细集合查出所有得替代关系表数据集合 List codeList = bomchildlist.Select(c => c.substitute_code).Distinct().ToList(); sublist.AddRange(_ic_substitute.GetListAsync(p => codeList.Contains(p.substitute_code) && !p.IsDeleted).Result); List subidlist = sublist.Select(c => c.mysql_id).Distinct().ToList(); suballlist.AddRange(_ic_substitute_group.GetListAsync(p => subidlist.Contains(p.substitute_group_id) && !p.IsDeleted).Result); List suballidlist = suballlist.Select(c => c.mysql_id).Distinct().ToList(); subdtllist.AddRange(_ic_substitute_group_detail.GetListAsync(p => suballidlist.Contains(p.substitute_group_id) && !p.IsDeleted).Result); //获取物料数据 List itemIds = bomlist.Select(p => p.icitem_id).ToList(); itemIds.AddRange(bomchildlist.Select(p => p.icitem_id).ToList()); itemIds.AddRange(subdtllist.Select(p => p.icitem_id).ToList()); icitemlist.AddRange(_ic_item.GetListAsync(p => itemIds.Contains(p.mysql_id) && !p.IsDeleted).Result); } /// /// 递归:获取icbom,icbomchild数据 /// /// /// /// public void GetIcBomData(List icBoms, List bomlist, List bomchildlist) { if (icBoms.Count == 0) { return; } //添加物料bom数据 foreach (var ib in icBoms) { if (!bomlist.Exists(s => s.mysql_id == ib.mysql_id)) { bomlist.Add(ib); } } //获取物料bom明细数据 //List childList = _ic_bom_child.GetManyByCondition(p => icBoms.Select(m => m.mysql_id).Contains(p.bom_id) && p.use_status == 1 && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted).Result.ToList(); List childList = _ic_bom_child.GetListAsync(p => icBoms.Select(m => m.mysql_id).Contains(p.bom_id) && p.company_id == param.company_id && !p.IsDeleted).Result.ToList(); //没有明细数据,终止 if (childList.Count == 0) { return; } foreach (var bchild in childList) { if (!bomchildlist.Exists(s => s.mysql_id == bchild.mysql_id)) { bomchildlist.Add(bchild); } } //通过物料bom明细数据反查物料bom数据 var boms = _ic_bom.GetListAsync(p => childList.Select(m => m.icitem_id).ToList().Contains(p.icitem_id) && p.use_status == 1 && p.company_id == param.company_id && !p.IsDeleted).Result.ToList(); foreach (var chd in childList) { var curBoms = boms.Where(p => p.icitem_id == chd.icitem_id).ToList(); GetIcBomData(curBoms, bomlist, bomchildlist); } } /// /// 自动生成BOM预处理清单 /// public void AutoCreateBomBill(string companyid, List ic_Boms = null) { ic_Boms ??= _ic_bom.GetListAsync(p => p.company_id.ToString() == companyid && !p.IsDeleted).Result.ToList(); var pretreatment = _mysql_b_bom_pretreatment.GetListAsync(s => ic_Boms.Select(c => c.mysql_id).ToList().Contains(s.sourceid)).Result; param.company_id = long.Parse(companyid); //物料bom List bomlist = new List(); List bomchildlist = new List(); //物料信息 List icitemlist = new List(); //替代关系 List sublist = new List(); List suballlist = new List(); List subdtllist = new List(); IcItemPretreatment(ic_Boms, bomlist, bomchildlist, icitemlist, sublist, suballlist, subdtllist); List deleteList = new List(); List addList = new List(); foreach (var bom in ic_Boms) { var pret = pretreatment.Find(s => s.sourceid == bom.mysql_id && s.bom_id == bom.mysql_id); if (pret != null) { if (pret.version == bom.version) { //如果存在,且版本相同,则不需要处理 continue; } //版本不同则重新生成 deleteList.Add(pret); } //bom层级组装 var getBomList = _pretreatmentAppService.BomPretreatment(bom.mysql_id, bomlist, bomchildlist, icitemlist); //bom替代关系组装 _pretreatmentAppService.BomSubstitute(getBomList, bomlist, bomchildlist, icitemlist, sublist, suballlist, subdtllist); GenerateSort(getBomList); var list = ObjectMapper.Map, List>(getBomList); list.ForEach(s => { s.sourceid = bom.mysql_id; s.company_id = bom.company_id; s.tenant_id = bom.tenant_id; s.factory_id = bom.factory_id; }); addList.AddRange(list); } if (deleteList.Any()) { _businessDbContext.BulkDelete(deleteList); } if (addList.Any()) { _businessDbContext.BulkInsert(addList); //await _mysql_b_bom_pretreatment.InsertManyAsync(addList); } } /// /// 生产长周期物料PR /// /// 需要传入工厂编码 /// public async Task CalcLongPeriodItemPR(InputDto input) { var ropParam = GetROPParam(input.factory_id.ToString()); //获取滚动未来六个月数据 var monthlist = GetPlanMonth(6); var productList = _replenishmentAnnualProduction.GetListAsync(a => monthlist.Contains(a.PlanMonth) && !a.IsDeleted && a.factory_id == input.factory_id).Result.OrderBy(s=>s.OrderNum).ToList(); var itemList= productList.Select(a=>a.SAPItemNumber).ToList(); var planList=_mysql_ic_item.GetListAsync(a=>itemList.Contains(a.number) && a.factory_id==input.factory_id && !a.IsDeleted).Result.ToList(); var mesItem=_itemMaster.Select(a => itemList.Contains(a.ItemNum) &&a.IsActive && a.Domain==input.factory_id.ToString()); var bomList = _mysql_ic_bom.GetListAsync(a => itemList.Contains(a.item_number) && !a.IsDeleted && a.factory_id == input.factory_id).Result; List moList=new List(); List moentryList = new List(); var list = _serialNumberAppService.GetBillNo(input.factory_id.ToString(), "MPO", productList.Count(), "", 1); long bangid = help.NextId(); for (int i = 0;i< productList.Count();i++) { var ic_item = planList.Find(x => x.number == productList[i].SAPItemNumber); var bom = bomList.Find(a => a.item_number == productList[i].SAPItemNumber); if (ic_item.PurLT < ropParam.LongPeriodDay) continue; mes_morder mes_Morder = new mes_morder(); mes_Morder.GenerateNewId(help.NextId()); mes_Morder.morder_type = MorderEnum.JhMorder; //mes_Morder.morder_icitem_type mes_Morder.work_order_type = MorderEnum.CgMorder; mes_Morder.morder_state = MorderEnum.Initial_state; mes_Morder.morder_no = list[i].NbrResult; mes_Morder.fms_number = ic_item.fms_number; mes_Morder.bom_number = bom.bom_number; mes_Morder.fmodel = ic_item.model; mes_Morder.urgent = 1; mes_Morder.moentry_startup_status = 0; mes_Morder.tenant_id = input.tenant_id; mes_Morder.factory_id = input.factory_id; mes_Morder.company_id = input.company_id; mes_Morder.org_id = input.org_id; mes_Morder.product_code = ic_item.number; mes_Morder.product_name = ic_item.name; mes_Morder.morder_date = DateTime.Now.Date.AddDays(1); mes_Morder.moentry_sys_etime = Convert.ToDateTime(productList[i].PlanMonth + "-01"); //mes_Morder.morder_fstate = "计划"; //TODO:目前没有取值位置 mes_Morder.moentry_prd = null; mes_Morder.moentry_prdname = null; mes_Morder.moentry_wrkc = null; mes_Morder.moentry_wrkcname = null; mes_Morder.picking_qty = 0; //TODO:可删除主表字段 mes_Morder.unit = ic_item.unit; mes_Morder.morder_production_number = productList[i].Qty; mes_Morder.need_number = productList[i].Qty; mes_Morder.remaining_number = 0; mes_Morder.create_time = DateTime.Now; //生成工单子表数据 mes_moentry mes_Moentry = new mes_moentry(); mes_Moentry.GenerateNewId(help.NextId()); mes_Moentry.moentry_moid = mes_Morder.Id; mes_Moentry.moentry_mono = mes_Morder.morder_no; mes_Moentry.unit = ic_item.unit; mes_Moentry.morder_production_number = productList[i].Qty; mes_Moentry.need_number = productList[i].Qty; mes_Moentry.remaining_number = 0; mes_Moentry.tenant_id = input.tenant_id; mes_Moentry.factory_id = input.factory_id; mes_Moentry.company_id = input.company_id; mes_Moentry.org_id = input.org_id; mes_Moentry.create_time = DateTime.Now; moList.Add(mes_Morder); moentryList.Add(mes_Moentry); } //长周期物料只产生PR不生成实际工单 //await _mysql_mes_morder.InsertManyAsync(moList); //await _mysql_mes_moentry.InsertManyAsync(moentryList); await PlanOrderResourceCheck(moList,moentryList, bangid,input); return "OK"; } public ReplenishmentDto GetROPParam(string domain) { //select [Domain],Val,Ufld1,UDeci1 from GeneralizedCodeMaster where [Domain] = '1001' AND FldName ='SystemConfig' // AND Val IN('ReplenishmentCalcPeriod', 'HistoryOutStockMonth', 'SaleFcstMonth', 'SaleFcstEntryDate', 'UpdateWeeks', 'WeekPlanUpdateDay', 'LongPeriodDay') List list = new List(); list.Add("ReplenishmentCalcPeriod"); list.Add("HistoryOutStockMonth"); list.Add("SaleFcstMonth"); list.Add("SaleFcstEntryDate"); list.Add("UpdateWeeks"); list.Add("WeekPlanUpdateDay"); list.Add("LongPeriodDay"); var configList = _generalizedCodeMaster.Select(a => a.Domain == domain && a.FldName == "SystemConfig" && list.Contains(a.Val)); ReplenishmentDto replenishmentDto = new ReplenishmentDto(); replenishmentDto.HistoryOutStockMonth = 3; replenishmentDto.ReplenishmentCalcPeriod = "W"; replenishmentDto.HistoryOutStockMonth = 2; replenishmentDto.SaleFcstMonth = 2; replenishmentDto.SaleFcstEntryDate = 27; replenishmentDto.UpdateWeeks = 12; replenishmentDto.WeekPlanUpdateDay = 7;//每周日更新 replenishmentDto.LongPeriodDay = 60; if (configList?.Find(a => a.Val == "HistoryOutStockMonth") != null) { replenishmentDto.HistoryOutStockMonth = (int)configList?.Find(a => a.Val == "HistoryOutStockMonth").UDeci1; } if (configList?.Find(a => a.Val == "ReplenishmentCalcPeriod") != null) { switch (configList?.Find(a => a.Val == "ReplenishmentCalcPeriod").Ufld1) { case "月": replenishmentDto.ReplenishmentCalcPeriod = "M"; break; case "周": replenishmentDto.ReplenishmentCalcPeriod = "W"; break; case "": replenishmentDto.ReplenishmentCalcPeriod = "D"; break; default: replenishmentDto.ReplenishmentCalcPeriod = "W"; break; } } if (configList?.Find(a => a.Val == "SaleFcstMonth") != null) { replenishmentDto.SaleFcstMonth = (int)configList?.Find(a => a.Val == "SaleFcstMonth").UDeci1; } if (configList?.Find(a => a.Val == "SaleFcstEntryDate") != null) { replenishmentDto.SaleFcstEntryDate = (int)configList?.Find(a => a.Val == "SaleFcstEntryDate").UDeci1; } if (configList?.Find(a => a.Val == "UpdateWeeks") != null) { replenishmentDto.UpdateWeeks = (int)configList?.Find(a => a.Val == "UpdateWeeks").UDeci1; } if (configList?.Find(a => a.Val == "WeekPlanUpdateDay") != null) { switch (configList?.Find(a => a.Val == "WeekPlanUpdateDay").Ufld1) { case "周一": replenishmentDto.WeekPlanUpdateDay = 1; break; case "周二": replenishmentDto.WeekPlanUpdateDay = 2; break; case "周三": replenishmentDto.WeekPlanUpdateDay = 3; break; case "周四": replenishmentDto.WeekPlanUpdateDay = 4; break; case "周五": replenishmentDto.WeekPlanUpdateDay = 5; break; case "周六": replenishmentDto.WeekPlanUpdateDay = 6; break; case "周日": replenishmentDto.WeekPlanUpdateDay = 7; break; default: replenishmentDto.WeekPlanUpdateDay = 7; break; } } if (configList?.Find(a => a.Val == "LongPeriodDay") != null) { replenishmentDto.LongPeriodDay = (int)configList?.Find(a => a.Val == "LongPeriodDay").UDeci1; } return replenishmentDto; } /// /// 计算ABC分类和FMR分类 /// /// public void CalcFMRAndABC(ReplenishmentROP replenishmentModel, ReplenishmentDto replenishmentDto,List shipList,List pickbilllist, InputDto input) { //总的移库次数 int totalCount = shipList.Count() + pickbilllist.Count(); //平均出库次数 decimal avgOutStock = totalCount / replenishmentDto.HistoryOutStockMonth; //总出货金额 decimal? totalPrice = shipList.Sum(a => a.Price * a.QtyToShip); //按照物料分组统计出货金额 var itemGroup = shipList.GroupBy(p => p.ContainerItem) .Select(p => new ASNBOLShipperDetail { Price = p.Sum(a => a.Price * a.QtyToShip) * 100 / totalPrice, ContainerItem = p.Key }).ToList().OrderByDescending(c => c.Price).ThenByDescending(c => c.ContainerItem); var AIndex = (int)Math.Ceiling(itemGroup.Count() * 0.75);//A类:0-75% var BIndex = (int)Math.Ceiling(itemGroup.Count() * 0.9) - AIndex;//A类:75%-90% var CIndex = itemGroup.Count() - AIndex - BIndex;//A类:90%-100% var AItem = itemGroup.Take(AIndex); var BItem = itemGroup.Skip(AIndex).Take(BIndex); var CItem = itemGroup.Skip(AIndex + BIndex).Take(CIndex); if (AItem.Any(a => a.ContainerItem == replenishmentModel.number)) { replenishmentModel.abc = "A";//金额占比前75%的物料划为A类 } else if (BItem.Any(a => a.ContainerItem == replenishmentModel.number)) { replenishmentModel.abc = "B";//金额占比前75%-90%的物料划为B类 } else if (CItem.Any(a => a.ContainerItem == replenishmentModel.number)) { replenishmentModel.abc = "C";//金额占比后10%的物料划为C类 } else { replenishmentModel.abc = "B";//原材料默认为B } int itemMoveCount = shipList.Count(a => a.ContainerItem == replenishmentModel.number) + pickbilllist.Count(a => a.ItemNum == replenishmentModel.number); if (itemMoveCount >= avgOutStock) { replenishmentModel.fmr = "F"; } else if (itemMoveCount >= avgOutStock / 2 && itemMoveCount < avgOutStock || itemMoveCount == 0) { replenishmentModel.fmr = "M";//没有出库移库记录的默认为M } else if (itemMoveCount < avgOutStock / 2) { replenishmentModel.fmr = "R"; } if (replenishmentModel.abc == "A" && replenishmentModel.fmr == "R") { replenishmentModel.service_level_pct = (decimal?)0.8; } if (replenishmentModel.fmr != "R") { replenishmentModel.service_level_pct = (decimal?)0.96; } if (replenishmentModel.abc != "A" && replenishmentModel.fmr == "R") { replenishmentModel.service_level_pct = (decimal?)0.9; } } /// /// 算平均月需求数量 /// /// 发货计划 /// /// /// /// public decimal CalcAvgDemand(List list, string itemNum,int? erp_cls, List pickbilllist=null) { if(erp_cls!=3) { if(list.Where(i => i.SAPItemNumber == itemNum).Count()>0) { return list.Where(i => i.SAPItemNumber == itemNum).Average(a => a.Qty); } return 0; }else { var pickItemList = pickbilllist.Select(a => a.ItemNum).Distinct(); var sumQty = pickbilllist.Where(a => a.ItemNum == itemNum)?.Sum(a => a.QtyChange); var month = pickbilllist.Where(a => a.ItemNum == itemNum)?.Select(a => a.CreateTime.Value.Month).Distinct().Count(); if (month == 0) return sumQty.Value; else return sumQty.GetValueOrDefault()/month.Value; } } /// /// /// /// 补货模型 /// 本月实际出库 /// 全局参数 /// 物料编码 /// public decimal CalcAvgOutStock(List list, ReplenishmentDto replenishmentDto, decimal MQty, string itemNum) { return (list.Where(i => i.number == itemNum && i.zero_based_seqno > 1 - replenishmentDto.HistoryOutStockMonth && i.zero_based_seqno <= 0).Select(a => a.actual_out_qty.Value).Sum() + MQty) / (replenishmentDto.HistoryOutStockMonth + 1); } public double CalcVariance(List list, string itemNum, int? erp_cls,decimal? monthl_avg_demand, List pickbilllist = null) { if (erp_cls != 3) { var nums = list.Where(i => i.SAPItemNumber == itemNum).Select(a => a.Qty); if (nums.Count() <= 1) return 0; return Math.Sqrt(nums.Sum(x => Math.Pow(Convert.ToDouble(x) - Convert.ToDouble(monthl_avg_demand), 2)) / (nums.Count() - 1)); } else { var nums = pickbilllist.Where(i => i.ItemNum == itemNum).Select(a =>a.QtyChange); if (nums.Count() <= 1) return 0; return Math.Sqrt(nums.Sum(x => Math.Pow(Convert.ToDouble(x) - Convert.ToDouble(monthl_avg_demand), 2)) / (nums.Count() - 1)); } } /// /// 获取指定日期在为一年中为第几周 /// /// 指定时间 /// 返回第几周 public static int GetWeekOfYear(DateTime dt) { System.Globalization.GregorianCalendar gc = new System.Globalization.GregorianCalendar(); int weekOfYear = gc.GetWeekOfYear(dt, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Monday); return weekOfYear; } /// /// 获取指定日期在该月的第几周 /// /// /// 1表示周一至周日为一周,2表示周日至周六为一周 /// public static int WeekOfMonth(DateTime day, int WeekStart) { DateTime FirstofMonth = Convert.ToDateTime(day.Date.Year + "-" + day.Date.Month + "-" + 1); int i = (int)FirstofMonth.Date.DayOfWeek; if (i == 0) { i = 7; } if (WeekStart == 1) { return (day.Date.Day + i - 2) / 7 + 1; } if (WeekStart == 2) { return (day.Date.Day + i - 1) / 7; } return 0; } //获取上周开始时间 public static DateTime getPreWeekStartTime() { var weekday = (int)DateTime.Now.DayOfWeek; return Convert.ToDateTime(DateTime.Now.AddDays(-7 - weekday + 1).ToString("yyyy-MM-dd 00:00:00.000")); } //获取上周结束时间 public static DateTime getPreWeekEndTime() { int weekday = (int)DateTime.Now.DayOfWeek; return Convert.ToDateTime(DateTime.Now.AddDays(weekday - 7 + 1).ToString("yyyy-MM-dd 23:59:59.000")); } /// /// 获取上个月开始时间 /// /// /// public static DateTime getPreMonthStartTime() { return Convert.ToDateTime(DateTime.Now.AddMonths(-1).AddDays(1 - DateTime.Now.Day).ToString("yyyy-MM-dd 00:00:00.000")); } /// /// 获取上月结束时间 /// /// /// public static DateTime getPreMonthEndTime() { return Convert.ToDateTime(DateTime.Now.AddDays(-DateTime.Now.Day).ToString("yyyy-MM-dd 23:59:59.000")); } /// /// 获取前后某个,整数月开始时间 /// /// 正数表示后多少个月的第一天,负数为前多少个月第一天 /// public static DateTime getMonthStartTime(int months) { return getPreMonthStartTime().AddMonths(months + 1); } /// /// 获取前后某个,整数月结束时间 /// /// 正数表示后多少个月的第一天,负数为前多少个月第一天 /// public static DateTime getMonthEndTime(int months) { return getPreMonthEndTime().AddMonths(months + 1); } /// /// 获取未来几个月PlanMonth /// /// /// public List GetPlanMonth(int monthCount) { List result=new List(); for(int i=0;i< monthCount; i++) { var months = DateTime.Now.AddMonths(i); result.Add($"{months.Year}-{months.Month.ToString("00")}"); } return result; } /// /// 获取前面几个月PlanMonth /// /// /// public List GetPrePlanMonth(int monthCount) { List result = new List(); for (int i = 0; i < monthCount; i++) { var months = DateTime.Now.AddMonths(-1*(i)); result.Add($"{months.Year}-{months.Month.ToString("00")}"); } return result; } } }