MonthlyCapacityLoadAppService.cs 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  1. using Azure.Identity;
  2. using Business.Core.Utilities;
  3. using Business.Domain;
  4. using Business.Dto;
  5. using Business.EntityFrameworkCore.SqlRepositories;
  6. using Business.ResourceExamineManagement.Dto;
  7. using Business.SaleForecast;
  8. using Business.StructuredDB.WMS;
  9. using Microsoft.CodeAnalysis.CSharp.Syntax;
  10. using Spire.Pdf.General.Render.Decode.Jpeg2000.j2k.quantization;
  11. using Spire.Pdf.General.Render.Decode.Jpeg2000.j2k.roi.encoder;
  12. using System;
  13. using System.Collections.Generic;
  14. using System.Linq;
  15. using System.Runtime.CompilerServices;
  16. using System.Threading.Tasks;
  17. using ThoughtWorks.QRCode.Geom;
  18. using Volo.Abp.Application.Services;
  19. using Volo.Abp.DependencyInjection;
  20. using Volo.Abp.Domain.Repositories;
  21. using Volo.Abp.MultiTenancy;
  22. using Volo.Abp.Uow;
  23. using Volo.Abp.Validation.Localization;
  24. namespace Business.SaleForecastManagement
  25. {
  26. /// <summary>
  27. /// 产能分析
  28. /// </summary>
  29. public class MonthlyCapacityLoadAppService : ApplicationService, IMonthlyCapacityLoadAppService, ITransientDependency
  30. {
  31. #region 服务
  32. /// <summary>
  33. /// 物料
  34. /// </summary>
  35. private IRepository<ic_item, long> _ic_item;
  36. /// <summary>
  37. /// 生产线明细
  38. /// </summary>
  39. private ISqlRepository<ProdLineDetail> _prodLineDetail;
  40. /// <summary>
  41. /// 工作日历数据
  42. /// </summary>
  43. private ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
  44. /// <summary>
  45. /// 产线休息时间记录表
  46. /// </summary>
  47. private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
  48. /// <summary>
  49. /// 节假日记录表
  50. /// </summary>
  51. private ISqlRepository<HolidayMaster> _holidayMaster;
  52. /// <summary>
  53. /// 工作单元
  54. /// </summary>
  55. private readonly IUnitOfWorkManager _unitOfWorkManager;
  56. /// <summary>
  57. /// 日志
  58. /// </summary>
  59. private readonly ICurrentTenant _currentTenant;
  60. /// <summary>
  61. /// 年度生产需求大纲
  62. /// </summary>
  63. private IRepository<YearDemandManagement, long> _yearDemandManagement;
  64. /// <summary>
  65. /// 年度生产需求大纲历史记录
  66. /// </summary>
  67. private IRepository<YearDemandManagementHistory, long> _yearDemandManagementHistory;
  68. /// <summary>
  69. /// 国内终端预测
  70. /// </summary>
  71. private IRepository<DomesticTerminalFcst, long> _domesticTerminalFcst;
  72. /// <summary>
  73. /// 海外销售预测
  74. /// </summary>
  75. private IRepository<OverseasSaleFcst, long> _overseasSaleFcst;
  76. /// <summary>
  77. /// 平台预测收集
  78. /// </summary>
  79. private IRepository<PlatformFcstCollect, long> _platformFcstCollect;
  80. /// <summary>
  81. /// 标准物料规格型号设置表
  82. /// </summary>
  83. private IRepository<StandardItemModelSet,long> _standardItemModelSet;
  84. /// <summary>
  85. /// 库存明细表
  86. /// </summary>
  87. private ISqlRepository<LocationDetail> _locationDetail;
  88. /// <summary>
  89. /// 工单
  90. /// </summary>
  91. private ISqlRepository<WorkOrdMaster> _workOrdMaster;
  92. /// <summary>
  93. /// 整体需求计划
  94. /// </summary>
  95. private IRepository<OverallDemandPlan, long> _overallDemandPlan;
  96. /// <summary>
  97. /// 月度产能共识表
  98. /// </summary>
  99. private IRepository<MonthlyProdCapacity, long> _monthlyProdCapacity;
  100. /// <summary>
  101. /// 月度产能共识明细表
  102. /// </summary>
  103. private IRepository<MonthlyProdCapacityDtl, long> _monthlyProdCapacityDtl;
  104. /// <summary>
  105. /// SKU版本维护表
  106. /// </summary>
  107. private IRepository<SkuVersionSet, long> _skuVersionSet;
  108. /// <summary>
  109. /// 计划订单表
  110. /// </summary>
  111. private IRepository<crm_planorder, long> _crm_planorder;
  112. /// <summary>
  113. /// 平台库存监控月份
  114. /// </summary>
  115. private IRepository<PlatStockMonitorSetting, long> _platStockMonitorSetting;
  116. /// <summary>
  117. /// 平台库存表
  118. /// </summary>
  119. private IRepository<WMS_PlatformInventory, long> _platformInventory;
  120. /// <summary>
  121. /// 参数设置表
  122. /// </summary>
  123. private ISqlRepository<GeneralizedCodeMaster> _generalizedCodeMaster;
  124. /// <summary>
  125. /// 雪花算法
  126. /// </summary>
  127. SnowFlake help = new SnowFlake();
  128. #endregion
  129. #region 构造函数
  130. /// <summary>
  131. /// 构造函数
  132. /// </summary>
  133. public MonthlyCapacityLoadAppService(
  134. IRepository<ic_item, long> ic_item,
  135. ISqlRepository<ProdLineDetail> prodLineDetail,
  136. ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
  137. ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
  138. ISqlRepository<HolidayMaster> holidayMaster,
  139. IRepository<MonthlyProdCapacity, long> monthlyProdCapacity,
  140. IUnitOfWorkManager unitOfWorkManager,
  141. ICurrentTenant currentTenant,
  142. IRepository<StandardItemModelSet,long> standardItemModelSet,
  143. IRepository<YearDemandManagement, long> yearDemandManagement,
  144. IRepository<DomesticTerminalFcst, long> domesticTerminalFcst,
  145. IRepository<OverseasSaleFcst, long> overseasSaleFcst,
  146. IRepository<PlatformFcstCollect, long> platformFcstCollect,
  147. IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory,
  148. ISqlRepository<LocationDetail> locationDetail,
  149. ISqlRepository<WorkOrdMaster> workOrdMaster,
  150. IRepository<OverallDemandPlan, long> overallDemandPlan,
  151. IRepository<MonthlyProdCapacityDtl, long> monthlyProdCapacityDtl,
  152. IRepository<SkuVersionSet, long> skuVersionSet,
  153. IRepository<crm_planorder, long> crm_planorder,
  154. IRepository<PlatStockMonitorSetting, long> platStockMonitorSetting,
  155. IRepository<WMS_PlatformInventory, long> platformInventory,
  156. ISqlRepository<GeneralizedCodeMaster> generalizedCodeMaster
  157. )
  158. {
  159. _ic_item = ic_item;
  160. _prodLineDetail = prodLineDetail;
  161. _shopCalendarWorkCtr = shopCalendarWorkCtr;
  162. _qualityLineWorkDetail = qualityLineWorkDetail;
  163. _holidayMaster = holidayMaster;
  164. _monthlyProdCapacity = monthlyProdCapacity;
  165. _unitOfWorkManager = unitOfWorkManager;
  166. _currentTenant = currentTenant;
  167. _standardItemModelSet = standardItemModelSet;
  168. _yearDemandManagement= yearDemandManagement;
  169. _domesticTerminalFcst= domesticTerminalFcst;
  170. _overseasSaleFcst = overseasSaleFcst;
  171. _platformFcstCollect= platformFcstCollect;
  172. _yearDemandManagementHistory= yearDemandManagementHistory;
  173. _locationDetail = locationDetail;
  174. _workOrdMaster = workOrdMaster;
  175. _overallDemandPlan = overallDemandPlan;
  176. _monthlyProdCapacityDtl = monthlyProdCapacityDtl;
  177. _skuVersionSet= skuVersionSet;
  178. _crm_planorder= crm_planorder;
  179. _platStockMonitorSetting = platStockMonitorSetting;
  180. _platformInventory = platformInventory;
  181. _generalizedCodeMaster = generalizedCodeMaster;
  182. }
  183. #endregion
  184. /// <summary>
  185. /// 月度需求预测更新
  186. /// </summary>
  187. /// <param name="input"></param>
  188. /// <returns></returns>
  189. /// <exception cref="NotImplementedException"></exception>
  190. public async Task<string> DemandAnalysis(InputDto input)
  191. {
  192. //1.0 获取当前年年度生产需求大纲
  193. 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();
  194. //1.0 获取下一年年度生产大纲
  195. 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();
  196. //记录历史版本:当前年+下一年
  197. var updateMonth = input.year.ToString() + "-" + input.month.ToString("00");
  198. var histories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(yearDemands);
  199. histories.ForEach(p => {
  200. p.UpdateMonth = updateMonth;
  201. });
  202. var nextHistories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(nextYearDemands);
  203. nextHistories.ForEach(p => {
  204. p.UpdateMonth = updateMonth;
  205. });
  206. //1.1、获取海外销售预测数据
  207. 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;
  208. //1.2、获取平台预测收集数据
  209. 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;
  210. //1.3、获取国内终端预测-T1汇总数据
  211. 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;
  212. //1.3.1、获取T2-海王,T2-国科数据
  213. 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;
  214. //1.4、根据年度生产大纲,获取规格型号对应的补货周期
  215. List<string> allModels = yearDemands.Select(p=>p.Model).Distinct().ToList();
  216. if (nextYearDemands.Any())
  217. {
  218. allModels.AddRange(nextYearDemands.Select(p => p.Model).ToList());
  219. allModels = allModels.Distinct().ToList();
  220. }
  221. 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;
  222. //1.5、获取规格型号对应的标准SKU数据:获取最小包装单位
  223. List<string> itemNums = standards.Select(p=>p.ItemNumber).Distinct().ToList();
  224. 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;
  225. //1.6、获取成品库存、灭菌库存
  226. //List<LocationDetail> locations = _locationDetail.Select(p=>p.Domain == input.factory_id.ToString() && p.IsActive && itemNums.Contains(p.ItemNum));
  227. //1.7、获取在制库存:计划开始时间、计划结束时间都在本月,并且未关闭的工单
  228. //DateTime monStart = Convert.ToDateTime(input.year + "-" + input.month + "-01");
  229. //DateTime monEnd = monStart.AddMonths(1).AddDays(-1);
  230. //List<WorkOrdMaster> workOrds = _workOrdMaster.Select(p=>p.Domain);
  231. //1.9、获取节假日设置
  232. 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);
  233. //2.0、获取平台库存监控月份设置
  234. 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;
  235. #region 数据校验
  236. //1、校验当前年的年度生产大纲是否导入
  237. if (!yearDemands.Any())
  238. {
  239. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  240. return "NO|请导入" + input.year + "年的年度生产需求后再操作!";
  241. }
  242. //2、如果当前月份大于10,则需校验下一年的年度生产大纲
  243. if (input.month > 10 && !nextYearDemands.Any())
  244. {
  245. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + (input.year+1) + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  246. return "NO|请导入" + (input.year + 1) + "年的年度生产需求后再操作!";
  247. }
  248. //3、校验当前月份的海外销售预测是否导入
  249. if (!overseasSales.Any())
  250. {
  251. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!", _currentTenant.Id.ToString());
  252. return "NO|请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!";
  253. }
  254. //4、校验当前月份的平台预测收集是否导入
  255. if (!platformFcsts.Any())
  256. {
  257. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!", _currentTenant.Id.ToString());
  258. return "NO|请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!";
  259. }
  260. //5、校验当前月份的国内终端预测是否导入
  261. if (!domesticFcst.Any())
  262. {
  263. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!", _currentTenant.Id.ToString());
  264. return "NO|请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!";
  265. }
  266. //6、校验终端+平台导入的规格型号是否已维护标准SKU
  267. if (allModels.Count() != standards.Count())
  268. {
  269. allModels.RemoveAll(standards.Select(p=>p.Model).ToList());
  270. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",",allModels) + "】没有维护标准SKU,请维护后再操作!", _currentTenant.Id.ToString());
  271. return "NO|规格型号【" + string.Join(",", allModels) + "】没有维护标准SKU,请维护后再操作!";
  272. }
  273. //7、校验是否已维护补货周期
  274. var zeros = standards.Where(p => p.ReplenishCycle == 0).ToList();
  275. if (zeros.Any())
  276. {
  277. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",", zeros.Select(p=>p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!", _currentTenant.Id.ToString());
  278. return "NO|规格型号【" + string.Join(",", zeros.Select(p => p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!";
  279. }
  280. #endregion
  281. //计算当前年月的N0,N+1,N+2
  282. List<string> planMons = new List<string>();
  283. int newYear = input.month == 12 ? input.year + 1 : input.year;
  284. int newMonth = input.month == 12 ? 1 : input.month + 1;
  285. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  286. planMons.Add(strN1);
  287. newYear = newMonth == 12 ? newYear + 1 : newYear;
  288. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  289. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  290. planMons.Add(strN2);
  291. //需要回写的数据
  292. List<YearDemandManagement> updates = new List<YearDemandManagement>();
  293. #region 计算国内终端预测,回写年度生产大纲
  294. //获取导入的T1、平台数据
  295. List<string> gnModels = new List<string>();
  296. if (domesticFcst.Any())
  297. {
  298. gnModels.AddRange(domesticFcst.Select(p => p.Model).ToList());
  299. }
  300. if (platformFcsts.Any())
  301. {
  302. gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
  303. }
  304. gnModels = gnModels.Distinct().ToList();
  305. foreach (var gnModel in gnModels)
  306. {
  307. //N+1月
  308. YearDemandManagement monthN1 = new YearDemandManagement();
  309. //N+2月
  310. YearDemandManagement monthN2 = new YearDemandManagement();
  311. //获取需要回写的数据
  312. if (input.month < 11)
  313. {
  314. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  315. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  316. }
  317. if (input.month == 11)
  318. {
  319. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  320. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  321. }
  322. if (input.month == 12)
  323. {
  324. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  325. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  326. }
  327. if (monthN1 == null || monthN2 == null)
  328. {
  329. continue;
  330. }
  331. //T1
  332. var sumT1N1 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p=>p.Qty);
  333. var sumT1N2 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  334. //平台
  335. var sumPN1 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  336. var sumPN2 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  337. //合计
  338. var sumN1 = sumT1N1+ sumPN1;
  339. var sumN2 = sumT1N2 + sumPN2;
  340. //当前规格型号对应标准SKU的最小包装单位、补货周期
  341. var curStd = standards.FirstOrDefault(p => p.Model == gnModel);
  342. decimal packQty = 1m;//最小包装单位
  343. decimal cycle = 0m;//补货周期
  344. if (curStd != null)
  345. {
  346. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  347. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  348. cycle = curStd.ReplenishCycle;
  349. }
  350. //TODO:获取成品库存、在制库存、灭菌库存,参与计算
  351. //N+1月使用N+2月的再订货点参与计算
  352. decimal rop = CalcRop(strN2+"-01", sumN2, packQty, holidays, cycle);
  353. monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 + rop - 0 - 0 - 0) / packQty) * packQty;
  354. //N+2月使用本月的需求量,下一月的工作天数
  355. rop = CalcRop(Convert.ToDateTime(strN2 + "-01").AddMonths(1).ToString("yyyy-MM-dd"), sumN2, packQty, holidays, cycle);
  356. monthN2.Qty = Math.Ceiling((sumN2 + rop - 0 - 0 - 0) / packQty) * packQty;
  357. //负数置0
  358. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  359. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  360. //记录需要回写的数据
  361. updates.Add(monthN1);
  362. updates.Add(monthN2);
  363. }
  364. #endregion
  365. #region 海外销售预测,回写年度生产大纲
  366. List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
  367. foreach (var hwModel in hwModels)
  368. {
  369. //N+1月
  370. YearDemandManagement monthN1 = new YearDemandManagement();
  371. //N+2月
  372. YearDemandManagement monthN2 = new YearDemandManagement();
  373. //获取需要回写的数据
  374. if (input.month < 11)
  375. {
  376. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  377. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  378. }
  379. if (input.month == 11)
  380. {
  381. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  382. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  383. }
  384. if (input.month == 12)
  385. {
  386. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  387. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  388. }
  389. if (monthN1 == null || monthN2 == null) {
  390. continue;
  391. }
  392. var sumN1 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  393. var sumN2 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  394. //当前规格型号对应标准SKU的最小包装单位、补货周期
  395. var curStd = standards.FirstOrDefault(p => p.Model == hwModel);
  396. decimal packQty = 1m;//最小包装单位
  397. if (curStd != null)
  398. {
  399. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  400. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  401. }
  402. //海外生产需求量=当月的50%+下一月的50%-成品库存-在制库存-灭菌库存
  403. //TODO:获取成品库存、在制库存、灭菌库存,参与运算
  404. //计算N+1月,N+2月
  405. monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 - 0 - 0 - 0) / packQty) * packQty;
  406. monthN2.Qty = Math.Ceiling((sumN2 - 0 - 0 - 0) / packQty) *packQty; ;
  407. //负数置0
  408. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  409. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  410. //记录需要回写的数据
  411. updates.Add(monthN1);
  412. updates.Add(monthN2);
  413. }
  414. #endregion
  415. //生成月度产能共识
  416. var capacityDto = CapacityAnalysis(input, updates, planMons);
  417. //生成下一个月的补货计划
  418. var nextMonPFcsts = platformFcsts.Where(p => p.PlanMonth == strN2).ToList();
  419. var nextMonDFcsts = domesticFcst.Where(p => p.PlanMonth == strN2).ToList();
  420. var nextMonT2Fcsts = T2Fcsts.Where(p => p.PlanMonth == strN2).ToList();
  421. var nextMonMonitors = monitorSettings.Where(p => p.PlanMonth == strN1).ToList();
  422. var replenishs = MonthlyReplenish(input,strN1, nextMonPFcsts, nextMonDFcsts, nextMonT2Fcsts, standards, items, holidays, nextMonMonitors);
  423. //保存数据
  424. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  425. {
  426. try
  427. {
  428. //判断是否存在当前年月的历史版本,不存在则插入
  429. 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;
  430. if (!dbHistory.Any())
  431. {
  432. await _yearDemandManagementHistory.InsertManyAsync(histories);
  433. }
  434. if (input.month > 10)
  435. {
  436. //判断是否存在下一年月的历史版本,不存在则插入
  437. 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;
  438. if (!dbHistory.Any())
  439. {
  440. await _yearDemandManagementHistory.InsertManyAsync(nextHistories);
  441. }
  442. }
  443. //回写年度生产大纲
  444. await _yearDemandManagement.UpdateManyAsync(updates);
  445. //先删除产能共识、产能共识明细
  446. 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);
  447. 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);
  448. await _monthlyProdCapacity.InsertManyAsync(capacityDto.mains);
  449. await _monthlyProdCapacityDtl.InsertManyAsync(capacityDto.details);
  450. //生成下一月的补货计划
  451. 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);
  452. await _crm_planorder.InsertManyAsync(replenishs);
  453. await unitOfWork.CompleteAsync();
  454. }
  455. catch (Exception e)
  456. {
  457. unitOfWork.Dispose();
  458. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "【" + input.year + "年" + input.month + "月】月度需求预测更新失败:" + e.Message, _currentTenant.Id.ToString());
  459. return "NO|" + e.Message;
  460. };
  461. }
  462. return "OK|修订成功!";
  463. }
  464. /// <summary>
  465. /// 生成月度产能共识
  466. /// </summary>
  467. /// <param name="input"></param>
  468. /// <returns></returns>
  469. public MonthlyCapacityDto CapacityAnalysis(InputDto input,List<YearDemandManagement> yearDemands, List<string> planMons)
  470. {
  471. //1、获取数据
  472. //1.1 根据规格型号获取物料数据
  473. List<string> models = yearDemands.Select(p => p.Model).Distinct().ToList();
  474. 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;
  475. //1.3 根据物料编码获取产线数据
  476. 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();
  477. //1.4 根据产线获取工作日历数据和产线休息配置数据
  478. List<ShopCalendarWorkCtr> calendars = _shopCalendarWorkCtr.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  479. List<QualityLineWorkDetail> lineWorks = _qualityLineWorkDetail.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  480. //1.5 获取当前年和下一年的节假日配置数据
  481. 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);
  482. //月度产能共识表
  483. List<MonthlyProdCapacity> capacities = new List<MonthlyProdCapacity>();
  484. //月度产能共识明细表
  485. List<MonthlyProdCapacityDtl> capacityDtls = new List<MonthlyProdCapacityDtl>();
  486. //产线
  487. List<string> lines = lineDtls.Select(p => p.Line).Distinct().ToList();
  488. foreach (var item in lines)
  489. {
  490. //计算每天工作时间
  491. var curCal = calendars.FirstOrDefault(p => p.ProdLine == item);
  492. if (curCal == null)
  493. {
  494. continue;
  495. }
  496. foreach (var pm in planMons)
  497. {
  498. //添加月度产能共识产能效率数据
  499. MonthlyProdCapacity dtl = new MonthlyProdCapacity();
  500. dtl.Year = input.year;
  501. dtl.Month = input.month;
  502. dtl.PlanMonth = pm;
  503. dtl.ProdLine = item;
  504. dtl.DailyWorks = curCal.ShiftsHours1 + curCal.ShiftsHours2 + curCal.ShiftsHours3 + curCal.ShiftsHours4;
  505. dtl.FlightQty = 1;
  506. //计算当月工作天数
  507. 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();
  508. //当月天数
  509. int days = DateTime.DaysInMonth(input.year, input.month);
  510. //当月周末天数
  511. int weekDays = CalcWeekDays(days, Convert.ToDateTime(pm + "-01"));
  512. dtl.MonthWorks = days - weekDays - curHoildays.Where(p => p.Ufld1 == "休假").Count() + curHoildays.Where(p => p.Ufld1 == "调班").Count();
  513. dtl.AvailableTimes = dtl.DailyWorks * dtl.MonthWorks;
  514. //计算产线耗时
  515. var curLines = lineDtls.Where(p => p.Line == item).ToList();
  516. decimal sumTimes = 0.00m;
  517. List<ProdLineDto> lineDtos = new List<ProdLineDto>();
  518. foreach (var ld in curLines)
  519. {
  520. if (lineDtos.Exists(p=>p.Part == ld.Part && p.Line == ld.Line))
  521. {
  522. continue;
  523. }
  524. var curStand = standards.FirstOrDefault(p => p.ItemNumber == ld.Part);
  525. if (curStand == null)
  526. {
  527. continue;
  528. }
  529. var curDemands = yearDemands.Where(p => p.Model == curStand.Model && p.PlanMonth == pm).ToList();
  530. var line = curLines.Where(p=>p.Part == ld.Part && p.Line == ld.Line).OrderByDescending(p => p.Op).First();
  531. sumTimes += line.Rate == 0 ? 0 : (Math.Ceiling(curDemands.Sum(p => p.Qty) / line.Rate));
  532. MonthlyProdCapacityDtl capacityDtl = new MonthlyProdCapacityDtl();
  533. capacityDtl.Year = input.year;
  534. capacityDtl.Month = input.month;
  535. capacityDtl.PlanMonth = pm;
  536. capacityDtl.ProdLine = item;
  537. capacityDtl.Model = curStand.Model;
  538. capacityDtl.Qty = curDemands.Sum(p => p.Qty);
  539. capacityDtl.tenant_id = input.tenant_id;
  540. capacityDtl.company_id = input.company_id;
  541. capacityDtl.factory_id = input.factory_id;
  542. capacityDtl.org_id = input.org_id;
  543. capacityDtl.create_by = input.create_by;
  544. capacityDtl.create_by_name = input.create_by_name;
  545. capacityDtl.create_time = DateTime.Now;
  546. capacityDtls.Add(capacityDtl);
  547. lineDtos.Add(new ProdLineDto {
  548. Part = ld.Part,
  549. Line= ld.Line
  550. });
  551. }
  552. dtl.NeedWorks = sumTimes;
  553. dtl.ProdRate = 100;
  554. dtl.Rate = dtl.AvailableTimes == 0 ? 0 : Math.Floor(dtl.NeedWorks / dtl.AvailableTimes * 100);
  555. dtl.IsOverTime = dtl.NeedWorks > dtl.AvailableTimes ? "是" : "否";
  556. dtl.OverTimes = dtl.IsOverTime == "是" ? (dtl.NeedWorks - dtl.AvailableTimes) : 0;
  557. dtl.tenant_id = input.tenant_id;
  558. dtl.company_id= input.company_id;
  559. dtl.factory_id = input.factory_id;
  560. dtl.org_id= input.org_id;
  561. dtl.create_by= input.create_by;
  562. dtl.create_by_name = input.create_by_name;
  563. dtl.create_time = DateTime.Now;
  564. capacities.Add(dtl);
  565. }
  566. }
  567. return new MonthlyCapacityDto{
  568. mains = capacities,
  569. details = capacityDtls
  570. };
  571. }
  572. /// <summary>
  573. /// T1、平台自动补货
  574. /// </summary>
  575. /// <param name="input"></param>
  576. /// <param name="strN1">计划补货月份:N+1月</param>
  577. /// <param name="platformFcsts">平台预测</param>
  578. /// <param name="domesticFcsts">T1预测</param>
  579. /// <param name="standards">标准SKU设置</param>
  580. /// <param name="items">物料</param>
  581. /// <param name="holidays">节假日</param>
  582. /// <param name="monitorSettings">库存监控月份设置</param>
  583. /// <returns></returns>
  584. 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)
  585. {
  586. List<crm_planorder> planorders = new List<crm_planorder>();
  587. //获取T1中规格型号对应的不同版本的物料编码
  588. var T1Models = domesticFcsts.Select(p => p.Model).Distinct().ToList();
  589. 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;
  590. //获取T1库存
  591. List<string> itemNums = skus.Select(p=>p.ItemNum).Distinct().ToList();
  592. List<LocationDetail> locationDetails = _locationDetail.Select(p=> itemNums.Contains(p.ItemNum) && p.Domain == input.factory_id.ToString() && p.IsActive);
  593. //获取临期库存设置
  594. GeneralizedCodeMaster master = _generalizedCodeMaster.Select(p => p.Domain == input.factory_id.ToString() && p.IsActive && p.Val == "LongPeriodItemPlanMonth").FirstOrDefault();
  595. int month = master == null ? 0 : Convert.ToInt16(master.UDeci1);
  596. DateTime strTime = Convert.ToDateTime(platformFcsts[0].PlanMonth + "-01").AddMonths(month);
  597. //获取平台库存
  598. var PModels = platformFcsts.Select(p => p.Model).Distinct().ToList();
  599. 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;
  600. //计算T1补货
  601. foreach (var item in T1Models)
  602. {
  603. //获取当前规格型号对应的预测数据
  604. var curFcsts = domesticFcsts.Where(p=>p.Model == item).ToList();
  605. //当前规格型号对应的库存数量
  606. decimal sumQty = 0.00m;
  607. //当前的物料版本
  608. var curSkus = skus.Where(p => p.Model == item).ToList();
  609. if (curSkus.Any())
  610. {
  611. sumQty = locationDetails.Where(p => curSkus.Select(p => p.ItemNum).Contains(p.ItemNum)).Sum(p => p.QtyOnHand);
  612. }
  613. //计算Rop
  614. //当前规格型号对应标准SKU的最小包装单位、补货周期
  615. var curStd = standards.FirstOrDefault(p => p.Model == item);
  616. decimal packQty = 1m;//最小包装单位
  617. decimal cycle = 0m;//补货周期
  618. if (curStd != null)
  619. {
  620. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  621. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  622. cycle = curStd.ReplenishCycle;
  623. }
  624. //N+1月使用N+2月的再订货点参与计算
  625. decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curFcsts.Sum(p=>p.Qty), packQty, holidays, cycle);
  626. if(sumQty < rop)
  627. {
  628. planorders.Add(new crm_planorder {
  629. PlanMonth = strN1,
  630. Model = curFcsts[0].Model,
  631. ItemNum = curStd?.ItemNumber,
  632. ProdLine = curFcsts[0].ProdLine,
  633. ProdType = "",
  634. Qty = rop,
  635. Type = "计划单-T1直发补货"
  636. });
  637. }
  638. }
  639. //计算平台补货:海王
  640. var hwFcsts = platformFcsts.Where(p => p.Platform == "海王").ToList();
  641. var hwModels = hwFcsts.Select(p=>p.Model).Distinct().ToList();
  642. foreach (var item in hwModels)
  643. {
  644. //获取当前规格型号对应的平台预测数据
  645. var curFcsts = hwFcsts.Where(p => p.Model == item).ToList();
  646. //当前规格型号对应的库存数量
  647. decimal sumQty = 0.00m;
  648. //当前规格型号对应标准SKU的最小包装单位、补货周期
  649. var curStd = standards.FirstOrDefault(p => p.Model == item);
  650. decimal packQty = 1m;//最小包装单位
  651. decimal cycle = 0m;//补货周期
  652. if (curStd != null)
  653. {
  654. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  655. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  656. cycle = curStd.ReplenishCycle;
  657. }
  658. //T2海王预测数据
  659. var curT2Hws = T2Fcsts.Where(p => p.Model == item && p.TypeEnum == 4).ToList();
  660. //计算Rop:N+1月使用N+2月的再订货点参与计算
  661. decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curT2Hws.Sum(p => p.Qty), packQty, holidays, cycle);
  662. //获取海王当前规格型号库存
  663. sumQty = pInventories.Where(p => p.SpecificationModel == item && p.Code == "HW0001").Sum(p=>p.InventoryQuantity);
  664. //获取N+1月的库存监控设置
  665. var curMonitor = monitorSettings.FirstOrDefault(p => p.Platform == "海王" && p.ProdType == curFcsts[0].ProdType);
  666. if (curMonitor == null)
  667. {
  668. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "海王未维护产品类型为【"+ curFcsts[0].ProdType + "】的库存监控月份", _currentTenant.Id.ToString());
  669. continue;
  670. }
  671. //最小覆盖月份库存
  672. var minQty = rop * curMonitor.MinTimes;
  673. //最大库存月份库存
  674. var maxQty = rop * curMonitor.MaxTimes;
  675. //需补货数量
  676. decimal needQty = 0m;
  677. if (sumQty >= minQty && sumQty < maxQty)//大于等于最低库存覆盖月,小于最高库存覆盖月
  678. {
  679. planorders.Add(new crm_planorder
  680. {
  681. PlanMonth = strN1,
  682. Model = curFcsts[0].Model,
  683. ItemNum = curStd?.ItemNumber,
  684. ProdLine = curT2Hws[0].ProdLine,
  685. ProdType = "",
  686. Qty = maxQty - sumQty,
  687. Type = "计划单-T2平台补货"
  688. });
  689. }
  690. if (sumQty < minQty)//低于最低库存覆盖月
  691. {
  692. while (sumQty < minQty)
  693. {
  694. sumQty += rop;
  695. needQty += rop;
  696. }
  697. planorders.Add(new crm_planorder
  698. {
  699. PlanMonth = strN1,
  700. Model = curFcsts[0].Model,
  701. ItemNum = curStd?.ItemNumber,
  702. ProdLine = curT2Hws[0].ProdLine,
  703. ProdType = "",
  704. Qty = needQty,
  705. Type = "计划单-T2平台补货"
  706. });
  707. }
  708. }
  709. //计算平台补货:国科
  710. var gkFcsts = platformFcsts.Where(p => p.Platform == "国科").ToList();
  711. var gkModels = gkFcsts.Select(p => p.Model).Distinct().ToList();
  712. foreach (var item in gkModels)
  713. {
  714. //获取当前规格型号对应的预测数据
  715. var curFcsts = gkFcsts.Where(p => p.Model == item).ToList();
  716. //当前规格型号对应的库存数量
  717. decimal sumQty = 0.00m;
  718. //当前规格型号对应标准SKU的最小包装单位、补货周期
  719. var curStd = standards.FirstOrDefault(p => p.Model == item);
  720. decimal packQty = 1m;//最小包装单位
  721. decimal cycle = 0m;//补货周期
  722. if (curStd != null)
  723. {
  724. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  725. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  726. cycle = curStd.ReplenishCycle;
  727. }
  728. //T2国科预测数据
  729. var curT2Gks = T2Fcsts.Where(p => p.Model == item && p.TypeEnum == 5).ToList();
  730. //计算Rop:N+1月使用N+2月的再订货点参与计算
  731. decimal rop = CalcRop(curFcsts[0].PlanMonth + "-01", curT2Gks.Sum(p => p.Qty), packQty, holidays, cycle);
  732. //获取国科当前规格型号库存
  733. sumQty = pInventories.Where(p => p.SpecificationModel == item && p.Code == "GK0001").Sum(p => p.InventoryQuantity);
  734. //获取N+1月的库存监控设置
  735. var curMonitor = monitorSettings.FirstOrDefault(p => p.Platform == "国科" && p.ProdType == curFcsts[0].ProdType);
  736. if (curMonitor == null)
  737. {
  738. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "国科未维护产品类型为【" + curFcsts[0].ProdType + "】的库存监控月份", _currentTenant.Id.ToString());
  739. continue;
  740. }
  741. //最小覆盖月份库存
  742. var minQty = rop * curMonitor.MinTimes;
  743. //最大库存月份库存
  744. var maxQty = rop * curMonitor.MaxTimes;
  745. //需补货数量
  746. decimal needQty = 0m;
  747. if (sumQty >= minQty && sumQty < maxQty)
  748. {
  749. planorders.Add(new crm_planorder
  750. {
  751. PlanMonth = strN1,
  752. Model = curFcsts[0].Model,
  753. ItemNum = curStd?.ItemNumber,
  754. ProdLine = curT2Gks[0].ProdLine,
  755. ProdType = "",
  756. Qty = maxQty - sumQty,
  757. Type = "计划单-T2平台补货"
  758. });
  759. }
  760. if (sumQty < minQty)
  761. {
  762. while (sumQty < minQty)
  763. {
  764. sumQty += rop;
  765. needQty += rop;
  766. }
  767. planorders.Add(new crm_planorder
  768. {
  769. PlanMonth = strN1,
  770. Model = curFcsts[0].Model,
  771. ItemNum = curStd?.ItemNumber,
  772. ProdLine = curT2Gks[0].ProdLine,
  773. ProdType = "",
  774. Qty = needQty,
  775. Type = "计划单-T2平台补货"
  776. });
  777. }
  778. }
  779. planorders.ForEach(p => {
  780. p.Source = "系统运算";
  781. p.tenant_id = input.tenant_id;
  782. p.company_id = input.company_id;
  783. p.factory_id = input.factory_id;
  784. p.org_id = input.org_id;
  785. p.create_by = input.create_by;
  786. p.create_by_name = input.create_by_name;
  787. p.create_time = DateTime.Now;
  788. });
  789. return planorders;
  790. }
  791. /// <summary>
  792. /// 计算再订货点
  793. /// </summary>
  794. /// <param name="planMonth">计划年月(yyyy-MM-dd)</param>
  795. /// <param name="qty">需求量</param>
  796. /// <param name="qty">最小包装数量</param>
  797. /// <param name="holidays">节假日</param>
  798. /// <param name="replenishCycle">补货周期</param>
  799. /// <returns></returns>
  800. private decimal CalcRop(string planMonth,decimal qty,decimal packQty,List<HolidayMaster> holidays,decimal replenishCycle)
  801. {
  802. decimal rop = 0.0m;
  803. //获取当月天数
  804. int year = Convert.ToInt16(planMonth.Substring(0, 4));
  805. int month = Convert.ToInt16(planMonth.Substring(5, 2));
  806. int days = DateTime.DaysInMonth(year, month);
  807. //计算当前月的周末天数
  808. int weeks = CalcWeekDays(days, Convert.ToDateTime(planMonth));
  809. //获取当前年月的节假日设置
  810. var curHolidays = holidays.Where(p => p.Dated.Value.Year == year && p.Dated.Value.Month == month).ToList();
  811. //当月工作天数
  812. int workDays = days - weeks - curHolidays.Where(p => p.Ufld1 == "休假").Count() + curHolidays.Where(p => p.Ufld1 == "调班").Count();
  813. //rop = (需求量/月工作天数*补货周期)=>按照最小包装单位元整
  814. rop = Math.Ceiling(qty / workDays * replenishCycle / packQty) * packQty;
  815. return rop;
  816. }
  817. /// <summary>
  818. /// 计算当月有多少个周末
  819. /// </summary>
  820. /// <param name="days"></param>
  821. /// <param name="startDay"></param>
  822. /// <returns></returns>
  823. private int CalcWeekDays(int days, DateTime startDay)
  824. {
  825. int sumDays = 0;
  826. for (int i = 0; i < days; i++)
  827. {
  828. int weekDays = (int)startDay.AddDays(i).DayOfWeek;
  829. if (weekDays == 0 || weekDays == 6)
  830. {
  831. sumDays++;
  832. }
  833. }
  834. return sumDays;
  835. }
  836. /// <summary>
  837. /// 生成整体需求计划
  838. /// </summary>
  839. /// <param name="input"></param>
  840. /// <returns></returns>
  841. /// <exception cref="NotImplementedException"></exception>
  842. public async Task<string> OverallDemandPlan(InputDto input)
  843. {
  844. //1.1、获取海外销售预测数据
  845. 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();
  846. //1.2、获取平台预测收集数据
  847. 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();
  848. //1.3、获取国内终端预测-T1汇总数据
  849. 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();
  850. //计算当前年月的N+1,N+2
  851. string strN0 = input.year.ToString() + "-" + input.month.ToString("00");
  852. int newYear = input.month == 12 ? input.year + 1 : input.year;
  853. int newMonth = input.month == 12 ? 1 : input.month + 1;
  854. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  855. newYear = newMonth == 12 ? newYear + 1 : newYear;
  856. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  857. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  858. //整体需求计划
  859. List<OverallDemandPlan> plans = new List<OverallDemandPlan>();
  860. //计算海外
  861. List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
  862. int OrderNum = 1;
  863. foreach (var item in hwModels)
  864. {
  865. var curFcsts = overseasSales.Where(p => p.Model == item).ToList();
  866. OverallDemandPlan plan = new OverallDemandPlan();
  867. plan.Area = "海外";
  868. plan.Model = item;
  869. plan.PlanMonth = strN0;
  870. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
  871. plan.OrderNum = OrderNum;
  872. plans.Add(plan);
  873. plan = new OverallDemandPlan();
  874. plan.Area = "海外";
  875. plan.Model = item;
  876. plan.PlanMonth = strN1;
  877. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
  878. plan.OrderNum = OrderNum;
  879. plans.Add(plan);
  880. plan = new OverallDemandPlan();
  881. plan.Area = "海外";
  882. plan.Model = item;
  883. plan.PlanMonth = strN2;
  884. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
  885. plan.OrderNum = OrderNum;
  886. plans.Add(plan);
  887. OrderNum += 1;
  888. }
  889. //计算国内
  890. List<string> gnModels = domesticFcsts.Select(p=>p.Model).ToList();
  891. gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
  892. gnModels = gnModels.Distinct().ToList();
  893. OrderNum = 1;
  894. foreach (var item in gnModels)
  895. {
  896. var curDFcsts = domesticFcsts.Where(p => p.Model == item).ToList();
  897. var curPFcsts = platformFcsts.Where(p => p.Model == item).ToList();
  898. OverallDemandPlan plan = new OverallDemandPlan();
  899. plan.Area = "国内";
  900. plan.Model = item;
  901. plan.PlanMonth = strN0;
  902. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
  903. plan.OrderNum = OrderNum;
  904. plans.Add(plan);
  905. plan = new OverallDemandPlan();
  906. plan.Area = "国内";
  907. plan.Model = item;
  908. plan.PlanMonth = strN1;
  909. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
  910. plan.OrderNum = OrderNum;
  911. plans.Add(plan);
  912. plan = new OverallDemandPlan();
  913. plan.Area = "国内";
  914. plan.Model = item;
  915. plan.PlanMonth = strN2;
  916. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
  917. plan.OrderNum = OrderNum;
  918. plans.Add(plan);
  919. OrderNum += 1;
  920. }
  921. plans.ForEach(p => {
  922. p.Year= input.year;
  923. p.Month= input.month;
  924. p.tenant_id = input.tenant_id;
  925. p.company_id = input.company_id;
  926. p.factory_id= input.factory_id;
  927. p.org_id= input.org_id;
  928. p.create_by = input.create_by;
  929. p.create_by_name= input.create_by_name;
  930. p.create_time = DateTime.Now;
  931. });
  932. //保存数据
  933. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  934. {
  935. try
  936. {
  937. //先删除
  938. 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);
  939. //保存整体需求计划
  940. await _overallDemandPlan.InsertManyAsync(plans);
  941. await unitOfWork.CompleteAsync();
  942. }
  943. catch (Exception e)
  944. {
  945. unitOfWork.Dispose();
  946. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("OverallDemandPlan", "生成【" + input.year + "年" + input.month + "月】整体需求计划失败:" + e.Message, _currentTenant.Id.ToString());
  947. return "NO|" + e.Message;
  948. };
  949. }
  950. return "OK|刷新成功!";
  951. }
  952. }
  953. }