MonthlyCapacityLoadAppService.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  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. namespace Business.SaleForecastManagement
  21. {
  22. /// <summary>
  23. /// 产能分析
  24. /// </summary>
  25. public class MonthlyCapacityLoadAppService : ApplicationService, IMonthlyCapacityLoadAppService, ITransientDependency
  26. {
  27. #region 服务
  28. /// <summary>
  29. /// 物料
  30. /// </summary>
  31. private IRepository<ic_item, long> _ic_item;
  32. /// <summary>
  33. /// 整体需求计划明细
  34. /// </summary>
  35. private ISqlRepository<OverallDemandPlanDtl> _overallDemandPlanDtl;
  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 ISqlRepository<MonthlyProdCapacityMain> _monthlyProdCapacityMain;
  56. /// <summary>
  57. /// 月度产能共识明细表
  58. /// </summary>
  59. private ISqlRepository<MonthlyProdCapacityDtl> _monthlyProdCapacityDtl;
  60. /// <summary>
  61. /// 工作单元
  62. /// </summary>
  63. private readonly IUnitOfWorkManager _unitOfWorkManager;
  64. /// <summary>
  65. /// 日志
  66. /// </summary>
  67. private readonly ICurrentTenant _currentTenant;
  68. /// <summary>
  69. /// 年度生产需求大纲
  70. /// </summary>
  71. private IRepository<YearDemandManagement, long> _yearDemandManagement;
  72. /// <summary>
  73. /// 年度生产需求大纲历史记录
  74. /// </summary>
  75. private IRepository<YearDemandManagementHistory, long> _yearDemandManagementHistory;
  76. /// <summary>
  77. /// 国内终端预测
  78. /// </summary>
  79. private IRepository<DomesticTerminalFcst, long> _domesticTerminalFcst;
  80. /// <summary>
  81. /// 海外销售预测
  82. /// </summary>
  83. private IRepository<OverseasSaleFcst, long> _overseasSaleFcst;
  84. /// <summary>
  85. /// 平台预测收集
  86. /// </summary>
  87. private IRepository<PlatformFcstCollect, long> _platformFcstCollect;
  88. /// <summary>
  89. /// 标准物料规格型号设置表
  90. /// </summary>
  91. private IRepository<StandardItemModelSet,long> _standardItemModelSet;
  92. /// <summary>
  93. /// 雪花算法
  94. /// </summary>
  95. SnowFlake help = new SnowFlake();
  96. #endregion
  97. #region 构造函数
  98. /// <summary>
  99. /// 构造函数
  100. /// </summary>
  101. public MonthlyCapacityLoadAppService(
  102. IRepository<ic_item, long> ic_item,
  103. ISqlRepository<OverallDemandPlanDtl> overallDemandPlanDtl,
  104. ISqlRepository<ProdLineDetail> prodLineDetail,
  105. ISqlRepository<ShopCalendarWorkCtr> shopCalendarWorkCtr,
  106. ISqlRepository<QualityLineWorkDetail> qualityLineWorkDetail,
  107. ISqlRepository<HolidayMaster> holidayMaster,
  108. ISqlRepository<MonthlyProdCapacityMain> monthlyProdCapacityMain,
  109. ISqlRepository<MonthlyProdCapacityDtl> monthlyProdCapacityDtl,
  110. IUnitOfWorkManager unitOfWorkManager,
  111. ICurrentTenant currentTenant,
  112. IRepository<StandardItemModelSet,long> standardItemModelSet,
  113. IRepository<YearDemandManagement, long> yearDemandManagement,
  114. IRepository<DomesticTerminalFcst, long> domesticTerminalFcst,
  115. IRepository<OverseasSaleFcst, long> overseasSaleFcst,
  116. IRepository<PlatformFcstCollect, long> platformFcstCollect,
  117. IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory
  118. )
  119. {
  120. _ic_item = ic_item;
  121. _overallDemandPlanDtl = overallDemandPlanDtl;
  122. _prodLineDetail = prodLineDetail;
  123. _shopCalendarWorkCtr = shopCalendarWorkCtr;
  124. _qualityLineWorkDetail = qualityLineWorkDetail;
  125. _holidayMaster = holidayMaster;
  126. _monthlyProdCapacityMain = monthlyProdCapacityMain;
  127. _monthlyProdCapacityDtl = monthlyProdCapacityDtl;
  128. _unitOfWorkManager = unitOfWorkManager;
  129. _currentTenant = currentTenant;
  130. _standardItemModelSet = standardItemModelSet;
  131. _yearDemandManagement= yearDemandManagement;
  132. _domesticTerminalFcst= domesticTerminalFcst;
  133. _overseasSaleFcst = overseasSaleFcst;
  134. _platformFcstCollect= platformFcstCollect;
  135. _yearDemandManagementHistory= yearDemandManagementHistory;
  136. }
  137. #endregion
  138. ///// <summary>
  139. ///// 产能分析
  140. ///// </summary>
  141. ///// <param name="input"></param>
  142. ///// <returns></returns>
  143. ///// <exception cref="NotImplementedException"></exception>
  144. //public async Task<string> CapacityAnalysis(InputDto input)
  145. //{
  146. // //1、获取数据
  147. // //1.1 根据年、月、版本号获取整体需求计划明细
  148. // List<OverallDemandPlanDtl> planDtls = _overallDemandPlanDtl.Select(p => p.Year == input.year && p.Month == input.month && p.Version == input.version && p.tenant_id == input.tenant_id && p.factory_id == input.factory_id).OrderBy(p => p.ProdLine).ThenBy(p => p.ProdRange).ThenBy(p => p.Model).ThenBy(p => p.PlanDate).ToList();
  149. // //1.2 根据规格型号获取物料数据
  150. // List<string> models = planDtls.Select(p => p.Model).Distinct().ToList();
  151. // List<StandardItemModelSet> standards = _standardItemModelSet.Select(p => models.Contains(p.Model) && p.tenant_id == input.tenant_id && p.factory_id == input.factory_id);
  152. // //1.3 根据物料编码获取产线数据
  153. // List<ProdLineDetail> lines = _prodLineDetail.Select(p => standards.Select(m => m.ItemNumber).Contains(p.Part) && p.Domain == input.factory_id.ToString() && p.IsActive);
  154. // //1.4 根据产线获取工作日历数据和产线休息配置数据
  155. // List<ShopCalendarWorkCtr> calendars = _shopCalendarWorkCtr.Select(p => lines.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  156. // List<QualityLineWorkDetail> lineWorks = _qualityLineWorkDetail.Select(p => lines.Select(m => m.Line).Contains(p.ProdLine) && p.Domain == input.factory_id.ToString() && p.IsActive);
  157. // //1.5 获取当前年和下一年的节假日配置数据
  158. // 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);
  159. // //月度产能共识主表
  160. // List<MonthlyProdCapacityMain> capacityMains = new List<MonthlyProdCapacityMain>();
  161. // //月度产能共识明细表
  162. // List<MonthlyProdCapacityDtl> capacityDtls = new List<MonthlyProdCapacityDtl>();
  163. // foreach (var item in planDtls)
  164. // {
  165. // //获取当前产品的生产线
  166. // var std = standards.FirstOrDefault(p => p.Model == item.Model);
  167. // if (std == null)
  168. // {
  169. // new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("CapacityAnalysis", "规格型号【" + item.Model + "】没有维护标准物料数据", _currentTenant.Id.ToString());
  170. // return "NO|规格型号【" + item.Model + "】没有维护标准物料数据,请维护后再发布!";
  171. // }
  172. // var curLines = lines.Where(p => p.Part == std.ItemNumber).OrderBy(p => p.Line).ToList();
  173. // //过滤产线
  174. // var distLines = curLines.Select(p => p.Line).Distinct().ToList();
  175. // if (distLines.Count == 0)
  176. // {
  177. // new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("CapacityAnalysis", "物料【" + std.ItemNumber + "】没有维护产线数据", _currentTenant.Id.ToString());
  178. // return "NO|物料【" + std.ItemNumber + "】没有维护产线数据,请维护后再发布!";
  179. // }
  180. // foreach (var dl in distLines)
  181. // {
  182. // //添加月度产能共识主表数据
  183. // MonthlyProdCapacityMain main = new MonthlyProdCapacityMain();
  184. // main.Id = help.NextId();
  185. // main.Version = input.version;//整体需求计划版本号
  186. // main.Year = Convert.ToInt16(item.PlanDate.Substring(0, 4));
  187. // main.Month = Convert.ToInt16(item.PlanDate.Substring(4, 2));
  188. // main.ProdRange = item.ProdRange;
  189. // main.Model = item.Model;
  190. // main.ProdQty = item.Qty;
  191. // main.ProdLine = dl;
  192. // main.Qty = item.Qty;
  193. // main.tenant_id = item.tenant_id;
  194. // main.factory_id = item.factory_id;
  195. // main.Version = item.Version;
  196. // main.CreateTime = DateTime.Now;
  197. // capacityMains.Add(main);
  198. // //添加月度产能共识产能效率数据
  199. // MonthlyProdCapacityDtl dtl = new MonthlyProdCapacityDtl();
  200. // dtl.MainId = main.Id;
  201. // dtl.Version = input.version;
  202. // dtl.Year = main.Year;
  203. // dtl.Month = main.Month;
  204. // dtl.ProdLine = dl;
  205. // //计算每天工作时间
  206. // var curCal = calendars.FirstOrDefault(p => p.ProdLine == dl);
  207. // if (curCal == null)
  208. // {
  209. // continue;
  210. // }
  211. // dtl.DailyWorks = curCal.ShiftsHours1;
  212. // dtl.FlightQty = 1;
  213. // //计算当月工作天数
  214. // var curHoildays = holidays.Where(p => p.Dated.Value.Year == main.Year && p.Dated.Value.Month == main.Month).ToList();
  215. // //当月天数
  216. // int days = DateTime.DaysInMonth(main.Year.Value, main.Month.Value);
  217. // //当月周末天数
  218. // int weekDays = CalcWeekDays(days, Convert.ToDateTime(main.Year.ToString() + "-" + main.Month.ToString() + "-01"));
  219. // dtl.YearWorks = days - weekDays - curHoildays.Where(p => p.Ufld1 == "休假").Count() + curHoildays.Where(p => p.Ufld1 == "调班").Count();
  220. // dtl.AvailableTimes = dtl.DailyWorks * dtl.FlightQty * dtl.YearWorks;
  221. // //计算产线耗时
  222. // var line = curLines.Where(p => p.Line == dl).OrderByDescending(p => p.Op).First();
  223. // dtl.NeedWorks = line.Rate == 0 ? 0 : (Math.Ceiling(main.Qty.GetValueOrDefault() / line.Rate));
  224. // dtl.ProdRate = 100;
  225. // dtl.Rate = dtl.AvailableTimes == 0 ? 0 : Math.Floor(dtl.NeedWorks.GetValueOrDefault() / dtl.AvailableTimes.GetValueOrDefault() * 100);
  226. // dtl.IsOverTime = dtl.NeedWorks > dtl.YearWorks ? "是" : "否";
  227. // dtl.OverTimes = dtl.IsOverTime == "是" ? (dtl.NeedWorks - dtl.YearWorks) : 0;
  228. // dtl.tenant_id = main.tenant_id;
  229. // dtl.factory_id = main.factory_id;
  230. // capacityDtls.Add(dtl);
  231. // }
  232. // }
  233. // //保存数据
  234. // using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  235. // {
  236. // try
  237. // {
  238. // //先删除数据
  239. // _monthlyProdCapacityMain.Delete(p => p.tenant_id == input.tenant_id && p.factory_id == input.factory_id && p.Version.Contains(input.version.Substring(0, 7)));
  240. // _monthlyProdCapacityDtl.Delete(p => p.tenant_id == input.tenant_id && p.factory_id == input.factory_id && p.Version.Contains(input.version.Substring(0, 7)));
  241. // //插入数据
  242. // _monthlyProdCapacityMain.Insert(capacityMains);
  243. // _monthlyProdCapacityDtl.Insert(capacityDtls);
  244. // await unitOfWork.CompleteAsync();
  245. // }
  246. // catch (Exception e)
  247. // {
  248. // unitOfWork.Dispose();
  249. // new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("CapacityAnalysis", "生成【" + input.year + "年" + input.month + "月】月度产能共识失败:" + e.Message, _currentTenant.Id.ToString());
  250. // return "NO|" + e.Message;
  251. // };
  252. // }
  253. // return "OK|发布成功!";
  254. //}
  255. /// <summary>
  256. /// 计算当月有多少个周末
  257. /// </summary>
  258. /// <param name="days"></param>
  259. /// <param name="startDay"></param>
  260. /// <returns></returns>
  261. private int CalcWeekDays(int days, DateTime startDay)
  262. {
  263. int sumDays = 0;
  264. for (int i = 0; i < days; i++)
  265. {
  266. int weekDays = (int)startDay.AddDays(i).DayOfWeek;
  267. if (weekDays == 0 || weekDays == 6)
  268. {
  269. sumDays++;
  270. }
  271. }
  272. return sumDays;
  273. }
  274. /// <summary>
  275. /// 月度需求预测更新
  276. /// </summary>
  277. /// <param name="input"></param>
  278. /// <returns></returns>
  279. /// <exception cref="NotImplementedException"></exception>
  280. public async Task<string> DemandAnalysis(InputDto input)
  281. {
  282. //1.0 获取当前年年度生产需求大纲
  283. 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();
  284. //1.0 获取下一年年度生产大纲
  285. 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();
  286. //记录历史版本:当前年+下一年
  287. var updateMonth = input.year.ToString() + "-" + input.month.ToString("00");
  288. var histories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(yearDemands);
  289. histories.ForEach(p => {
  290. p.UpdateMonth = updateMonth;
  291. });
  292. var nextHistories = ObjectMapper.Map<List<YearDemandManagement>, List<YearDemandManagementHistory>>(nextYearDemands);
  293. nextHistories.ForEach(p => {
  294. p.UpdateMonth = updateMonth;
  295. });
  296. //1.1、获取海外销售预测数据
  297. 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;
  298. //1.2、获取平台预测收集数据
  299. 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;
  300. //1.3、获取国内终端预测-T1汇总数据
  301. 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;
  302. //1.4、根据年度生产大纲,获取规格型号对应的补货周期
  303. List<string> allModels = yearDemands.Select(p=>p.Model).Distinct().ToList();
  304. if (nextYearDemands.Any())
  305. {
  306. allModels.AddRange(nextYearDemands.Select(p => p.Model).ToList());
  307. allModels = allModels.Distinct().ToList();
  308. }
  309. 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;
  310. //1.5、获取规格型号对应的标准SKU数据:获取最小包装单位
  311. List<string> itemNums = standards.Select(p=>p.ItemNumber).Distinct().ToList();
  312. 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;
  313. //1.6、获取成品库存
  314. //1.7、获取在制库存
  315. //1.8、获取灭菌库存
  316. //1.9、获取节假日设置
  317. 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);
  318. #region 数据校验
  319. //1、校验当前年的年度生产大纲是否导入
  320. if (!yearDemands.Any())
  321. {
  322. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  323. return "NO|请导入" + input.year + "年的年度生产需求后再操作!";
  324. }
  325. //2、如果当前月份大于10,则需校验下一年的年度生产大纲
  326. if (input.month > 10 && !nextYearDemands.Any())
  327. {
  328. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + (input.year+1) + "年的年度生产需求后再操作!", _currentTenant.Id.ToString());
  329. return "NO|请导入" + (input.year + 1) + "年的年度生产需求后再操作!";
  330. }
  331. //3、校验当前月份的海外销售预测是否导入
  332. if (!overseasSales.Any())
  333. {
  334. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!", _currentTenant.Id.ToString());
  335. return "NO|请导入" + input.year + "年" + input.month + "月海外销售预测后再操作!";
  336. }
  337. //4、校验当前月份的平台预测收集是否导入
  338. if (!platformFcsts.Any())
  339. {
  340. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!", _currentTenant.Id.ToString());
  341. return "NO|请导入" + input.year + "年" + input.month + "月平台需求收集后再操作!";
  342. }
  343. //5、校验当前月份的国内终端预测是否导入
  344. if (!domesticFcst.Any())
  345. {
  346. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!", _currentTenant.Id.ToString());
  347. return "NO|请导入" + input.year + "年" + input.month + "月国内终端预测后再操作!";
  348. }
  349. //6、校验终端+平台导入的规格型号是否已维护标准SKU
  350. if (allModels.Count() != standards.Count())
  351. {
  352. allModels.RemoveAll(standards.Select(p=>p.Model).ToList());
  353. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",",allModels) + "】没有维护标准SKU,请维护后再操作!", _currentTenant.Id.ToString());
  354. return "NO|规格型号【" + string.Join(",", allModels) + "】没有维护标准SKU,请维护后再操作!";
  355. }
  356. //7、校验是否已维护补货周期
  357. var zeros = standards.Where(p => p.ReplenishCycle == 0).ToList();
  358. if (zeros.Any())
  359. {
  360. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "规格型号【" + string.Join(",", zeros.Select(p=>p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!", _currentTenant.Id.ToString());
  361. return "NO|规格型号【" + string.Join(",", zeros.Select(p => p.Model).ToList()) + "】没有维护补货周期,请维护后再操作!";
  362. }
  363. #endregion
  364. //计算当前年月的N+1,N+2
  365. int newYear = input.month == 12 ? input.year + 1 : input.year;
  366. int newMonth = input.month == 12 ? 1 : input.month + 1;
  367. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  368. newYear = newMonth == 12 ? newYear + 1 : newYear;
  369. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  370. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  371. //需要回写的数据
  372. List<YearDemandManagement> updates = new List<YearDemandManagement>();
  373. #region 计算国内终端预测,回写年度生产大纲
  374. //获取导入的T1、平台数据
  375. List<string> gnModels = new List<string>();
  376. if (domesticFcst.Any())
  377. {
  378. gnModels.AddRange(domesticFcst.Select(p => p.Model).ToList());
  379. }
  380. if (platformFcsts.Any())
  381. {
  382. gnModels.AddRange(platformFcsts.Select(p => p.Model).ToList());
  383. }
  384. gnModels = gnModels.Distinct().ToList();
  385. foreach (var gnModel in gnModels)
  386. {
  387. //N+1月
  388. YearDemandManagement monthN1 = new YearDemandManagement();
  389. //N+2月
  390. YearDemandManagement monthN2 = new YearDemandManagement();
  391. //获取需要回写的数据
  392. if (input.month < 11)
  393. {
  394. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  395. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  396. }
  397. if (input.month == 11)
  398. {
  399. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  400. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  401. }
  402. if (input.month == 12)
  403. {
  404. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN1);
  405. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "国内" && p.Model == gnModel && p.PlanMonth == strN2);
  406. }
  407. if (monthN1 == null || monthN2 == null)
  408. {
  409. continue;
  410. }
  411. //T1
  412. var sumT1N1 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p=>p.Qty);
  413. var sumT1N2 = domesticFcst.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  414. //平台
  415. var sumPN1 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  416. var sumPN2 = platformFcsts.Where(p => p.Model == gnModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  417. //合计
  418. var sumN1 = sumT1N1+ sumPN1;
  419. var sumN2 = sumT1N2 + sumPN2;
  420. //当前规格型号对应标准SKU的最小包装单位、补货周期
  421. var curStd = standards.FirstOrDefault(p => p.Model == gnModel);
  422. decimal packQty = 1m;//最小包装单位
  423. decimal cycle = 0m;//补货周期
  424. if (curStd != null)
  425. {
  426. var curItem = items.FirstOrDefault(p => p.number == curStd.ItemNumber);
  427. packQty = curItem == null ? 1 : (curItem.minpackqty.GetValueOrDefault() == 0.0m ? 1 : curItem.minpackqty.Value);
  428. cycle = curStd.ReplenishCycle;
  429. }
  430. //TODO:获取成品库存、在制库存、灭菌库存,参与计算
  431. //N+1月使用N+2月的再订货点参与计算
  432. decimal rop = CalcRop(strN2+"-01", sumN2, packQty, holidays, cycle);
  433. monthN1.Qty = sumN1 / 2 + sumN2 / 2 + rop - 0 - 0 - 0;
  434. //N+2月使用本月的需求量,下一月的工作天数
  435. rop = CalcRop(Convert.ToDateTime(strN2 + "-01").AddMonths(1).ToString("yyyy-MM-dd"), sumN2, packQty, holidays, cycle);
  436. monthN2.Qty = sumN2 + rop - 0 - 0 - 0;
  437. //负数置0
  438. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  439. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  440. //记录需要回写的数据
  441. updates.Add(monthN1);
  442. updates.Add(monthN2);
  443. }
  444. #endregion
  445. #region 海外销售预测,回写年度生产大纲
  446. List<string> hwModels = overseasSales.Select(p => p.Model).Distinct().ToList();
  447. foreach (var hwModel in hwModels)
  448. {
  449. //N+1月
  450. YearDemandManagement monthN1 = new YearDemandManagement();
  451. //N+2月
  452. YearDemandManagement monthN2 = new YearDemandManagement();
  453. //获取需要回写的数据
  454. if (input.month < 11)
  455. {
  456. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  457. monthN2 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  458. }
  459. if (input.month == 11)
  460. {
  461. monthN1 = yearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  462. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  463. }
  464. if (input.month == 12)
  465. {
  466. monthN1 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN1);
  467. monthN2 = nextYearDemands.FirstOrDefault(p => p.Area == "海外" && p.Model == hwModel && p.PlanMonth == strN2);
  468. }
  469. if (monthN1 == null || monthN2 == null) {
  470. continue;
  471. }
  472. var sumN1 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN1).Sum(p => p.Qty);
  473. var sumN2 = overseasSales.Where(p => p.Model == hwModel && p.PlanMonth == strN2).Sum(p => p.Qty);
  474. //海外生产需求量=当月的50%+下一月的50%-成品库存-在制库存-灭菌库存
  475. //TODO:获取成品库存、在制库存、灭菌库存,参与运算
  476. //计算N+1月,N+2月
  477. monthN1.Qty = sumN1 / 2 + sumN2 / 2 - 0 - 0 - 0;
  478. monthN2.Qty = sumN2 - 0 - 0 - 0;
  479. //负数置0
  480. monthN1.Qty = monthN1.Qty < 0 ? 0m : monthN1.Qty;
  481. monthN2.Qty = monthN2.Qty < 0 ? 0m : monthN2.Qty;
  482. //记录需要回写的数据
  483. updates.Add(monthN1);
  484. updates.Add(monthN2);
  485. }
  486. #endregion
  487. //保存数据
  488. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  489. {
  490. try
  491. {
  492. //判断是否存在当前年月的历史版本,不存在则插入
  493. var dbHistory = _yearDemandManagementHistory.GetListAsync(p=>p.UpdateMonth == updateMonth && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id).Result;
  494. if (!dbHistory.Any())
  495. {
  496. await _yearDemandManagementHistory.InsertManyAsync(histories);
  497. }
  498. if (input.month > 10)
  499. {
  500. //判断是否存在下一年月的历史版本,不存在则插入
  501. dbHistory = _yearDemandManagementHistory.GetListAsync(p => p.UpdateMonth == updateMonth && p.tenant_id == input.tenant_id && p.company_id == input.company_id && p.factory_id == input.factory_id).Result;
  502. if (!dbHistory.Any())
  503. {
  504. await _yearDemandManagementHistory.InsertManyAsync(nextHistories);
  505. }
  506. }
  507. //回写年度生产大纲
  508. await _yearDemandManagement.UpdateManyAsync(updates);
  509. await unitOfWork.CompleteAsync();
  510. }
  511. catch (Exception e)
  512. {
  513. unitOfWork.Dispose();
  514. new NLogHelper("MonthlyCapacityLoadAppService").WriteLog("DemandAnalysis", "【" + input.year + "年" + input.month + "月】月度需求预测更新失败:" + e.Message, _currentTenant.Id.ToString());
  515. return "NO|" + e.Message;
  516. };
  517. }
  518. return "OK|发布成功!";
  519. }
  520. /// <summary>
  521. /// 计算再订货点
  522. /// </summary>
  523. /// <param name="planMonth">计划年月(yyyy-MM-dd)</param>
  524. /// <param name="qty">需求量</param>
  525. /// <param name="qty">最小包装数量</param>
  526. /// <param name="holidays">节假日</param>
  527. /// <param name="replenishCycle">补货周期</param>
  528. /// <returns></returns>
  529. private decimal CalcRop(string planMonth,decimal qty,decimal packQty,List<HolidayMaster> holidays,decimal replenishCycle)
  530. {
  531. decimal rop = 0.0m;
  532. //获取当月天数
  533. int year = Convert.ToInt16(planMonth.Substring(0, 4));
  534. int month = Convert.ToInt16(planMonth.Substring(5, 2));
  535. int days = DateTime.DaysInMonth(year, month);
  536. //计算当前月的周末天数
  537. int weeks = CalcWeekDays(days, Convert.ToDateTime(planMonth));
  538. //获取当前年月的节假日设置
  539. var curHolidays = holidays.Where(p => p.Dated.Value.Year == year && p.Dated.Value.Month == month).ToList();
  540. //当月工作天数
  541. int workDays = days - weeks - curHolidays.Where(p => p.Ufld1 == "休假").Count() + curHolidays.Where(p => p.Ufld1 == "调班").Count();
  542. //rop = (需求量/月工作天数*补货周期)=>按照最小包装单位元整
  543. rop = Math.Ceiling(qty / workDays * replenishCycle / packQty) * packQty;
  544. return rop;
  545. }
  546. }
  547. }