|
|
@@ -1,1034 +0,0 @@
|
|
|
-using Business.Core.Utilities;
|
|
|
-using Business.Domain;
|
|
|
-using Business.Dto;
|
|
|
-using Business.EntityFrameworkCore.SqlRepositories;
|
|
|
-using Business.SaleForecast;
|
|
|
-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;
|
|
|
-
|
|
|
-namespace Business.SaleForecastManagement
|
|
|
-{
|
|
|
- /// <summary>
|
|
|
- /// 产能分析
|
|
|
- /// </summary>
|
|
|
- public class ReplenishmentAppService : ApplicationService, IMonthlyCapacityLoadAppService, ITransientDependency
|
|
|
- {
|
|
|
- #region 服务
|
|
|
- /// <summary>
|
|
|
- /// 物料
|
|
|
- /// </summary>
|
|
|
- private IRepository<ic_item, long> _ic_item;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 生产线明细
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<ProdLineDetail> _prodLineDetail;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 工作日历数据
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 产线休息时间记录表
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 节假日记录表
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<HolidayMaster> _holidayMaster;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 工作单元
|
|
|
- /// </summary>
|
|
|
- private readonly IUnitOfWorkManager _unitOfWorkManager;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 日志
|
|
|
- /// </summary>
|
|
|
- private readonly ICurrentTenant _currentTenant;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 年度生产需求大纲
|
|
|
- /// </summary>
|
|
|
- private IRepository<YearDemandManagement, long> _yearDemandManagement;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 年度生产需求大纲历史记录
|
|
|
- /// </summary>
|
|
|
- private IRepository<YearDemandManagementHistory, long> _yearDemandManagementHistory;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 国内终端预测
|
|
|
- /// </summary>
|
|
|
- private IRepository<DomesticTerminalFcst, long> _domesticTerminalFcst;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 海外销售预测
|
|
|
- /// </summary>
|
|
|
- private IRepository<OverseasSaleFcst, long> _overseasSaleFcst;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 平台预测收集
|
|
|
- /// </summary>
|
|
|
- private IRepository<PlatformFcstCollect, long> _platformFcstCollect;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 标准物料规格型号设置表
|
|
|
- /// </summary>
|
|
|
- private IRepository<StandardItemModelSet,long> _standardItemModelSet;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 库存明细表
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<LocationDetail> _locationDetail;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 工单
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<WorkOrdMaster> _workOrdMaster;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 整体需求计划
|
|
|
- /// </summary>
|
|
|
- private IRepository<OverallDemandPlan, long> _overallDemandPlan;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 月度产能共识表
|
|
|
- /// </summary>
|
|
|
- private IRepository<MonthlyProdCapacity, long> _monthlyProdCapacity;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 月度产能共识明细表
|
|
|
- /// </summary>
|
|
|
- private IRepository<MonthlyProdCapacityDtl, long> _monthlyProdCapacityDtl;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// SKU版本维护表
|
|
|
- /// </summary>
|
|
|
- private IRepository<SkuVersionSet, long> _skuVersionSet;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计划订单表
|
|
|
- /// </summary>
|
|
|
- private IRepository<crm_planorder, long> _crm_planorder;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 平台库存监控月份
|
|
|
- /// </summary>
|
|
|
- private IRepository<PlatStockMonitorSetting, long> _platStockMonitorSetting;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 平台库存表
|
|
|
- /// </summary>
|
|
|
- private IRepository<WMS_PlatformInventory, long> _platformInventory;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 参数设置表
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<GeneralizedCodeMaster> _generalizedCodeMaster;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// SAP库存表
|
|
|
- /// </summary>
|
|
|
- private ISqlRepository<SAPInv> _SAPInv;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 雪花算法
|
|
|
- /// </summary>
|
|
|
- SnowFlake help = new SnowFlake();
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 构造函数
|
|
|
- /// <summary>
|
|
|
- /// 构造函数
|
|
|
- /// </summary>
|
|
|
- public ReplenishmentAppService(
|
|
|
- IRepository<ic_item, long> ic_item,
|
|
|
- ISqlRepository<ProdLineDetail> prodLineDetail,
|
|
|
- ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
|
|
|
- ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
|
|
|
- ISqlRepository<HolidayMaster> holidayMaster,
|
|
|
- IRepository<MonthlyProdCapacity, long> monthlyProdCapacity,
|
|
|
- IUnitOfWorkManager unitOfWorkManager,
|
|
|
- ICurrentTenant currentTenant,
|
|
|
- IRepository<StandardItemModelSet,long> standardItemModelSet,
|
|
|
- IRepository<YearDemandManagement, long> yearDemandManagement,
|
|
|
- IRepository<DomesticTerminalFcst, long> domesticTerminalFcst,
|
|
|
- IRepository<OverseasSaleFcst, long> overseasSaleFcst,
|
|
|
- IRepository<PlatformFcstCollect, long> platformFcstCollect,
|
|
|
- IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory,
|
|
|
- ISqlRepository<LocationDetail> locationDetail,
|
|
|
- ISqlRepository<WorkOrdMaster> workOrdMaster,
|
|
|
- IRepository<OverallDemandPlan, long> overallDemandPlan,
|
|
|
- IRepository<MonthlyProdCapacityDtl, long> monthlyProdCapacityDtl,
|
|
|
- IRepository<SkuVersionSet, long> skuVersionSet,
|
|
|
- IRepository<crm_planorder, long> crm_planorder,
|
|
|
- IRepository<PlatStockMonitorSetting, long> platStockMonitorSetting,
|
|
|
- IRepository<WMS_PlatformInventory, long> platformInventory,
|
|
|
- ISqlRepository<GeneralizedCodeMaster> generalizedCodeMaster,
|
|
|
- ISqlRepository<SAPInv> SAPInv
|
|
|
- )
|
|
|
- {
|
|
|
- _ic_item = ic_item;
|
|
|
- _prodLineDetail = prodLineDetail;
|
|
|
- _shopCalendarWorkCtr = shopCalendarWorkCtr;
|
|
|
- _qualityLineWorkDetail = qualityLineWorkDetail;
|
|
|
- _holidayMaster = holidayMaster;
|
|
|
- _monthlyProdCapacity = monthlyProdCapacity;
|
|
|
- _unitOfWorkManager = unitOfWorkManager;
|
|
|
- _currentTenant = currentTenant;
|
|
|
- _standardItemModelSet = standardItemModelSet;
|
|
|
- _yearDemandManagement= yearDemandManagement;
|
|
|
- _domesticTerminalFcst= domesticTerminalFcst;
|
|
|
- _overseasSaleFcst = overseasSaleFcst;
|
|
|
- _platformFcstCollect= platformFcstCollect;
|
|
|
- _yearDemandManagementHistory= yearDemandManagementHistory;
|
|
|
- _locationDetail = locationDetail;
|
|
|
- _workOrdMaster = workOrdMaster;
|
|
|
- _overallDemandPlan = overallDemandPlan;
|
|
|
- _monthlyProdCapacityDtl = monthlyProdCapacityDtl;
|
|
|
- _skuVersionSet= skuVersionSet;
|
|
|
- _crm_planorder= crm_planorder;
|
|
|
- _platStockMonitorSetting = platStockMonitorSetting;
|
|
|
- _platformInventory = platformInventory;
|
|
|
- _generalizedCodeMaster = generalizedCodeMaster;
|
|
|
- _SAPInv = SAPInv;
|
|
|
- }
|
|
|
- #endregion
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 月度需求预测更新
|
|
|
- /// </summary>
|
|
|
- /// <param name="input"></param>
|
|
|
- /// <returns></returns>
|
|
|
- /// <exception cref="NotImplementedException"></exception>
|
|
|
- public async Task<string> DemandAnalysis(InputDto input)
|
|
|
- {
|
|
|
- //1.0 获取当前年年度生产需求大纲
|
|
|
- List<YearDemandManagement> yearDemands = _yearDemandManagement.GetListAsync(p => p.Year == input.year && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p=>p.OrderNum).ToList();
|
|
|
- //1.0 获取下一年年度生产大纲
|
|
|
- List<YearDemandManagement> nextYearDemands = _yearDemandManagement.GetListAsync(p => p.Year == (input.year+1) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p => p.OrderNum).ToList();
|
|
|
- //记录历史版本:当前年+下一年
|
|
|
- var updateMonth = input.year.ToString() + "-" + input.month.ToString("00");
|
|
|
- var histories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(yearDemands);
|
|
|
- histories.ForEach(p => {
|
|
|
- p.UpdateMonth = updateMonth;
|
|
|
- });
|
|
|
- var nextHistories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(nextYearDemands);
|
|
|
- nextHistories.ForEach(p => {
|
|
|
- p.UpdateMonth = updateMonth;
|
|
|
- });
|
|
|
- //1.1、获取海外销售预测数据
|
|
|
- List<OverseasSaleFcst> overseasSales = _overseasSaleFcst.GetListAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.2、获取平台预测收集数据
|
|
|
- List<PlatformFcstCollect> platformFcsts = _platformFcstCollect.GetListAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.3、获取国内终端预测-T1汇总数据
|
|
|
- List<DomesticTerminalFcst> domesticFcst = _domesticTerminalFcst.GetListAsync(p =>p.TypeEnum == 2 && p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.3.1、获取T2-海王,T2-国科数据
|
|
|
- List<DomesticTerminalFcst> T2Fcsts = _domesticTerminalFcst.GetListAsync(p => (p.TypeEnum == 4 || p.TypeEnum == 5) && p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.4、根据年度生产大纲,获取规格型号对应的补货周期
|
|
|
- List<string> allModels = yearDemands.Select(p=>p.Model).Distinct().ToList();
|
|
|
- if (nextYearDemands.Any())
|
|
|
- {
|
|
|
- allModels.AddRange(nextYearDemands.Select(p => p.Model).ToList());
|
|
|
- allModels = allModels.Distinct().ToList();
|
|
|
- }
|
|
|
- List<StandardItemModelSet> standards = _standardItemModelSet.GetListAsync(p => allModels.Contains(p.Model) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.5、获取规格型号对应的标准SKU数据:获取最小包装单位
|
|
|
- List<string> itemNums = standards.Select(p=>p.ItemNumber).Distinct().ToList();
|
|
|
- List<ic_item> items = _ic_item.GetListAsync(p=> itemNums.Contains(p.number) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.6、获取成品库存、灭菌库存、在制库存(会从SAP同步的库存表更新到LocationDetail、ic_item表中)
|
|
|
- //List<SAPInv> sAPInvs = _SAPInv.Select(p => p.WERKS == input.factory_id.ToString() && itemNums.Contains(p.MATNR));
|
|
|
- List<LocationDetail> locations = _locationDetail.Select(p => p.Domain == input.factory_id.ToString() && itemNums.Contains(p.ItemNum) && p.IsActive);
|
|
|
- //1.7、获取节假日设置
|
|
|
- List<HolidayMaster> holidays = _holidayMaster.Select(p => (p.Dated.Value.Year == input.year || p.Dated.Value.Year == (input.year + 1)) && p.Domain == input.factory_id.ToString() && p.IsActive);
|
|
|
- //1.8、获取平台库存监控月份设置
|
|
|
- List<PlatStockMonitorSetting> monitorSettings = _platStockMonitorSetting.GetListAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //1.9、获取客户要求交期在N+1月的销售订单
|
|
|
- //List<>
|
|
|
- #region 数据校验
|
|
|
- //1、校验当前年的年度生产大纲是否导入
|
|
|
- if (!yearDemands.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|请导入" + input.year + "年的年度生产需求后再操作!";
|
|
|
- }
|
|
|
- //2、如果当前月份大于10,则需校验下一年的年度生产大纲
|
|
|
- if (input.month > 10 && !nextYearDemands.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + (input.year+1) + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|请导入" + (input.year + 1) + "年的年度生产需求后再操作!";
|
|
|
- }
|
|
|
- //3、校验当前月份的海外销售预测是否导入
|
|
|
- if (!overseasSales.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!";
|
|
|
- }
|
|
|
- //4、校验当前月份的平台预测收集是否导入
|
|
|
- if (!platformFcsts.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!";
|
|
|
- }
|
|
|
- //5、校验当前月份的国内终端预测是否导入
|
|
|
- if (!domesticFcst.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!";
|
|
|
- }
|
|
|
- //6、校验终端+平台导入的规格型号是否已维护标准SKU
|
|
|
- if (allModels.Count() != standards.Count())
|
|
|
- {
|
|
|
- allModels.RemoveAll(standards.Select(p=>p.Model).ToList());
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",",allModels) + "】没有维护标准SKU,请维护后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|规格型号【" + string.Join(",", allModels) + "】没有维护标准SKU,请维护后再操作!";
|
|
|
- }
|
|
|
- //7、校验是否已维护补货周期
|
|
|
- var zeros = standards.Where(p => p.ReplenishCycle == 0).ToList();
|
|
|
- if (zeros.Any())
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",", zeros.Select(p=>p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!", _currentTenant.Id.ToString());
|
|
|
- return "NO|规格型号【" + string.Join(",", zeros.Select(p => p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!";
|
|
|
- }
|
|
|
- #endregion
|
|
|
-
|
|
|
- //计算当前年月的N0,N+1,N+2
|
|
|
- List<string> planMons = new List<string>();
|
|
|
- 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<YearDemandManagement> updates = new List<YearDemandManagement>();
|
|
|
- #region 计算国内终端预测,回写年度生产大纲
|
|
|
- //获取导入的T1、平台数据
|
|
|
- List<string> gnModels = new List<string>();
|
|
|
- if (domesticFcst.Any())
|
|
|
- {
|
|
|
- gnModels.AddRange(domesticFcst.Select(p => p.Model).ToList());
|
|
|
- }
|
|
|
- if (platformFcsts.Any())
|
|
|
- {
|
|
|
- gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
|
|
|
- }
|
|
|
- gnModels = gnModels.Distinct().ToList();
|
|
|
- foreach (var gnModel in gnModels)
|
|
|
- {
|
|
|
- //N+1月
|
|
|
- YearDemandManagement monthN1 = new YearDemandManagement();
|
|
|
- //N+2月
|
|
|
- YearDemandManagement monthN2 = new YearDemandManagement();
|
|
|
- //获取需要回写的数据
|
|
|
- if (input.month < 11)
|
|
|
- {
|
|
|
- monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (input.month == 11)
|
|
|
- {
|
|
|
- monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (input.month == 12)
|
|
|
- {
|
|
|
- monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (monthN1 == null || monthN2 == null)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- //T1
|
|
|
- var sumT1N1 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p=>p.Qty);
|
|
|
- var sumT1N2 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
|
|
|
- //平台
|
|
|
- var sumPN1 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p => p.Qty);
|
|
|
- var sumPN2 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
|
|
|
- //合计
|
|
|
- var sumN1 = sumT1N1+ sumPN1;
|
|
|
- var sumN2 = sumT1N2 + sumPN2;
|
|
|
-
|
|
|
- //当前规格型号对应标准SKU的最小包装单位、补货周期
|
|
|
- var curStd = standards.FirstOrDefault(p => p.Model == gnModel);
|
|
|
- decimal packQty = 1m;//最小包装单位
|
|
|
- decimal cycle = 0m;//补货周期
|
|
|
- //获取成品库存、在制库存、灭菌库存,参与计算
|
|
|
- decimal cpQty = 0m;
|
|
|
- decimal zzQty = 0m;
|
|
|
- decimal mjQty = 0m;
|
|
|
- if (curStd != null)
|
|
|
- {
|
|
|
- var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
|
|
|
- packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
|
|
|
- cycle = curStd.ReplenishCycle;
|
|
|
- //成品库存
|
|
|
- cpQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "8001").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- //在制库存
|
|
|
- zzQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "8000").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- //灭菌库存
|
|
|
- mjQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "5008").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- }
|
|
|
- //N+1月使用N+2月的再订货点参与计算
|
|
|
- decimal rop = CalcRop(strN2+"-01", sumN2, packQty, holidays, cycle);
|
|
|
- monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 + rop - cpQty - zzQty - mjQty) / packQty) * packQty;
|
|
|
- //N+2月使用本月的需求量,下一月的工作天数
|
|
|
- rop = CalcRop(Convert.ToDateTime(strN2 + "-01").AddMonths(1).ToString("yyyy-MM-dd"), sumN2, packQty, holidays, cycle);
|
|
|
- monthN2.Qty = Math.Ceiling((sumN2 + rop - cpQty - zzQty - mjQty) / packQty) * packQty;
|
|
|
- //负数置0
|
|
|
- monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
|
|
|
- monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
|
|
|
- //记录需要回写的数据
|
|
|
- updates.Add(monthN1);
|
|
|
- updates.Add(monthN2);
|
|
|
- }
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 海外销售预测,回写年度生产大纲
|
|
|
- List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
|
|
|
- foreach (var hwModel in hwModels)
|
|
|
- {
|
|
|
- //N+1月
|
|
|
- YearDemandManagement monthN1 = new YearDemandManagement();
|
|
|
- //N+2月
|
|
|
- YearDemandManagement monthN2 = new YearDemandManagement();
|
|
|
- //获取需要回写的数据
|
|
|
- if (input.month < 11)
|
|
|
- {
|
|
|
- monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (input.month == 11)
|
|
|
- {
|
|
|
- monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (input.month == 12)
|
|
|
- {
|
|
|
- monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
|
|
|
- monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
|
|
|
- }
|
|
|
- if (monthN1 == null || monthN2 == null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- var sumN1 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN1).Sum(p => p.Qty);
|
|
|
- var sumN2 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN2).Sum(p => p.Qty);
|
|
|
-
|
|
|
- //当前规格型号对应标准SKU的最小包装单位、补货周期
|
|
|
- var curStd = standards.FirstOrDefault(p => p.Model == hwModel);
|
|
|
- decimal packQty = 1m;//最小包装单位
|
|
|
- //获取成品库存、在制库存、灭菌库存,参与计算
|
|
|
- decimal cpQty = 0m;
|
|
|
- decimal zzQty = 0m;
|
|
|
- decimal mjQty = 0m;
|
|
|
- if (curStd != null)
|
|
|
- {
|
|
|
- var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
|
|
|
- packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
|
|
|
- //成品库存
|
|
|
- cpQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "8001").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- //在制库存
|
|
|
- zzQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "8000").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- //灭菌库存
|
|
|
- mjQty = locations.Where(p => p.ItemNum == curStd.ItemNumber && p.Location == "5008").Sum(p => Convert.ToDecimal(p.QtyOnHand));
|
|
|
- }
|
|
|
- //海外生产需求量=当月的50%+下一月的50%-成品库存-在制库存-灭菌库存
|
|
|
- //TODO:获取成品库存、在制库存、灭菌库存,参与运算
|
|
|
- //计算N+1月,N+2月
|
|
|
- monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 - cpQty - zzQty - mjQty) / packQty) * packQty;
|
|
|
- monthN2.Qty = Math.Ceiling((sumN2 - cpQty - zzQty - mjQty) / packQty) *packQty; ;
|
|
|
- //负数置0
|
|
|
- monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
|
|
|
- monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
|
|
|
- //记录需要回写的数据
|
|
|
- updates.Add(monthN1);
|
|
|
- updates.Add(monthN2);
|
|
|
- }
|
|
|
- #endregion
|
|
|
-
|
|
|
- //生成月度产能共识
|
|
|
- var capacityDto = CapacityAnalysis(input, updates, planMons);
|
|
|
-
|
|
|
- //生成下一个月的补货计划
|
|
|
- var nextMonPFcsts = platformFcsts.Where(p => p.PlanMonth == strN2).ToList();
|
|
|
- var nextMonDFcsts = domesticFcst.Where(p => p.PlanMonth == strN2).ToList();
|
|
|
- var nextMonT2Fcsts = T2Fcsts.Where(p => p.PlanMonth == strN2).ToList();
|
|
|
- var nextMonMonitors = monitorSettings.Where(p => p.PlanMonth == strN1).ToList();
|
|
|
- var replenishs = MonthlyReplenish(input,strN1, nextMonPFcsts, nextMonDFcsts, nextMonT2Fcsts, standards, items, holidays, nextMonMonitors, locations);
|
|
|
-
|
|
|
- //保存数据
|
|
|
- using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- //判断是否存在当前年月的历史版本,不存在则插入
|
|
|
- var dbHistory = _yearDemandManagementHistory.GetListAsync(p=>p.Year ==input.year && p.UpdateMonth == updateMonth && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id).Result;
|
|
|
- if (!dbHistory.Any())
|
|
|
- {
|
|
|
- await _yearDemandManagementHistory.InsertManyAsync(histories);
|
|
|
- }
|
|
|
- if (input.month > 10)
|
|
|
- {
|
|
|
- //判断是否存在下一年月的历史版本,不存在则插入
|
|
|
- dbHistory = _yearDemandManagementHistory.GetListAsync(p =>(p.Year == input.year + 1) && p.UpdateMonth == updateMonth && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id).Result;
|
|
|
- if (!dbHistory.Any())
|
|
|
- {
|
|
|
- await _yearDemandManagementHistory.InsertManyAsync(nextHistories);
|
|
|
- }
|
|
|
- }
|
|
|
- //回写年度生产大纲
|
|
|
- await _yearDemandManagement.UpdateManyAsync(updates);
|
|
|
- //先删除产能共识、产能共识明细
|
|
|
- await _monthlyProdCapacity.HardDeleteAsync(p=>p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id);
|
|
|
- await _monthlyProdCapacityDtl.HardDeleteAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id);
|
|
|
- await _monthlyProdCapacity.InsertManyAsync(capacityDto.mains);
|
|
|
- await _monthlyProdCapacityDtl.InsertManyAsync(capacityDto.details);
|
|
|
- //生成下一月的补货计划
|
|
|
- await _crm_planorder.HardDeleteAsync(p=>p.PlanMonth == strN1 && p.Source =="系统运算" && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id);
|
|
|
- await _crm_planorder.InsertManyAsync(replenishs);
|
|
|
-
|
|
|
- await unitOfWork.CompleteAsync();
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- unitOfWork.Dispose();
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "【" + input.year + "年" + input.month + "月】月度需求预测更新失败:" + e.Message, _currentTenant.Id.ToString());
|
|
|
- return "NO|" + e.Message;
|
|
|
- };
|
|
|
- }
|
|
|
- return "OK|刷新成功!";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 生成月度产能共识
|
|
|
- /// </summary>
|
|
|
- /// <param name="input"></param>
|
|
|
- /// <returns></returns>
|
|
|
- public MonthlyCapacityDto CapacityAnalysis(InputDto input,List<YearDemandManagement> yearDemands, List<string> planMons)
|
|
|
- {
|
|
|
- //1、获取数据
|
|
|
- //1.1 根据规格型号获取物料数据
|
|
|
- List<string> models = yearDemands.Select(p => p.Model).Distinct().ToList();
|
|
|
- List<StandardItemModelSet> standards = _standardItemModelSet.GetListAsync(p => models.Contains(p.Model) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id).Result;
|
|
|
- //1.3 根据物料编码获取产线数据
|
|
|
- List<ProdLineDetail> lineDtls = _prodLineDetail.Select(p => standards.Select(m => m.ItemNumber).Contains(p.Part) && p.Domain == input.factory_id.ToString() && p.IsActive).OrderBy(p => p.Line).ToList();
|
|
|
- //1.4 根据产线获取工作日历数据和产线休息配置数据
|
|
|
- List<ShopCalendarWorkCtr> calendars = _shopCalendarWorkCtr.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
|
|
|
- List<QualityLineWorkDetail> lineWorks = _qualityLineWorkDetail.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
|
|
|
- //1.5 获取当前年和下一年的节假日配置数据
|
|
|
- List<HolidayMaster> holidays = _holidayMaster.Select(p => (p.Dated.Value.Year == input.year || p.Dated.Value.Year == (input.year + 1)) && p.Domain == input.factory_id.ToString() && p.IsActive);
|
|
|
-
|
|
|
- //月度产能共识表
|
|
|
- List<MonthlyProdCapacity> capacities = new List<MonthlyProdCapacity>();
|
|
|
- //月度产能共识明细表
|
|
|
- List<MonthlyProdCapacityDtl> capacityDtls = new List<MonthlyProdCapacityDtl>();
|
|
|
-
|
|
|
- //产线
|
|
|
- List<string> lines = lineDtls.Select(p => p.Line).Distinct().ToList();
|
|
|
- foreach (var item in lines)
|
|
|
- {
|
|
|
- //计算每天工作时间
|
|
|
- var curCal = calendars.FirstOrDefault(p => p.ProdLine == item);
|
|
|
- if (curCal == null)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- foreach (var pm in planMons)
|
|
|
- {
|
|
|
- //添加月度产能共识产能效率数据
|
|
|
- MonthlyProdCapacity dtl = new MonthlyProdCapacity();
|
|
|
- dtl.Year = input.year;
|
|
|
- dtl.Month = input.month;
|
|
|
- dtl.PlanMonth = pm;
|
|
|
- dtl.ProdLine = item;
|
|
|
-
|
|
|
- dtl.DailyWorks = curCal.ShiftsHours1 + curCal.ShiftsHours2 + curCal.ShiftsHours3 + curCal.ShiftsHours4;
|
|
|
- dtl.FlightQty = 1;
|
|
|
- //计算当月工作天数
|
|
|
- var curHoildays = holidays.Where(p => p.Dated.Value.Year == Convert.ToInt16(pm.Substring(0,4)) && p.Dated.Value.Month == Convert.ToInt16(pm.Substring(5, 2))).ToList();
|
|
|
- //当月天数
|
|
|
- int days = DateTime.DaysInMonth(input.year, input.month);
|
|
|
- //当月周末天数
|
|
|
- int weekDays = CalcWeekDays(days, Convert.ToDateTime(pm + "-01"));
|
|
|
- dtl.MonthWorks = days - weekDays - curHoildays.Where(p => p.Ufld1 == "休假").Count() + curHoildays.Where(p => p.Ufld1 == "调班").Count();
|
|
|
- dtl.AvailableTimes = dtl.DailyWorks * dtl.MonthWorks;
|
|
|
- //计算产线耗时
|
|
|
- var curLines = lineDtls.Where(p => p.Line == item).ToList();
|
|
|
- decimal sumTimes = 0.00m;
|
|
|
- List<ProdLineDto> lineDtos = new List<ProdLineDto>();
|
|
|
- foreach (var ld in curLines)
|
|
|
- {
|
|
|
- if (lineDtos.Exists(p=>p.Part == ld.Part && p.Line == ld.Line))
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- var curStand = standards.FirstOrDefault(p => p.ItemNumber == ld.Part);
|
|
|
- if (curStand == null)
|
|
|
- {
|
|
|
- continue;
|
|
|
- }
|
|
|
- var curDemands = yearDemands.Where(p => p.Model == curStand.Model && p.PlanMonth == pm).ToList();
|
|
|
- var line = curLines.Where(p=>p.Part == ld.Part && p.Line == ld.Line).OrderByDescending(p => p.Op).First();
|
|
|
- sumTimes += line.Rate == 0 ? 0 : (Math.Ceiling(curDemands.Sum(p => p.Qty) / line.Rate));
|
|
|
-
|
|
|
- MonthlyProdCapacityDtl capacityDtl = new MonthlyProdCapacityDtl();
|
|
|
- capacityDtl.Year = input.year;
|
|
|
- capacityDtl.Month = input.month;
|
|
|
- capacityDtl.PlanMonth = pm;
|
|
|
- capacityDtl.ProdLine = item;
|
|
|
- capacityDtl.Model = curStand.Model;
|
|
|
- capacityDtl.Qty = curDemands.Sum(p => p.Qty);
|
|
|
- capacityDtl.tenant_id = input.tenant_id;
|
|
|
- capacityDtl.company_id = input.company_id;
|
|
|
- capacityDtl.factory_id = input.factory_id;
|
|
|
- capacityDtl.org_id = input.org_id;
|
|
|
- capacityDtl.create_by = input.create_by;
|
|
|
- capacityDtl.create_by_name = input.create_by_name;
|
|
|
- capacityDtl.create_time = DateTime.Now;
|
|
|
- capacityDtls.Add(capacityDtl);
|
|
|
-
|
|
|
- lineDtos.Add(new ProdLineDto {
|
|
|
- Part = ld.Part,
|
|
|
- Line= ld.Line
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- dtl.NeedWorks = sumTimes;
|
|
|
- dtl.ProdRate = 100;
|
|
|
- dtl.Rate = dtl.AvailableTimes == 0 ? 0 : Math.Floor(dtl.NeedWorks / dtl.AvailableTimes * 100);
|
|
|
- dtl.IsOverTime = dtl.NeedWorks > dtl.AvailableTimes ? "是" : "否";
|
|
|
- dtl.OverTimes = dtl.IsOverTime == "是" ? (dtl.NeedWorks - dtl.AvailableTimes) : 0;
|
|
|
- dtl.tenant_id = input.tenant_id;
|
|
|
- dtl.company_id= input.company_id;
|
|
|
- dtl.factory_id = input.factory_id;
|
|
|
- dtl.org_id= input.org_id;
|
|
|
- dtl.create_by= input.create_by;
|
|
|
- dtl.create_by_name = input.create_by_name;
|
|
|
- dtl.create_time = DateTime.Now;
|
|
|
- capacities.Add(dtl);
|
|
|
- }
|
|
|
- }
|
|
|
- return new MonthlyCapacityDto{
|
|
|
- mains = capacities,
|
|
|
- details = capacityDtls
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// T1、平台自动补货
|
|
|
- /// </summary>
|
|
|
- /// <param name="input"></param>
|
|
|
- /// <param name="strN1">计划补货月份:N+1月</param>
|
|
|
- /// <param name="platformFcsts">平台预测</param>
|
|
|
- /// <param name="domesticFcsts">T1预测</param>
|
|
|
- /// <param name="standards">标准SKU设置</param>
|
|
|
- /// <param name="items">物料</param>
|
|
|
- /// <param name="holidays">节假日</param>
|
|
|
- /// <param name="monitorSettings">库存监控月份设置</param>
|
|
|
- /// <param name="locations">SAP同步库存</param>
|
|
|
- /// <returns></returns>
|
|
|
- public List<crm_planorder> MonthlyReplenish(InputDto input,string strN1, List<PlatformFcstCollect> platformFcsts, List<DomesticTerminalFcst> domesticFcsts, List<DomesticTerminalFcst> T2Fcsts, List<StandardItemModelSet> standards, List<ic_item> items, List<HolidayMaster> holidays, List<PlatStockMonitorSetting> monitorSettings, List<LocationDetail> locations)
|
|
|
- {
|
|
|
- List<crm_planorder> planorders = new List<crm_planorder>();
|
|
|
- //获取T1中规格型号对应的不同版本的物料编码
|
|
|
- var T1Models = domesticFcsts.Select(p => p.Model).Distinct().ToList();
|
|
|
- List<SkuVersionSet> skus = _skuVersionSet.GetListAsync(p=> T1Models.Contains(p.Model) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result;
|
|
|
- //获取T1库存
|
|
|
- //List<string> itemNums = skus.Select(p=>p.ItemNum).Distinct().ToList();
|
|
|
- //List<LocationDetail> locationDetails = _locationDetail.Select(p=> itemNums.Contains(p.ItemNum) && p.Domain == input.factory_id.ToString() && p.IsActive);
|
|
|
-
|
|
|
- //获取临期库存设置
|
|
|
- GeneralizedCodeMaster master = _generalizedCodeMaster.Select(p => p.Domain == input.factory_id.ToString() && p.IsActive && p.Val == "LongPeriodItemPlanMonth").FirstOrDefault();
|
|
|
- int month = master == null ? 0 : Convert.ToInt16(master.UDeci1);
|
|
|
- DateTime strTime = Convert.ToDateTime(platformFcsts[0].PlanMonth + "-01").AddMonths(month);
|
|
|
- //获取平台库存
|
|
|
- var PModels = platformFcsts.Select(p => p.Model).Distinct().ToList();
|
|
|
- List<WMS_PlatformInventory> pInventories = _platformInventory.GetListAsync(p=> PModels.Contains(p.SpecificationModel) && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && p.PeriodOfValidity >= strTime).Result;
|
|
|
-
|
|
|
- //计算T1补货
|
|
|
- foreach (var item in T1Models)
|
|
|
- {
|
|
|
- //获取当前规格型号对应的预测数据
|
|
|
- var curFcsts = domesticFcsts.Where(p=>p.Model == item).ToList();
|
|
|
- //当前规格型号对应的库存数量
|
|
|
- decimal sumQty = 0.00m;
|
|
|
- //当前的物料版本
|
|
|
- var curSkus = skus.Where(p => p.Model == item).ToList();
|
|
|
- if (curSkus.Any())
|
|
|
- {
|
|
|
- //sumQty = sAPInvs.Where(p => curSkus.Select(p => p.ItemNum).Contains(p.MATNR)).Sum(p => Convert.ToDecimal(p.LABST));
|
|
|
- sumQty = locations.Where(p => curSkus.Select(p => p.ItemNum).Contains(p.ItemNum) && p.Location == "8001").Sum(p => p.QtyOnHand);
|
|
|
- }
|
|
|
- //计算Rop
|
|
|
- //当前规格型号对应标准SKU的最小包装单位、补货周期
|
|
|
- var curStd = standards.FirstOrDefault(p => p.Model == item);
|
|
|
- decimal packQty = 1m;//最小包装单位
|
|
|
- decimal cycle = 0m;//补货周期
|
|
|
- if (curStd != null)
|
|
|
- {
|
|
|
- var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
|
|
|
- packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
|
|
|
- cycle = curStd.ReplenishCycle;
|
|
|
- }
|
|
|
- //N+1月使用N+2月的再订货点参与计算
|
|
|
- decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curFcsts.Sum(p=>p.Qty), packQty, holidays, cycle);
|
|
|
- if(sumQty < rop)
|
|
|
- {
|
|
|
- planorders.Add(new crm_planorder {
|
|
|
- PlanMonth = strN1,
|
|
|
- Model = curFcsts[0].Model,
|
|
|
- ItemNum = curStd?.ItemNumber,
|
|
|
- ProdLine = curFcsts[0].ProdLine,
|
|
|
- ProdType = "",
|
|
|
- Qty = rop,
|
|
|
- Type = "计划单-T1直发补货"
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //计算平台补货:海王
|
|
|
- var hwFcsts = platformFcsts.Where(p => p.Platform == "海王").ToList();
|
|
|
- var hwModels = hwFcsts.Select(p=>p.Model).Distinct().ToList();
|
|
|
- foreach (var item in hwModels)
|
|
|
- {
|
|
|
- //获取当前规格型号对应的平台预测数据
|
|
|
- var curFcsts = hwFcsts.Where(p => p.Model == item).ToList();
|
|
|
- //当前规格型号对应的库存数量
|
|
|
- decimal sumQty = 0.00m;
|
|
|
- //当前规格型号对应标准SKU的最小包装单位、补货周期
|
|
|
- var curStd = standards.FirstOrDefault(p => p.Model == item);
|
|
|
- decimal packQty = 1m;//最小包装单位
|
|
|
- decimal cycle = 0m;//补货周期
|
|
|
- if (curStd != null)
|
|
|
- {
|
|
|
- var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
|
|
|
- packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
|
|
|
- cycle = curStd.ReplenishCycle;
|
|
|
- }
|
|
|
- //T2海王预测数据
|
|
|
- var curT2Hws = T2Fcsts.Where(p => p.Model == item && p.TypeEnum == 4).ToList();
|
|
|
- //计算Rop:N+1月使用N+2月的再订货点参与计算
|
|
|
- decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curT2Hws.Sum(p => p.Qty), packQty, holidays, cycle);
|
|
|
- //获取海王当前规格型号库存
|
|
|
- sumQty = pInventories.Where(p => p.SpecificationModel == item && p.Code == "HW0001").Sum(p=>p.InventoryQuantity);
|
|
|
- //获取N+1月的库存监控设置
|
|
|
- var curMonitor = monitorSettings.FirstOrDefault(p => p.Platform == "海王" && p.ProdType == curFcsts[0].ProdType);
|
|
|
- if (curMonitor == null)
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "海王未维护产品类型为【"+ curFcsts[0].ProdType + "】的库存监控月份", _currentTenant.Id.ToString());
|
|
|
- continue;
|
|
|
- }
|
|
|
- //最小覆盖月份库存
|
|
|
- var minQty = rop * curMonitor.MinTimes;
|
|
|
- //最大库存月份库存
|
|
|
- var maxQty = rop * curMonitor.MaxTimes;
|
|
|
- //需补货数量
|
|
|
- decimal needQty = 0m;
|
|
|
- if (sumQty >= minQty && sumQty < maxQty)//大于等于最低库存覆盖月,小于最高库存覆盖月
|
|
|
- {
|
|
|
- planorders.Add(new crm_planorder
|
|
|
- {
|
|
|
- PlanMonth = strN1,
|
|
|
- Model = curFcsts[0].Model,
|
|
|
- ItemNum = curStd?.ItemNumber,
|
|
|
- ProdLine = curT2Hws[0].ProdLine,
|
|
|
- ProdType = "",
|
|
|
- Qty = maxQty - sumQty,
|
|
|
- Type = "计划单-T2平台补货"
|
|
|
- });
|
|
|
- }
|
|
|
- if (sumQty < minQty)//低于最低库存覆盖月
|
|
|
- {
|
|
|
- while (sumQty < minQty)
|
|
|
- {
|
|
|
- sumQty += rop;
|
|
|
- needQty += rop;
|
|
|
- }
|
|
|
- planorders.Add(new crm_planorder
|
|
|
- {
|
|
|
- PlanMonth = strN1,
|
|
|
- Model = curFcsts[0].Model,
|
|
|
- ItemNum = curStd?.ItemNumber,
|
|
|
- ProdLine = curT2Hws[0].ProdLine,
|
|
|
- ProdType = "",
|
|
|
- Qty = needQty,
|
|
|
- Type = "计划单-T2平台补货"
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- //计算平台补货:国科
|
|
|
- var gkFcsts = platformFcsts.Where(p => p.Platform == "国科").ToList();
|
|
|
- var gkModels = gkFcsts.Select(p => p.Model).Distinct().ToList();
|
|
|
- foreach (var item in gkModels)
|
|
|
- {
|
|
|
- //获取当前规格型号对应的预测数据
|
|
|
- var curFcsts = gkFcsts.Where(p => p.Model == item).ToList();
|
|
|
- //当前规格型号对应的库存数量
|
|
|
- decimal sumQty = 0.00m;
|
|
|
- //当前规格型号对应标准SKU的最小包装单位、补货周期
|
|
|
- var curStd = standards.FirstOrDefault(p => p.Model == item);
|
|
|
- decimal packQty = 1m;//最小包装单位
|
|
|
- decimal cycle = 0m;//补货周期
|
|
|
- if (curStd != null)
|
|
|
- {
|
|
|
- var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
|
|
|
- packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
|
|
|
- cycle = curStd.ReplenishCycle;
|
|
|
- }
|
|
|
- //T2国科预测数据
|
|
|
- var curT2Gks = T2Fcsts.Where(p => p.Model == item && p.TypeEnum == 5).ToList();
|
|
|
- //计算Rop:N+1月使用N+2月的再订货点参与计算
|
|
|
- decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curT2Gks.Sum(p => p.Qty), packQty, holidays, cycle);
|
|
|
- //获取国科当前规格型号库存
|
|
|
- sumQty = pInventories.Where(p => p.SpecificationModel == item && p.Code == "GK0001").Sum(p => p.InventoryQuantity);
|
|
|
- //获取N+1月的库存监控设置
|
|
|
- var curMonitor = monitorSettings.FirstOrDefault(p => p.Platform == "国科" && p.ProdType == curFcsts[0].ProdType);
|
|
|
- if (curMonitor == null)
|
|
|
- {
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "国科未维护产品类型为【" + curFcsts[0].ProdType + "】的库存监控月份", _currentTenant.Id.ToString());
|
|
|
- continue;
|
|
|
- }
|
|
|
- //最小覆盖月份库存
|
|
|
- var minQty = rop * curMonitor.MinTimes;
|
|
|
- //最大库存月份库存
|
|
|
- var maxQty = rop * curMonitor.MaxTimes;
|
|
|
- //需补货数量
|
|
|
- decimal needQty = 0m;
|
|
|
- if (sumQty >= minQty && sumQty < maxQty)
|
|
|
- {
|
|
|
- planorders.Add(new crm_planorder
|
|
|
- {
|
|
|
- PlanMonth = strN1,
|
|
|
- Model = curFcsts[0].Model,
|
|
|
- ItemNum = curStd?.ItemNumber,
|
|
|
- ProdLine = curT2Gks[0].ProdLine,
|
|
|
- ProdType = "",
|
|
|
- Qty = maxQty - sumQty,
|
|
|
- Type = "计划单-T2平台补货"
|
|
|
- });
|
|
|
- }
|
|
|
- if (sumQty < minQty)
|
|
|
- {
|
|
|
- while (sumQty < minQty)
|
|
|
- {
|
|
|
- sumQty += rop;
|
|
|
- needQty += rop;
|
|
|
- }
|
|
|
- planorders.Add(new crm_planorder
|
|
|
- {
|
|
|
- PlanMonth = strN1,
|
|
|
- Model = curFcsts[0].Model,
|
|
|
- ItemNum = curStd?.ItemNumber,
|
|
|
- ProdLine = curT2Gks[0].ProdLine,
|
|
|
- ProdType = "",
|
|
|
- Qty = needQty,
|
|
|
- Type = "计划单-T2平台补货"
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- planorders.ForEach(p => {
|
|
|
- p.Source = "系统运算";
|
|
|
- p.tenant_id = input.tenant_id;
|
|
|
- p.company_id = input.company_id;
|
|
|
- p.factory_id = input.factory_id;
|
|
|
- p.org_id = input.org_id;
|
|
|
- p.create_by = input.create_by;
|
|
|
- p.create_by_name = input.create_by_name;
|
|
|
- p.create_time = DateTime.Now;
|
|
|
- });
|
|
|
- return planorders;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计算再订货点
|
|
|
- /// </summary>
|
|
|
- /// <param name="planMonth">计划年月(yyyy-MM-dd)</param>
|
|
|
- /// <param name="qty">需求量</param>
|
|
|
- /// <param name="qty">最小包装数量</param>
|
|
|
- /// <param name="holidays">节假日</param>
|
|
|
- /// <param name="replenishCycle">补货周期</param>
|
|
|
- /// <returns></returns>
|
|
|
- private decimal CalcRop(string planMonth,decimal qty,decimal packQty,List<HolidayMaster> holidays,decimal replenishCycle)
|
|
|
- {
|
|
|
- decimal rop = 0.0m;
|
|
|
- //获取当月天数
|
|
|
- int year = Convert.ToInt16(planMonth.Substring(0, 4));
|
|
|
- int month = Convert.ToInt16(planMonth.Substring(5, 2));
|
|
|
- int days = DateTime.DaysInMonth(year, month);
|
|
|
- //计算当前月的周末天数
|
|
|
- int weeks = CalcWeekDays(days, Convert.ToDateTime(planMonth));
|
|
|
- //获取当前年月的节假日设置
|
|
|
- var curHolidays = holidays.Where(p => p.Dated.Value.Year == year && p.Dated.Value.Month == month).ToList();
|
|
|
- //当月工作天数
|
|
|
- int workDays = days - weeks - curHolidays.Where(p => p.Ufld1 == "休假").Count() + curHolidays.Where(p => p.Ufld1 == "调班").Count();
|
|
|
- //rop = (需求量/月工作天数*补货周期)=>按照最小包装单位元整
|
|
|
- rop = Math.Ceiling(qty / workDays * replenishCycle / packQty) * packQty;
|
|
|
- return rop;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计算当月有多少个周末
|
|
|
- /// </summary>
|
|
|
- /// <param name="days"></param>
|
|
|
- /// <param name="startDay"></param>
|
|
|
- /// <returns></returns>
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 生成整体需求计划
|
|
|
- /// </summary>
|
|
|
- /// <param name="input"></param>
|
|
|
- /// <returns></returns>
|
|
|
- /// <exception cref="NotImplementedException"></exception>
|
|
|
- public async Task<string> OverallDemandPlan(InputDto input)
|
|
|
- {
|
|
|
- //1.1、获取海外销售预测数据
|
|
|
- List<OverseasSaleFcst> overseasSales = _overseasSaleFcst.GetListAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p => p.OrderNum).ToList();
|
|
|
- //1.2、获取平台预测收集数据
|
|
|
- List<PlatformFcstCollect> platformFcsts = _platformFcstCollect.GetListAsync(p => p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p=>p.OrderNum).ToList();
|
|
|
- //1.3、获取国内终端预测-T1汇总数据
|
|
|
- List<DomesticTerminalFcst> domesticFcsts = _domesticTerminalFcst.GetListAsync(p => p.TypeEnum == 2 && p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id && !p.IsDeleted).Result.OrderBy(p => p.OrderNum).ToList();
|
|
|
-
|
|
|
- //计算当前年月的N+1,N+2
|
|
|
- string strN0 = input.year.ToString() + "-" + input.month.ToString("00");
|
|
|
- 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");
|
|
|
- newYear = newMonth == 12 ? newYear + 1 : newYear;
|
|
|
- newMonth = newMonth == 12 ? 1 : newMonth + 1;
|
|
|
- string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
|
|
|
-
|
|
|
- //整体需求计划
|
|
|
- List<OverallDemandPlan> plans = new List<OverallDemandPlan>();
|
|
|
- //计算海外
|
|
|
- List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
|
|
|
- int OrderNum = 1;
|
|
|
- foreach (var item in hwModels)
|
|
|
- {
|
|
|
- var curFcsts = overseasSales.Where(p => p.Model == item).ToList();
|
|
|
- OverallDemandPlan plan = new OverallDemandPlan();
|
|
|
- plan.Area = "海外";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN0;
|
|
|
- plan.Qty = curFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- plan = new OverallDemandPlan();
|
|
|
- plan.Area = "海外";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN1;
|
|
|
- plan.Qty = curFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- plan = new OverallDemandPlan();
|
|
|
- plan.Area = "海外";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN2;
|
|
|
- plan.Qty = curFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- OrderNum += 1;
|
|
|
- }
|
|
|
- //计算国内
|
|
|
- List<string> gnModels = domesticFcsts.Select(p=>p.Model).ToList();
|
|
|
- gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
|
|
|
- gnModels = gnModels.Distinct().ToList();
|
|
|
- OrderNum = 1;
|
|
|
- foreach (var item in gnModels)
|
|
|
- {
|
|
|
- var curDFcsts = domesticFcsts.Where(p => p.Model == item).ToList();
|
|
|
- var curPFcsts = platformFcsts.Where(p => p.Model == item).ToList();
|
|
|
- OverallDemandPlan plan = new OverallDemandPlan();
|
|
|
- plan.Area = "国内";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN0;
|
|
|
- plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- plan = new OverallDemandPlan();
|
|
|
- plan.Area = "国内";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN1;
|
|
|
- plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- plan = new OverallDemandPlan();
|
|
|
- plan.Area = "国内";
|
|
|
- plan.Model = item;
|
|
|
- plan.PlanMonth = strN2;
|
|
|
- plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
|
|
|
- plan.OrderNum = OrderNum;
|
|
|
- plans.Add(plan);
|
|
|
-
|
|
|
- OrderNum += 1;
|
|
|
- }
|
|
|
-
|
|
|
- plans.ForEach(p => {
|
|
|
- p.Year= input.year;
|
|
|
- p.Month= input.month;
|
|
|
- p.tenant_id = input.tenant_id;
|
|
|
- p.company_id = input.company_id;
|
|
|
- p.factory_id= input.factory_id;
|
|
|
- p.org_id= input.org_id;
|
|
|
- p.create_by = input.create_by;
|
|
|
- p.create_by_name= input.create_by_name;
|
|
|
- p.create_time = DateTime.Now;
|
|
|
- });
|
|
|
-
|
|
|
- //保存数据
|
|
|
- using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
|
|
|
- {
|
|
|
- try
|
|
|
- {
|
|
|
- //先删除
|
|
|
- await _overallDemandPlan.HardDeleteAsync(p=>p.Year == input.year && p.Month == input.month && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id);
|
|
|
- //保存整体需求计划
|
|
|
- await _overallDemandPlan.InsertManyAsync(plans);
|
|
|
- await unitOfWork.CompleteAsync();
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- unitOfWork.Dispose();
|
|
|
- new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("OverallDemandPlan", "生成【" + input.year + "年" + input.month + "月】整体需求计划失败:" + e.Message, _currentTenant.Id.ToString());
|
|
|
- return "NO|" + e.Message;
|
|
|
- };
|
|
|
- }
|
|
|
- return "OK|刷新成功!";
|
|
|
- }
|
|
|
- }
|
|
|
-}
|