ResourceExamineAppService.cs 110 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322
  1. using Microsoft.EntityFrameworkCore;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Linq.Dynamic.Core;
  6. using System.Threading.Tasks;
  7. using Volo.Abp.Application.Dtos;
  8. using Volo.Abp.Domain.Repositories;
  9. using Business.Models;
  10. using Microsoft.AspNetCore.Authorization;
  11. using Business.Permissions;
  12. using XCZ;
  13. using Business.ResourceExamineManagement.Dto;
  14. using Bussiness.Model.MES.IC;
  15. using AutoMapper.Internal.Mappers;
  16. using Bussiness.Model.Tech;
  17. using Bussiness.Model.Production;
  18. using Business.Core.MongoDBHelper;
  19. using Business.Core.Utilities;
  20. using Hangfire.Storage.Monitoring;
  21. using Business.BookManagement.Dto;
  22. using Volo.Abp.ObjectMapping;
  23. using Volo.Abp.Application.Services;
  24. using ZstdSharp.Unsafe;
  25. using System.Transactions;
  26. using NUglify.JavaScript.Syntax;
  27. using System.Linq.Expressions;
  28. using XCZ.Extensions;
  29. using System.ComponentModel;
  30. using System.Reflection.Emit;
  31. using NUglify.Helpers;
  32. using Microsoft.AspNetCore.SignalR.Protocol;
  33. using System.ComponentModel.Design;
  34. using Volo.Abp.Validation.StringValues;
  35. using System.Runtime.CompilerServices;
  36. using MongoDB.Driver;
  37. using Volo.Abp.Validation.Localization;
  38. using Hangfire.Annotations;
  39. using System.Globalization;
  40. using MongoDB.Driver.Linq;
  41. using Bussiness.Model.SRM;
  42. using System.ComponentModel.DataAnnotations;
  43. using Bussiness.Model.Sale;
  44. using Amazon.Runtime.Internal.Transform;
  45. using System.IO.Compression;
  46. using System.Collections;
  47. using System.Data.SqlTypes;
  48. using Microsoft.EntityFrameworkCore.Diagnostics;
  49. using Hangfire.Server;
  50. using WkHtmlToPdfDotNet;
  51. using Microsoft.EntityFrameworkCore.Migrations.Operations;
  52. using Microsoft.AspNetCore.SignalR;
  53. using System.Diagnostics;
  54. using Newtonsoft.Json;
  55. namespace Business.ResourceExamineManagement
  56. {
  57. /// <summary>
  58. /// 资源检查
  59. /// </summary>
  60. //[Authorize(BusinessPermissions.ResourceExamine.Default)]
  61. public class ResourceExamineAppService : ApplicationService, IResourceExamineAppService
  62. {
  63. #region 服务
  64. SnowFlake help = new SnowFlake();
  65. /// <summary>
  66. /// 工艺路径
  67. /// </summary>
  68. private readonly IMongoDB<mes_technique> _mes_technique;
  69. private IRepository<mes_technique, long> _mysql_mes_technique;
  70. /// <summary>
  71. /// 工序
  72. /// </summary>
  73. private readonly IMongoDB<mes_process> _mes_process;
  74. private IRepository<mes_process, long> _mysql_mes_process;
  75. /// <summary>
  76. /// 工艺关联工序
  77. /// </summary>
  78. private readonly IMongoDB<mes_tech_process> _mes_tech_process;
  79. private IRepository<mes_tech_process, long> _mysql_mes_tech_process;
  80. /// <summary>
  81. /// 工作日历
  82. /// </summary>
  83. private readonly IMongoDB<mes_work_calendar> _mes_work_calendar;
  84. /// <summary>
  85. /// 工作日历明细
  86. /// </summary>
  87. private readonly IMongoDB<mes_work_calendar_list> _mes_work_calendar_list;
  88. /// <summary>
  89. /// 工艺工序关联工位
  90. /// </summary>
  91. private readonly IMongoDB<mes_tech_proc_workshop> _mes_tech_proc_workshop;
  92. /// <summary>
  93. /// 排程占用记录
  94. /// </summary>
  95. private readonly IMongoDB<mes_schedule_occupy> _mes_schedule_occupy;
  96. /// <summary>
  97. /// 物料占用记录
  98. /// </summary>
  99. private readonly IMongoDB<ic_item_stockoccupy> _ic_item_stockoccupy;
  100. /// <summary>
  101. /// 物料详情
  102. /// </summary>
  103. private readonly IMongoDB<ic_item> _ic_item;
  104. private IRepository<ic_item, long> _mysql_ic_item;
  105. /// <summary>
  106. /// 物料BOM
  107. /// </summary>
  108. private readonly IMongoDB<ic_bom> _ic_bom;
  109. private IRepository<ic_bom, long> _mysql_ic_bom;
  110. /// <summary>
  111. /// 物料BOM明细
  112. /// </summary>
  113. private readonly IMongoDB<ic_bom_child> _ic_bom_child;
  114. private IRepository<ic_bom_child, long> _mysql_ic_bom_child;
  115. /// <summary>
  116. /// 物料库存表
  117. /// </summary>
  118. private readonly IMongoDB<ic_item_stock> _ic_item_stock;
  119. private IRepository<ic_item_stock, long> _mysql_ic_item_stock;
  120. /// <summary>
  121. /// 物料采购计划表
  122. /// </summary>
  123. private readonly IMongoDB<ic_plan> _ic_plan;
  124. private IRepository<ic_plan, long> _mysql_ic_plan;
  125. /// <summary>
  126. /// 物料工厂明细表
  127. /// </summary>
  128. private readonly IMongoDB<ic_factory_details> _ic_factory_details;
  129. private IRepository<ic_factory_details, long> _mysql_ic_factory_details;
  130. /// <summary>
  131. /// 物料采购报价单
  132. /// </summary>
  133. private readonly IMongoDB<ic_item_pur> _ic_item_pur;
  134. private IRepository<ic_item_pur, long> _mysql_ic_item_pur;
  135. /// <summary>
  136. /// 采购申请单
  137. /// </summary>
  138. private readonly IMongoDB<srm_pr_main> _srm_pr_main;
  139. private IRepository<srm_pr_main, long> _mysql_srm_pr_main;
  140. /// <summary>
  141. /// 采购订单表
  142. /// </summary>
  143. private readonly IMongoDB<srm_po_main> _srm_po_main;
  144. private IRepository<srm_po_main, long> _mysql_srm_po_main;
  145. /// <summary>
  146. /// 采购订单明细表
  147. /// </summary>
  148. private readonly IMongoDB<srm_po_list> _srm_po_list;
  149. private IRepository<srm_po_list, long> _mysql_srm_po_list;
  150. /// <summary>
  151. /// 采购订单占用详情
  152. /// </summary>
  153. private readonly IMongoDB<srm_po_occupy> _srm_po_occupy;
  154. private IRepository<srm_po_occupy, long> _mysql_srm_po_occupy;
  155. /// <summary>
  156. /// 委外工单
  157. /// </summary>
  158. private readonly IMongoDB<mes_oorder> _mes_oorder;
  159. private IRepository<mes_oorder, long> _mysql_mes_oorder;
  160. /// <summary>
  161. /// 物料质检表
  162. /// </summary>
  163. private readonly IMongoDB<ic_check> _ic_check;
  164. /// <summary>
  165. /// 替代群组
  166. /// </summary>
  167. private readonly IMongoDB<ic_substitute> _ic_substitute;
  168. /// <summary>
  169. /// 替代群组
  170. /// </summary>
  171. private readonly IMongoDB<ic_substitute_all> _ic_substitute_all;
  172. /// <summary>
  173. /// 替代群组
  174. /// </summary>
  175. private readonly IMongoDB<ic_substitute_all_dtl> _ic_substitute_all_dtl;
  176. /// <summary>
  177. /// 生产工单主表
  178. /// </summary>
  179. private readonly IMongoDB<mes_morder> _mes_morder;
  180. /// <summary>
  181. /// 生产工单子表
  182. /// </summary>
  183. private readonly IMongoDB<mes_moentry> _mes_moentry;
  184. /// <summary>
  185. /// 在制工单占用记录表
  186. /// </summary>
  187. private readonly IMongoDB<mes_mooccupy> _mes_mooccupy;
  188. /// <summary>
  189. /// mysql在制工单占用表
  190. /// </summary>
  191. private readonly IRepository<mes_mooccupy, long> _mysql_mes_mooccupy;
  192. /// <summary>
  193. /// 销售订单
  194. /// </summary>
  195. private readonly IRepository<crm_seorder, long> _mysql_crm_seorder;
  196. private readonly IMongoDB<crm_seorder> _crm_seorder;
  197. /// <summary>
  198. /// 销售订单明细
  199. /// </summary>
  200. private readonly IRepository<crm_seorderentry, long> _mysql_crm_seorderentry;
  201. private readonly IMongoDB<crm_seorderentry> _crm_seorderentry;
  202. /// <summary>
  203. /// 生产工单主表
  204. /// </summary>
  205. private readonly IRepository<mes_morder, long> _mysql_mes_morder;
  206. /// <summary>
  207. /// 生产工单子表
  208. /// </summary>
  209. private readonly IRepository<mes_moentry, long> _mysql_mes_moentry;
  210. /// <summary>
  211. /// 资源检查入参
  212. /// </summary>
  213. private readonly SeorderentryDto param = new SeorderentryDto();
  214. #endregion
  215. #region 构造函数
  216. /// <summary>
  217. /// 构造函数
  218. /// </summary>
  219. /// <param name="icitem"></param>
  220. /// <param name="icbom"></param>
  221. public ResourceExamineAppService(
  222. IMongoDB<mes_technique> mes_technique,
  223. IMongoDB<mes_process> mes_process,
  224. IMongoDB<mes_tech_process> mes_tech_process,
  225. IMongoDB<mes_tech_proc_workshop> mes_tech_proc_workshop,
  226. IMongoDB<ic_item> ic_item,
  227. IMongoDB<ic_bom> ic_bom,
  228. IMongoDB<ic_bom_child> ic_bom_child,
  229. IMongoDB<ic_item_stock> ic_item_stock,
  230. IMongoDB<ic_check> ic_check,
  231. IMongoDB<ic_factory_details> ic_factory_details,
  232. IMongoDB<mes_oorder> mes_oorder,
  233. IMongoDB<srm_pr_main> srm_pr_main,
  234. IMongoDB<srm_po_main> srm_po_main,
  235. IMongoDB<srm_po_list> srm_po_list,
  236. IMongoDB<crm_seorder> crm_seorder,
  237. IMongoDB<crm_seorderentry> crm_seorderentry,
  238. IMongoDB<srm_po_occupy> srm_po_occupy,
  239. IMongoDB<ic_item_pur> ic_item_pur,
  240. IMongoDB<ic_plan> ic_plan,
  241. IMongoDB<ic_substitute> ic_substitute,
  242. IMongoDB<ic_substitute_all> ic_substitute_all,
  243. IMongoDB<ic_substitute_all_dtl> ic_substitute_all_dtl,
  244. IMongoDB<mes_morder> mes_morder,
  245. IMongoDB<mes_moentry> mes_moentry,
  246. IMongoDB<mes_mooccupy> mes_mooccupy,
  247. IMongoDB<ic_item_stockoccupy> ic_item_stockoccupy,
  248. IRepository<ic_item, long> mysql_ic_item,
  249. IRepository<ic_bom, long> mysql_ic_bom,
  250. IRepository<ic_bom_child, long> mysql_ic_bom_child,
  251. IRepository<mes_technique, long> mysql_mes_technique,
  252. IRepository<crm_seorder, long> mysql_crm_seorder,
  253. IRepository<crm_seorderentry, long> mysql_crm_seorderentry,
  254. IRepository<ic_item_stock, long> mysql_ic_item_stock,
  255. IRepository<ic_factory_details, long> mysql_ic_factory_details,
  256. IRepository<mes_oorder, long> mysql_mes_oorder,
  257. IRepository<srm_pr_main, long> mysql_srm_pr_main,
  258. IRepository<mes_mooccupy, long> mysql_mes_mooccupy,
  259. IRepository<mes_morder, long> mysql_mes_morder,
  260. IRepository<mes_moentry, long> mysql_mes_moentry,
  261. IRepository<mes_process, long> mysql_mes_process,
  262. IRepository<mes_tech_process, long> mysql_mes_tech_process,
  263. IRepository<srm_po_main, long> mysql_srm_po_main,
  264. IRepository<srm_po_list, long> mysql_srm_po_list,
  265. IRepository<srm_po_occupy, long> mysql_srm_po_occupy,
  266. IRepository<ic_item_pur, long> mysql_ic_item_pur,
  267. IRepository<ic_plan, long> mysql_ic_plan
  268. )
  269. {
  270. _mes_technique = mes_technique;
  271. _mes_process = mes_process;
  272. _mes_tech_process = mes_tech_process;
  273. _mes_tech_proc_workshop = mes_tech_proc_workshop;
  274. _ic_item = ic_item;
  275. _ic_bom = ic_bom;
  276. _ic_bom_child = ic_bom_child;
  277. _ic_item_stock = ic_item_stock;
  278. _ic_check = ic_check;
  279. _ic_factory_details = ic_factory_details;
  280. _mes_oorder = mes_oorder;
  281. _srm_pr_main = srm_pr_main;
  282. _srm_po_main = srm_po_main;
  283. _srm_po_list = srm_po_list;
  284. _crm_seorder = crm_seorder;
  285. _crm_seorderentry = crm_seorderentry;
  286. _srm_po_occupy = srm_po_occupy;
  287. _ic_item_pur = ic_item_pur;
  288. _ic_plan = ic_plan;
  289. _ic_substitute = ic_substitute;
  290. _ic_substitute_all = ic_substitute_all;
  291. _ic_substitute_all_dtl = ic_substitute_all_dtl;
  292. _mes_morder = mes_morder;
  293. _mes_moentry = mes_moentry;
  294. _mes_mooccupy = mes_mooccupy;
  295. _ic_item_stockoccupy = ic_item_stockoccupy;
  296. _mysql_ic_item = mysql_ic_item;
  297. _mysql_ic_bom = mysql_ic_bom;
  298. _mysql_ic_bom_child = mysql_ic_bom_child;
  299. _mysql_crm_seorder = mysql_crm_seorder;
  300. _mysql_crm_seorderentry = mysql_crm_seorderentry;
  301. _mysql_mes_technique = mysql_mes_technique;
  302. _mysql_ic_item_stock = mysql_ic_item_stock;
  303. _mysql_ic_factory_details = mysql_ic_factory_details;
  304. _mysql_mes_oorder = mysql_mes_oorder;
  305. _mysql_srm_pr_main = mysql_srm_pr_main;
  306. _mysql_mes_mooccupy = mysql_mes_mooccupy;
  307. _mysql_mes_morder = mysql_mes_morder;
  308. _mysql_mes_moentry = mysql_mes_moentry;
  309. _mysql_mes_process = mysql_mes_process;
  310. _mysql_mes_tech_process = mysql_mes_tech_process;
  311. _mysql_srm_po_main = mysql_srm_po_main;
  312. _mysql_srm_po_list = mysql_srm_po_list;
  313. _mysql_srm_po_occupy = mysql_srm_po_occupy;
  314. _mysql_ic_item_pur = mysql_ic_item_pur;
  315. _mysql_ic_plan = mysql_ic_plan;
  316. }
  317. #endregion
  318. /// <summary>
  319. /// 资源检查
  320. /// </summary>
  321. /// <param name="input"></param>
  322. /// <returns></returns>
  323. /// <exception cref="NotImplementedException"></exception>
  324. public async Task<string> ReceiveResult(SeorderentryDto input)
  325. {
  326. //资源检查入参全局变量赋值
  327. param.sorderId = input.sorderId;
  328. param.tenantId = input.tenantId;
  329. param.factoryId = input.factoryId;
  330. //资源检查结果
  331. PschedDto rtn = new PschedDto();
  332. rtn.sorderid = input.sorderId;
  333. //资源检查明细list
  334. List<ExamineResult> examines = new List<ExamineResult>();
  335. ExamineResult dtl;
  336. //生成当前计算bangid
  337. SnowFlake snow = new SnowFlake();
  338. long bangid = snow.NextId();
  339. //获取订单数据
  340. crm_seorder sorder = _mysql_crm_seorder.GetListAsync(p => p.tenant_id == input.tenantId && p.factory_id == input.factoryId && p.Id == input.sorderId && !p.IsDeleted).Result.FirstOrDefault();
  341. if (sorder == null)
  342. {
  343. throw new NotImplementedException("订单数据不存在!");
  344. }
  345. //获取订单行数据
  346. List<crm_seorderentry> sentrys =await _mysql_crm_seorderentry.GetListAsync(p => p.tenant_id == input.tenantId && p.factory_id == input.factoryId && p.seorder_id == input.sorderId && !p.IsDeleted);
  347. //数据库快照-同步mysql库数据到mongoDB中
  348. //await SyncData(input.tenantId, input.factoryId, bangid);
  349. //通过订单行的产品代码获取物料BOM数据
  350. //FilterDefinition<ic_bom> filter = Builders<ic_bom>.Filter.In(s => s.bom_number, sentrys.Select(m => m.bom_number).ToList());
  351. //List<ic_bom> boms = _ic_bom.GetManyByIds(filter).Result.Where(p => p.factory_id == input.factoryId && p.tenant_id == input.tenantId && !p.IsDeleted).ToList();
  352. List<ic_bom> boms = _ic_bom.Find(p=>sentrys.Select(m=>m.item_number).Contains(p.item_number) && p.factory_id == input.factoryId && p.tenant_id == input.tenantId && !p.IsDeleted).Result.ToList();
  353. //通过物料id获取产品提前期
  354. List<ICItemLeadTimeDto> leadTimes = GetLeadTime(boms.Select(p => p.icitem_id).ToList(), input.tenantId, input.factoryId);
  355. //物料bom
  356. List<ic_bom> bomlist = new List<ic_bom>();
  357. //物料bom明细
  358. List<ic_bom_child> bomchildlist = new List<ic_bom_child>();
  359. //获取物料bom,物料bom明细
  360. GetIcBomData(boms, bomlist, bomchildlist);
  361. //获取物料数据
  362. List<long> itemIds = bomlist.Select(p => p.icitem_id).ToList();
  363. itemIds.AddRange(bomchildlist.Select(p => p.icitem_id).ToList());
  364. List<ic_item> icitemlist = _ic_item.GetManyByCondition(p => itemIds.Contains(p.Id) && !p.IsDeleted).Result;
  365. foreach (var item in sentrys)
  366. {
  367. //工单资源检查信息
  368. dtl = new ExamineResult();
  369. dtl.sentry_id = item.Id;
  370. //TODO:最早开始时间默认3天后(后期调整)
  371. dtl.earliest_times = DateTime.Now.Date.AddDays(3);
  372. //获取当前物料bom数据
  373. var childBom = boms.Where(p => p.bom_number == item.bom_number).FirstOrDefault();
  374. //bom层级组装
  375. var getBomList = BomPretreatment(item.Id, childBom.Id, item.qty.GetInt(), bomlist, bomchildlist, icitemlist);
  376. //bom替代关系组装
  377. BomSubstitute(getBomList, bomlist, bomchildlist, icitemlist);
  378. //库存初始化
  379. BomStock(getBomList, bangid, input.factoryId);
  380. //计算
  381. calcTest(getBomList, bangid, item.Id, item.qty.Value, input, item.plan_date);
  382. //TODO:最晚开始时间
  383. var curFacDtl = leadTimes.FirstOrDefault(p => p.item_id == childBom.icitem_id);
  384. //最晚开工时间=订单行客户要求交期-运输提前期-库存提前期-生产提前期-下单提前期
  385. dtl.latest_times = item.plan_date.GetValueOrDefault().AddDays(-Convert.ToDouble(curFacDtl?.transportation_leadtime.GetValueOrDefault() + curFacDtl?.stock_leadtime.GetValueOrDefault() + curFacDtl?.production_leadtime.GetValueOrDefault() + curFacDtl?.order_leadtime.GetValueOrDefault()));
  386. //物料齐套时间
  387. dtl.kitting_times = getBomList.Where(p => p.is_use).OrderByDescending(m => m.kitting_time).First().kitting_time.GetValueOrDefault();
  388. //替代关系展开list
  389. dtl.substitutes = getBomList;
  390. //添加订单行开工信息
  391. examines.Add(dtl);
  392. //生成工单 TODO:0=产品数量
  393. //GenerateMorder(o, 0);
  394. }
  395. //订单行资源检查明细list
  396. rtn.examines = examines;
  397. return JsonConvert.SerializeObject(rtn);
  398. }
  399. /// <summary>
  400. /// 递归:获取icbom,icbomchild数据
  401. /// </summary>
  402. /// <param name="icBoms"></param>
  403. /// <param name="bomlist"></param>
  404. /// <param name="bomchildlist"></param>
  405. public void GetIcBomData(List<ic_bom> icBoms, List<ic_bom> bomlist, List<ic_bom_child> bomchildlist)
  406. {
  407. if (icBoms.Count() == 0)
  408. {
  409. return;
  410. }
  411. //添加物料bom数据
  412. bomlist.AddRange(icBoms);
  413. //获取物料bom明细数据
  414. List<ic_bom_child> childList = _ic_bom_child.GetManyByCondition(p => icBoms.Select(m=>m.Id).Contains(p.bom_id) && p.use_status == 1 && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted).Result.ToList();
  415. //没有明细数据,终止
  416. if (childList.Count == 0)
  417. {
  418. return;
  419. }
  420. bomchildlist.AddRange(childList);
  421. //通过物料bom明细数据反查物料bom数据
  422. var boms = _ic_bom.GetManyByCondition(p => childList.Select(m=>m.icitem_id).ToList().Contains(p.icitem_id) && p.fse_status == 1 && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted).Result.ToList();
  423. foreach (var chd in childList)
  424. {
  425. var curBoms = boms.Where(p => p.icitem_id == chd.icitem_id).ToList();
  426. GetIcBomData(curBoms, bomlist, bomchildlist);
  427. }
  428. }
  429. /// <summary>
  430. /// 数据库快照
  431. /// </summary>
  432. /// <returns></returns>
  433. public async Task SyncData(long tenantId, long factoryId, long bangid)
  434. {
  435. //同步物料库存数据
  436. var icitemStokc = _mysql_ic_item_stock.GetListAsync(p => p.tenant_id == tenantId && p.factory_id == factoryId).Result;
  437. if (icitemStokc.Count > 0)
  438. {
  439. //设置当前计算bangid
  440. icitemStokc.ForEach(item => { item.bang_id = bangid; });
  441. //插入数据
  442. await _ic_item_stock.InsertMany(icitemStokc);
  443. }
  444. //在制工单占用记录表
  445. var mes_mooccupy = _mysql_mes_mooccupy.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  446. if (mes_mooccupy.Count > 0)
  447. {
  448. mes_mooccupy.ForEach(item => { item.bang_id = bangid; });
  449. await _mes_mooccupy.InsertMany(mes_mooccupy);
  450. }
  451. //工单主表
  452. var mes_morder = _mysql_mes_morder.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  453. if (mes_morder.Count > 0)
  454. {
  455. mes_morder.ForEach(item => { item.bang_id = bangid; });
  456. await _mes_morder.InsertMany(mes_morder);
  457. }
  458. //工单子表
  459. var mes_moentry = _mysql_mes_moentry.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  460. if (mes_moentry.Count > 0)
  461. {
  462. mes_moentry.ForEach(item => { item.bang_id = bangid; });
  463. await _mes_moentry.InsertMany(mes_moentry);
  464. }
  465. //TODO:要不要根据某些条件只同步有效的数据
  466. //销售订单
  467. var crm_seorder = _mysql_crm_seorder.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  468. if (crm_seorder.Count > 0)
  469. {
  470. crm_seorder.ForEach(item => { item.bang_id = bangid; });
  471. await _crm_seorder.InsertMany(crm_seorder);
  472. }
  473. //销售订单明细
  474. var crm_seorderentry = _mysql_crm_seorderentry.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  475. if (crm_seorderentry.Count > 0)
  476. {
  477. crm_seorderentry.ForEach(item => { item.bang_id = bangid; });
  478. await _crm_seorderentry.InsertMany(crm_seorderentry);
  479. }
  480. //采购订单
  481. var srm_po_main = _mysql_srm_po_main.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  482. if (srm_po_main.Count > 0)
  483. {
  484. srm_po_main.ForEach(item => { item.bang_id = bangid; });
  485. await _srm_po_main.InsertMany(srm_po_main);
  486. }
  487. //采购订单明细
  488. var srm_po_list = _mysql_srm_po_list.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  489. if (srm_po_list.Count > 0)
  490. {
  491. srm_po_list.ForEach(item => { item.bang_id = bangid; });
  492. await _srm_po_list.InsertMany(srm_po_list);
  493. }
  494. //采购订单占用详情
  495. var srm_po_occupy = _mysql_srm_po_occupy.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  496. if (srm_po_occupy.Count > 0)
  497. {
  498. srm_po_occupy.ForEach(item => { item.bang_id = bangid; });
  499. await _srm_po_occupy.InsertMany(srm_po_occupy);
  500. }
  501. //工厂物料明细表
  502. var ic_factory_details = _mysql_ic_factory_details.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  503. if (ic_factory_details.Count > 0)
  504. {
  505. ic_factory_details.ForEach(item => { item.bang_id = bangid; });
  506. await _ic_factory_details.InsertMany(ic_factory_details);
  507. }
  508. //物料采购计划表
  509. var ic_plan = _mysql_ic_plan.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  510. if (ic_plan.Count > 0)
  511. {
  512. ic_plan.ForEach(item => { item.bang_id = bangid; });
  513. await _ic_plan.InsertMany(ic_plan);
  514. }
  515. //物料采购报价单
  516. var ic_item_pur = _mysql_ic_item_pur.GetListAsync(x => x.tenant_id == tenantId && x.factory_id == factoryId).Result;
  517. if (ic_item_pur.Count > 0)
  518. {
  519. ic_item_pur.ForEach(item => { item.bang_id = bangid; });
  520. await _ic_item_pur.InsertMany(ic_item_pur);
  521. }
  522. }
  523. /// <summary>
  524. /// 产能计算
  525. /// </summary>
  526. /// <param name="bom_number">bom编号</param>
  527. /// <param name="version">版本号</param>
  528. /// <param name="packages">需要生产产品件数</param>
  529. /// <returns>生产时长</returns>
  530. public async Task<decimal> ProductiveExamine(string bom_number, string version, int packages)
  531. {
  532. if (packages <= 0)
  533. {
  534. throw new NotImplementedException("产能计算参数有误!");
  535. }
  536. #region 1、数据准备
  537. //1.1、获取工艺路径数据
  538. mes_technique tech = _mes_technique.Find(p => p.bom == bom_number && p.bomver == version && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted).Result.FirstOrDefault();
  539. if (tech == null)
  540. {
  541. throw new NotImplementedException("请先配置工艺路径!");
  542. }
  543. //1.2、获取工艺路径关联工序数据
  544. List<mes_tech_process> tech_Processes = await _mes_tech_process.GetManyByCondition(p => p.tech_id == tech.Id && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
  545. if (tech_Processes.Count == 0)
  546. {
  547. throw new NotImplementedException("请先配置工序!");
  548. }
  549. //1.3、获取当前工艺路径下的工序数据
  550. //FilterDefinition<mes_process> filter = Builders<mes_process>.Filter.In(s => s.Id, tech_Processes.Select(m => m.proc_id).ToList());
  551. //List<mes_process> process = await _mes_process.GetManyByIds(filter);
  552. List<long> procIds = tech_Processes.Select(m => m.proc_id.Value).ToList();
  553. List<mes_process> process = await _mes_process.GetManyByCondition(p => procIds.Contains(p.Id) && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
  554. //1.3、获取工艺工序关联工位信息
  555. List<long> techProcIds = tech_Processes.Select(m => m.Id).ToList();
  556. List<mes_tech_proc_workshop> tech_Proc_Workshops = await _mes_tech_proc_workshop.GetManyByCondition(p => techProcIds.Contains(p.tech_proc_id.Value) && p.tenant_id == param.tenantId && p.factory_id == param.factoryId && !p.IsDeleted);
  557. #endregion
  558. //1、获取工艺路径下的第一层级工序
  559. List<mes_tech_process> fistChilds = tech_Processes.Where(p => p.parentprocid == tech.Id).ToList();
  560. if (fistChilds.Count == 0)
  561. {
  562. throw new NotImplementedException("当前工艺路径没有配置工序,请调整!");
  563. }
  564. List<mes_tech_process> sortChilds = new List<mes_tech_process>();
  565. //添加最后一个工序
  566. var last = fistChilds.First(p => p.nextprocid == null);
  567. sortChilds.Add(last);
  568. //递归按工序先后顺序排序
  569. SortProcess(fistChilds, last.Id, sortChilds);
  570. //总耗时(分钟)
  571. //decimal sumTimes = CalcTakeTimeByLq(sortChilds, packages);//通过Lq计算
  572. decimal sumTimes = CalcTakeTimeByLqt(sortChilds, packages);//通过Lqt计算
  573. return sumTimes;
  574. }
  575. /// <summary>
  576. /// 递归:工序按照先后顺序排序-暂时不考虑两个分支合并到一个分支的情况
  577. /// </summary>
  578. /// <param name="Processes"></param>
  579. /// <param name="processId"></param>
  580. /// <param name="sortProcesses"></param>
  581. private void SortProcess(List<mes_tech_process> Processes, long processId, List<mes_tech_process> sortProcesses)
  582. {
  583. var curProcess = Processes.Where(p => p.nextprocid == processId).FirstOrDefault();
  584. if (curProcess != null)
  585. {
  586. sortProcesses.AddFirst(curProcess);
  587. SortProcess(Processes, curProcess.Id, sortProcesses);
  588. }
  589. }
  590. /// <summary>
  591. /// 通过Lq计算工艺耗时
  592. /// </summary>
  593. /// <param name="Processes"></param>
  594. /// <param name="packages"></param>
  595. /// <returns></returns>
  596. private decimal CalcTakeTimeByLq(List<mes_tech_process> Processes, int packages)
  597. {
  598. decimal sumTimes = 0.00m;//总耗时(分钟)
  599. //工序需要等待时间记录
  600. List<StartTimeDto> starts = new List<StartTimeDto>();
  601. StartTimeDto dto;
  602. foreach (var chd in Processes)
  603. {
  604. dto = new StartTimeDto();
  605. if (chd.nextprocid == null)//最后一个工序
  606. {
  607. //计算最后一个工序耗时
  608. dto = CalcProcTakeTimeByLq(chd, packages, packages);
  609. }
  610. else
  611. {
  612. dto = CalcProcTakeTimeByLq(chd, chd.lq.Value, packages);
  613. }
  614. sumTimes += dto.wait_time;
  615. //添加记录
  616. starts.Add(dto);
  617. }
  618. return sumTimes;
  619. }
  620. /// <summary>
  621. /// 通过Lq计算当前工序前置准备时间
  622. /// </summary>
  623. /// <param name="proc"></param>
  624. /// <param name="quantity">LeadQuantity to Start Next</param>
  625. /// <param name="packages">件数</param>
  626. /// <returns></returns>
  627. private StartTimeDto CalcProcTakeTimeByLq(mes_tech_process proc, decimal quantity, int packages)
  628. {
  629. //记录当前工序耗时
  630. StartTimeDto dto = new StartTimeDto();
  631. //添加耗时记录
  632. dto.tech_id = proc.tech_id.Value;
  633. dto.proc_id = proc.proc_id.Value;
  634. dto.nextproc_id = proc.nextprocid;
  635. if (proc.wctype == 1)//人工型:数量/uph(一小时生产数量)*60(小时转换为分钟)/wsinuse(工位数)
  636. {
  637. dto.wait_time = quantity / proc.uph.Value * 60 / proc.wsinuse.Value;
  638. dto.take_time = packages / proc.uph.Value * 60 / proc.wsinuse.Value;
  639. }
  640. else if (proc.wctype == 2)//流水线型:数量*ct(生产一件所需时间)/wsinuse(工位数)
  641. {
  642. dto.wait_time = quantity * proc.ct.Value / proc.wsinuse.Value;
  643. dto.take_time = packages * proc.ct.Value / proc.wsinuse.Value;
  644. }
  645. else if (proc.wctype == 3)//设备型:向上取整(数量/一次可加工数量/wsinuse(工位数))*ct(老化一次所需时间)
  646. {
  647. dto.wait_time = Math.Ceiling(quantity / proc.upe.Value / proc.wsinuse.Value) * proc.ct.Value;
  648. dto.take_time = Math.Ceiling(packages / proc.upe.Value / proc.wsinuse.Value) * proc.ct.Value;
  649. }
  650. return dto;
  651. }
  652. /// <summary>
  653. /// 通过Lqt计算工艺耗时
  654. /// </summary>
  655. /// <param name="Processes"></param>
  656. /// <param name="packages"></param>
  657. /// <returns></returns>
  658. private decimal CalcTakeTimeByLqt(List<mes_tech_process> Processes, int packages)
  659. {
  660. //总耗时
  661. decimal sumTimes = 0;
  662. //工序需要等待时间记录
  663. List<StartTimeDto> starts = new List<StartTimeDto>();
  664. StartTimeDto dto;
  665. foreach (var chd in Processes)
  666. {
  667. dto = new StartTimeDto();
  668. //添加耗时记录
  669. dto.tech_id = chd.tech_id.Value;
  670. dto.proc_id = chd.proc_id.Value;
  671. dto.nextproc_id = chd.nextprocid;
  672. //计算当前工序生产耗时
  673. dto.take_time = CalcProcTakeTime(chd, packages);
  674. if (chd.nextprocid == null)//最后一个工序
  675. {
  676. dto.wait_time = dto.take_time;
  677. }
  678. else
  679. {
  680. dto.wait_time = chd.lqt.Value;
  681. }
  682. sumTimes += dto.wait_time;
  683. //添加记录
  684. starts.Add(dto);
  685. }
  686. return sumTimes;
  687. }
  688. /// <summary>
  689. /// 计算当前工序生产时间
  690. /// </summary>
  691. /// <param name="proc"></param>
  692. /// <param name="packages">件数</param>
  693. /// <returns></returns>
  694. private decimal CalcProcTakeTime(mes_tech_process proc, int packages)
  695. {
  696. //当前工序生产时间
  697. decimal takeTiem = 0.00m;
  698. if (proc.wctype == 1)//人工型:数量/uph(一小时生产数量)*60(小时转换为分钟)/wsinuse(工位数)
  699. {
  700. takeTiem = packages / proc.uph.Value * 60 / proc.wsinuse.Value;
  701. }
  702. else if (proc.wctype == 2)//流水线型:数量*ct(生产一件所需时间)/wsinuse(工位数)
  703. {
  704. takeTiem = packages * proc.ct.Value / proc.wsinuse.Value;
  705. }
  706. else if (proc.wctype == 3)//设备型:向上取整(数量/一次可加工数量/wsinuse(工位数))*ct(老化一次所需时间)
  707. {
  708. takeTiem = Math.Ceiling(packages / proc.upe.Value / proc.wsinuse.Value) * proc.ct.Value;
  709. }
  710. return takeTiem;
  711. }
  712. /// <summary>
  713. /// 生成工单
  714. /// </summary>
  715. /// <param name="seorderentry">销售订单子表</param>
  716. /// <param name="BomNumber">Bom编号</param>
  717. /// <param name="version">Bom版本</param>
  718. /// <param name="number">物料编码</param>
  719. /// <param name="Quantity"></param>
  720. /// <param name="ParentId"></param>
  721. public void GenerateMorder(crm_seorderentry seorderentry, string BomNumber,string version, string number, decimal? Quantity, long? ParentId)
  722. {
  723. //1.库存、在制工单检查完成后 当前BOM需要自制时 产生工单。
  724. //2.每一个销售订单行对应一个工单。
  725. //TODO:考虑性能问题 循环调用,后期改造传递数组,批量处理数据后,批量保存。
  726. //获取销售订单信息
  727. var seorder = _mysql_crm_seorder.FindAsync(x => x.Id == seorderentry.seorder_id).Result;
  728. //物料BOM
  729. //var ic_bom = _ic_bom.GetManyByCondition(x => x.bom_number == BomNumber && x.factory_id == seorderentry.factory_id).Result.FirstOrDefault();
  730. //物料详情
  731. var ic_item = _ic_item.GetManyByCondition(x => x.number == number && x.factory_id == seorderentry.factory_id).Result.FirstOrDefault();
  732. mes_morder mes_Morder = new mes_morder();
  733. mes_Morder.GenerateNewId();
  734. mes_Morder.morder_type = "销售工单";
  735. mes_Morder.parent_id = ParentId;
  736. //mes_Morder.morder_icitem_type
  737. mes_Morder.work_order_type = "常规工单";
  738. mes_Morder.morder_state = "初始";
  739. mes_Morder.morder_no = string.Format("VMO{0}", DateTime.Now.ToString("yyyyMMddhhmmss"));//测试编码
  740. mes_Morder.fms_number = ic_item.fms_number;
  741. mes_Morder.bom_number = BomNumber;
  742. mes_Morder.fmodel = ic_item.model;
  743. //最早的开工时间3天后、 最晚时间为订单承诺时间-采购提前期-质检提前期-入库提前期-发料提前期 =最晚开工时间 最早或最晚为系统建议开工日期
  744. //根绝系统配置参数 取最早或者最晚开始日期
  745. var StartDate = DateTime.Now.Date.AddDays(3);
  746. mes_Morder.moentry_sys_stime = StartDate;
  747. if (!string.IsNullOrEmpty(BomNumber))
  748. {
  749. var ProductiveDate = ProductiveExamine(BomNumber, version,(int)(Quantity.Value));
  750. //系统建议完工日期为 开工日期+产能检查时间=完工日期
  751. var Day = ProductiveDate.Result / (60 * 10); //返回的分钟除以十个小时得出工作天数;
  752. mes_Morder.moentry_sys_etime = StartDate.AddDays((double)Day);
  753. mes_Morder.morder_need_time = ProductiveDate.Result;
  754. }
  755. mes_Morder.moentry_startup_status = 0;
  756. mes_Morder.tenant_id = seorderentry.tenant_id;
  757. mes_Morder.factory_id = seorderentry.factory_id;
  758. mes_Morder.product_code = number;
  759. mes_Morder.product_name = ic_item.name;
  760. mes_Morder.project_name = seorder.project_name;
  761. mes_Morder.planner_num = seorderentry.planner_no;
  762. mes_Morder.planner_name = seorderentry.planner_name;
  763. mes_Morder.morder_date = DateTime.Now.Date;
  764. //mes_Morder.morder_fstate = "计划";
  765. //TODO:目前没有取值位置
  766. mes_Morder.moentry_prd = null;
  767. mes_Morder.moentry_prdname = null;
  768. mes_Morder.moentry_wrkc = null;
  769. mes_Morder.moentry_wrkcname = null;
  770. mes_Morder.picking_qty = 0;
  771. //TODO:可删除主表字段
  772. mes_Morder.unit = ic_item.unit;
  773. mes_Morder.morder_production_number = Quantity;
  774. mes_Morder.need_number = Quantity;
  775. mes_Morder.remaining_number = 0;
  776. //生成工单子表数据
  777. mes_moentry mes_Moentry = new mes_moentry();
  778. mes_Moentry.GenerateNewId();
  779. mes_Moentry.moentry_moid = mes_Morder.Id;
  780. mes_Moentry.moentry_mono = mes_Morder.morder_no;
  781. mes_Moentry.soentry_id = seorderentry.Id;
  782. mes_Moentry.fbill_no = seorderentry.bill_no;
  783. mes_Moentry.fentry_id = seorderentry.entry_seq.Value;
  784. mes_Moentry.unit = ic_item.unit;
  785. mes_Moentry.morder_production_number = Quantity;
  786. mes_Moentry.need_number = Quantity;
  787. mes_Moentry.remaining_number = 0;
  788. mes_Moentry.factory_id = seorderentry.factory_id;
  789. //using (TransactionScope scope = new TransactionScope())
  790. // {
  791. _mes_morder.InsertOne(mes_Morder);
  792. _mes_moentry.InsertOne(mes_Moentry);
  793. //scope.Complete();
  794. //}
  795. }
  796. /// <summary>
  797. /// 检查成品库存
  798. /// </summary>
  799. /// <param name="seorderentry_id">销售订单子表ID</param>
  800. /// <returns></returns>
  801. public async Task<bool> CheckFinishedProductInventory(long seorderentry_id)
  802. {
  803. //获取销售订单子表
  804. var seorderentry = await _mysql_crm_seorderentry.FindAsync(x => x.Id == seorderentry_id);
  805. if (string.IsNullOrEmpty(seorderentry.bom_number))
  806. {
  807. return false;
  808. }
  809. //物料BOM
  810. var ic_bom = _ic_bom.GetManyByCondition(x => x.bom_number == seorderentry.bom_number && x.tenant_id == seorderentry.tenant_id).Result.FirstOrDefault();
  811. //物料库存表
  812. var ic_Item_Stocks = await _ic_item_stock.GetManyByCondition(x => x.icitem_id == ic_bom.icitem_id && x.tenant_id == seorderentry.tenant_id);
  813. if (seorderentry.qty <= ic_Item_Stocks.Sum(x => x.sqty))
  814. {
  815. return true;
  816. }
  817. else
  818. {
  819. return false;
  820. }
  821. }
  822. /// <summary>
  823. /// 检查在制工单
  824. /// </summary>
  825. /// <param name="bomNumber">Bom编码</param>
  826. /// <param name="Quantity">需要数量</param>
  827. /// <param name="DeliverDate">交付日期</param>
  828. /// <param name="seorderentry_id">销售订单子表ID</param>
  829. /// <returns></returns>
  830. public async Task<int> CheckMorder(string bomNumber, decimal? Quantity, DateTime DeliverDate, crm_seorderentry seorderentry)
  831. {
  832. if (string.IsNullOrEmpty(bomNumber) || Quantity == null)
  833. {
  834. //TODO:入参异常;
  835. throw new NotImplementedException("BOM编码或需求数量不能为空!");
  836. }
  837. var Number = 3; //1,满足,2数量满足,时间不满足,3不满足
  838. //获取销售订单信息
  839. //var seorder = await _crm_seorder.FindAsync(x => x.Id == OrderId);
  840. //var seorderentry = await _mysql_crm_seorderentry.FindAsync(x => x.Id == seorderentry_id);
  841. //根据Bom编码查询出对应工单并且状态不为完成、关闭,非委外工单。
  842. //TODO:工单类型;
  843. var morderList = await _mes_morder.GetManyByCondition(x => x.bom_number == bomNumber && (x.morder_state != "完成" || x.morder_state != "关闭"
  844. && x.morder_icitem_type != "相关委外工单") && !x.IsDeleted && x.tenant_id == seorderentry.tenant_id);
  845. //获取物料详情
  846. var ic_item = _ic_item.GetManyByCondition(x => x.number == seorderentry.item_number && x.tenant_id == seorderentry.tenant_id).Result.FirstOrDefault();
  847. //工单已被占用后要与占用表关联查询...减去占用量后 剩下生产数量可供下个销售工单使用。
  848. var mes_mooccupyList = await _mes_mooccupy.GetManyByCondition(x => x.moo_state == 1 && !x.IsDeleted && x.tenant_id == seorderentry.tenant_id);
  849. //首先满足需求数量工单其次判断是否满足交付日期、当数量不满足时继续查找最早交付日期订单 工单数量累加。
  850. //当前工单计划日期-1天 小于交付日期 && 计算生产数量-入库数据并且大于需求产品数量。
  851. var morderDataList = morderList.Where(x => x.planner_end_date.Value.AddDays(-1) < DeliverDate &&
  852. (x.morder_production_number - x.inventory_number) > Quantity).ToList();
  853. if (morderDataList.Count > 0)
  854. {
  855. //存在此数据满足当前BOM交付找到最早日期工单,则返回无需后续继续检查。
  856. var morder = morderDataList.OrderByDescending(x => x.planner_end_date).FirstOrDefault();
  857. var mooccupies = mes_mooccupyList.Where(x => x.moo_moid == morder.Id).ToList();
  858. var mes_Mooccupy = GetMooccupies(seorderentry, ic_item, morder, mooccupies);
  859. await _mes_mooccupy.InsertOne(mes_Mooccupy);
  860. Number = 1;
  861. }
  862. else
  863. {
  864. // 寻找最早日期工单 && 计算生产数量-入库数据并且大于需求产品数量后累加直到满足需求产品数量
  865. var morderListData = morderList.Where(x => x.planner_end_date.Value.AddDays(-1) < DeliverDate).OrderByDescending(x => x.planner_end_date).ToList();
  866. List<mes_mooccupy> mes_Mooccupies = new List<mes_mooccupy>();
  867. decimal? number = Quantity;
  868. foreach (var item in morderListData)
  869. {
  870. //查询出工单已占用总数量
  871. var mooccupies = mes_mooccupyList.Where(x => x.moo_moid == item.Id).ToList();
  872. var mes_Mooccupy = GetMooccupies(seorderentry, ic_item, item, mooccupies);
  873. mes_Mooccupies.Add(mes_Mooccupy);
  874. //需求数量-占用量后小于或等于0 停止循环占用工单
  875. if (number - mes_Mooccupy.moo_qty <= 0)
  876. {
  877. break;
  878. }
  879. }
  880. var morderDatas = morderList.Where(x => x.planner_end_date.Value.AddDays(-1) > DeliverDate &&
  881. (x.morder_production_number - x.inventory_number) > Quantity).ToList();
  882. if (number > 0)
  883. {
  884. Number = 3;
  885. }
  886. if (morderDatas.Count > 0)
  887. {
  888. Number = 2;
  889. }
  890. else
  891. {
  892. Number = 1;
  893. }
  894. }
  895. return Number;
  896. }
  897. /// <summary>
  898. /// 拼接工单占用表
  899. /// </summary>
  900. /// <param name="seorderentry">销售订单子表</param>
  901. /// <param name="mysql_ic_item">物料详情表</param>
  902. /// <param name="item">工单表</param>
  903. /// <param name="mes_mooccupy">占用工单表</param>
  904. /// <returns></returns>
  905. public mes_mooccupy GetMooccupies(crm_seorderentry seorderentry, ic_item ic_item, mes_morder item, List<mes_mooccupy> mes_mooccupy)
  906. {
  907. decimal? Sumqty = 0;
  908. if (mes_mooccupy.Count > 0)
  909. {
  910. Sumqty = mes_mooccupy.Sum(x => x.moo_qty);
  911. }
  912. //生成mes_mooccupy工单占用表数据,代表多个工单被某个销售订单已占用。
  913. mes_mooccupy mes_Mooccupy = new mes_mooccupy();
  914. mes_Mooccupy.GenerateNewId();
  915. mes_Mooccupy.moo_id_type = "分配";
  916. mes_Mooccupy.moo_id_billid = seorderentry.seorder_id;//销售订单ID
  917. mes_Mooccupy.fbill_no = seorderentry.bill_no;//销售订单号
  918. mes_Mooccupy.fentry_id = seorderentry.entry_seq.Value;//销售订单行
  919. mes_Mooccupy.fitem_name = ic_item.name;//物料名称
  920. mes_Mooccupy.fitem_number = ic_item.number;
  921. mes_Mooccupy.fmodel = ic_item.model;//规格型号
  922. mes_Mooccupy.moo_moid = item.Id;
  923. mes_Mooccupy.moo_mo = item.morder_no;
  924. //占用量=生产计划数量-入库数量-已被占用数量
  925. mes_Mooccupy.moo_qty = item.morder_production_number - item.inventory_number - Sumqty;
  926. mes_Mooccupy.moo_stime = DateTime.Now.Date;
  927. mes_Mooccupy.moo_etime = DateTime.Now;//日期来源需确定
  928. mes_Mooccupy.moo_state = 1;
  929. mes_Mooccupy.moo_cbr = string.Empty;
  930. //mes_Mooccupy.moo_ctime = ;
  931. mes_Mooccupy.moo_creason = string.Empty;
  932. mes_Mooccupy.tenant_id = seorderentry.tenant_id;//取销售子表企业ID
  933. mes_Mooccupy.factory_id = seorderentry.factory_id;
  934. mes_Mooccupy.factory_id = seorderentry.factory_id;
  935. return mes_Mooccupy;
  936. }
  937. /// <summary>
  938. /// BOM预处理
  939. /// </summary>
  940. /// <param name="orderid"></param>
  941. /// <param name="BomId"></param>
  942. /// <param name="Quantity"></param>
  943. public List<BomChildExamineDto> BomPretreatment(long? orderid, long? BomId, int Quantity, List<ic_bom> bomlist, List<ic_bom_child> bomchildlist, List<ic_item> icitemlist)
  944. {
  945. if (orderid == null)
  946. {
  947. //throw new bu
  948. }
  949. if (BomId == null)
  950. {
  951. //throw new bu
  952. }
  953. List<BomChildExamineDto> returnlist = new List<BomChildExamineDto>();
  954. var bom = bomlist.Find(s => s.Id == BomId);
  955. if (bom == null)
  956. {
  957. return returnlist;
  958. }
  959. var dto = new BomChildExamineDto();
  960. dto.item_id = bom.icitem_id;
  961. dto.bom_id = BomId.Value;
  962. dto.level = 1;
  963. dto.id = help.NextId();
  964. dto.parent_id = help.NextId();
  965. dto.qty = 1;
  966. dto.num = "1";
  967. dto.isbom = 1;
  968. dto.is_replace = 0;
  969. dto.haveicsubs = 0;
  970. dto.substitute_code = "";
  971. dto.icitem_ids = "";
  972. int type = 0;
  973. GetBomList(bomlist, bomchildlist, icitemlist, dto, returnlist, type);
  974. return returnlist;
  975. }
  976. /// <summary>
  977. /// BOM预处理层级组装
  978. /// </summary>
  979. /// <param name="bomlist"></param>
  980. /// <param name="bomchildlist"></param>
  981. /// <param name="icitemlist"></param>
  982. /// <param name="dto"></param>
  983. /// <param name="returnlist"></param>
  984. public void GetBomList(List<ic_bom> bomlist, List<ic_bom_child> bomchildlist, List<ic_item> icitemlist, BomChildExamineDto dto, List<BomChildExamineDto> returnlist, int type)
  985. {
  986. int level = dto.level + 1;//初始化定义level层级
  987. var bom = bomlist.Where(s => s.Id == dto.bom_id).FirstOrDefault();
  988. ic_item item = new ic_item();
  989. if (bom != null)
  990. {
  991. item = icitemlist.Where(a => a.Id == bom.icitem_id).FirstOrDefault();
  992. }
  993. else
  994. {
  995. item = icitemlist.Where(a => a.Id == dto.item_id).FirstOrDefault();
  996. }
  997. if (item == null)
  998. {
  999. return;
  1000. }
  1001. dto.item_id = item.Id;
  1002. dto.item_name = bom.item_name;
  1003. dto.item_code = bom.item_number;
  1004. dto.model = item.model;
  1005. dto.unit = bom.unit;
  1006. dto.erp_cls = item.erp_cls;
  1007. dto.erp_cls_name = item.erp_cls_name;
  1008. dto.type = type;
  1009. //var bdto = ObjectMapper.Map<ic_bom,BomChildExamineDto>(bom);
  1010. returnlist.Add(dto);
  1011. var childlist = bomchildlist.Where(a => a.bom_id == bom.Id).ToList();
  1012. int idx = 1;
  1013. foreach (var c in childlist)
  1014. {
  1015. string childNum = dto.num + "." + idx.ToString();
  1016. var icitem = icitemlist.Where(a => a.Id == c.icitem_id).FirstOrDefault();
  1017. var childBom = bomlist.Where(a => a.icitem_id == c.icitem_id).FirstOrDefault();
  1018. //如果此明细查的到BOM信息,则代表此child是一个子BOM。
  1019. if (childBom != null)
  1020. {
  1021. var cdto = new BomChildExamineDto();
  1022. cdto.id = help.NextId();
  1023. cdto.level = level;
  1024. cdto.parent_id = dto.id;
  1025. cdto.bom_child_id = c.Id;
  1026. cdto.qty = c.qty.Value;
  1027. cdto.backflush = c.backflush;
  1028. cdto.num = childNum;
  1029. cdto.isbom = 1;
  1030. cdto.is_replace = c.is_replace;
  1031. cdto.haveicsubs = c.haveicsubs;
  1032. cdto.substitute_code = c.substitute_code;
  1033. cdto.icitem_ids = c.icitem_ids;
  1034. cdto.type = type;
  1035. cdto.item_id = childBom.icitem_id;
  1036. cdto.bom_id = childBom.Id;
  1037. //递归寻找子级
  1038. GetBomList(bomlist, bomchildlist, icitemlist, cdto, returnlist, type);
  1039. }
  1040. else
  1041. {
  1042. if (icitem != null)
  1043. {
  1044. var childDto = new BomChildExamineDto();
  1045. childDto.level = level;
  1046. childDto.bom_id = dto.bom_id;
  1047. childDto.bom_child_id = c.Id;
  1048. childDto.id = help.NextId();
  1049. childDto.parent_id = dto.id;
  1050. childDto.item_id = icitem.Id;
  1051. childDto.item_name = icitem.name;
  1052. childDto.item_code = icitem.number;
  1053. childDto.num = childNum;
  1054. childDto.model = icitem.model;
  1055. childDto.unit = c.unit;
  1056. childDto.erp_cls = icitem.erp_cls;
  1057. childDto.erp_cls_name = icitem.erp_cls_name;
  1058. childDto.backflush = c.backflush;
  1059. childDto.qty = c.qty.Value;
  1060. childDto.isbom = 0;
  1061. childDto.is_replace = c.is_replace;
  1062. childDto.haveicsubs = c.haveicsubs;
  1063. childDto.substitute_code = c.substitute_code;
  1064. childDto.icitem_ids = c.icitem_ids;
  1065. childDto.type = type;
  1066. returnlist.Add(childDto);
  1067. }
  1068. }
  1069. idx++;
  1070. }
  1071. }
  1072. /// <summary>
  1073. /// BOM替代关系预处理
  1074. /// </summary>
  1075. public void BomSubstitute(List<BomChildExamineDto> returnlist, List<ic_bom> bomlist, List<ic_bom_child> bomchildlist, List<ic_item> icitemlist)
  1076. {
  1077. List<string> codeList = returnlist.Select(c => c.substitute_code).ToList();
  1078. var sublist = _ic_substitute.GetManyByCondition(p => codeList.Contains(p.substitute_code) && !p.IsDeleted).Result;
  1079. List<long> subidlist = sublist.Select(c => c.Id).ToList();
  1080. var suballlist = _ic_substitute_all.GetManyByCondition(p => subidlist.Contains(p.substitute_id) && !p.IsDeleted).Result;
  1081. List<long> suballidlist = suballlist.Select(c => c.Id).ToList();
  1082. var subdtllist = _ic_substitute_all_dtl.GetManyByCondition(p => suballidlist.Contains(p.substitute_allid) && !p.IsDeleted).Result;
  1083. List<long> childidList = new List<long>();
  1084. int type = 1;
  1085. List<BomChildExamineDto> addlist = new List<BomChildExamineDto>();
  1086. //除顶级外,其他层级关系全带出来。生成平铺
  1087. foreach (var item in returnlist)
  1088. {
  1089. //最顶级、虚拟件
  1090. if (item.level != 1 && item.erp_cls != 4 && !childidList.Contains(item.bom_child_id.GetValueOrDefault()))
  1091. {
  1092. //有替代关系
  1093. if (item.haveicsubs == 1)
  1094. {
  1095. if (!string.IsNullOrEmpty(item.icitem_ids))
  1096. {
  1097. long cid = 1;
  1098. var cids = item.icitem_ids.Split(',');
  1099. foreach (var c in cids)
  1100. {
  1101. if (long.TryParse(c, out cid))
  1102. {
  1103. childidList.Add(cid);
  1104. }
  1105. }
  1106. }
  1107. //找到当前物料的替代群组关系集
  1108. var sl = sublist.Find(s => s.substitute_code == item.substitute_code);
  1109. if (sl != null)
  1110. {
  1111. var sall = suballlist.Where(s => s.substitute_id == sl.Id).ToList();
  1112. foreach (var sal in sall)
  1113. {
  1114. var sadl = subdtllist.Where(s => s.substitute_allid == sal.Id).ToList();
  1115. List<long> dtlItemId = sadl.Select(m => m.icitem_id).ToList();
  1116. var dtlitemlist = _ic_item.GetManyByCondition(p => dtlItemId.Contains(p.Id) && !p.IsDeleted).Result;
  1117. icitemlist.AddRange(dtlitemlist);
  1118. foreach (var dtl in sadl)
  1119. {
  1120. if (dtl.ismain != 0)//替代关系里,已经将BOM料当成主料存放于替代群组里了。
  1121. {
  1122. //递归将替代关系组装出来。
  1123. SubstitutePretreatment(sl, sal, dtl, item, addlist, icitemlist, bomlist, bomchildlist, type);
  1124. }
  1125. else
  1126. {
  1127. //将主料赋值上属性
  1128. var dtlitem = returnlist.Find(s => s.item_id == dtl.icitem_id && s.level == item.level);
  1129. dtlitem.substitute_all_num = sal.order_num;//群组优先级
  1130. }
  1131. }
  1132. }
  1133. }
  1134. }
  1135. }
  1136. }
  1137. returnlist.AddRange(addlist);
  1138. }
  1139. /// <summary>
  1140. /// 替代关系递归组装出来
  1141. /// </summary>
  1142. /// <param name="sal"></param>
  1143. /// <param name="dtl"></param>
  1144. /// <param name="toDto"></param>
  1145. /// <param name="returnlist"></param>
  1146. /// <param name="icitemlist"></param>
  1147. /// <param name="bomlist"></param>
  1148. /// <param name="bomchildlist"></param>
  1149. public void SubstitutePretreatment(ic_substitute sl, ic_substitute_all sal, ic_substitute_all_dtl dtl, BomChildExamineDto toDto, List<BomChildExamineDto> returnlist, List<ic_item> icitemlist, List<ic_bom> bomlist, List<ic_bom_child> bomchildlist, int type)
  1150. {
  1151. //如果dtl对应的icitem是BOM,还需要向下继续展开。
  1152. //List<BomChildExamineDto> returnlist = new List<BomChildExamineDto>();
  1153. var dto = new BomChildExamineDto();
  1154. var bom = bomlist.Where(s => s.icitem_id == dtl.icitem_id).FirstOrDefault();
  1155. var icitem = icitemlist.Find(s => s.Id == dtl.icitem_id);
  1156. if (icitem == null)
  1157. {
  1158. return;
  1159. }
  1160. dto.id = help.NextId();
  1161. dto.level = toDto.level;
  1162. dto.parent_id = toDto.parent_id;
  1163. dto.item_id = icitem.Id;
  1164. dto.item_name = icitem.name;
  1165. dto.item_code = icitem.number;
  1166. dto.num = toDto.num;
  1167. dto.model = icitem.model;
  1168. dto.unit = icitem.unit;
  1169. dto.erp_cls = icitem.erp_cls;
  1170. dto.erp_cls_name = icitem.erp_cls_name;
  1171. dto.backflush = toDto.backflush;
  1172. //dto.qty = toDto.qty;
  1173. //dto.replace_amount = dtl.replace_amount.Value;
  1174. dto.qty = dtl.replace_amount == null ? 1 : dtl.replace_amount.Value;
  1175. dto.is_replace = 0;
  1176. dto.haveicsubs = 0;
  1177. dto.substitute_code = "";
  1178. dto.icitem_ids = "";
  1179. dto.substitute_strategy = sl.substitute_strategy == null ? 0 : sl.substitute_strategy.Value;//替代策略
  1180. dto.substitute_mode = sl.substitute_mode == null ? 0 : sl.substitute_mode.Value;//替代方式
  1181. dto.type = type;
  1182. dto.substitute_all_num = sal.order_num;//群组优先级
  1183. if (bom != null)
  1184. {
  1185. dto.bom_id = bom.Id;
  1186. dto.qty = dtl.replace_amount == null ? 1 : dtl.replace_amount.Value;
  1187. dto.isbom = 1;
  1188. dto.is_replace = 0;
  1189. dto.haveicsubs = 0;
  1190. dto.substitute_code = "";
  1191. dto.icitem_ids = "";
  1192. GetBomList(bomlist, bomchildlist, icitemlist, dto, returnlist, type);
  1193. }
  1194. else
  1195. {
  1196. dto.bom_id = null;
  1197. dto.isbom = 0;
  1198. returnlist.Add(dto);
  1199. }
  1200. }
  1201. /// <summary>
  1202. /// 计算物料库存量
  1203. /// </summary>
  1204. /// <param name="returnlist"></param>
  1205. public void BomStock(List<BomChildExamineDto> returnlist, long bangid, long factoryid)
  1206. {
  1207. returnlist = returnlist.OrderBy(s => s.num).ToList();
  1208. //获取当前工厂下物料库存数据
  1209. List<long> icitemIds = returnlist.Select(c => c.item_id).ToList();
  1210. var stocklist = _ic_item_stock.GetManyByCondition(p => p.factory_id == factoryid && icitemIds.Contains(p.icitem_id)).Result;
  1211. //取当前订单的物料库存占用记录
  1212. //var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.bang_id == bangid && p.order_id == orderid).Result;
  1213. //计算剩余库存
  1214. foreach (var item in returnlist)
  1215. {
  1216. if (item.erp_cls == 4)//虚拟件不计算
  1217. {
  1218. continue;
  1219. }
  1220. //非虚拟件
  1221. //当前物料的库存数量
  1222. decimal stockQty = stocklist.Where(s => s.icitem_id == item.item_id).Sum(p => p.sqty.GetValueOrDefault());
  1223. //获取当前订单其他订单行当前物料的占用数量
  1224. //decimal otherStockQty = occupylist.Where(s => s.icitem_id == item.item_id).Sum(p => p.quantity);
  1225. //当前订单行物料库存情况
  1226. //item.sqty = stockQty - otherStockQty;
  1227. item.sqty = stockQty;
  1228. }
  1229. }
  1230. /// <summary>
  1231. /// 替代关系计算
  1232. /// </summary>
  1233. /// <param name="returnlist"></param>
  1234. /// <param name="bangid"></param>
  1235. /// <param name="orderid"></param>
  1236. /// <param name="count"></param>
  1237. /// <param name="input"></param>
  1238. /// <param name="plan_date"></param>
  1239. public void calcTest(List<BomChildExamineDto> returnlist, long bangid, long orderid, decimal count, SeorderentryDto input, DateTime
  1240. ? plan_date)
  1241. {
  1242. //占用情况
  1243. List<ic_item_stockoccupy> sklist = new List<ic_item_stockoccupy>();
  1244. //var occupylist = _ic_item_stockoccupy.GetManyByCondition(p => p.icitem_id == bangid && p.order_id == orderid).Result;
  1245. //第一级
  1246. returnlist = returnlist.OrderBy(s => s.num).ToList();
  1247. var childList = returnlist.Where(s => s.parent_id == returnlist[0].id && s.type == 0).ToList();
  1248. //1.如果主料够的时候,不需要显示替代料的平铺视图,如果主料不够,显示替代料的平铺视图。
  1249. //2.替代策略和替代方式,影响到的是甲乙组概念,替代按主料有限,取代按组的优先级。A与B的替代,则A和B各自会存在一个组。
  1250. List<long> calcIds = new List<long>();
  1251. //先处理下最顶级的产品需要数量
  1252. returnlist[0].needCount = returnlist[0].qty * count;
  1253. returnlist[0].lack_qty = returnlist[0].needCount - returnlist[0].sqty;
  1254. if (returnlist[0].lack_qty > 0)
  1255. {
  1256. var seorderentry = _mysql_crm_seorderentry.FindAsync(x => x.Id == orderid).Result;
  1257. //生成自制工单
  1258. //GenerateMorder(seorderentry, returnlist[0].lack_qty);
  1259. }
  1260. foreach (var item in returnlist)
  1261. {
  1262. if (item.level == 1)
  1263. {
  1264. continue;
  1265. }
  1266. //循环平铺整个资源检查的物料库存情况、缺料情况,子集缺料需要用父级缺料*子集使用数量-
  1267. CaclMaterialShortage(returnlist, item, count);
  1268. }
  1269. //这是从上往下展开计算缺料和可制
  1270. calcTest2(returnlist[0], childList, returnlist, sklist);
  1271. //returnlist[0].kz = childList.Min(s => s.kz);//得到最小可制数量。
  1272. //再加个循环,来根据替代关系里的检查结果,根据规则明确使用和生成占用关系。
  1273. CalcIcitem(childList, returnlist, bangid, orderid, input, sklist, plan_date);
  1274. }
  1275. /// <summary>
  1276. /// 循环计算物料情况
  1277. /// </summary>
  1278. /// <param name="childList"></param>
  1279. /// <param name="returnlist"></param>
  1280. /// <param name="bangid"></param>
  1281. /// <param name="orderid"></param>
  1282. /// <param name="input"></param>
  1283. /// <param name="sklist"></param>
  1284. /// <param name="plan_date"></param>
  1285. public void CalcIcitem(List<BomChildExamineDto> childList, List<BomChildExamineDto> returnlist, long bangid, long orderid, SeorderentryDto input, List<ic_item_stockoccupy> sklist, DateTime
  1286. ? plan_date)
  1287. {
  1288. foreach (var item in childList)
  1289. {
  1290. var cilList = returnlist.Where(s => s.parent_id == item.id && s.type == 0).ToList();
  1291. if (item.haveicsubs == 1)
  1292. {
  1293. //如果有替代关系,根据群组来明确使用哪个群组的替代料。按整批和混用逻辑来算
  1294. // 如果有群组替代,就移除掉被检查过的记录 item.icitem_ids
  1295. CalcStrategy(item, returnlist, bangid, sklist, input, plan_date);
  1296. }
  1297. else
  1298. {
  1299. //直接占用库存,缺料就生成采购
  1300. ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
  1301. itemStockoccupyDto.bang_id = bangid;
  1302. itemStockoccupyDto.icitem_id = item.item_id;
  1303. //修改:根据是否缺料判断使用库存,还是使用需要数量needcount
  1304. itemStockoccupyDto.quantity = item.sqty;
  1305. sklist.Add(itemStockoccupyDto);
  1306. item.is_use = true;
  1307. CalcIcitem(cilList, returnlist, bangid, orderid, input, sklist, plan_date);
  1308. if (item.erp_cls == 1)
  1309. {
  1310. //走自制
  1311. //走子物料
  1312. //foreach()
  1313. }
  1314. else if (item.erp_cls == 2 || item.erp_cls == 3)
  1315. {
  1316. var leadTimeList = GetLeadTime(new List<long> { item.item_id }, input.tenantId, input.factoryId);//提前期列表
  1317. var supplierList = GetSupplier(new List<BomChildExamineDto> { item }, input.tenantId, input.factoryId);//供应商列表
  1318. var planList = GetICPlan(new List<BomChildExamineDto> { item }, input.tenantId, input.factoryId);//plan列表
  1319. item.kitting_time = CreateSRMPR(item, input.tenantId, input.factoryId, bangid, item.erp_cls, leadTimeList, supplierList, planList, plan_date.Value);
  1320. if (item.erp_cls == 3)
  1321. {
  1322. //生成委外工单
  1323. CreateMesOOder(item, input.tenantId, input.factoryId, bangid, leadTimeList, supplierList, plan_date.Value);
  1324. //1.先生成委外工单。
  1325. //2.再根据委外工单需要检查库存材料,然后提供给第三方组装。
  1326. //3.如果委外工单的物料库存不够,先生成物料采购申请单,再生成物料的采购订单,到货后再走委外流程。
  1327. //4.再生成委外的采购申请单。
  1328. }
  1329. }
  1330. }
  1331. }
  1332. }
  1333. /// <summary>
  1334. /// 平铺计算物料情况
  1335. /// </summary>
  1336. /// <param name="returnlist"></param>
  1337. /// <param name="item"></param>
  1338. /// <param name="count"></param>
  1339. public void CaclMaterialShortage(List<BomChildExamineDto> returnlist, BomChildExamineDto item, decimal count)
  1340. {
  1341. var parent = returnlist.Find(s => s.id == item.parent_id);
  1342. //当前物料总共需要数量
  1343. item.needCount = parent.needCount * item.qty;
  1344. /*//当前库存可以满足的数量
  1345. item.satisfyNum = item.sqty;
  1346. //总需要数量减去库存量,得出缺料数量
  1347. item.lack_qty = item.needCount - item.satisfyNum;
  1348. //如果不满足,计算子物料,或者计算替代料
  1349. if (item.lack_qty < 0)
  1350. {
  1351. //库存满足
  1352. item.stock_state = 1;
  1353. item.lack_qty = 0;
  1354. }
  1355. else
  1356. {
  1357. item.stock_state = 0;
  1358. }*/
  1359. }
  1360. /// <summary>
  1361. /// 根据占用情况重新计算占用
  1362. /// </summary>
  1363. /// <param name="item"></param>
  1364. /// <param name="returnlist"></param>
  1365. /// <param name="sklist"></param>
  1366. public void RecalculationStock(BomChildExamineDto item, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist)
  1367. {
  1368. //再计算一边占用情况,这里根据父级产品额缺料量*当前子料的使用数量-子料库存量。得出当前子物料的缺料数量
  1369. var parent = returnlist.Find(s => s.id == item.parent_id);
  1370. decimal stockQty = sklist.Where(s => s.icitem_id == item.item_id).Sum(p => p.quantity);
  1371. item.sqty = item.sqty - stockQty;
  1372. item.lack_qty = parent.lack_qty * item.qty - item.sqty;
  1373. item.lack_qty = item.lack_qty > 0 ? item.lack_qty : 0;
  1374. item.stock_state = item.lack_qty > 0 ? 0 : 1;
  1375. }
  1376. /// <summary>
  1377. /// 根据替代策略计算
  1378. /// </summary>
  1379. /// <param name="item"></param>
  1380. /// <param name="returnlist"></param>
  1381. /// <param name="bangid"></param>
  1382. /// <param name="sklist"></param>
  1383. /// <param name="input"></param>
  1384. /// <param name="plan_date"></param>
  1385. public void CalcStrategy(BomChildExamineDto item, List<BomChildExamineDto> returnlist, long bangid, List<ic_item_stockoccupy> sklist, SeorderentryDto input, DateTime
  1386. ? plan_date)
  1387. {
  1388. //提取群组关系
  1389. var sublist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num && s.level == item.level).OrderBy(c => c.substitute_all_num).ToList();
  1390. List<BomChildExamineDto> select = new List<BomChildExamineDto>();
  1391. var parent = returnlist.Find(s => s.id == item.parent_id);
  1392. switch (item.substitute_strategy)
  1393. {
  1394. case 0://整批
  1395. WholeBatchCheck(sublist, returnlist, sklist, select);
  1396. //如果都需要采购的情况下,则默认使用优先级最高的
  1397. WholeBatch(item, sublist, returnlist, sklist, select, bangid, parent, input, plan_date);
  1398. break;
  1399. case 1://混用
  1400. MixedUse(item, sublist, returnlist, sklist, bangid, parent, input, plan_date);
  1401. break;
  1402. case 2://整批加混用
  1403. WholeBatchCheck(sublist, returnlist, sklist, select);
  1404. if (select.Count() == 0)
  1405. {
  1406. //走混用
  1407. MixedUse(item, sublist, returnlist, sklist, bangid, parent, input, plan_date);
  1408. }
  1409. else
  1410. {
  1411. //走整批
  1412. WholeBatch(item, sublist, returnlist, sklist, select, bangid, parent, input, plan_date);
  1413. }
  1414. break;
  1415. }
  1416. }
  1417. /// <summary>
  1418. /// 整批计算
  1419. /// </summary>
  1420. /// <param name="sublist"></param>
  1421. /// <param name="returnlist"></param>
  1422. /// <param name="sklist"></param>
  1423. /// <param name="select"></param>
  1424. public void WholeBatchCheck(List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, List<BomChildExamineDto> select)
  1425. {
  1426. for (int idx = 0; idx < 99; idx++)
  1427. {
  1428. var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
  1429. if (list.Any())
  1430. {
  1431. foreach (var s in list)
  1432. {
  1433. RecalculationStock(s, returnlist, sklist);
  1434. }
  1435. //可制计算:需要对list进行可制计算,按主料来算辅料
  1436. if (list.Where(s => s.stock_state != 1).Count() == 0)
  1437. {
  1438. //只满足充足或可制
  1439. //无缺料情况
  1440. select = list;
  1441. select.ForEach(s => { s.is_use = true; });
  1442. break;
  1443. }
  1444. idx++;
  1445. }
  1446. else
  1447. {
  1448. idx = 99;
  1449. }
  1450. }
  1451. }
  1452. /// <summary>
  1453. /// 整批占用
  1454. /// </summary>
  1455. /// <param name="item"></param>
  1456. /// <param name="sublist"></param>
  1457. /// <param name="returnlist"></param>
  1458. /// <param name="sklist"></param>
  1459. /// <param name="select"></param>
  1460. /// <param name="bangid"></param>
  1461. /// <param name="parent"></param>
  1462. /// <param name="input"></param>
  1463. /// <param name="plan_date"></param>
  1464. public void WholeBatch(BomChildExamineDto item, List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, List<BomChildExamineDto> select, long bangid, BomChildExamineDto parent, SeorderentryDto input, DateTime
  1465. ? plan_date)
  1466. {
  1467. if (select.Count() == 0)
  1468. {
  1469. //如果为空,则默认使用优先级为0的集合作为替代关系
  1470. if (item.substitute_mode == 0)
  1471. {
  1472. //替代
  1473. select = sublist.Where(s => s.type == 0).ToList();
  1474. }
  1475. else
  1476. {
  1477. //取代
  1478. select = sublist.Where(s => s.substitute_all_num == 0).ToList();
  1479. }
  1480. select.ForEach(s => { s.is_use = true; });
  1481. }
  1482. //占用库存
  1483. foreach (var slt in select)
  1484. {
  1485. ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
  1486. itemStockoccupyDto.bang_id = bangid;
  1487. itemStockoccupyDto.icitem_id = slt.item_id;
  1488. slt.is_use = true;
  1489. if (slt.lack_qty > 0)
  1490. {
  1491. itemStockoccupyDto.quantity = slt.sqty;
  1492. //库存不够的时候,根据属性生成采购和委外。
  1493. if (slt.erp_cls == 1)
  1494. {
  1495. slt.make_qty = slt.lack_qty;
  1496. /*var childList = returnlist.Where(s => s.parent_id == slt.id).ToList();
  1497. if (childList.Count() > 0)
  1498. {
  1499. CalcStrategy(slt, returnlist, bangid, sklist);
  1500. }*/
  1501. }
  1502. else if (slt.erp_cls == 2 || slt.erp_cls == 3)
  1503. { //生成采购订单
  1504. //slt.purchase_qty = slt.lack_qty;
  1505. var leadTimeList = GetLeadTime(new List<long> { slt.item_id }, input.tenantId, input.factoryId);//提前期列表
  1506. var supplierList = GetSupplier(new List<BomChildExamineDto> { slt }, input.tenantId, input.factoryId);//供应商列表
  1507. var planList = GetICPlan(new List<BomChildExamineDto> { slt }, input.tenantId, input.factoryId);//plan列表
  1508. slt.kitting_time = CreateSRMPR(slt, input.tenantId, input.factoryId, bangid, slt.erp_cls, leadTimeList, supplierList, planList, plan_date.Value);
  1509. if (item.erp_cls == 3)
  1510. {
  1511. //生成委外工单
  1512. CreateMesOOder(item, input.tenantId, input.factoryId, bangid, leadTimeList, supplierList, plan_date.Value);
  1513. }
  1514. }
  1515. }
  1516. else
  1517. {
  1518. itemStockoccupyDto.quantity = slt.needCount;
  1519. if (parent != null)
  1520. { //如果不缺料的情况下,则占用掉父级缺料乘以当前子集使用料数量
  1521. //itemStockoccupyDto.quantity = parent.lack_qty * slt.qty - slt.sqty;
  1522. itemStockoccupyDto.quantity = parent.lack_qty * slt.qty;
  1523. }
  1524. }
  1525. sklist.Add(itemStockoccupyDto);
  1526. }
  1527. }
  1528. /// <summary>
  1529. /// 混用占用
  1530. /// </summary>
  1531. /// <param name="item"></param>
  1532. /// <param name="sublist"></param>
  1533. /// <param name="returnlist"></param>
  1534. /// <param name="sklist"></param>
  1535. /// <param name="bangid"></param>
  1536. /// <param name="parent"></param>
  1537. /// <param name="input"></param>
  1538. /// <param name="plan_date"></param>
  1539. public void MixedUse(BomChildExamineDto item, List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sklist, long bangid, BomChildExamineDto parent, SeorderentryDto input, DateTime
  1540. ? plan_date)
  1541. {
  1542. decimal parent_lack = 0;
  1543. if (parent != null)
  1544. {
  1545. parent_lack = parent.lack_qty;
  1546. }
  1547. for (int idx = 0; idx < 99; idx++)
  1548. {
  1549. if (parent_lack <= 0)
  1550. {
  1551. break;
  1552. }
  1553. var list = sublist.Where(s => s.substitute_all_num == idx).ToList();
  1554. if (list.Any())
  1555. {
  1556. foreach (var s in list)
  1557. {
  1558. RecalculationStock(s, returnlist, sklist);
  1559. }
  1560. decimal minMake = 9999999;
  1561. foreach (var hy in list)
  1562. {
  1563. //混用先使用掉当前要用的数量
  1564. //得到库存最小数量,去占用,然后剩余的丢第二个循环里去占用。
  1565. decimal make = hy.sqty / hy.qty;
  1566. if (minMake > make)
  1567. {
  1568. minMake = make;
  1569. }
  1570. }
  1571. decimal use_p_num = 0;
  1572. if (parent_lack > minMake) //20>10
  1573. {
  1574. use_p_num = minMake;
  1575. parent_lack -= use_p_num;
  1576. }
  1577. else
  1578. {
  1579. use_p_num = parent_lack;
  1580. parent_lack = 0;
  1581. }
  1582. //根据混用逻辑,去占用物料
  1583. foreach (var zy in list)
  1584. {
  1585. zy.is_use = true;
  1586. ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
  1587. itemStockoccupyDto.bang_id = bangid;
  1588. itemStockoccupyDto.icitem_id = zy.item_id;
  1589. itemStockoccupyDto.quantity = use_p_num * zy.qty; ;
  1590. sklist.Add(itemStockoccupyDto);
  1591. }
  1592. idx++;
  1593. }
  1594. }
  1595. if (parent_lack > 0)
  1596. {
  1597. var select = new List<BomChildExamineDto>();
  1598. if (item.substitute_mode == 0)
  1599. {
  1600. //替代
  1601. select = sublist.Where(s => s.type == 0).ToList();
  1602. }
  1603. else
  1604. {
  1605. //取代
  1606. select = sublist.Where(s => s.substitute_all_num == 0).ToList();
  1607. }
  1608. //对select执行采购
  1609. foreach (var sct in select)
  1610. {
  1611. //找到当前物料的占用记录
  1612. var itemSockoccupy = sklist.Where(s => s.icitem_id == sct.item_id).ToList();
  1613. var num = parent_lack * sct.qty - itemSockoccupy.Sum(m => m.quantity);
  1614. if (sct.erp_cls == 1)
  1615. {
  1616. //自制
  1617. //GenerateMorder()
  1618. }
  1619. else if (sct.erp_cls == 2 || sct.erp_cls == 3)
  1620. {
  1621. var leadTimeList = GetLeadTime(new List<long> { sct.item_id }, input.tenantId, input.factoryId);//提前期列表
  1622. var supplierList = GetSupplier(new List<BomChildExamineDto> { sct }, input.tenantId, input.factoryId);//供应商列表
  1623. var planList = GetICPlan(new List<BomChildExamineDto> { sct }, input.tenantId, input.factoryId);//plan列表
  1624. sct.kitting_time = CreateSRMPR(sct, input.tenantId, input.factoryId, bangid, sct.erp_cls, leadTimeList, supplierList, planList, plan_date.Value);
  1625. if (item.erp_cls == 3)
  1626. {
  1627. //生成委外工单
  1628. CreateMesOOder(item, input.tenantId, input.factoryId, bangid, leadTimeList, supplierList, plan_date.Value);
  1629. }
  1630. }
  1631. }
  1632. }
  1633. }
  1634. /// <summary>
  1635. /// 可制占用计算---暂时未用
  1636. /// </summary>
  1637. /// <param name="item"></param>
  1638. /// <param name="returnlist"></param>
  1639. /// <param name="bangid"></param>
  1640. /// <param name="kznun"></param>
  1641. public void Sockoccupy(BomChildExamineDto item, List<BomChildExamineDto> returnlist, long bangid, decimal kznun)
  1642. {
  1643. ic_item_stockoccupy itemStockoccupyDto = new ic_item_stockoccupy();
  1644. itemStockoccupyDto.bang_id = bangid;
  1645. if (item.stock_state == 1)
  1646. {
  1647. //成品库存占用
  1648. //slt.needCount
  1649. //可制物料占用
  1650. //slt.kz;如果是BOM,就展开BOM占用子物料
  1651. itemStockoccupyDto.icitem_id = item.item_id;
  1652. itemStockoccupyDto.quantity = item.needCount;
  1653. }
  1654. else if (item.stock_state == 2 || item.stock_state == 3)
  1655. {
  1656. itemStockoccupyDto.quantity = item.sqty;
  1657. var num = item.lack_qty;//可制满足的情况下,直接占用可制。
  1658. var sltChild = returnlist.Where(s => s.parent_id == item.id).ToList();
  1659. if (sltChild.Count() > 0)
  1660. {
  1661. //有子集,则代表每条明细的库存加可制,才是父级的可制。
  1662. foreach (var c in sltChild)
  1663. {
  1664. Sockoccupy(c, returnlist, bangid, num);
  1665. }
  1666. }
  1667. }
  1668. //如果存在外购或者委外
  1669. else if (item.stock_state == 4 || item.stock_state == 5)
  1670. {
  1671. //根据最小颗粒度
  1672. }
  1673. }
  1674. /// <summary>
  1675. /// 计算物料是否缺料
  1676. /// </summary>
  1677. /// <param name="parent"></param>
  1678. /// <param name="bzlist"></param>
  1679. /// <param name="returnlist"></param>
  1680. /// <param name="sockoccupyList"></param>
  1681. public void calcTest2(BomChildExamineDto parent, List<BomChildExamineDto> bzlist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sockoccupyList)
  1682. {
  1683. //从第二级开始循环
  1684. foreach (var item in bzlist)
  1685. {
  1686. var childList = returnlist.Where(s => s.parent_id == item.id).ToList();
  1687. //存在替代关系
  1688. if (item.haveicsubs == 1)
  1689. {
  1690. //提取群组关系
  1691. var sublist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num && s.level == item.level).OrderBy(c => c.substitute_all_num).ToList();
  1692. //循环计算群组,看哪个群组满足要求,然后使用此群组,将群组的库存和子物料占用掉。
  1693. //计算此次群组是否有符合
  1694. MaterialCalc(sublist, returnlist, sockoccupyList);
  1695. }
  1696. else
  1697. {
  1698. //根据占用情况计算库存
  1699. Calczykc(item, sockoccupyList);
  1700. //如果有子集,则丢入循环,判断下库存可制等信息。
  1701. calcTest2(item, childList, returnlist, sockoccupyList);
  1702. /*item.kz = childList.Min(s => s.kz);
  1703. if (item.kz >= item.lack_qty)
  1704. {
  1705. item.stock_state = 3;
  1706. }
  1707. else
  1708. {
  1709. item.stock_state = 0;
  1710. }*/
  1711. }
  1712. }
  1713. }
  1714. /// <summary>
  1715. /// 递归计算物料信息
  1716. /// </summary>
  1717. /// <param name="sublist"></param>
  1718. public void MaterialCalc(List<BomChildExamineDto> sublist, List<BomChildExamineDto> returnlist, List<ic_item_stockoccupy> sockoccupyList)
  1719. {
  1720. int maxIdx = sublist.Max(s => s.substitute_all_num);
  1721. for (int i = 0; i <= maxIdx; i++)
  1722. {
  1723. var group = sublist.Where(s => s.substitute_all_num == i).ToList();
  1724. // int boolCount = 0;//代表某一颗物料无需采购,可以自制,则集合+1。
  1725. //如果替代料库存不够,但是可制够,则也考虑使用优先级最高
  1726. foreach (var g in group)
  1727. {
  1728. //根据占用情况计算库存
  1729. Calczykc(g, sockoccupyList);
  1730. /*if (g.stock_state != 1)
  1731. {
  1732. //判断此料是否BOM,如果是BOM,就考虑自制是否足够,此处递归检查子集
  1733. var gChildList = returnlist.Where(s => s.parent_id == g.id).ToList();
  1734. if (gChildList.Count > 0)
  1735. {
  1736. calcTest2(g, gChildList, returnlist, sockoccupyList);
  1737. //如果是自制,则考虑自制够不够
  1738. if (g.erp_cls == 1)
  1739. {
  1740. foreach (var cc in gChildList)
  1741. {
  1742. //如果子物料是BOM,则当前物料的可制数量应该是子物料的库存加子物料的可制除以子物料的使用量
  1743. if (returnlist.Where(s => s.parent_id == cc.id).Count() > 0)
  1744. {
  1745. cc.kz = Math.Floor((cc.sqty + cc.kz) / g.qty);
  1746. }
  1747. }
  1748. g.kz = gChildList.Min(s => s.kz);//得到最小可制数量。
  1749. //todo:如果是群组替代,可制数量应该跟随着主料来,不应该受限于最小可制单元。
  1750. g.stock_state = gChildList.Max(s => s.stock_state);
  1751. if (g.lack_qty <= g.kz)
  1752. {
  1753. g.stock_state = 2;
  1754. boolCount++;
  1755. }
  1756. }
  1757. }
  1758. else
  1759. {
  1760. g.kz = Math.Floor(g.sqty / g.qty);//自己不是BOM的情况下,算一下自己可制父级可以制造多少个,这个可制只是基于父级BOM才用来运算。
  1761. //todo:申老师说,如果是苏州工厂,原材料有可能也是自制的。
  1762. //所以这里在计算时,还可以直接拿缺料数量,去丢给苏州计算方法,得出原材料的库存加可制。
  1763. }
  1764. }
  1765. else
  1766. {
  1767. boolCount++;
  1768. }*/
  1769. }
  1770. /*if (boolCount == group.Count())
  1771. {
  1772. //如果检查集合满足条数与群组里的物料条数相同,则代表这个群组的数量是满足的。
  1773. group.ForEach(s =>
  1774. {
  1775. s.is_show = true;
  1776. });
  1777. break;//如果已经找到合适的替代群组关系,并且都不需要采购,则直接不继续检查了。
  1778. }*/
  1779. }
  1780. }
  1781. /// <summary>
  1782. /// 根据每个物料来实时计算占用情况
  1783. /// </summary>
  1784. /// <param name="item"></param>
  1785. /// <param name="sockoccupyList"></param>
  1786. public void Calczykc(BomChildExamineDto item, List<ic_item_stockoccupy> sockoccupyList)
  1787. {
  1788. //找到当前物料的占用记录
  1789. var itemSockoccupy = sockoccupyList.Where(s => s.icitem_id == item.item_id).ToList();
  1790. //计算库存减去占用
  1791. item.sqty -= itemSockoccupy.Sum(s => s.quantity);
  1792. //如果库存
  1793. item.sqty = item.sqty < 0 ? 0 : item.sqty;
  1794. //判断缺料数量
  1795. item.lack_qty = item.needCount - item.sqty;
  1796. //判断状态
  1797. item.stock_state = item.lack_qty > 0 ? 0 : 1;
  1798. }
  1799. #region 替代检查第一版,屏蔽
  1800. /*/// <summary>
  1801. /// 替代关系检查计算
  1802. /// </summary>
  1803. public void CalcIcitemSubstitute(List<BomChildExamineDto> returnlist, int count)
  1804. {
  1805. returnlist = returnlist.OrderBy(s => s.num).ToList();
  1806. //1.如果主料够的时候,不需要显示替代料的平铺视图,如果主料不够,显示替代料的平铺视图。
  1807. //2.替代策略和替代方式,影响到的是甲乙组概念,替代按主料有限,取代按组的优先级。A与B的替代,则A和B各自会存在一个组。
  1808. List<long> calcIds = new List<long>();
  1809. //先处理下最顶级的产品需要数量
  1810. returnlist[0].needCount = returnlist[0].qty * count;
  1811. returnlist[0].satisfyNum = returnlist[0].sqty;
  1812. returnlist[0].lack_qty = returnlist[0].needCount - returnlist[0].satisfyNum;
  1813. returnlist[0].is_show = true;
  1814. foreach (var item in returnlist)
  1815. {
  1816. //循环平铺整个资源检查的物料库存情况、缺料情况
  1817. CaclMaterialShortage(returnlist, item, count);
  1818. }
  1819. foreach (var item in returnlist)
  1820. {
  1821. //替代件不计算,替代件通过标准件的替代关系,去计算需要使用哪些物料
  1822. if (item.type == 1)
  1823. {
  1824. continue;
  1825. }
  1826. CaclBomChildUseShortage(returnlist, item);
  1827. }
  1828. }
  1829. /// <summary>
  1830. /// 物料计算
  1831. /// </summary>
  1832. /// <param name="returnlist"></param>
  1833. /// <param name="item"></param>
  1834. /// <param name="count"></param>
  1835. public void CaclBomChildUseShortage(List<BomChildExamineDto> returnlist, BomChildExamineDto item)
  1836. {
  1837. //判断是否是BOM,如果是BOM,还需要向下展开
  1838. var bomchild = returnlist.Where(s => s.parent_id == item.id).ToList();
  1839. if (bomchild.Count > 0)
  1840. {
  1841. foreach (var child in bomchild)
  1842. {
  1843. CaclBomChildUseShortage(returnlist, item);
  1844. //取子级的最高状态
  1845. item.stock_state = bomchild.Max(s => s.stock_state);
  1846. item.kitting_time = bomchild.Max(s => s.kitting_time);
  1847. }
  1848. }
  1849. //var tolist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num).ToList();
  1850. //有替代关系
  1851. if (item.haveicsubs == 1)
  1852. {
  1853. //首先判断标准件库存是否满足。
  1854. //如果是BOM,也需要向下展开,看子物料是否满足。
  1855. //不满足的情况下,则需要展开替代关系,根据替代策略和替代方式,来判定替代件的库存。
  1856. //假设子BOM有替代关系,则子BOM的子物料,也是有替代关系存在的,需要展开来看。
  1857. //1.假设标准件库存满足,则不计算替代件关系。
  1858. //2.如果标准件不满足,则替代件开始计算并展示。
  1859. //注意:不管是否需要替代件,标准件都需要展开。没有使用到的物料,定义为无需求。如果子BOM库存足够,则不需要显示子物料和替代关系。
  1860. var sublist = returnlist.Where(s => s.parent_id == item.parent_id && s.num == item.num && s.level == item.level).OrderBy(c => c.substitute_all_num).ToList();
  1861. //sublist找出当前含替代关系的标准件和替代件。
  1862. switch (item.substitute_strategy)
  1863. {
  1864. case 0://整批
  1865. break;
  1866. case 1://混用
  1867. break;
  1868. case 2://整批加混用
  1869. break;
  1870. }
  1871. if (item.substitute_mode == 0)
  1872. {
  1873. //替代
  1874. }
  1875. else
  1876. {
  1877. //取代
  1878. }
  1879. }
  1880. else//无替代关系
  1881. {
  1882. if (item.stock_state==0)
  1883. {
  1884. item.use_qty = item.needCount;
  1885. item.is_show = true;
  1886. }
  1887. else if (item.stock_state == 1)
  1888. {
  1889. item.use_qty = item.sqty;
  1890. // 缺料数量 item.lack_qty
  1891. // 使用数量将库存全部使用了 item.use_qty = item.sqty;
  1892. if (item.erp_cls == 2 || item.erp_cls == 3)
  1893. {
  1894. //委外、外购
  1895. //生成委外工单、采购申请单
  1896. //得到单据到货时间。
  1897. //todo:初步设置为7天到货期,后期根据实际业务来补充修改。
  1898. item.kitting_time = DateTime.Now.AddDays(7);
  1899. item.stock_state = 2;
  1900. }
  1901. else if (item.erp_cls == 1)
  1902. {
  1903. //自制
  1904. //调用产能计算,得到物料自制后的齐套时间。
  1905. //todo:初步设置为7天完成,等沟通调用方法,来修改此处。
  1906. item.kitting_time = DateTime.Now.AddDays(7);
  1907. item.stock_state = 3;
  1908. }
  1909. }
  1910. }
  1911. }
  1912. /// <summary>
  1913. /// 平铺物料缺料情况,展示所有主料+替代料的库存情况、缺料情况---需要修改成 库存情况、占用情况
  1914. /// </summary>
  1915. /// <param name="returnlist"></param>
  1916. /// <param name="item"></param>
  1917. /// <param name="count"></param>
  1918. public void CaclMaterialShortage(List<BomChildExamineDto> returnlist, BomChildExamineDto item, int count)
  1919. {
  1920. var parent = returnlist.Find(s => s.id == item.parent_id);
  1921. //当前物料总共需要数量
  1922. item.needCount = parent.needCount * item.qty;
  1923. //当前库存可以满足的数量
  1924. item.satisfyNum = item.sqty;
  1925. //总需要数量减去库存量,得出缺料数量
  1926. item.lack_qty = item.needCount - item.satisfyNum;
  1927. //如果不满足,计算子物料,或者计算替代料
  1928. if (item.lack_qty < 0)
  1929. {
  1930. //库存满足
  1931. item.stock_state = 0;
  1932. item.lack_qty = 0;
  1933. }
  1934. else
  1935. {
  1936. //找出自己的子集,存在子集则是BOM,不存在子集,则自己非BOM。
  1937. var childList = returnlist.Where(s => s.parent_id == item.id && s.type == item.type).ToList();
  1938. if (childList.Count > 0)
  1939. {
  1940. //自己是BOM
  1941. decimal kz = 0;//当前BOM可制数量
  1942. foreach (var child in childList)
  1943. {
  1944. //循环子集判断库存
  1945. CaclMaterialShortage(returnlist, child, count);
  1946. kz = kz > ((child.sqty+ child.kz) / child.qty) ? Math.Floor((child.sqty + child.kz) / child.qty) : kz;
  1947. }
  1948. //向下取整
  1949. item.kz =Math.Floor(kz);
  1950. }
  1951. else
  1952. {
  1953. //原材料没有可制数量。
  1954. item.kz = 0;
  1955. item.stock_state = 1;
  1956. }
  1957. }
  1958. }*/
  1959. #endregion
  1960. /// <summary>
  1961. /// 根据物料id获取物料4个提前期
  1962. /// </summary>
  1963. /// <param name="icItemIds">物料id</param>
  1964. /// <param name="tenantId">企业id</param>
  1965. /// <param name="factoryid">工厂id</param>
  1966. /// <returns></returns>
  1967. private List<ICItemLeadTimeDto> GetLeadTime(List<long> icItemIds, long tenantId, long factoryid)
  1968. {
  1969. ProjectionDefinitionBuilder<ic_factory_details> project = new ProjectionDefinitionBuilder<ic_factory_details>();
  1970. return _ic_factory_details.Find(p => icItemIds.Contains(p.icitem_id) && p.factory_id == factoryid && p.tenant_id == tenantId && !p.IsDeleted,
  1971. project.Include(p => p.icitem_id).Include(p => p.production_leadtime).Include(p => p.stock_leadtime).Include(p => p.transportation_leadtime).Include(p => p.order_leadtime)).Result.
  1972. Select(x => new ICItemLeadTimeDto { item_id = x.icitem_id, transportation_leadtime = x.transportation_leadtime, stock_leadtime = x.stock_leadtime, production_leadtime = x.production_leadtime, order_leadtime = x.order_leadtime }).AsQueryable<ICItemLeadTimeDto>().ToList();
  1973. }
  1974. //根据物料id获取物料供应商
  1975. private List<ic_item_pur> GetSupplier(List<BomChildExamineDto> returnlist, long tenantId, long factoryid)
  1976. {
  1977. return _ic_item_pur.Find(p => returnlist.Select(x => x.item_id).Contains(p.icitem_id) && p.tenant_id == tenantId && p.factory_id == factoryid && !p.IsDeleted).Result;
  1978. }
  1979. //根据物料id获取物料采购计划表
  1980. private List<ic_plan> GetICPlan(List<BomChildExamineDto> returnlist, long tenantId, long factoryid)
  1981. {
  1982. return _ic_plan.Find(p => returnlist.Select(x => x.item_id).Contains(p.icitem_id) && p.tenant_id == tenantId && p.factory_id == factoryid && !p.IsDeleted).Result;
  1983. }
  1984. /// <summary>
  1985. /// 检查在途量
  1986. /// </summary>
  1987. /// <param name="returnlist">物料列表</param>
  1988. /// <param name="factoryid">工厂id</param>
  1989. /// <param name="deliveryDate">销售订单交付日期</param>
  1990. /// <returns></returns>
  1991. private async Task<List<ICItemDateDto>> CheckOnOrder(List<BomChildExamineDto> returnlist, long tenantId, long factoryid, DateTime deliveryDate, long bangid)
  1992. {
  1993. //ToDo:企业Id,数据状态过滤以及isdeleted
  1994. var po_list = _srm_po_list.Find(p => returnlist.Select(x => x.item_id).Contains(p.icitem_id.Value) && p.tenant_id == tenantId && p.factory_id == factoryid && p.rarrdate >= DateTime.Now && p.rarrdate < deliveryDate && !p.IsDeleted).Result;
  1995. var itemlist = new List<ICItemDateDto>();//需要生成采购申请单的物料信息
  1996. var leadTimeList = GetLeadTime(returnlist.Select(p => p.item_id).ToList(), tenantId, factoryid);//提前期列表
  1997. var supplierList = GetSupplier(returnlist, tenantId, factoryid);//供应商列表
  1998. var planList = GetICPlan(returnlist, tenantId, factoryid);//plan列表
  1999. foreach (var item in returnlist)
  2000. {
  2001. //缺料
  2002. if (item.lack_qty > 0)
  2003. {
  2004. var itemPO = po_list.FindAll(x => x.icitem_id == item.item_id).OrderBy(v => v.rarrdate).ToList();
  2005. if (itemPO.Count <= 0 || itemPO.Sum(p => (p.qty - p.esqty)) < item.lack_qty)
  2006. {
  2007. //外购生成采购申请单
  2008. if (item.erp_cls == 3)
  2009. {
  2010. DateTime lastTime = CreateSRMPR(item, tenantId, factoryid, bangid, 2, leadTimeList, supplierList, planList, deliveryDate);
  2011. itemlist.Add(new ICItemDateDto { item_id = item.item_id, dateTime = lastTime });
  2012. }
  2013. else if (item.erp_cls == 2)
  2014. {
  2015. //委外生成委外采购申请单和委外工单
  2016. DateTime lastTime = CreateSRMPR(item, tenantId, factoryid, bangid, 1, leadTimeList, supplierList, planList, deliveryDate);
  2017. CreateMesOOder(item, tenantId, factoryid, bangid, leadTimeList, supplierList, deliveryDate);
  2018. itemlist.Add(new ICItemDateDto { item_id = item.item_id, dateTime = lastTime });
  2019. }
  2020. }
  2021. else
  2022. {
  2023. decimal? itemPOQty = 0;//当前物料已抵扣数量
  2024. for (int i = 0; i < itemPO.Count; i++)
  2025. {
  2026. if (itemPOQty + itemPO[i].qty - itemPO[i].esqty >= item.lack_qty)
  2027. {
  2028. //在途满足,写占用表
  2029. itemlist.Add(new ICItemDateDto { item_id = itemPO[i].icitem_id.Value, dateTime = itemPO[i].rarrdate });
  2030. srm_po_occupy po_Occupy = new srm_po_occupy();
  2031. po_Occupy.GenerateNewId();
  2032. po_Occupy.polist_id = itemPO[i].po_id;//采购订单id
  2033. po_Occupy.polist_row = itemPO[i].polist_row;//采购订单行号
  2034. po_Occupy.eid = 111;//客户订单行id
  2035. po_Occupy.bill_no = 111;//客户订单id
  2036. po_Occupy.type = "在途占用";//类型
  2037. po_Occupy.entry_id = 1;//行号
  2038. po_Occupy.qty = item.lack_qty - itemPOQty;//占用量
  2039. po_Occupy.stime = DateTime.Now;//开始时间
  2040. po_Occupy.etime = deliveryDate;//结束时间
  2041. po_Occupy.state = 1;//占用状态
  2042. po_Occupy.cby = "";//变更人
  2043. po_Occupy.creason = "";//变更原因
  2044. po_Occupy.ctime = DateTime.Now;//变更时间
  2045. po_Occupy.bang_id = bangid;
  2046. await _srm_po_occupy.InsertOne(po_Occupy);
  2047. break;
  2048. }
  2049. else
  2050. {
  2051. //不满足逐步扣减
  2052. itemPOQty = itemPOQty + itemPO[i].qty - itemPO[i].esqty;
  2053. srm_po_occupy po_Occupy = new srm_po_occupy();
  2054. po_Occupy.GenerateNewId();
  2055. po_Occupy.polist_id = itemPO[i].po_id;//采购订单id
  2056. po_Occupy.polist_row = itemPO[i].polist_row;//采购订单行号
  2057. po_Occupy.eid = 111;//客户订单行id
  2058. po_Occupy.bill_no = 111;//客户订单id
  2059. po_Occupy.type = "在途占用";//类型
  2060. po_Occupy.entry_id = 1;//行号
  2061. po_Occupy.qty = itemPO[i].qty - itemPO[i].esqty;//占用量
  2062. po_Occupy.stime = DateTime.Now;//开始时间
  2063. po_Occupy.etime = deliveryDate;//结束时间
  2064. po_Occupy.state = 1;//占用状态
  2065. po_Occupy.cby = "";//变更人
  2066. po_Occupy.creason = "";//变更原因
  2067. po_Occupy.ctime = DateTime.Now;//变更时间
  2068. po_Occupy.bang_id = bangid;
  2069. await _srm_po_occupy.InsertOne(po_Occupy);
  2070. }
  2071. }
  2072. }
  2073. }
  2074. }
  2075. return itemlist;
  2076. }
  2077. /// <summary>
  2078. /// 生成委外工单
  2079. /// </summary>
  2080. /// <param name="returnlist"></param>
  2081. /// <param name="factoryid"></param>
  2082. private DateTime CreateMesOOder(BomChildExamineDto returnlist, long tenantId, long factoryid, long bangId, List<ICItemLeadTimeDto> iCItemLeadTimes, List<ic_item_pur> supplierList, DateTime deliveryDate)
  2083. {
  2084. mes_oorder oOrder = new mes_oorder();
  2085. oOrder.GenerateNewId();
  2086. oOrder.oorder_no = getOrderNum("WW");//生产工单编号
  2087. oOrder.oorder_type = "委外工单";//生产工单类型
  2088. oOrder.oorder_date = DateTime.Now;//委外订单日期
  2089. oOrder.oorder_state = "已提交";//订单状态
  2090. oOrder.ooentry_prd = 10000;//生产组织
  2091. oOrder.ooentry_prdname = "1000";//生产组织名称
  2092. oOrder.ooentry_wrkc = 10000;//工作中心id
  2093. oOrder.ooentry_wrkcname = "10001";//工作中心名称
  2094. oOrder.planner_num = "wwww";//计划员工号
  2095. oOrder.planner_name = "qqq";//计划员名称
  2096. oOrder.ooentry_stime = DateTime.Now;//计划开工日期
  2097. oOrder.ooentry_etime = DateTime.Now;//计划完工日期
  2098. oOrder.product_code = "产品代码";//产品代码
  2099. oOrder.ffms_number = "1000";//fms旧料号
  2100. oOrder.product_name = "test";//产品名称
  2101. oOrder.specification_model = returnlist.model;//规格型号
  2102. oOrder.bom_number = "";//bom编码
  2103. oOrder.unit = returnlist.unit;//单位
  2104. oOrder.morder_progress = "";//工单进度
  2105. oOrder.morder_production_number = returnlist.lack_qty;//工单生产数量(计划数量)
  2106. oOrder.need_number = returnlist.lack_qty;//需求数量
  2107. oOrder.remaining_number = returnlist.lack_qty;//剩余可用数量
  2108. oOrder.work_number = 0;//报工数量
  2109. oOrder.inspection_number = 0;//报检数量
  2110. oOrder.qualified_number = 0;//合格数量
  2111. oOrder.inventory_number = 0;//入库数量
  2112. oOrder.notice_qty = 0;//已开通知单数量
  2113. oOrder.moentry_on = 1;//启动状态
  2114. //oOrder.start_time = DateTime.Now;//开始时间
  2115. //oOrder.pause_time = DateTime.Now;//最近暂停时间
  2116. //oOrder.restart_time = DateTime.Now;//最近重启时间
  2117. oOrder.project_name = returnlist.item_name;//项目名称
  2118. oOrder.sent_status = 1;//发料状态 1-待发料 2-已发料
  2119. oOrder.production_unit = returnlist.unit;//加工单位
  2120. oOrder.production_unit_code = "";//加工单位编码
  2121. oOrder.need_icitem_status = 1;//所需物料是否充足 1-充足 0-缺料
  2122. oOrder.tenant_id = tenantId;
  2123. oOrder.factory_id = factoryid;
  2124. oOrder.bang_id = bangId;
  2125. _mes_oorder.InsertOne(oOrder);
  2126. return DateTime.Now;
  2127. }
  2128. /// <summary>
  2129. /// 生成采购申请单,颗粒度是一个物料一个单,没必要弄列表
  2130. /// </summary>
  2131. /// <param name="returnlist"></param>
  2132. /// <param name="factoryid"></param>
  2133. /// <param name="orderType">2委外采购申请单,3采购申请单</param>
  2134. private DateTime CreateSRMPR(BomChildExamineDto returnlist, long tenantId, long factoryid, long bangId, int orderType, List<ICItemLeadTimeDto> iCItemLeadTimes, List<ic_item_pur> supplierList, List<ic_plan> planList, DateTime deliveryDate)
  2135. {
  2136. var leadTime = iCItemLeadTimes.Find(x => x.item_id == returnlist.item_id);
  2137. var supplier = supplierList.Find(x => x.icitem_id == returnlist.item_id);//默认取第一个供应商
  2138. var plan = planList.Find(x => x.icitem_id == returnlist.item_id);
  2139. if(leadTime==null || supplier==null || plan==null)
  2140. {
  2141. throw new NotImplementedException("未找到物料ic_factory_details或ic_item_pur或ic_plan信息!");
  2142. }
  2143. srm_pr_main srm_Pr = new srm_pr_main();
  2144. srm_Pr.GenerateNewId();
  2145. srm_Pr.pr_billno = getOrderNum("PR");//pr单号
  2146. srm_Pr.pr_mono = "";//关联工单号
  2147. srm_Pr.entity_id = 1;//工单行号
  2148. srm_Pr.pr_purchaseid = supplier.supplier_id;//供应商id
  2149. srm_Pr.pr_purchasenumber = supplier.supplier_number;//供应商编码
  2150. srm_Pr.pr_purchasename = supplier.supplier_name;//供应商名称
  2151. srm_Pr.pr_purchaser = supplier.purcher;//采购员
  2152. srm_Pr.pr_purchaser_num = "";//采购员工号(采购信息表)
  2153. srm_Pr.pr_rqty = returnlist.lack_qty;//需求数量
  2154. srm_Pr.pr_aqty = returnlist.lack_qty;//申请数量
  2155. srm_Pr.pr_sqty = returnlist.lack_qty;//建议数量
  2156. srm_Pr.icitem_id = returnlist.item_id;//物料id
  2157. srm_Pr.icitem_name = returnlist.item_name;//物料名称
  2158. srm_Pr.pr_order_type = 1;//单据类型
  2159. srm_Pr.pr_ssend_date = deliveryDate.AddDays((double)leadTime.order_leadtime * -1);//系统建议下单日期
  2160. srm_Pr.pr_sarrive_date = deliveryDate.AddDays((double)leadTime.transportation_leadtime * -1);//系统建议到达日期(建议到货日期)
  2161. srm_Pr.pr_psend_date = deliveryDate.AddDays((double)leadTime.order_leadtime * -1);//计划下单日期
  2162. srm_Pr.pr_parrive_date = deliveryDate.AddDays((double)leadTime.transportation_leadtime * -1);//计划到达日期
  2163. srm_Pr.pr_psend_date = deliveryDate.AddDays((double)leadTime.order_leadtime * -1);//计划下单日期
  2164. srm_Pr.pr_sysprice = returnlist.lack_qty * supplier.netpurchase_price * (1 + supplier.taxrate);//系统价格(含税)
  2165. srm_Pr.pr_orderprice = returnlist.lack_qty * supplier.netpurchase_price * (1 + supplier.taxrate);//订单价格(含税)
  2166. srm_Pr.pr_price = supplier.netpurchase_price;//采购净价(不含税)
  2167. srm_Pr.pr_rate = supplier.taxrate;//税率
  2168. srm_Pr.pr_unit = returnlist.unit;//单位
  2169. srm_Pr.state = 1;//状态
  2170. srm_Pr.old_apply_aqty = 0;//已申请数量
  2171. srm_Pr.pr_type = orderType;//申请类型
  2172. srm_Pr.currencytype = supplier.currency_type;//币种
  2173. srm_Pr.secInv_ratio = plan.secinv_ratio;//安全库存触发采购比例
  2174. srm_Pr.tenant_id = tenantId;
  2175. srm_Pr.factory_id = factoryid;
  2176. srm_Pr.bang_id = bangId;
  2177. _srm_pr_main.InsertOne(srm_Pr);
  2178. decimal? totalLeadTime = leadTime.transportation_leadtime + leadTime.stock_leadtime + leadTime.production_leadtime + leadTime.order_leadtime;
  2179. return deliveryDate.AddDays((double)totalLeadTime * -1);//减去提前期
  2180. }
  2181. // 生成订单编号 字母+年月日+8位随机数+时间戳
  2182. private string getOrderNum(string preCode)
  2183. {
  2184. string Dates = DateTime.Now.ToString("yyyyMMdd");//获取当前时间
  2185. Random Rdm = new Random(Guid.NewGuid().GetHashCode());//随机数
  2186. TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);//时间戳
  2187. string newts = Convert.ToInt64(ts.TotalMilliseconds).ToString();//时间戳
  2188. string new_orderNum = preCode + Dates + Rdm.Next(0, 100000000) + newts;
  2189. return new_orderNum;
  2190. }
  2191. }
  2192. }