MonthlyCapacityLoadAppService.cs 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  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 Microsoft.CodeAnalysis.CSharp.Syntax;
  9. using Spire.Pdf.General.Render.Decode.Jpeg2000.j2k.quantization;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.Linq;
  13. using System.Runtime.CompilerServices;
  14. using System.Threading.Tasks;
  15. using Volo.Abp.Application.Services;
  16. using Volo.Abp.DependencyInjection;
  17. using Volo.Abp.Domain.Repositories;
  18. using Volo.Abp.MultiTenancy;
  19. using Volo.Abp.Uow;
  20. using Volo.Abp.Validation.Localization;
  21. namespace Business.SaleForecastManagement
  22. {
  23. /// <summary>
  24. /// 产能分析
  25. /// </summary>
  26. public class MonthlyCapacityLoadAppService : ApplicationService, IMonthlyCapacityLoadAppService, ITransientDependency
  27. {
  28. #region 服务
  29. /// <summary>
  30. /// 物料
  31. /// </summary>
  32. private IRepository<ic_item, long> _ic_item;
  33. /// <summary>
  34. /// 生产线明细
  35. /// </summary>
  36. private ISqlRepository<ProdLineDetail> _prodLineDetail;
  37. /// <summary>
  38. /// 工作日历数据
  39. /// </summary>
  40. private ISqlRepository<ShopCalendarWorkCtr> _shopCalendarWorkCtr;
  41. /// <summary>
  42. /// 产线休息时间记录表
  43. /// </summary>
  44. private ISqlRepository<QualityLineWorkDetail> _qualityLineWorkDetail;
  45. /// <summary>
  46. /// 节假日记录表
  47. /// </summary>
  48. private ISqlRepository<HolidayMaster> _holidayMaster;
  49. /// <summary>
  50. /// 工作单元
  51. /// </summary>
  52. private readonly IUnitOfWorkManager _unitOfWorkManager;
  53. /// <summary>
  54. /// 日志
  55. /// </summary>
  56. private readonly ICurrentTenant _currentTenant;
  57. /// <summary>
  58. /// 年度生产需求大纲
  59. /// </summary>
  60. private IRepository<YearDemandManagement, long> _yearDemandManagement;
  61. /// <summary>
  62. /// 年度生产需求大纲历史记录
  63. /// </summary>
  64. private IRepository<YearDemandManagementHistory, long> _yearDemandManagementHistory;
  65. /// <summary>
  66. /// 国内终端预测
  67. /// </summary>
  68. private IRepository<DomesticTerminalFcst, long> _domesticTerminalFcst;
  69. /// <summary>
  70. /// 海外销售预测
  71. /// </summary>
  72. private IRepository<OverseasSaleFcst, long> _overseasSaleFcst;
  73. /// <summary>
  74. /// 平台预测收集
  75. /// </summary>
  76. private IRepository<PlatformFcstCollect, long> _platformFcstCollect;
  77. /// <summary>
  78. /// 标准物料规格型号设置表
  79. /// </summary>
  80. private IRepository<StandardItemModelSet,long> _standardItemModelSet;
  81. /// <summary>
  82. /// 库存明细表
  83. /// </summary>
  84. private ISqlRepository<LocationDetail> _locationDetail;
  85. /// <summary>
  86. /// 工单
  87. /// </summary>
  88. private ISqlRepository<WorkOrdMaster> _workOrdMaster;
  89. /// <summary>
  90. /// 整体需求计划
  91. /// </summary>
  92. private IRepository<OverallDemandPlan, long> _overallDemandPlan;
  93. /// <summary>
  94. /// 月度产能共识表
  95. /// </summary>
  96. private IRepository<MonthlyProdCapacity, long> _monthlyProdCapacity;
  97. /// <summary>
  98. /// 雪花算法
  99. /// </summary>
  100. SnowFlake help = new SnowFlake();
  101. #endregion
  102. #region 构造函数
  103. /// <summary>
  104. /// 构造函数
  105. /// </summary>
  106. public MonthlyCapacityLoadAppService(
  107. IRepository<ic_item, long> ic_item,
  108. ISqlRepository<ProdLineDetail> prodLineDetail,
  109. ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
  110. ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
  111. ISqlRepository<HolidayMaster> holidayMaster,
  112. IRepository<MonthlyProdCapacity, long> monthlyProdCapacity,
  113. IUnitOfWorkManager unitOfWorkManager,
  114. ICurrentTenant currentTenant,
  115. IRepository<StandardItemModelSet,long> standardItemModelSet,
  116. IRepository<YearDemandManagement, long> yearDemandManagement,
  117. IRepository<DomesticTerminalFcst, long> domesticTerminalFcst,
  118. IRepository<OverseasSaleFcst, long> overseasSaleFcst,
  119. IRepository<PlatformFcstCollect, long> platformFcstCollect,
  120. IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory,
  121. ISqlRepository<LocationDetail> locationDetail,
  122. ISqlRepository<WorkOrdMaster> workOrdMaster,
  123. IRepository<OverallDemandPlan, long> overallDemandPlan
  124. )
  125. {
  126. _ic_item = ic_item;
  127. _prodLineDetail = prodLineDetail;
  128. _shopCalendarWorkCtr = shopCalendarWorkCtr;
  129. _qualityLineWorkDetail = qualityLineWorkDetail;
  130. _holidayMaster = holidayMaster;
  131. _monthlyProdCapacity = monthlyProdCapacity;
  132. _unitOfWorkManager = unitOfWorkManager;
  133. _currentTenant = currentTenant;
  134. _standardItemModelSet = standardItemModelSet;
  135. _yearDemandManagement= yearDemandManagement;
  136. _domesticTerminalFcst= domesticTerminalFcst;
  137. _overseasSaleFcst = overseasSaleFcst;
  138. _platformFcstCollect= platformFcstCollect;
  139. _yearDemandManagementHistory= yearDemandManagementHistory;
  140. _locationDetail = locationDetail;
  141. _workOrdMaster = workOrdMaster;
  142. _overallDemandPlan = overallDemandPlan;
  143. }
  144. #endregion
  145. /// <summary>
  146. /// 月度需求预测更新
  147. /// </summary>
  148. /// <param name="input"></param>
  149. /// <returns></returns>
  150. /// <exception cref="NotImplementedException"></exception>
  151. public async Task<string> DemandAnalysis(InputDto input)
  152. {
  153. //1.0 获取当前年年度生产需求大纲
  154. 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();
  155. //1.0 获取下一年年度生产大纲
  156. 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();
  157. //记录历史版本:当前年+下一年
  158. var updateMonth = input.year.ToString() + "-" + input.month.ToString("00");
  159. var histories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(yearDemands);
  160. histories.ForEach(p => {
  161. p.UpdateMonth = updateMonth;
  162. });
  163. var nextHistories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(nextYearDemands);
  164. nextHistories.ForEach(p => {
  165. p.UpdateMonth = updateMonth;
  166. });
  167. //1.1、获取海外销售预测数据
  168. 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;
  169. //1.2、获取平台预测收集数据
  170. 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;
  171. //1.3、获取国内终端预测-T1汇总数据
  172. 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;
  173. //1.4、根据年度生产大纲,获取规格型号对应的补货周期
  174. List<string> allModels = yearDemands.Select(p=>p.Model).Distinct().ToList();
  175. if (nextYearDemands.Any())
  176. {
  177. allModels.AddRange(nextYearDemands.Select(p => p.Model).ToList());
  178. allModels = allModels.Distinct().ToList();
  179. }
  180. 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;
  181. //1.5、获取规格型号对应的标准SKU数据:获取最小包装单位
  182. List<string> itemNums = standards.Select(p=>p.ItemNumber).Distinct().ToList();
  183. 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;
  184. //1.6、获取成品库存、灭菌库存
  185. //List<LocationDetail> locations = _locationDetail.Select(p=>p.Domain == input.factory_id.ToString() && p.IsActive && itemNums.Contains(p.ItemNum));
  186. //1.7、获取在制库存:计划开始时间、计划结束时间都在本月,并且未关闭的工单
  187. //DateTime monStart = Convert.ToDateTime(input.year + "-" + input.month + "-01");
  188. //DateTime monEnd = monStart.AddMonths(1).AddDays(-1);
  189. //List<WorkOrdMaster> workOrds = _workOrdMaster.Select(p=>p.Domain);
  190. //1.9、获取节假日设置
  191. 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);
  192. #region 数据校验
  193. //1、校验当前年的年度生产大纲是否导入
  194. if (!yearDemands.Any())
  195. {
  196. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  197. return "NO|请导入" + input.year + "年的年度生产需求后再操作!";
  198. }
  199. //2、如果当前月份大于10,则需校验下一年的年度生产大纲
  200. if (input.month > 10 && !nextYearDemands.Any())
  201. {
  202. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + (input.year+1) + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  203. return "NO|请导入" + (input.year + 1) + "年的年度生产需求后再操作!";
  204. }
  205. //3、校验当前月份的海外销售预测是否导入
  206. if (!overseasSales.Any())
  207. {
  208. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!", _currentTenant.Id.ToString());
  209. return "NO|请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!";
  210. }
  211. //4、校验当前月份的平台预测收集是否导入
  212. if (!platformFcsts.Any())
  213. {
  214. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!", _currentTenant.Id.ToString());
  215. return "NO|请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!";
  216. }
  217. //5、校验当前月份的国内终端预测是否导入
  218. if (!domesticFcst.Any())
  219. {
  220. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!", _currentTenant.Id.ToString());
  221. return "NO|请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!";
  222. }
  223. //6、校验终端+平台导入的规格型号是否已维护标准SKU
  224. if (allModels.Count() != standards.Count())
  225. {
  226. allModels.RemoveAll(standards.Select(p=>p.Model).ToList());
  227. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",",allModels) + "】没有维护标准SKU,请维护后再操作!", _currentTenant.Id.ToString());
  228. return "NO|规格型号【" + string.Join(",", allModels) + "】没有维护标准SKU,请维护后再操作!";
  229. }
  230. //7、校验是否已维护补货周期
  231. var zeros = standards.Where(p => p.ReplenishCycle == 0).ToList();
  232. if (zeros.Any())
  233. {
  234. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",", zeros.Select(p=>p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!", _currentTenant.Id.ToString());
  235. return "NO|规格型号【" + string.Join(",", zeros.Select(p => p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!";
  236. }
  237. #endregion
  238. //计算当前年月的N0,N+1,N+2
  239. List<string> planMons = new List<string>();
  240. int newYear = input.month == 12 ? input.year + 1 : input.year;
  241. int newMonth = input.month == 12 ? 1 : input.month + 1;
  242. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  243. planMons.Add(strN1);
  244. newYear = newMonth == 12 ? newYear + 1 : newYear;
  245. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  246. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  247. planMons.Add(strN2);
  248. //需要回写的数据
  249. List<YearDemandManagement> updates = new List<YearDemandManagement>();
  250. #region 计算国内终端预测,回写年度生产大纲
  251. //获取导入的T1、平台数据
  252. List<string> gnModels = new List<string>();
  253. if (domesticFcst.Any())
  254. {
  255. gnModels.AddRange(domesticFcst.Select(p => p.Model).ToList());
  256. }
  257. if (platformFcsts.Any())
  258. {
  259. gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
  260. }
  261. gnModels = gnModels.Distinct().ToList();
  262. foreach (var gnModel in gnModels)
  263. {
  264. //N+1月
  265. YearDemandManagement monthN1 = new YearDemandManagement();
  266. //N+2月
  267. YearDemandManagement monthN2 = new YearDemandManagement();
  268. //获取需要回写的数据
  269. if (input.month < 11)
  270. {
  271. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  272. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  273. }
  274. if (input.month == 11)
  275. {
  276. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  277. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  278. }
  279. if (input.month == 12)
  280. {
  281. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  282. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  283. }
  284. if (monthN1 == null || monthN2 == null)
  285. {
  286. continue;
  287. }
  288. //T1
  289. var sumT1N1 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p=>p.Qty);
  290. var sumT1N2 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  291. //平台
  292. var sumPN1 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  293. var sumPN2 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  294. //合计
  295. var sumN1 = sumT1N1+ sumPN1;
  296. var sumN2 = sumT1N2 + sumPN2;
  297. //当前规格型号对应标准SKU的最小包装单位、补货周期
  298. var curStd = standards.FirstOrDefault(p => p.Model == gnModel);
  299. decimal packQty = 1m;//最小包装单位
  300. decimal cycle = 0m;//补货周期
  301. if (curStd != null)
  302. {
  303. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  304. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  305. cycle = curStd.ReplenishCycle;
  306. }
  307. //TODO:获取成品库存、在制库存、灭菌库存,参与计算
  308. //N+1月使用N+2月的再订货点参与计算
  309. decimal rop = CalcRop(strN2+"-01", sumN2, packQty, holidays, cycle);
  310. monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 + rop - 0 - 0 - 0) / packQty) * packQty;
  311. //N+2月使用本月的需求量,下一月的工作天数
  312. rop = CalcRop(Convert.ToDateTime(strN2 + "-01").AddMonths(1).ToString("yyyy-MM-dd"), sumN2, packQty, holidays, cycle);
  313. monthN2.Qty = Math.Ceiling((sumN2 + rop - 0 - 0 - 0) / packQty) * packQty;
  314. //负数置0
  315. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  316. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  317. //记录需要回写的数据
  318. updates.Add(monthN1);
  319. updates.Add(monthN2);
  320. }
  321. #endregion
  322. #region 海外销售预测,回写年度生产大纲
  323. List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
  324. foreach (var hwModel in hwModels)
  325. {
  326. //N+1月
  327. YearDemandManagement monthN1 = new YearDemandManagement();
  328. //N+2月
  329. YearDemandManagement monthN2 = new YearDemandManagement();
  330. //获取需要回写的数据
  331. if (input.month < 11)
  332. {
  333. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  334. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  335. }
  336. if (input.month == 11)
  337. {
  338. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  339. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  340. }
  341. if (input.month == 12)
  342. {
  343. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  344. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  345. }
  346. if (monthN1 == null || monthN2 == null) {
  347. continue;
  348. }
  349. var sumN1 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  350. var sumN2 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  351. //当前规格型号对应标准SKU的最小包装单位、补货周期
  352. var curStd = standards.FirstOrDefault(p => p.Model == hwModel);
  353. decimal packQty = 1m;//最小包装单位
  354. if (curStd != null)
  355. {
  356. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  357. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  358. }
  359. //海外生产需求量=当月的50%+下一月的50%-成品库存-在制库存-灭菌库存
  360. //TODO:获取成品库存、在制库存、灭菌库存,参与运算
  361. //计算N+1月,N+2月
  362. monthN1.Qty = Math.Ceiling((sumN1 / 2 + sumN2 / 2 - 0 - 0 - 0) / packQty) * packQty;
  363. monthN2.Qty = Math.Ceiling((sumN2 - 0 - 0 - 0) / packQty) *packQty; ;
  364. //负数置0
  365. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  366. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  367. //记录需要回写的数据
  368. updates.Add(monthN1);
  369. updates.Add(monthN2);
  370. }
  371. #endregion
  372. //生成月度产能共识
  373. List<MonthlyProdCapacity> capacities = CapacityAnalysis(input, updates, planMons);
  374. //保存数据
  375. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  376. {
  377. try
  378. {
  379. //判断是否存在当前年月的历史版本,不存在则插入
  380. 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;
  381. if (!dbHistory.Any())
  382. {
  383. await _yearDemandManagementHistory.InsertManyAsync(histories);
  384. }
  385. if (input.month > 10)
  386. {
  387. //判断是否存在下一年月的历史版本,不存在则插入
  388. 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;
  389. if (!dbHistory.Any())
  390. {
  391. await _yearDemandManagementHistory.InsertManyAsync(nextHistories);
  392. }
  393. }
  394. //回写年度生产大纲
  395. await _yearDemandManagement.UpdateManyAsync(updates);
  396. //先删除产能共识
  397. 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);
  398. await _monthlyProdCapacity.InsertManyAsync(capacities);
  399. await unitOfWork.CompleteAsync();
  400. }
  401. catch (Exception e)
  402. {
  403. unitOfWork.Dispose();
  404. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "【" + input.year + "年" + input.month + "月】月度需求预测更新失败:" + e.Message, _currentTenant.Id.ToString());
  405. return "NO|" + e.Message;
  406. };
  407. }
  408. return "OK|修订成功!";
  409. }
  410. /// <summary>
  411. /// 生成月度产能共识
  412. /// </summary>
  413. /// <param name="input"></param>
  414. /// <returns></returns>
  415. public List<MonthlyProdCapacity> CapacityAnalysis(InputDto input,List<YearDemandManagement> yearDemands, List<string> planMons)
  416. {
  417. //1、获取数据
  418. //1.1 根据规格型号获取物料数据
  419. List<string> models = yearDemands.Select(p => p.Model).Distinct().ToList();
  420. 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;
  421. //1.3 根据物料编码获取产线数据
  422. 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();
  423. //1.4 根据产线获取工作日历数据和产线休息配置数据
  424. List<ShopCalendarWorkCtr> calendars = _shopCalendarWorkCtr.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  425. List<QualityLineWorkDetail> lineWorks = _qualityLineWorkDetail.Select(p => lineDtls.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  426. //1.5 获取当前年和下一年的节假日配置数据
  427. 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);
  428. //月度产能共识表
  429. List<MonthlyProdCapacity> capacities = new List<MonthlyProdCapacity>();
  430. //产线
  431. List<string> lines = lineDtls.Select(p => p.Line).Distinct().ToList();
  432. foreach (var item in lines)
  433. {
  434. //计算每天工作时间
  435. var curCal = calendars.FirstOrDefault(p => p.ProdLine == item);
  436. if (curCal == null)
  437. {
  438. continue;
  439. }
  440. foreach (var pm in planMons)
  441. {
  442. //添加月度产能共识产能效率数据
  443. MonthlyProdCapacity dtl = new MonthlyProdCapacity();
  444. dtl.Year = input.year;
  445. dtl.Month = input.month;
  446. dtl.PlanMonth = pm;
  447. dtl.ProdLine = item;
  448. dtl.DailyWorks = curCal.ShiftsHours1 + curCal.ShiftsHours2 + curCal.ShiftsHours3 + curCal.ShiftsHours4;
  449. dtl.FlightQty = 1;
  450. //计算当月工作天数
  451. 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();
  452. //当月天数
  453. int days = DateTime.DaysInMonth(input.year, input.month);
  454. //当月周末天数
  455. int weekDays = CalcWeekDays(days, Convert.ToDateTime(pm + "-01"));
  456. dtl.MonthWorks = days - weekDays - curHoildays.Where(p => p.Ufld1 == "休假").Count() + curHoildays.Where(p => p.Ufld1 == "调班").Count();
  457. dtl.AvailableTimes = dtl.DailyWorks * dtl.MonthWorks;
  458. //计算产线耗时
  459. var curLines = lineDtls.Where(p => p.Line == item).ToList();
  460. var line = curLines.OrderByDescending(p => p.Op).First();
  461. var curStands = standards.Where(p=> curLines.Select(m=>m.Part).Contains(p.ItemNumber)).ToList();
  462. var curDemands = yearDemands.Where(p => curStands.Select(m => m.Model).Contains(p.Model) && p.PlanMonth == pm).ToList();
  463. dtl.NeedWorks = line.Rate == 0 ? 0 : (Math.Ceiling(curDemands.Sum(p=>p.Qty) / line.Rate));
  464. dtl.ProdRate = 100;
  465. dtl.Rate = dtl.AvailableTimes == 0 ? 0 : Math.Floor(dtl.NeedWorks / dtl.AvailableTimes * 100);
  466. dtl.IsOverTime = dtl.NeedWorks > dtl.AvailableTimes ? "是" : "否";
  467. dtl.OverTimes = dtl.IsOverTime == "是" ? (dtl.NeedWorks - dtl.AvailableTimes) : 0;
  468. dtl.tenant_id = input.tenant_id;
  469. dtl.company_id= input.company_id;
  470. dtl.factory_id = input.factory_id;
  471. dtl.org_id= input.org_id;
  472. dtl.create_by= input.create_by;
  473. dtl.create_by_name = input.create_by_name;
  474. dtl.create_time = DateTime.Now;
  475. capacities.Add(dtl);
  476. }
  477. }
  478. return capacities;
  479. }
  480. /// <summary>
  481. /// 计算再订货点
  482. /// </summary>
  483. /// <param name="planMonth">计划年月(yyyy-MM-dd)</param>
  484. /// <param name="qty">需求量</param>
  485. /// <param name="qty">最小包装数量</param>
  486. /// <param name="holidays">节假日</param>
  487. /// <param name="replenishCycle">补货周期</param>
  488. /// <returns></returns>
  489. private decimal CalcRop(string planMonth,decimal qty,decimal packQty,List<HolidayMaster> holidays,decimal replenishCycle)
  490. {
  491. decimal rop = 0.0m;
  492. //获取当月天数
  493. int year = Convert.ToInt16(planMonth.Substring(0, 4));
  494. int month = Convert.ToInt16(planMonth.Substring(5, 2));
  495. int days = DateTime.DaysInMonth(year, month);
  496. //计算当前月的周末天数
  497. int weeks = CalcWeekDays(days, Convert.ToDateTime(planMonth));
  498. //获取当前年月的节假日设置
  499. var curHolidays = holidays.Where(p => p.Dated.Value.Year == year && p.Dated.Value.Month == month).ToList();
  500. //当月工作天数
  501. int workDays = days - weeks - curHolidays.Where(p => p.Ufld1 == "休假").Count() + curHolidays.Where(p => p.Ufld1 == "调班").Count();
  502. //rop = (需求量/月工作天数*补货周期)=>按照最小包装单位元整
  503. rop = Math.Ceiling(qty / workDays * replenishCycle / packQty) * packQty;
  504. return rop;
  505. }
  506. /// <summary>
  507. /// 计算当月有多少个周末
  508. /// </summary>
  509. /// <param name="days"></param>
  510. /// <param name="startDay"></param>
  511. /// <returns></returns>
  512. private int CalcWeekDays(int days, DateTime startDay)
  513. {
  514. int sumDays = 0;
  515. for (int i = 0; i < days; i++)
  516. {
  517. int weekDays = (int)startDay.AddDays(i).DayOfWeek;
  518. if (weekDays == 0 || weekDays == 6)
  519. {
  520. sumDays++;
  521. }
  522. }
  523. return sumDays;
  524. }
  525. /// <summary>
  526. /// 生成整体需求计划
  527. /// </summary>
  528. /// <param name="input"></param>
  529. /// <returns></returns>
  530. /// <exception cref="NotImplementedException"></exception>
  531. public async Task<string> OverallDemandPlan(InputDto input)
  532. {
  533. //1.1、获取海外销售预测数据
  534. 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();
  535. //1.2、获取平台预测收集数据
  536. 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();
  537. //1.3、获取国内终端预测-T1汇总数据
  538. 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();
  539. //计算当前年月的N+1,N+2
  540. string strN0 = input.year.ToString() + "-" + input.month.ToString("00");
  541. int newYear = input.month == 12 ? input.year + 1 : input.year;
  542. int newMonth = input.month == 12 ? 1 : input.month + 1;
  543. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  544. newYear = newMonth == 12 ? newYear + 1 : newYear;
  545. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  546. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  547. //整体需求计划
  548. List<OverallDemandPlan> plans = new List<OverallDemandPlan>();
  549. //计算海外
  550. List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
  551. int OrderNum = 1;
  552. foreach (var item in hwModels)
  553. {
  554. var curFcsts = overseasSales.Where(p => p.Model == item).ToList();
  555. OverallDemandPlan plan = new OverallDemandPlan();
  556. plan.Area = "海外";
  557. plan.Model = item;
  558. plan.PlanMonth = strN0;
  559. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
  560. plan.OrderNum = OrderNum;
  561. plans.Add(plan);
  562. plan = new OverallDemandPlan();
  563. plan.Area = "海外";
  564. plan.Model = item;
  565. plan.PlanMonth = strN1;
  566. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
  567. plan.OrderNum = OrderNum;
  568. plans.Add(plan);
  569. plan = new OverallDemandPlan();
  570. plan.Area = "海外";
  571. plan.Model = item;
  572. plan.PlanMonth = strN2;
  573. plan.Qty = curFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
  574. plan.OrderNum = OrderNum;
  575. plans.Add(plan);
  576. OrderNum += 1;
  577. }
  578. //计算国内
  579. List<string> gnModels = domesticFcsts.Select(p=>p.Model).ToList();
  580. gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
  581. gnModels = gnModels.Distinct().ToList();
  582. OrderNum = 1;
  583. foreach (var item in gnModels)
  584. {
  585. var curDFcsts = domesticFcsts.Where(p => p.Model == item).ToList();
  586. var curPFcsts = platformFcsts.Where(p => p.Model == item).ToList();
  587. OverallDemandPlan plan = new OverallDemandPlan();
  588. plan.Area = "国内";
  589. plan.Model = item;
  590. plan.PlanMonth = strN0;
  591. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN0).Sum(p => p.Qty);
  592. plan.OrderNum = OrderNum;
  593. plans.Add(plan);
  594. plan = new OverallDemandPlan();
  595. plan.Area = "国内";
  596. plan.Model = item;
  597. plan.PlanMonth = strN1;
  598. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN1).Sum(p => p.Qty);
  599. plan.OrderNum = OrderNum;
  600. plans.Add(plan);
  601. plan = new OverallDemandPlan();
  602. plan.Area = "国内";
  603. plan.Model = item;
  604. plan.PlanMonth = strN2;
  605. plan.Qty = curDFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty) + curPFcsts.Where(p => p.PlanMonth == strN2).Sum(p => p.Qty);
  606. plan.OrderNum = OrderNum;
  607. plans.Add(plan);
  608. OrderNum += 1;
  609. }
  610. plans.ForEach(p => {
  611. p.Year= input.year;
  612. p.Month= input.month;
  613. p.tenant_id = input.tenant_id;
  614. p.company_id = input.company_id;
  615. p.factory_id= input.factory_id;
  616. p.org_id= input.org_id;
  617. p.create_by = input.create_by;
  618. p.create_by_name= input.create_by_name;
  619. p.create_time = DateTime.Now;
  620. });
  621. //保存数据
  622. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  623. {
  624. try
  625. {
  626. //先删除
  627. 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);
  628. //保存整体需求计划
  629. await _overallDemandPlan.InsertManyAsync(plans);
  630. await unitOfWork.CompleteAsync();
  631. }
  632. catch (Exception e)
  633. {
  634. unitOfWork.Dispose();
  635. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("OverallDemandPlan", "生成【" + input.year + "年" + input.month + "月】整体需求计划失败:" + e.Message, _currentTenant.Id.ToString());
  636. return "NO|" + e.Message;
  637. };
  638. }
  639. return "OK|刷新成功!";
  640. }
  641. }
  642. }