IntegrationBOMWindowViewModel.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Input;
  8. using System.Windows.Media.Imaging;
  9. using Comal.Classes;
  10. using InABox.Clients;
  11. using InABox.Configuration;
  12. using InABox.Core;
  13. using InABox.Integration.Logikal;
  14. using InABox.WPF;
  15. using Microsoft.Xaml.Behaviors.Core;
  16. using PRSDesktop.Integrations.Logikal;
  17. namespace PRSDesktop.Integrations.Common;
  18. public class IntegrationBOMWindowViewModel : DependencyObject
  19. {
  20. public static DependencyProperty JobIDProperty = DependencyProperty.Register(
  21. nameof(JobID),
  22. typeof(Guid),
  23. typeof(IntegrationBOMWindowViewModel)
  24. );
  25. public Guid JobID
  26. {
  27. get => (Guid)GetValue(JobIDProperty);
  28. set => SetValue(JobIDProperty, value);
  29. }
  30. public static DependencyProperty BOMProperty = DependencyProperty.Register(
  31. nameof(BOM),
  32. typeof(ILogikalPartsResponse<LogikalFinish, LogikalProfile, LogikalComponent, LogikalGlass, LogikalLabour>),
  33. typeof(IntegrationBOMWindowViewModel),
  34. new FrameworkPropertyMetadata(BOMChanged));
  35. private static void BOMChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  36. {
  37. if (d is not IntegrationBOMWindowViewModel model)
  38. return;
  39. var bom =
  40. e.NewValue as ILogikalPartsResponse<LogikalFinish, LogikalProfile, LogikalComponent, LogikalGlass,
  41. LogikalLabour>;
  42. var styles = model.ExtractMappings<ProductStyleIntegrationSource, LogikalFinish, ProductStyle, ProductStyleLink, ProductStyleIntegrationSource>(
  43. bom?.Finishes, x => x.Code, x => x.Description, x => x.Code);
  44. var profiles = model.ExtractMappings<ProductIntegrationSource,LogikalProfile,Product,ProductLink, ProductIntegrationSource>(
  45. bom?.Profiles, x => x.Code, x => x.Description, x => x.Code);
  46. var components = model.ExtractMappings<ProductIntegrationSource,LogikalComponent,Product,ProductLink, ProductIntegrationSource>(
  47. bom?.Components, x => x.Code, x => x.Description, x => x.Code);
  48. var glass = model.ExtractMappings<ProductIntegrationSource,LogikalGlass,Product,ProductLink, ProductIntegrationSource>(
  49. bom?.Glass, x => x.Code, x => x.Description, x => x.Code);
  50. var labour = model.ExtractMappings<ActivityIntegrationSource,LogikalLabour,Activity,ActivityLink, ActivityIntegrationSource>(
  51. bom?.Labour, x => x.Code, x => x.Description, x => x.Code);
  52. Task.WaitAll(styles, profiles, components, glass, labour);
  53. model.Styles = styles.Result;
  54. model.Profiles = profiles.Result;
  55. model.Components = components.Result;
  56. model.Glass = glass.Result;
  57. model.Labour = labour.Result;
  58. model.CheckChanged();
  59. }
  60. public ILogikalPartsResponse<LogikalFinish, LogikalProfile, LogikalComponent, LogikalGlass, LogikalLabour>? BOM
  61. {
  62. get => GetValue(BOMProperty) as ILogikalPartsResponse<LogikalFinish, LogikalProfile, LogikalComponent, LogikalGlass, LogikalLabour>;
  63. set => SetValue(BOMProperty, value);
  64. }
  65. private static readonly DependencyProperty StylesProperty = DependencyProperty.Register(
  66. nameof(Styles),
  67. typeof(List<ProductStyleIntegrationSource>),
  68. typeof(IntegrationBOMWindowViewModel)
  69. );
  70. public List<ProductStyleIntegrationSource>? Styles
  71. {
  72. get => GetValue(StylesProperty) as List<ProductStyleIntegrationSource>;
  73. set => SetValue(StylesProperty, value);
  74. }
  75. private static readonly DependencyProperty ProfilesProperty = DependencyProperty.Register(
  76. nameof(Profiles),
  77. typeof(List<ProductIntegrationSource>),
  78. typeof(IntegrationBOMWindowViewModel)
  79. );
  80. public List<ProductIntegrationSource>? Profiles
  81. {
  82. get => GetValue(ProfilesProperty) as List<ProductIntegrationSource>;
  83. set => SetValue(ProfilesProperty, value);
  84. }
  85. private static readonly DependencyProperty ComponentsProperty = DependencyProperty.Register(
  86. nameof(Components),
  87. typeof(List<ProductIntegrationSource>),
  88. typeof(IntegrationBOMWindowViewModel)
  89. );
  90. public List<ProductIntegrationSource>? Components
  91. {
  92. get => GetValue(ComponentsProperty) as List<ProductIntegrationSource>;
  93. set => SetValue(ComponentsProperty, value);
  94. }
  95. private static readonly DependencyProperty GlassProperty = DependencyProperty.Register(
  96. nameof(Glass),
  97. typeof(List<ProductIntegrationSource>),
  98. typeof(IntegrationBOMWindowViewModel)
  99. );
  100. public List<ProductIntegrationSource>? Glass
  101. {
  102. get => GetValue(GlassProperty) as List<ProductIntegrationSource>;
  103. set => SetValue(GlassProperty, value);
  104. }
  105. private static readonly DependencyProperty LabourProperty = DependencyProperty.Register(
  106. nameof(Labour),
  107. typeof(List<ActivityIntegrationSource>),
  108. typeof(IntegrationBOMWindowViewModel)
  109. );
  110. public List<ActivityIntegrationSource>? Labour
  111. {
  112. get => GetValue(LabourProperty) as List<ActivityIntegrationSource>;
  113. set => SetValue(LabourProperty, value);
  114. }
  115. private static readonly DependencyProperty SectionsProperty = DependencyProperty.Register(
  116. nameof(Sections),
  117. typeof(Dictionary<string, BitmapImage>),
  118. typeof(IntegrationBOMWindowViewModel)
  119. );
  120. private Dictionary<string,BitmapImage> _sections = new()
  121. {
  122. { nameof(Styles), PRSDesktop.Resources.palette.AsBitmapImage(64, 64) },
  123. { nameof(Profiles), PRSDesktop.Resources.profile.AsBitmapImage(64, 64) },
  124. { nameof(Components), PRSDesktop.Resources.fixings.AsBitmapImage(64, 64) },
  125. { nameof(Glass), PRSDesktop.Resources.glass.AsBitmapImage(64, 64) },
  126. { nameof(Labour), PRSDesktop.Resources.quality.AsBitmapImage(64, 64) },
  127. };
  128. public Dictionary<string, BitmapImage> Sections
  129. {
  130. get => (Dictionary<string, BitmapImage>)GetValue(SectionsProperty);
  131. set => SetValue(SectionsProperty, value);
  132. }
  133. private static readonly DependencyProperty SelectedSectionProperty = DependencyProperty.Register(
  134. nameof(SelectedSection),
  135. typeof(KeyValuePair<string, BitmapImage>),
  136. typeof(IntegrationBOMWindowViewModel)
  137. );
  138. public KeyValuePair<string, BitmapImage> SelectedSection
  139. {
  140. get => (KeyValuePair<string, BitmapImage>)GetValue(SelectedSectionProperty);
  141. set => SetValue(SelectedSectionProperty, value);
  142. }
  143. private static readonly DependencyProperty MappingsCompleteProperty = DependencyProperty.Register(
  144. nameof(MappingsComplete),
  145. typeof(bool),
  146. typeof(IntegrationBOMWindowViewModel)
  147. );
  148. public bool MappingsComplete
  149. {
  150. get => (bool)GetValue(MappingsCompleteProperty);
  151. set => SetValue(MappingsCompleteProperty, value);
  152. }
  153. private LogikalSettings _settings;
  154. private ProductDimensionUnit _profileUom;
  155. private ProductDimensionUnit _componentUom;
  156. private ProductDimensionUnit _glassUom;
  157. public IntegrationBOMWindowViewModel()
  158. {
  159. Sections = _sections;
  160. SelectedSection = Sections.First();
  161. _settings = new GlobalConfiguration<LogikalSettings>().Load();
  162. var uoms = new Client<ProductDimensionUnit>().Query(
  163. new Filter<ProductDimensionUnit>(x => x.Code).IsEqualTo(_settings.ProfileUom)
  164. .Or(x => x.Code).IsEqualTo(_settings.ComponentUom)
  165. .Or(x => x.Code).IsEqualTo(_settings.GlassUom)
  166. ).ToObjects<ProductDimensionUnit>().ToArray();
  167. _profileUom = uoms.FirstOrDefault(x=>x.Code == _settings.ProfileUom) ?? new ProductDimensionUnit();
  168. _componentUom = uoms.FirstOrDefault(x=>x.Code == _settings.ComponentUom) ?? new ProductDimensionUnit();
  169. _glassUom = uoms.FirstOrDefault(x=>x.Code == _settings.GlassUom) ?? new ProductDimensionUnit();
  170. }
  171. public Task<List<TCode>> ExtractMappings<TCode,TLogikal,TEntity,TLink,TMapping>(
  172. IEnumerable<TLogikal>? items,
  173. Func<TLogikal,string?> logikalcode,
  174. Func<TLogikal,string?> logikaldescription,
  175. Expression<Func<TEntity,object?>> entitycode)
  176. where TCode : BaseIntegrationSource<TEntity,TLink>, new()
  177. where TEntity : Entity, IRemotable,IPersistent, new()
  178. where TLink :EntityLink<TEntity>
  179. where TMapping : BaseIntegrationSource<TEntity,TLink>, IRemotable, IPersistent, new()
  180. {
  181. return Task.Run(() =>
  182. {
  183. var f = entitycode.Compile();
  184. var results = new List<TCode>();
  185. if (items == null)
  186. return results;
  187. var sourceitems = new Dictionary<string, string>();
  188. foreach (var item in items)
  189. sourceitems[logikalcode(item) ?? ""] = logikaldescription(item) ?? "";
  190. MultiQuery query = new();
  191. query.Add<TEntity>(
  192. new Filter<TEntity>(entitycode).InList(sourceitems.Keys.ToArray()),
  193. Columns.Required<TEntity>().Add(x => x.ID).Add(entitycode)
  194. );
  195. var entitycodecol = $"Entity.{CoreUtils.GetFullPropertyName(entitycode, ".")}";
  196. query.Add<TMapping>(
  197. new Filter<TMapping>(x => x.Code).InList(sourceitems.Keys.ToArray()),
  198. Columns.Required<TMapping>()
  199. .Add(x => x.ID)
  200. .Add(x => x.Code)
  201. .Add(x=>x.Entity.ID)
  202. .Add(entitycodecol)
  203. );
  204. query.Query();
  205. var mappings = query.Get<TMapping>().ToObjects<TMapping>().ToArray();
  206. var entities = query.Get<TEntity>().ToDictionary(entitycode, x => x.ID);
  207. foreach (var sourceitem in sourceitems)
  208. {
  209. var result = new TCode()
  210. {
  211. Code = sourceitem.Key,
  212. Description = sourceitem.Value
  213. };
  214. var mapping = mappings.FirstOrDefault(x => string.Equals(x.Code, sourceitem.Key));
  215. if (mapping != null)
  216. {
  217. result.Entity.ID = mapping.Entity.ID;
  218. CoreUtils.SetPropertyValue(result, entitycodecol,
  219. CoreUtils.GetPropertyValue(mapping, entitycodecol));
  220. }
  221. else if (entities.ContainsKey(sourceitem.Key))
  222. {
  223. result.Entity.ID = entities[sourceitem.Key];
  224. CoreUtils.SetPropertyValue(result.Entity, CoreUtils.GetFullPropertyName(entitycode, "."),
  225. sourceitem.Key);
  226. }
  227. results.Add(result);
  228. result.PropertyChanged += (s, e) =>
  229. {
  230. // TMapping mapping = mappingtable.Rows.FirstOrDefault(r =>
  231. // string.Equals(r.Get<TMapping, String>(c => c.Code), result.Code))?.ToObject<TMapping>();
  232. // if (mapping == null)
  233. // mapping = new TMapping() { Code = result.Code };
  234. // mapping.Entity.ID = result.Entity.ID;
  235. // new Client<TMapping>().Save(mapping, "Created from BOM Integration Window");
  236. CheckChanged();
  237. };
  238. }
  239. return results;
  240. });
  241. }
  242. private void CheckChanged()
  243. {
  244. Dispatcher.BeginInvoke(() =>
  245. {
  246. var styles = Styles?.All(x => x.Entity.ID != Guid.Empty) ?? false;
  247. var profiles = Profiles?.All(x => x.Entity.ID != Guid.Empty) ?? false;
  248. var glass = Glass?.All(x => x.Entity.ID != Guid.Empty) ?? false;
  249. var components = Components?.All(x => x.Entity.ID != Guid.Empty) ?? false;
  250. var labour = Labour?.All(x => x.Entity.ID != Guid.Empty) ?? false;
  251. MappingsComplete = styles && profiles && glass && components && labour;
  252. });
  253. }
  254. public ICommand CreateStyle => new ActionCommand(
  255. o =>
  256. {
  257. if (o is IntegrationGridCreateEntityArgs<ProductStyle, ProductStyleIntegrationSource> args)
  258. {
  259. args.Entity.Code = args.Mapping.Code ?? "";
  260. args.Entity.Description = args.Mapping.Description ?? "";
  261. args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
  262. }
  263. }
  264. );
  265. public ICommand CreateProfile => new ActionCommand(
  266. o =>
  267. {
  268. if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
  269. {
  270. args.Entity.Code = args.Mapping.Code ?? "";
  271. args.Entity.Name = args.Mapping.Description ?? "";
  272. args.Entity.UnitOfMeasure.CopyFrom(_profileUom);
  273. args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
  274. }
  275. }
  276. );
  277. public ICommand CreateComponent => new ActionCommand(
  278. o =>
  279. {
  280. if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
  281. {
  282. args.Entity.Code = args.Mapping.Code ?? "";
  283. args.Entity.Name = args.Mapping.Description ?? "";
  284. args.Entity.UnitOfMeasure.CopyFrom(_componentUom);
  285. args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
  286. }
  287. }
  288. );
  289. public ICommand CreateGlass => new ActionCommand(
  290. o =>
  291. {
  292. if (o is IntegrationGridCreateEntityArgs<Product, ProductIntegrationSource> args)
  293. {
  294. args.Entity.Code = args.Mapping.Code ?? "";
  295. args.Entity.Name = args.Mapping.Description ?? "";
  296. args.Entity.UnitOfMeasure.CopyFrom(_glassUom);
  297. args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
  298. }
  299. }
  300. );
  301. public ICommand CreateActivity => new ActionCommand(
  302. o =>
  303. {
  304. if (o is IntegrationGridCreateEntityArgs<Activity, ActivityIntegrationSource> args)
  305. {
  306. args.Entity.Code = args.Mapping.Code ?? "";
  307. args.Entity.Description = args.Mapping.Description ?? "";
  308. args.Entity.Problem.Notes = ["Created from BOM Integration Window"];
  309. }
  310. }
  311. );
  312. public void CreateBOM()
  313. {
  314. if (BOM == null)
  315. return;
  316. List<JobBillOfMaterialsItem> items = new List<JobBillOfMaterialsItem>();
  317. foreach (var profile in BOM.Profiles)
  318. {
  319. var profilemapping = Profiles?.FirstOrDefault(x => x.Code == profile.Code);
  320. var stylemapping = Styles?.FirstOrDefault(x => string.Equals(x.Code, profile.Finish));
  321. if (profilemapping != null)
  322. {
  323. JobBillOfMaterialsItem newitem = new JobBillOfMaterialsItem();
  324. newitem.Product.CopyFrom(profilemapping.Entity);
  325. newitem.Dimensions.Unit.CopyFrom(profilemapping.Entity.UnitOfMeasure);
  326. newitem.Dimensions.Length = profile.Length;
  327. if (stylemapping != null)
  328. newitem.Style.CopyFrom(stylemapping.Entity);
  329. newitem.Quantity = profile.Quantity;
  330. newitem.UnitCost = profile.Cost;
  331. items.Add(newitem);
  332. }
  333. }
  334. foreach (var component in BOM.Components)
  335. {
  336. var componentmapping = Components?.FirstOrDefault(x => string.Equals(x.Code, component.Code));
  337. if (componentmapping != null)
  338. {
  339. JobBillOfMaterialsItem newitem = new JobBillOfMaterialsItem();
  340. newitem.Product.CopyFrom(componentmapping.Entity);
  341. newitem.Dimensions.Unit.CopyFrom(componentmapping.Entity.UnitOfMeasure);
  342. newitem.Dimensions.Quantity = component.PackSize;
  343. newitem.Quantity = component.Quantity;
  344. newitem.UnitCost = component.Cost;
  345. items.Add(newitem);
  346. }
  347. }
  348. foreach (var glass in BOM.Glass)
  349. {
  350. var glassmapping = Glass?.FirstOrDefault(x => string.Equals(x.Code, glass.Code));
  351. if (glassmapping != null)
  352. {
  353. JobBillOfMaterialsItem newitem = new JobBillOfMaterialsItem();
  354. newitem.Product.CopyFrom(glassmapping.Entity);
  355. newitem.Dimensions.Unit.CopyFrom(glassmapping.Entity.UnitOfMeasure);
  356. newitem.Dimensions.Height = glass.Height;
  357. newitem.Dimensions.Height = glass.Width;
  358. newitem.Quantity = glass.Quantity;
  359. newitem.UnitCost = glass.Cost;
  360. items.Add(newitem);
  361. }
  362. }
  363. List<JobBillOfMaterialsActivity> activities = new List<JobBillOfMaterialsActivity>();
  364. foreach (var activity in BOM.Labour)
  365. {
  366. var activitymapping = Labour?.FirstOrDefault(x => string.Equals(x.Code, activity.Code));
  367. if (activitymapping != null)
  368. {
  369. JobBillOfMaterialsActivity newactivity = new JobBillOfMaterialsActivity();
  370. newactivity.ActivityLink.CopyFrom(activitymapping.Entity);
  371. newactivity.Duration = TimeSpan.FromHours(activity.Quantity);
  372. newactivity.HourlyCost = activity.Cost;
  373. activities.Add(newactivity);
  374. }
  375. }
  376. var _jobbom = new JobBillOfMaterials();
  377. _jobbom.Job.ID = JobID;
  378. _jobbom.Description = $"BOM Imported {DateTime.Now}";
  379. Progress.ShowModal("Creating BOM...", progress =>
  380. {
  381. Client.Save(_jobbom, "Imported From Logikal");
  382. progress.Report("Updating Items");
  383. foreach (var item in items)
  384. {
  385. item.BillOfMaterials.ID = _jobbom.ID;
  386. item.Job.ID = _jobbom.Job.ID;
  387. }
  388. Client.Save(items, "Imported From Logikal");
  389. progress.Report("Updating Labour");
  390. foreach (var activity in activities)
  391. {
  392. activity.BillOfMaterials.ID = _jobbom.ID;
  393. activity.JobLink.ID = _jobbom.Job.ID;
  394. }
  395. Client.Save(activities, "Imported From Logikal");
  396. });
  397. }
  398. }