AnnualProductionOutlineAppService.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. using Business.Core.Enum;
  2. using Business.Core.Utilities;
  3. using Business.Domain;
  4. using Business.Dto;
  5. using Business.EntityFrameworkCore.SqlRepositories;
  6. using Business.SaleForecast;
  7. using Business.StructuredDB.SaleFcst;
  8. using Business.StructuredDB.WMS;
  9. using Microsoft.EntityFrameworkCore.Infrastructure;
  10. using NetTopologySuite.Algorithm;
  11. using RazorEngine;
  12. using Spire.Pdf.General.Render.Decode.Jpeg2000.j2k.wavelet.synthesis;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using Volo.Abp.Application.Dtos;
  19. using Volo.Abp.Application.Services;
  20. using Volo.Abp.DependencyInjection;
  21. using Volo.Abp.Domain.Repositories;
  22. using Volo.Abp.MultiTenancy;
  23. using Volo.Abp.Uow;
  24. using ZstdSharp.Unsafe;
  25. using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
  26. namespace Business.SaleForecastManagement
  27. {
  28. public class AnnualProductionOutlineAppService : ApplicationService, ITransientDependency, IAnnualProductionOutlineAppService
  29. {
  30. #region 服务
  31. /// <summary>
  32. /// 工作单元
  33. /// </summary>
  34. private readonly IUnitOfWorkManager _unitOfWorkManager;
  35. /// <summary>
  36. /// 日志
  37. /// </summary>
  38. private readonly ICurrentTenant _currentTenant;
  39. /// <summary>
  40. /// 年度销售预测
  41. /// </summary>
  42. private IRepository<YearDemandManagement, long> _yearDemandManagement;
  43. /// <summary>
  44. /// 年度销售预测历史记录
  45. /// </summary>
  46. private IRepository<YearDemandManagementHistory, long> _yearDemandManagementHistory;
  47. /// <summary>
  48. /// 年度生产大纲
  49. /// </summary>
  50. private IRepository<AnnualProductionOutline, long> _annualProductionOutline;
  51. /// <summary>
  52. /// 生产线明细
  53. /// </summary>
  54. private ISqlRepository<ProdLineDetail> _prodLineDetail;
  55. /// <summary>
  56. /// 标准工艺流程表
  57. /// </summary>
  58. private ISqlRepository<RoutingOpDetail> _routingOpDetail;
  59. /// <summary>
  60. /// 库存表
  61. /// </summary>
  62. private ISqlRepository<LocationDetail> _locationDetail;
  63. /// <summary>
  64. /// 平台库存表
  65. /// </summary>
  66. private readonly IRepository<WMS_PlatformInventory, long> _PlatformInventory;
  67. /// <summary>
  68. /// 已发货记录
  69. /// </summary>
  70. private ISqlRepository<ASNBOLShipperDetail> _ASNBOLShipperDetail;
  71. /// <summary>
  72. /// 工单工艺路线明细
  73. /// </summary>
  74. private ISqlRepository<WorkOrdRouting> _workOrdRouting;
  75. /// <summary>
  76. /// 补货模型
  77. /// </summary>
  78. private IRepository<ReplenishmentModel, long> _replenishmentModel;
  79. /// <summary>
  80. /// 主生产计划
  81. /// </summary>
  82. private IRepository<ProductionMasterPlan, long> _productionMasterPlan;
  83. /// <summary>
  84. /// 雪花算法
  85. /// </summary>
  86. SnowFlake help = new SnowFlake();
  87. #endregion
  88. #region 构造函数
  89. /// <summary>
  90. /// 构造函数
  91. /// </summary>
  92. public AnnualProductionOutlineAppService(
  93. IUnitOfWorkManager unitOfWorkManager,
  94. ICurrentTenant currentTenant,
  95. IRepository<YearDemandManagement, long> yearDemandManagement,
  96. IRepository<AnnualProductionOutline, long> annualProductionOutline,
  97. IRepository<YearDemandManagementHistory, long> yearDemandManagementHistory,
  98. ISqlRepository<ProdLineDetail> prodLineDetail,
  99. IRepository<WMS_PlatformInventory, long> PlatformInventory,
  100. ISqlRepository<RoutingOpDetail> routingOpDetail,
  101. ISqlRepository<ASNBOLShipperDetail> ASNBOLShipperDetail,
  102. ISqlRepository<WorkOrdRouting> workOrdRouting,
  103. ISqlRepository<LocationDetail> locationDetail,
  104. IRepository<ReplenishmentModel, long> replenishmentModel,
  105. IRepository<ProductionMasterPlan, long> productionMasterPlan
  106. )
  107. {
  108. _unitOfWorkManager = unitOfWorkManager;
  109. _currentTenant = currentTenant;
  110. _PlatformInventory = PlatformInventory;
  111. _prodLineDetail = prodLineDetail;
  112. _workOrdRouting = workOrdRouting;
  113. _routingOpDetail = routingOpDetail;
  114. _locationDetail = locationDetail;
  115. _ASNBOLShipperDetail = ASNBOLShipperDetail;
  116. _yearDemandManagement = yearDemandManagement;
  117. _yearDemandManagementHistory = yearDemandManagementHistory;
  118. _annualProductionOutline = annualProductionOutline;
  119. _replenishmentModel = replenishmentModel;
  120. _productionMasterPlan = productionMasterPlan;
  121. }
  122. #endregion
  123. /// <summary>
  124. /// 生成年度生产大纲
  125. /// </summary>
  126. /// <param name="input"></param>
  127. /// <returns></returns>
  128. /// <exception cref="NotImplementedException"></exception>
  129. public async Task<string> SaveAnnualProductionOutline(InputDto input)
  130. {
  131. //获取当前导入或修改数据
  132. 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).ThenBy(o => o.PlanMonth).ToList();
  133. //标准工艺路径表
  134. List<RoutingOpDetail> routingOps = _routingOpDetail.Select(p => yearDemands.Select(m => m.SAPItemNumber).Contains(p.RoutingCode) && p.Domain == input.factory_id.ToString() && p.IsActive);
  135. var routingOpList = routingOps.Where(x => x.Descr == "组装").ToList();
  136. //生产线明细表
  137. List<ProdLineDetail> prodLines = _prodLineDetail.Select(p => yearDemands.Select(m => m.SAPItemNumber).Contains(p.Part) && p.Domain == input.factory_id.ToString() && p.IsActive && routingOpList.Select(m => m.Op).Contains(p.Op)).OrderBy(x => x.Sequence).ToList();
  138. List<LocationDetail> locations = _locationDetail.Select(x => yearDemands.Select(m => m.SAPItemNumber).Contains(x.ItemNum) && x.Domain == input.factory_id.ToString() && x.IsActive).ToList();
  139. //平台库存
  140. var platformInvList = _PlatformInventory.GetListAsync(a => yearDemands.Select(m => m.SAPItemNumber).Contains(a.SAPItemNumber) && a.tenant_id == input.tenant_id && a.factory_id == input.factory_id && !a.IsDeleted).Result;
  141. var replenishmentModels = _replenishmentModel.GetListAsync(x => !x.IsDeleted && x.isparam && yearDemands.Select(m => m.SAPItemNumber).Contains(x.number)).Result.ToList();
  142. // 获取某年某月的起始日期和结束日期
  143. int year = input.year;
  144. int month = DateTime.Now.Month;
  145. DateTime start = new DateTime(year, month, 1);
  146. DateTime end = start.AddMonths(1).AddDays(-1);
  147. //取当月发货出库记录
  148. var shipList = _ASNBOLShipperDetail.Select(a => a.Domain == input.factory_id.ToString() && a.IsActive && a.shtype == "SH" && a.Typed != "S" && a.RealQty > 0 && yearDemands.Select(p => p.SAPItemNumber).Contains(a.ContainerItem)).Where(s => s.ShipDate >= start && s.ShipDate <= end);
  149. #region 在制数量
  150. //获取在制数量 获取工单数取每年4月到12月底的工单
  151. DateTime startYear = new DateTime(year, 4, 1);
  152. DateTime endYear = new DateTime(year, 12, 31);
  153. List<WorkOrdRouting> workOrdRoutings = _workOrdRouting.Select(x => x.IsActive && x.QtyComplete > 0 && x.Domain == input.factory_id.ToString()).Where(p => p.DueDate >= startYear && p.DueDate <= endYear).ToList();
  154. decimal? InProductionQty = 0.00m;
  155. var workOrds = workOrdRoutings.GroupBy(x => x.WorkOrd).ToList();
  156. //按照工单循环
  157. //某工单10-90工序 Max(10-80工序QtyComplete)-90工序QtyComplete =在制数量
  158. foreach (var item in workOrds)
  159. {
  160. var workOrdRoutingList = workOrdRoutings.Where(x => x.WorkOrd == item.Key).OrderByDescending(o => o.OP).ToList();
  161. //找出最大工序
  162. var MaxOp = workOrdRoutingList.FirstOrDefault();
  163. //查询出其他工序最大值
  164. var MaxQtyComplete = workOrdRoutingList.Where(x => x.RecID != MaxOp.RecID).ToList().Max(o => o.QtyComplete);
  165. InProductionQty += MaxQtyComplete - MaxOp.QtyComplete;
  166. }
  167. #endregion
  168. //年度生产大纲实体
  169. List<AnnualProductionOutline> annualProductionOutlines = new List<AnnualProductionOutline>();
  170. List<YearDemandManagement> frontYearDemand = new List<YearDemandManagement>();
  171. foreach (var item in yearDemands)
  172. {
  173. var routingOp = routingOps.Where(x => x.RoutingCode == item.SAPItemNumber).ToList();
  174. //组装标准工时
  175. var Assembly = routingOp.Where(x => x.Descr == "组装").FirstOrDefault();
  176. //热封标准工时
  177. var HeatSealing = routingOp.Where(x => x.Descr == "热封").FirstOrDefault();
  178. //包装标准工时
  179. var Packaging = routingOp.Where(x => x.Descr == "包装").FirstOrDefault();
  180. var prodLine = prodLines.Where(x => x.Part == item.SAPItemNumber).OrderBy(x => x.Sequence).FirstOrDefault();
  181. //不同库位库存数量
  182. var locationList = locations.Where(x => x.ItemNum == item.SAPItemNumber).ToList();
  183. //平台数据
  184. var platformInvs = platformInvList.Where(x => x.SAPItemNumber == item.SAPItemNumber).ToList();
  185. //销售预测 对应 Excel中公式 AVERAGE 如果预测为0不参与计算排产批量
  186. var QtySum = yearDemands.Where(x => x.SAPItemNumber == item.SAPItemNumber && x.Qty > 0).ToList();
  187. //排产批量:(AVG(1 - 12月销售预测)/ 100 )=(0.45 = 1 小数向上取整) *100 = 100
  188. var pcpl = Math.Ceiling(QtySum.Sum(p => p.Qty) / QtySum.Count()) * 100;
  189. //库存合计 + 在制+已发货 + 灭菌中 TODO: 灭菌中取值待确定
  190. var ship = shipList.Where(x => x.ContainerItem == item.SAPItemNumber).ToList();
  191. var locationSum = (locationList.Count == 0 ? 0 : locationList.Sum(x => x.QtyOnHand)) + (platformInvs.Count == 0 ? 0 : platformInvs.Sum(x => x.InventoryQuantity)) + (ship.Count == 0 ? 0 : ship.Sum(x => x.RealQty)) + InProductionQty + 0;
  192. //前面N个月的生产数量
  193. var frontQtySum = annualProductionOutlines.Sum(x => x.Qty);
  194. //生产数量:3月为例子,if((库存合计和前2月生产数量)-(前2个月销售预测数据+安全库存)-当月销售预测数据 / 2 < 0)
  195. // { 排产批量 * ((-(库存合计+前2个月生产数量)) + (安全库存+前2个月销售预测) + 当月销售预测 / 2 ) / 排产批量 ) } else {0}
  196. decimal ProduceQty = 0.00m;
  197. //判断库存是否满足需要,满足则不用生产
  198. var num = (locationSum.Value + frontQtySum) - frontYearDemand.Sum(m => m.Qty) + 0 - item.Qty / 2;
  199. if (num < 0)
  200. {
  201. ProduceQty = pcpl * Math.Ceiling((-(locationSum.Value + frontQtySum)) + (frontYearDemand.Sum(m => m.Qty) + 0) + item.Qty / 2);
  202. }
  203. else
  204. {
  205. ProduceQty = 0;
  206. }
  207. //生成年度生产大纲
  208. AnnualProductionOutline annualProductionOutline = new AnnualProductionOutline();
  209. annualProductionOutline.Year = item.Year;
  210. annualProductionOutline.Area = item.Area;
  211. annualProductionOutline.ProdLine = item.ProdLine;
  212. annualProductionOutline.ProdRange = item.ProdRange;
  213. annualProductionOutline.WorkshopLine = prodLine == null ? "" : prodLine.Line;
  214. annualProductionOutline.SAPItemNumber = item.SAPItemNumber;
  215. annualProductionOutline.Model = item.Model;
  216. annualProductionOutline.Languages = item.Languages;
  217. annualProductionOutline.PlanMonth = item.PlanMonth;
  218. annualProductionOutline.Qty = ProduceQty;
  219. annualProductionOutline.StandardHours = (Assembly == null ? 0 : Assembly.RunTime) + (HeatSealing == null ? 0 : HeatSealing.RunTime) + (Packaging == null ? 0 : Packaging.RunTime);
  220. //组装热封包装工时乘以数量 =单月工时
  221. annualProductionOutline.AssemblyHours = Assembly == null ? 0 : Assembly.RunTime * ProduceQty;
  222. annualProductionOutline.HeatSealingHours = HeatSealing == null ? 0 : HeatSealing.RunTime * ProduceQty;
  223. annualProductionOutline.PackagingHours = Packaging == null ? 0 : Packaging.RunTime * ProduceQty;
  224. annualProductionOutline.Totalhours = annualProductionOutline.AssemblyHours + annualProductionOutline.HeatSealingHours + annualProductionOutline.Totalhours;
  225. annualProductionOutline.OrderNum = item.OrderNum;
  226. annualProductionOutlines.Add(annualProductionOutline);
  227. frontYearDemand.Add(item);
  228. }
  229. //保存数据
  230. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  231. {
  232. try
  233. {
  234. //await _annualProductionOutline.InsertManyAsync(annualProductionOutlines);
  235. await unitOfWork.CompleteAsync();
  236. }
  237. catch (Exception e)
  238. {
  239. unitOfWork.Dispose();
  240. new NLogHelper("AnnualProductionOutlineAppService").WriteLog("SaveAnnualProductionOutline", "【" + input.year + "年" + "】年度生成大纲生成失败:" + e.Message, _currentTenant.Id.ToString());
  241. return "NO|" + e.Message;
  242. };
  243. }
  244. return "OK";
  245. }
  246. /// <summary>
  247. /// 生成主计划
  248. /// </summary>
  249. /// <param name="input"></param>
  250. /// <returns></returns>
  251. public async Task<string> SaveProductionMasterPlan(InputDto input)
  252. {
  253. //计算当前年月的N0,N+1,N+2
  254. List<string> planMons = new List<string>();
  255. string strN0 = input.year.ToString() + "-" + input.month.ToString("00");
  256. planMons.Add(strN0);
  257. int newYear = input.month == 12 ? input.year + 1 : input.year;
  258. int newMonth = input.month == 12 ? 1 : input.month + 1;
  259. string strN1 = newYear.ToString() + "-" + newMonth.ToString("00");
  260. planMons.Add(strN1);
  261. newYear = newMonth == 12 ? newYear + 1 : newYear;
  262. newMonth = newMonth == 12 ? 1 : newMonth + 1;
  263. string strN2 = newYear.ToString() + "-" + newMonth.ToString("00");
  264. planMons.Add(strN2);
  265. //N0,N+1,N+2月度发货计划
  266. var productionMasterPlan = _productionMasterPlan.GetListAsync(x => x.Year == input.year && !x.IsDeleted && x.tenant_id == input.tenant_id && x.company_id == input.company_id && x.factory_id == input.factory_id && planMons.Contains(x.PlanMonth)).Result.OrderBy(p => p.OrderNum).ThenBy(o => o.PlanMonth).ToList();
  267. foreach (var item in productionMasterPlan)
  268. {
  269. }
  270. //保存数据
  271. using (var unitOfWork = _unitOfWorkManager.Begin(false, true))
  272. {
  273. try
  274. {
  275. //await _annualProductionOutline.InsertManyAsync(annualProductionOutlines);
  276. await unitOfWork.CompleteAsync();
  277. }
  278. catch (Exception e)
  279. {
  280. unitOfWork.Dispose();
  281. new NLogHelper("AnnualProductionOutlineAppService").WriteLog("SaveProductionMasterPlan", "【" + input.year + "年" + "】主计划生成失败:" + e.Message, _currentTenant.Id.ToString());
  282. return "NO|" + e.Message;
  283. };
  284. }
  285. return "OK";
  286. }
  287. }
  288. }