MonthlyCapacityLoadAppService.cs 56 KB

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