MonthlyCapacityLoadAppService.cs 56 KB

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