|
|
@@ -26,6 +26,9 @@ 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;
|
|
|
|
|
|
namespace Business.Replenishment
|
|
|
{
|
|
|
@@ -57,6 +60,7 @@ namespace Business.Replenishment
|
|
|
private readonly IRepository<srm_pr_main, long> _mysql_srm_pr_main;
|
|
|
private readonly IRepository<mo_srm_po_occupy, long> _srm_po_occupy;
|
|
|
private readonly IRepository<srm_po_occupy, long> _mysql_srm_po_occupy;
|
|
|
+ private readonly IRepository<srm_pr_main_occupy, long> _mysql_srm_pr_main_occupy;
|
|
|
private readonly IRepository<mo_mes_morder, long> _mes_morder;
|
|
|
private readonly IRepository<mo_mes_moentry, long> _mes_moentry;
|
|
|
private readonly IRepository<mo_mes_mooccupy, long> _mes_mooccupy;
|
|
|
@@ -88,11 +92,13 @@ namespace Business.Replenishment
|
|
|
private readonly ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
|
|
|
private readonly ISqlRepository<PeriodSequenceDet> _periodSequenceDet;
|
|
|
private readonly ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
|
|
|
+ private readonly ISqlRepository<CustMaster> _custMaster;
|
|
|
private readonly IRepository<mo_srm_po_main, long> _srm_po_main;
|
|
|
private readonly IRepository<srm_po_main, long> _mysql_srm_po_main;
|
|
|
private readonly IRepository<mo_srm_purchase, long> _srm_purchase;
|
|
|
private readonly IRepository<srm_purchase, long> _mysql_srm_purchase;
|
|
|
private readonly IRepository<mes_morder, long> _mysql_mes_morder;
|
|
|
+ private readonly IRepository<crm_seorder, long> _mysql_crm_seorder;
|
|
|
private readonly IRepository<crm_seorderentry, long> _mysql_crm_seorderentry;
|
|
|
private readonly IRepository<mo_mes_oorder, long> _mes_oorder;
|
|
|
private readonly ISqlRepository<ScheduleResultOpMaster> _scheduleResultOpMaster;
|
|
|
@@ -237,6 +243,7 @@ namespace Business.Replenishment
|
|
|
IRepository<srm_pr_main, long> mysql_srm_pr_main,
|
|
|
IRepository<mo_srm_po_occupy, long> srm_po_occupy,
|
|
|
IRepository<srm_po_occupy, long> mysql_srm_po_occupy,
|
|
|
+ IRepository<srm_pr_main_occupy, long> mysql_srm_pr_main_occupy,
|
|
|
IRepository<mo_mes_morder, long> mes_morder,
|
|
|
IRepository<mo_mes_moentry, long> mes_moentry,
|
|
|
IRepository<mo_mes_mooccupy, long> mes_mooccupy,
|
|
|
@@ -273,6 +280,7 @@ namespace Business.Replenishment
|
|
|
IRepository<mo_srm_purchase, long> srm_purchase,
|
|
|
IRepository<srm_purchase, long> mysql_srm_purchase,
|
|
|
IRepository<mes_morder, long> mysql_mes_morder,
|
|
|
+ IRepository<crm_seorder, long> mysql_crm_seorder,
|
|
|
IRepository<crm_seorderentry, long> mysql_crm_seorderentry,
|
|
|
IRepository<mo_mes_oorder, long> mes_oorder,
|
|
|
ISqlRepository<ScheduleResultOpMaster> scheduleResultOpMaster,
|
|
|
@@ -283,6 +291,7 @@ namespace Business.Replenishment
|
|
|
IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory,
|
|
|
IRepository<WMS_PlatformInventory, long> PlatformInventory,
|
|
|
ISqlRepository<WorkOrdRouting> workOrdRouting,
|
|
|
+ ISqlRepository<CustMaster> custMaster,
|
|
|
IRepository<MonthlyProdCapacity, long> monthlyProdCapacity,
|
|
|
IRepository<MonthlyProdCapacityDtl, long> monthlyProdCapacityDtl,
|
|
|
IRepository<crm_planorder, long> crm_planorder,
|
|
|
@@ -318,12 +327,14 @@ namespace Business.Replenishment
|
|
|
_mysql_srm_pr_main= mysql_srm_pr_main;
|
|
|
_srm_po_occupy = srm_po_occupy;
|
|
|
_mysql_srm_po_occupy = mysql_srm_po_occupy;
|
|
|
- _mes_morder= mes_morder;
|
|
|
+ _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;
|
|
|
@@ -354,6 +365,7 @@ namespace Business.Replenishment
|
|
|
_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;
|
|
|
@@ -503,6 +515,11 @@ namespace Business.Replenishment
|
|
|
annualProductionOutline.Totalhours = annualProductionOutline.AssemblyHours + annualProductionOutline.HeatSealingHours + annualProductionOutline.Totalhours;
|
|
|
annualProductionOutline.OrderNum = item.OrderNum;
|
|
|
annualProductionOutlines.Add(annualProductionOutline);
|
|
|
+ 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;
|
|
|
frontYearDemand.Add(item);
|
|
|
}
|
|
|
//保存数据
|
|
|
@@ -510,6 +527,7 @@ namespace Business.Replenishment
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
+ await _replenishmentAnnualProduction.DeleteAsync(a => a.Year == input.year && a.tenant_id == input.tenant_id && a.factory_id == input.factory_id);
|
|
|
await _replenishmentAnnualProduction.InsertManyAsync(annualProductionOutlines);
|
|
|
await unitOfWork.CompleteAsync();
|
|
|
}
|
|
|
@@ -531,34 +549,79 @@ namespace Business.Replenishment
|
|
|
public async Task<string> SaveProductionMasterPlan(InputDto input)
|
|
|
{
|
|
|
//计算当前年月的N0,N+1,N+2
|
|
|
- List<string> planMons = new List<string>();
|
|
|
- string strN0 = input.year.ToString() + "-" + input.month.ToString("00");
|
|
|
- planMons.Add(strN0);
|
|
|
- int newYear = input.month == 12 ? input.year + 1 : input.year;
|
|
|
- int newMonth = input.month == 12 ? 1 : input.month + 1;
|
|
|
- string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
|
|
|
- planMons.Add(strN1);
|
|
|
- newYear = newMonth == 12 ? newYear + 1 : newYear;
|
|
|
- newMonth = newMonth == 12 ? 1 : newMonth + 1;
|
|
|
- string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
|
|
|
- planMons.Add(strN2);
|
|
|
+ List<string> planMons = GetPlanMonth(4);
|
|
|
+ List<ProductionMasterPlan> productionMasters= new List<ProductionMasterPlan>();
|
|
|
+ List<ProductionMasterPlan> pre3MonthPlan = _productionMasterPlan.GetListAsync(a => GetPlanMonth(-3).Contains(a.PlanMonth) && a.factory_id == input.factory_id && a.tenant_id == input.tenant_id && a.company_id == input.company_id).Result;
|
|
|
//N0,N+1,N+2月度发货计划
|
|
|
- var productionMasterPlan = _productionMasterPlan.GetListAsync(x => x.Year == input.year && !x.IsDeleted && x.tenant_id == input.tenant_id && x.company_id == input.company_id && x.factory_id == input.factory_id && planMons.Contains(x.PlanMonth)).Result.OrderBy(p => p.OrderNum).ThenBy(o => o.PlanMonth).ToList();
|
|
|
- foreach (var item in productionMasterPlan)
|
|
|
+ var annualProduction = _replenishmentAnnualProduction.GetListAsync(x => planMons.Contains(x.PlanMonth) && !x.IsDeleted && x.tenant_id == input.tenant_id && x.company_id == input.company_id && x.factory_id == input.factory_id).Result.OrderBy(p => p.PlanMonth).ThenBy(o => o.OrderNum).ToList();
|
|
|
+
|
|
|
+ var productionMasterPlan = _productionMasterPlan.GetListAsync(x => planMons.Contains(x.PlanMonth) && !x.IsDeleted && x.tenant_id == input.tenant_id && x.company_id == input.company_id && x.factory_id == input.factory_id).Result.OrderBy(p => p.PlanMonth).ThenBy(o => o.OrderNum).ToList();
|
|
|
+ List<LocationDetail> 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 = item.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
|
|
|
{
|
|
|
- //await _annualProductionOutline.InsertManyAsync(annualProductionOutlines);
|
|
|
+ await _productionMasterPlan.InsertManyAsync(productionMasters);
|
|
|
await unitOfWork.CompleteAsync();
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
@@ -569,7 +632,55 @@ namespace Business.Replenishment
|
|
|
};
|
|
|
}
|
|
|
return "OK";
|
|
|
+ }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 获取物料在制数量
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="itemNumbers"></param>
|
|
|
+ /// <param name="input"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public List<EOPDto> GetInProdcutQty(List<string> itemNumbers,InputDto input)
|
|
|
+ {
|
|
|
+ List<EOPDto> itemQty = new List<EOPDto>();
|
|
|
+ DateTime startYear = new DateTime(DateTime.Now.AddYears(-1).Year, 1, 1);
|
|
|
+ DateTime endYear = new DateTime(DateTime.Now.Year, 12, 31);
|
|
|
+ List<WorkOrdRouting> 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<string, decimal> dictInProduction = new Dictionary<string, decimal>();
|
|
|
+ //按照工单循环
|
|
|
+ //某工单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;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -586,6 +697,9 @@ namespace Business.Replenishment
|
|
|
string ropResult=await CalcROP(input);
|
|
|
if(ropResult!="OK")
|
|
|
return ropResult;
|
|
|
+ string masterPlanResult = await SaveProductionMasterPlan(input);
|
|
|
+ if (masterPlanResult != "OK")
|
|
|
+ return masterPlanResult;
|
|
|
return "OK|刷新成功!";
|
|
|
}
|
|
|
|
|
|
@@ -737,7 +851,7 @@ namespace Business.Replenishment
|
|
|
//}
|
|
|
|
|
|
/// <summary>
|
|
|
- /// 调整ROP和最高库存水位
|
|
|
+ /// 调整ROP和最高库存水位(按照成品展开到原材料,原材料用领料算出库)
|
|
|
/// </summary>
|
|
|
/// <param name="input"></param>
|
|
|
/// <returns></returns>
|
|
|
@@ -745,40 +859,44 @@ namespace Business.Replenishment
|
|
|
{
|
|
|
//1.获取补货模型全局参数
|
|
|
ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString());
|
|
|
-
|
|
|
- //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周采购补货
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
//获取月度发货计划(一次导入三个月的销售预测),因为是固定格式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 itemList = _ic_item.GetListAsync(a => planList.Select(p => p.SAPItemNumber).Contains(a.number) && a.tenant_id == input.tenant_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
- var mesItemList = _itemMaster.Select(a => planList.Select(p => p.SAPItemNumber).Contains(a.ItemNum) && a.Domain == input.factory_id.ToString() && a.IsActive);
|
|
|
- var srm_purchaseList = _srmPurchase.GetListAsync(a => planList.Select(p => p.SAPItemNumber).Contains(a.number) && a.tenant_id == input.tenant_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
+ var boms = _ic_bom.GetListAsync(a => planList.Select(p => p.SAPItemNumber).ToList().Contains(a.item_number) && a.tenant_id == input.tenant_id && 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<mo_ic_bom> autoCreates = new List<mo_ic_bom>();
|
|
|
+ boms.ForEach(p =>
|
|
|
+ {
|
|
|
+ if (!pretreatments.Where(s => s.sourceid == p.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.tenant_id == input.tenant_id && a.company_id == input.company_id && !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.tenant_id == input.tenant_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
|
|
|
//获取补货模型前H周期和未来F个周期的数据
|
|
|
var ropModelList = _replenishmentROP.GetListAsync(a => a.zero_based_seqno >= -1 * replenishmentDto.HistoryOutStockMonth && a.zero_based_seqno <= replenishmentDto.SaleFcstMonth).Result;
|
|
|
- //发货计划物料列表
|
|
|
- List<string> planItemList = planList?.Select(a => a.SAPItemNumber).ToList();
|
|
|
//获取成品库存、灭菌库存、在制库存(会从SAP同步的库存表更新到LocationDetail、ic_item表中)
|
|
|
//List<SAPInv> 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() && planList.Select(a => a.SAPItemNumber).Contains(p.ItemNum) && p.IsActive);
|
|
|
- var sapItemInv = _SAPInv.Select(a => planItemList.Contains(a.MATNR) && a.WERKS == input.factory_id.ToString());
|
|
|
-
|
|
|
- //取上一个月发货出库记录
|
|
|
- var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && planItemList.Contains(a.ContainerItem) && a.ShipDate >= getMonthStartTime(-6) && a.ShipDate <= getMonthEndTime(-1));
|
|
|
+ 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 && planItemList.Contains(a.ContainerItem) && a.ShipDate >= getMonthStartTime(0) && a.ShipDate <= DateTime.Now);
|
|
|
+ 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
|
|
|
@@ -808,72 +926,91 @@ namespace Business.Replenishment
|
|
|
//需要按照成品资源检查计算原材料
|
|
|
planList?.Where(s => s.PlanMonth == planMonth).ToList()?.ForEach(a =>
|
|
|
{
|
|
|
- ReplenishmentROP rop = new ReplenishmentROP();
|
|
|
- rop.number = a.SAPItemNumber;
|
|
|
- var icItem = itemList.Find(s => s.number == a.SAPItemNumber);
|
|
|
- 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 == a.SAPItemNumber)?.QtyOnHand;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- rop.actual_period_start_instock = sapItemInv.Find(s => s.MATNR == a.SAPItemNumber)?.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 == a.SAPItemNumber))
|
|
|
- {
|
|
|
- rop.actual_out_qty = itemMGroup.Find(m => m.ContainerItem == a.SAPItemNumber)?.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);
|
|
|
- rop.monthl_avg_demand = CalcAvgDemand(planList, a.SAPItemNumber);
|
|
|
- rop.monthl_avg_demand_variance = Math.Ceiling(Convert.ToDecimal(CalcVariance(planList, a.SAPItemNumber)));
|
|
|
- rop.monthl_avg_outstock = CalcAvgOutStock(ropModelList, replenishmentDto, rop.actual_out_qty.Value, a.SAPItemNumber);
|
|
|
- if (mesItemList.Find(s => s.ItemNum == a.SAPItemNumber) != null)
|
|
|
- {
|
|
|
- rop.stock_turnover = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber).StockTurnOver;
|
|
|
- rop.supply_leadtime = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber)?.PurLT;
|
|
|
- rop.stock_turnover = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber)?.StockTurnOver;
|
|
|
- }
|
|
|
- else
|
|
|
+ var itemSourceId = boms.Find(b => b.item_number == a.SAPItemNumber)?.mysql_id;
|
|
|
+ if (itemSourceId != null)
|
|
|
{
|
|
|
- rop.stock_turnover = 4;
|
|
|
- }
|
|
|
- if (srm_purchaseList.Find(s => s.number == a.SAPItemNumber) != null)
|
|
|
- {
|
|
|
- rop.supply_leadtime = srm_purchaseList.Find(s => s.number == a.SAPItemNumber).lead_time;
|
|
|
+ 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, input);
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- CalcFMRAndABC(rop, replenishmentDto, input);
|
|
|
- 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()); });
|
|
|
|
|
|
@@ -994,8 +1131,8 @@ namespace Business.Replenishment
|
|
|
rop.zero_based_seqno = 0;
|
|
|
rop.period_start_date = getMonthStartTime(0);
|
|
|
rop.period_end_date = getMonthEndTime(0);
|
|
|
- rop.monthl_avg_demand = CalcAvgDemand(planList, a.SAPItemNumber);
|
|
|
- rop.monthl_avg_demand_variance = Math.Ceiling(Convert.ToDecimal(CalcVariance(planList, a.SAPItemNumber)));
|
|
|
+ //rop.monthl_avg_demand = CalcAvgDemand(planList, a.SAPItemNumber);
|
|
|
+ //rop.monthl_avg_demand_variance = Math.Ceiling(Convert.ToDecimal(CalcVariance(planList, a.SAPItemNumber)));
|
|
|
rop.monthl_avg_outstock = CalcAvgOutStock(ropModelList, replenishmentDto, rop.actual_out_qty.Value, a.SAPItemNumber);
|
|
|
if (mesItemList.Find(s => s.ItemNum == a.SAPItemNumber) != null)
|
|
|
{
|
|
|
@@ -1041,15 +1178,25 @@ namespace Business.Replenishment
|
|
|
/// <returns></returns>
|
|
|
public async Task<string> 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.company_id==input.company_id && !a.IsDeleted).Result;
|
|
|
+ var itemStock = CalcStock(ropModelList.Select(a => a.number).ToList(), input);
|
|
|
+ ropModelList.Where(a => a.erp_cls == 1)?.ToList().ForEach(b =>
|
|
|
+ {
|
|
|
+
|
|
|
+ });
|
|
|
+ //成品半成品取下周一开始+取供应提前期为结束时间
|
|
|
+ //原材料取第二天开始+取供应提前期为结束时间
|
|
|
+
|
|
|
|
|
|
//DOP运行资源检查每个原材料SKU的可用库存,当可用库存<=修正后ROP时,自动生成月度采购申请(Purchase request,PR46年月日4位流水号)=EOP;可用库存为没有被其他SO和PO占用的(合格原材料库存+在途库存);
|
|
|
|
|
|
//每天DOP检查每个SKU的可用库存<=SS时,第二次触发PO或PR,每次补货数量=圆整后EOP;SKU可用库存=实际库存+WIP或在途库存-销售订单占用-计划订单占用;当M+1\M+2实际有销售订单产生时,相同SKU的销售订单和计划订单先进行冲销;
|
|
|
|
|
|
|
|
|
- //1.获取补货模型全局参数
|
|
|
- ReplenishmentDto replenishmentDto = GetROPParam(input.factory_id.ToString());
|
|
|
+
|
|
|
+
|
|
|
|
|
|
//Step1:按照瑞奇、两大平台分别根据历史出库数据和预测出货数据计算ROP
|
|
|
//Step2:计算瑞奇的M-M+2共12周补货(每次补EOP/4),计算海王的M-M+2共12周补货(每次补EOP/4),计算国科的M-M+2共12周补货(每次补EOP/4)
|
|
|
@@ -1060,134 +1207,147 @@ namespace Business.Replenishment
|
|
|
|
|
|
|
|
|
|
|
|
- //获取月度发货计划(一次导入三个月的销售预测),因为是固定格式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();
|
|
|
+ return "OK";
|
|
|
+ }
|
|
|
|
|
|
|
|
|
- var itemList = _ic_item.GetListAsync(a => planList.Select(p => p.SAPItemNumber).Contains(a.number) && a.tenant_id == input.tenant_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
- var mesItemList = _itemMaster.Select(a => planList.Select(p => p.SAPItemNumber).Contains(a.ItemNum) && a.Domain == input.factory_id.ToString() && a.IsActive);
|
|
|
- var srm_purchaseList = _srmPurchase.GetListAsync(a => planList.Select(p => p.SAPItemNumber).Contains(a.number) && a.tenant_id == input.tenant_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
+ /// <summary>
|
|
|
+ /// 根据工单编号拍优先级
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="workOrdMasters">工单编号列表</param>
|
|
|
+ /// <param name="domain">工厂编码</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ //计算工单优先级
|
|
|
+ public List<WorkOrdMaster> CalcPriority(List<string> 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<string, int> dict = new Dictionary<string, int>();
|
|
|
+ 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<WorkOrdMaster> orderedList = new List<WorkOrdMaster>();
|
|
|
+ decimal Priority = 1.0m;
|
|
|
+ for (int i=0;i< T1WOListNew.Count;i++)
|
|
|
+ {
|
|
|
+ T1WOListNew[i].Priority = Priority;
|
|
|
+ orderedList.Add(T1WOListNew[i]);
|
|
|
+ Priority = Priority + 1.0m;
|
|
|
+ }
|
|
|
|
|
|
- //获取补货模型前H周期和未来F个周期的数据
|
|
|
- var ropModelList = _replenishmentROP.GetListAsync(a => a.zero_based_seqno >= -1 * replenishmentDto.HistoryOutStockMonth && a.zero_based_seqno <= replenishmentDto.SaleFcstMonth).Result;
|
|
|
- //发货计划物料列表
|
|
|
- List<string> planItemList = planList?.Select(a => a.SAPItemNumber).ToList();
|
|
|
- //获取成品库存、灭菌库存、在制库存(会从SAP同步的库存表更新到LocationDetail、ic_item表中)
|
|
|
- //List<SAPInv> 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() && planList.Select(a => a.SAPItemNumber).Contains(p.ItemNum) && p.IsActive);
|
|
|
- var sapItemInv = _SAPInv.Select(a => planItemList.Contains(a.MATNR) && a.WERKS == input.factory_id.ToString());
|
|
|
+ for (int i = 0; i < T1WOListInProduct.Count; i++)
|
|
|
+ {
|
|
|
+ T1WOListInProduct[i].Priority = Priority;
|
|
|
+ orderedList.Add(T1WOListInProduct[i]);
|
|
|
+ Priority = Priority + 1.0m;
|
|
|
+ }
|
|
|
|
|
|
- //取上一个月发货出库记录
|
|
|
- var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && planItemList.Contains(a.ContainerItem) && a.ShipDate >= getMonthStartTime(-1) && a.ShipDate <= getMonthEndTime(-1));
|
|
|
+ for (int i = 0; i < T2WOListNew.Count; i++)
|
|
|
+ {
|
|
|
+ T2WOListNew[i].Priority = Priority;
|
|
|
+ orderedList.Add(T2WOListNew[i]);
|
|
|
+ Priority = Priority + 1.0m;
|
|
|
+ }
|
|
|
|
|
|
- //取本月发货出库记录
|
|
|
- var shipMList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && planItemList.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<ReplenishmentROP> addList = new List<ReplenishmentROP>();
|
|
|
- List<ReplenishmentROP> updateList = new List<ReplenishmentROP>();//更新上一个月的实际出库数量
|
|
|
- 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);
|
|
|
- }
|
|
|
- });
|
|
|
- //需要按照成品资源检查计算原材料
|
|
|
- planList?.Where(s => s.PlanMonth == planMonth).ToList()?.ForEach(a =>
|
|
|
+ for (int i = 0; i < T2WOListInProduct.Count; i++)
|
|
|
{
|
|
|
- ReplenishmentROP rop = new ReplenishmentROP();
|
|
|
- rop.number = a.SAPItemNumber;
|
|
|
- var icItem = itemList.Find(s => s.number == a.SAPItemNumber);
|
|
|
- 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 == a.SAPItemNumber)?.QtyOnHand;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- rop.actual_period_start_instock = sapItemInv.Find(s => s.MATNR == a.SAPItemNumber)?.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 == a.SAPItemNumber))
|
|
|
- {
|
|
|
- rop.actual_out_qty = itemMGroup.Find(m => m.ContainerItem == a.SAPItemNumber)?.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);
|
|
|
- rop.monthl_avg_demand = CalcAvgDemand(planList, a.SAPItemNumber);
|
|
|
- rop.monthl_avg_demand_variance = Math.Ceiling(Convert.ToDecimal(CalcVariance(planList, a.SAPItemNumber)));
|
|
|
- rop.monthl_avg_outstock = CalcAvgOutStock(ropModelList, replenishmentDto, rop.actual_out_qty.Value, a.SAPItemNumber);
|
|
|
- if (mesItemList.Find(s => s.ItemNum == a.SAPItemNumber) != null)
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 根据物料编码获取可用库存
|
|
|
+ /// 成品半成品可用库存为没有被其他SO和PO占用的(合格成品或半成品库存+WIP)
|
|
|
+ /// 原材料可用库存为没有被其他SO和PO占用的(合格原材料库存+在途库存);
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="itemNumbers">物料编码列表</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public List<EOPDto> CalcStock(List<string> itemNumbers,InputDto input)
|
|
|
+ {
|
|
|
+ List<EOPDto> itemQty = new List<EOPDto>();
|
|
|
+ var itemList=_mysql_ic_item.GetListAsync(a => itemNumbers.Contains(a.number) && a.factory_id == input.factory_id && a.company_id ==input.company_id && !a.IsDeleted).Result;
|
|
|
+ var itemStockList=_mysql_ic_item_stock.GetListAsync(a => itemNumbers.Contains(a.icitem_number) && a.factory_id == input.factory_id && a.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
+ var pooccupylist=_mysql_srm_po_occupy.GetListAsync(a => a.state == 1 && !a.IsDeleted && a.factory_id == input.factory_id && a.company_id == input.company_id && a.tenant_id == input.tenant_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.company_id == input.company_id && !a.IsDeleted).Result;
|
|
|
+ var molist=_mysql_mes_morder.GetListAsync(a => itemNumbers.Contains(a.product_code) && a.factory_id == input.factory_id && a.company_id == input.company_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.company_id == input.company_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)
|
|
|
{
|
|
|
- rop.stock_turnover = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber).StockTurnOver;
|
|
|
- rop.supply_leadtime = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber)?.PurLT;
|
|
|
- rop.stock_turnover = mesItemList.Find(s => s.ItemNum == a.SAPItemNumber)?.StockTurnOver;
|
|
|
+ stockqty = itemStockList.Find(s => s.icitem_number == a).sqty.Value;
|
|
|
}
|
|
|
- else
|
|
|
+ decimal poOccupyQty = 0;
|
|
|
+ for(int i=0;i<pooccupylist.Count;i++)
|
|
|
{
|
|
|
- rop.stock_turnover = 4;
|
|
|
+ for(int j=0;j<poList.Count;j++)
|
|
|
+ {
|
|
|
+ if (pooccupylist[i].polist_id== poList[j].Id && pooccupylist[i].polist_row == poList[j].polist_row && poList[j].ItemNum==a)
|
|
|
+ {
|
|
|
+ poOccupyQty += pooccupylist[i].qty.Value;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- if (srm_purchaseList.Find(s => s.number == a.SAPItemNumber) != null)
|
|
|
+
|
|
|
+ decimal prOccupyQty = 0;
|
|
|
+ for (int i = 0; i < proccupylist.Count; i++)
|
|
|
{
|
|
|
- rop.supply_leadtime = srm_purchaseList.Find(s => s.number == a.SAPItemNumber).lead_time;
|
|
|
+ for (int j = 0; j < molist.Count; j++)
|
|
|
+ {
|
|
|
+ if (proccupylist[i].morder_mo == molist[j].morder_no && molist[j].product_code == a)
|
|
|
+ {
|
|
|
+ prOccupyQty += proccupylist[i].qty.Value;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- CalcFMRAndABC(rop, replenishmentDto, input);
|
|
|
- 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);
|
|
|
+ eOPDto.Qty = stockqty - poOccupyQty - prOccupyQty;
|
|
|
+ itemQty.Add(eOPDto);
|
|
|
});
|
|
|
- addList?.ForEach(item => { item.GenerateNewId(help.NextId()); });
|
|
|
-
|
|
|
- await _replenishmentROP.InsertManyAsync(addList);
|
|
|
- await _replenishmentROP.UpdateManyAsync(updateList);
|
|
|
- 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";
|
|
|
+ return itemQty;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -2107,7 +2267,7 @@ namespace Business.Replenishment
|
|
|
/// </summary>
|
|
|
public void AutoCreateBomBill(string companyid, List<mo_ic_bom> ic_Boms = null)
|
|
|
{
|
|
|
- ic_Boms ??= _ic_bom.GetListAsync(p => p.company_id.ToString() == companyid && (p.bom_number == "10100012_V1.0" || p.bom_number == "10100241_V1.0" || p.bom_number == "BOM00042070") && !p.IsDeleted).Result.ToList();
|
|
|
+ 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);
|
|
|
@@ -2186,7 +2346,7 @@ namespace Business.Replenishment
|
|
|
public async Task<string> CalcLongPeriodItemPR(InputDto input)
|
|
|
{
|
|
|
//获取滚动未来六个月数据
|
|
|
- var monthlist = GetSixPlanMonth();
|
|
|
+ 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();
|
|
|
@@ -2495,7 +2655,7 @@ namespace Business.Replenishment
|
|
|
var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.ShipDate >= DateTime.Now.AddMonths(-6) && a.RealQty > 0 && replenishmentModel.number == a.ContainerItem);
|
|
|
//领料出库记录
|
|
|
//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) && replenishmentModel.number == a.ItemNum);
|
|
|
+ List<InvTransHist> pickbilllist = _invTransHist.Select(a => a.Domain == input.factory_id.ToString() && a.TransType == "iss-wo" && a.IsActive && a.CreateTime >= DateTime.Now.AddMonths(-6) && replenishmentModel.number == a.ItemNum);
|
|
|
//总的移库次数
|
|
|
int totalCount = shipList.Count() + pickbilllist.Count();
|
|
|
//平均出库次数
|
|
|
@@ -2568,9 +2728,25 @@ namespace Business.Replenishment
|
|
|
/// <param name="itemNum"></param>
|
|
|
/// <param name="month"></param>
|
|
|
/// <returns></returns>
|
|
|
- public decimal CalcAvgDemand(List<MonthlyShipmentPlan> list, string itemNum)
|
|
|
+ public decimal CalcAvgDemand(List<MonthlyShipmentPlan> list, string itemNum,int? erp_cls, List<InvTransHist> pickbilllist=null)
|
|
|
{
|
|
|
- return list.Where(i => i.SAPItemNumber == itemNum).Select(a => a.Qty).Average();
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
/// <summary>
|
|
|
///
|
|
|
@@ -2585,13 +2761,22 @@ namespace Business.Replenishment
|
|
|
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<MonthlyShipmentPlan> list, string itemNum)
|
|
|
+ public double CalcVariance(List<MonthlyShipmentPlan> list, string itemNum, int? erp_cls,decimal? monthl_avg_demand, List<InvTransHist> pickbilllist = null)
|
|
|
{
|
|
|
- var avg = CalcAvgDemand(list, itemNum);
|
|
|
- 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(avg), 2)) / (nums.Count() - 1));
|
|
|
+ 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));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -2683,13 +2868,33 @@ namespace Business.Replenishment
|
|
|
return getPreMonthEndTime().AddMonths(months + 1);
|
|
|
}
|
|
|
|
|
|
- public List<string> GetSixPlanMonth()
|
|
|
+ /// <summary>
|
|
|
+ /// 获取未来几个月PlanMonth
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="monthCount"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public List<string> GetPlanMonth(int monthCount)
|
|
|
{
|
|
|
List<string> result=new List<string>();
|
|
|
- for(int i=0;i<6;i++)
|
|
|
+ for(int i=0;i< monthCount; i++)
|
|
|
+ {
|
|
|
+ var months = DateTime.Now.AddMonths(i);
|
|
|
+ result.Add($"{months.Year}-{months.Month.ToString("00")}");
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// 获取前面几个月PlanMonth
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="monthCount"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public List<string> GetPrePlanMonth(int monthCount)
|
|
|
+ {
|
|
|
+ List<string> result = new List<string>();
|
|
|
+ for (int i = 0; i < monthCount; i++)
|
|
|
{
|
|
|
- var months = DateTime.Now.AddMonths(i + 1);
|
|
|
- result.Add($"{months.Year}-{months.Month}");
|
|
|
+ var months = DateTime.Now.AddMonths(-1*(i));
|
|
|
+ result.Add($"{months.Year}-{months.Month.ToString("00")}");
|
|
|
}
|
|
|
return result;
|
|
|
}
|