StockMovementTimberlinePoster.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. using Comal.Classes;
  2. using CsvHelper.Configuration.Attributes;
  3. using CsvHelper.Configuration;
  4. using CsvHelper;
  5. using InABox.Core.Postable;
  6. using InABox.Core;
  7. using InABox.Poster.Timberline;
  8. using InABox.Scripting;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Globalization;
  12. using System.IO;
  13. using System.Linq;
  14. using System.Text;
  15. using System.Threading.Tasks;
  16. using System.Windows;
  17. using Microsoft.Win32;
  18. using CsvHelper.TypeConversion;
  19. namespace PRS.Shared
  20. {
  21. public interface IStockMovementTimberlineLine
  22. {
  23. DateTime TransactionDate { get; set; }
  24. DateTime AccountingDate { get; set; }
  25. string Description { get; set; }
  26. double Amount { get; set; }
  27. string DebitAccount { get; set; }
  28. string CreditAccount { get; set; }
  29. string Reference1 { get; set; }
  30. string Reference2 { get; set; }
  31. }
  32. public enum StockMovementTimberlineTransactionType
  33. {
  34. APCost = 1,
  35. JCCost = 2,
  36. PRCost = 3,
  37. EQCost = 4,
  38. IVCost = 5
  39. }
  40. public class StockMovementTimberlineTransactionTypeConverter : DefaultTypeConverter
  41. {
  42. public override object? ConvertFromString(string? text, IReaderRow row, MemberMapData memberMapData)
  43. {
  44. if (Enum.TryParse<StockMovementTimberlineTransactionType>(text, out var type))
  45. {
  46. return type;
  47. }
  48. return base.ConvertFromString(text, row, memberMapData);
  49. }
  50. public override string? ConvertToString(object? value, IWriterRow row, MemberMapData memberMapData)
  51. {
  52. if (value is StockMovementTimberlineTransactionType type)
  53. {
  54. return ((int)type).ToString();
  55. }
  56. return "";
  57. }
  58. }
  59. public class StockMovementTimberlineDirectCost : IStockMovementTimberlineLine
  60. {
  61. [Index(0)]
  62. public string RecordID { get; set; } = "DC";
  63. [Index(1)]
  64. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  65. public string Job { get; set; }
  66. [Index(2)]
  67. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  68. public string Extra { get; set; }
  69. [Index(3)]
  70. [TypeConverter(typeof(TimberlinePosterStringConverter), 12)]
  71. public string CostCode { get; set; }
  72. [Index(4)]
  73. [TypeConverter(typeof(TimberlinePosterStringConverter), 3)]
  74. public string Category { get; set; }
  75. [Index(5)]
  76. [TypeConverter(typeof(StockMovementTimberlineTransactionTypeConverter))]
  77. public StockMovementTimberlineTransactionType TransactionType { get; set; }
  78. [Index(6)]
  79. [TypeConverter(typeof(TimberlinePosterDateConverter))]
  80. public DateTime TransactionDate { get; set; }
  81. [Index(7)]
  82. [TypeConverter(typeof(TimberlinePosterDateConverter))]
  83. public DateTime AccountingDate { get; set; }
  84. [Index(8)]
  85. [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
  86. public string Description { get; set; }
  87. [Index(9)]
  88. public double Units { get; set; }
  89. [Index(10)]
  90. public double UnitCost { get; set; }
  91. [Index(11)]
  92. public double Amount { get; set; }
  93. [Index(12)]
  94. [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
  95. public string DebitAccount { get; set; }
  96. [Index(13)]
  97. [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
  98. public string CreditAccount { get; set; }
  99. [Index(14)]
  100. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  101. public string Reference1 { get; set; }
  102. [Index(15)]
  103. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  104. public string Reference2 { get; set; }
  105. [Index(16)]
  106. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  107. public string StandardItem { get; set; }
  108. }
  109. public class StockMovementTimberlineGL : IStockMovementTimberlineLine
  110. {
  111. [Index(0)]
  112. public string RecordID { get; set; } = "GL";
  113. [Index(1)]
  114. [TypeConverter(typeof(TimberlinePosterDateConverter))]
  115. public DateTime TransactionDate { get; set; }
  116. [Index(2)]
  117. [TypeConverter(typeof(TimberlinePosterDateConverter))]
  118. public DateTime AccountingDate { get; set; }
  119. [Index(3)]
  120. [TypeConverter(typeof(TimberlinePosterStringConverter), 30)]
  121. public string Description { get; set; }
  122. [Index(4)]
  123. public double Amount { get; set; }
  124. [Index(5)]
  125. [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
  126. public string DebitAccount { get; set; }
  127. [Index(6)]
  128. [TypeConverter(typeof(TimberlinePosterStringConverter), 25)]
  129. public string CreditAccount { get; set; }
  130. [Index(7)]
  131. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  132. public string Reference1 { get; set; }
  133. [Index(8)]
  134. [TypeConverter(typeof(TimberlinePosterStringConverter), 10)]
  135. public string Reference2 { get; set; }
  136. }
  137. public class StockMovementTimberlineSettings : TimberlinePosterSettings<StockMovement>
  138. {
  139. protected override string DefaultScript()
  140. {
  141. return @"
  142. using PRS.Shared;
  143. using InABox.Core;
  144. using System.Collections.Generic;
  145. public class Module
  146. {
  147. public void BeforePost(IDataModel<StockMovement> model)
  148. {
  149. // Perform pre-processing
  150. }
  151. public void ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
  152. {
  153. // Do extra processing for a direct cost line; return false to fail this movement
  154. return true;
  155. }
  156. public void ProcessGLLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineGL line)
  157. {
  158. // Do extra processing for a GL line; return false to fail this movement
  159. return true;
  160. }
  161. public void AfterPost(IDataModel<StockMovement> model)
  162. {
  163. // Perform post-processing
  164. }
  165. }";
  166. }
  167. }
  168. public class StockMovementTimberlineResult : TimberlinePostResult<IStockMovementTimberlineLine, StockMovement>
  169. {
  170. }
  171. public class StockMovementTimberlinePoster : ITimberlinePoster<StockMovement, StockMovementTimberlineSettings>
  172. {
  173. public ScriptDocument? Script { get; set; }
  174. public StockMovementTimberlineSettings Settings { get; set; }
  175. public bool BeforePost(IDataModel<StockMovement> model)
  176. {
  177. model.SetIsDefault<Document>(false, alias: "CompanyLogo");
  178. model.SetIsDefault<CoreTable>(false, alias: "CompanyInformation");
  179. model.SetIsDefault<Employee>(false);
  180. model.SetColumns(new Columns<StockMovement>(x => x.Transaction));
  181. model.AddChildTable<StockMovement, StockMovement>(x => x.Transaction, x => x.Transaction,
  182. parentalias: "StockMovement", childalias: "FullTransactions",
  183. isdefault: true,
  184. filter: new Filter<StockMovement>(x => x.IsTransfer).IsEqualTo(true),
  185. columns: new Columns<StockMovement>(x => x.ID)
  186. .Add(x => x.Transaction)
  187. .Add(x => x.Job.ID)
  188. .Add(x => x.Product.ID)
  189. .Add(x => x.IsTransfer)
  190. );
  191. model.AddLookupTable<StockMovement, Product>(x => x.Product.ID, x => x.ID, sourcealias: "FullTransactions",
  192. columns: new Columns<Product>(x => x.ID)
  193. .Add(x => x.CostCentre.Code));
  194. model.AddLookupTable<StockMovement, Job>(x => x.Job.ID, x => x.ID, sourcealias: "FullTransactions",
  195. columns: new Columns<Job>(x => x.ID)
  196. .Add(x => x.JobNumber));
  197. Script?.Execute(methodname: "BeforePost", parameters: new object[] { model });
  198. return true;
  199. }
  200. private bool ProcessDirectCostLine(IDataModel<StockMovement> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
  201. {
  202. return Script?.Execute(methodname: "ProcessDirectCostLine", parameters: new object[] { model, stockMovement, line }) != false;
  203. }
  204. private bool ProcessGLLine(IDataModel<PurchaseOrder> model, StockMovement stockMovement, StockMovementTimberlineDirectCost line)
  205. {
  206. return Script?.Execute(methodname: "ProcessGLLine", parameters: new object[] { model, stockMovement, line }) != false;
  207. }
  208. private StockMovementTimberlineResult DoProcess(IDataModel<StockMovement> model)
  209. {
  210. var result = new StockMovementTimberlineResult();
  211. var firstMovements = model.GetTable<StockMovement>();
  212. var full = model.GetTable<StockMovement>("FullTransactions")
  213. .ToObjects<StockMovement>();
  214. var products = model.GetTable<Product>().ToObjects<Product>()
  215. .ToDictionary(x => x.ID, x => x);
  216. var jobs = model.GetTable<Job>().ToObjects<Job>()
  217. .ToDictionary(x => x.ID, x => x);
  218. foreach (var movement in full)
  219. {
  220. IStockMovementTimberlineLine line;
  221. if(movement.Job.ID != Guid.Empty)
  222. {
  223. var job = jobs[movement.Job.ID];
  224. var dc = new StockMovementTimberlineDirectCost
  225. {
  226. Job = job.JobNumber,
  227. Extra = "",
  228. CostCode = products[movement.Product.ID].CostCentre.Code,
  229. Category = "",
  230. Units = movement.Units,
  231. UnitCost = movement.Cost
  232. };
  233. line = dc;
  234. }
  235. else
  236. {
  237. line = new StockMovementTimberlineGL
  238. {
  239. };
  240. }
  241. }
  242. /*var lines = model.GetTable<PurchaseOrderItem>("PurchaseOrder_PurchaseOrderItem").ToObjects<PurchaseOrderItem>()
  243. .GroupBy(x => x.PurchaseOrderLink.ID).ToDictionary(x => x.Key, x => x.ToList());
  244. foreach (var purchaseOrder in model.GetTable<PurchaseOrder>().ToObjects<PurchaseOrder>())
  245. {
  246. var c = new PurchaseOrderTimberlineHeader
  247. {
  248. CommitmentID = purchaseOrder.PONumber,
  249. CommitmentType = PurchaseOrderTimberlineCommitmentType.PO,
  250. Description = purchaseOrder.Description,
  251. VendorID = purchaseOrder.SupplierLink.Code,
  252. Date = purchaseOrder.IssuedDate,
  253. // RetainagePercent
  254. // Committed to JC
  255. Closed = purchaseOrder.ClosedDate != DateTime.MinValue,
  256. // Printed
  257. };
  258. if (!ProcessHeader(model, purchaseOrder, c))
  259. {
  260. cs.AddFailed(purchaseOrder, "Failed by script.");
  261. }
  262. else
  263. {
  264. // Dictionary from line number to POItem.
  265. var items = new Dictionary<int, PurchaseOrderItem>();
  266. var POItems = lines.GetValueOrDefault(purchaseOrder.ID)?.ToList() ?? new List<PurchaseOrderItem>();
  267. foreach (var purchaseOrderItem in POItems)
  268. {
  269. if (int.TryParse(purchaseOrderItem.PostedReference, out var itemNumber))
  270. {
  271. if (items.TryGetValue(itemNumber, out var oldItem))
  272. {
  273. // Theoretically shouldn't happen, but just in case.
  274. MessageBox.Show($"Warning: Multiple PurchaseOrder Items have the same line number for export; the line number for '{purchaseOrderItem.Description}' will be changed in the export.");
  275. Logger.Send(LogType.Error, "", $"Purchase Order Post: Multiple POItems with the same Line Number; changing line number of POItem {purchaseOrderItem.ID}");
  276. purchaseOrderItem.PostedReference = "";
  277. }
  278. else
  279. {
  280. items[itemNumber] = purchaseOrderItem;
  281. }
  282. }
  283. }
  284. var success = true;
  285. foreach (var purchaseOrderItem in POItems)
  286. {
  287. if (!int.TryParse(purchaseOrderItem.PostedReference, out var itemNumber))
  288. {
  289. itemNumber = 1;
  290. while (items.ContainsKey(itemNumber))
  291. {
  292. ++itemNumber;
  293. }
  294. items[itemNumber] = purchaseOrderItem;
  295. purchaseOrderItem.PostedReference = itemNumber.ToString();
  296. }
  297. var ci = new PurchaseOrderTimberlineLine
  298. {
  299. CommitmentID = purchaseOrder.PONumber,
  300. ItemNumber = itemNumber,
  301. Description = purchaseOrderItem.Description,
  302. // RetainagePercent = ,
  303. DeliveryDate = purchaseOrderItem.ReceivedDate,
  304. //ScopeOfWork
  305. Job = purchaseOrderItem.Job.JobNumber,
  306. //Extra = purchaseOrderItem.Job
  307. CostCode = purchaseOrderItem.CostCentre.Code,
  308. //Category = purchaseOrderItem.cat
  309. TaxGroup = purchaseOrderItem.TaxCode.Code,
  310. Units = purchaseOrderItem.Qty,
  311. UnitCost = purchaseOrderItem.Cost,
  312. UnitDescription = purchaseOrderItem.Dimensions.UnitSize,
  313. Amount = purchaseOrderItem.IncTax,
  314. // BoughtOut
  315. };
  316. if (!ProcessLine(model, purchaseOrderItem, ci))
  317. {
  318. success = false;
  319. break;
  320. }
  321. c.Lines.Add(ci);
  322. }
  323. if (success)
  324. {
  325. foreach (var item in POItems)
  326. {
  327. cs.AddFragment(item);
  328. }
  329. cs.AddSuccess(purchaseOrder, c);
  330. }
  331. else
  332. {
  333. cs.AddFailed(purchaseOrder, "Failed by script.");
  334. }
  335. }
  336. }*/
  337. return result;
  338. }
  339. public IPostResult<StockMovement> Process(IDataModel<StockMovement> model)
  340. {
  341. var result = DoProcess(model);
  342. var dlg = new SaveFileDialog()
  343. {
  344. Filter = "CSV Files (*.csv)|*.csv"
  345. };
  346. if (dlg.ShowDialog() == true)
  347. {
  348. using (var writer = new StreamWriter(dlg.FileName))
  349. {
  350. using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
  351. foreach (var line in result.Exports)
  352. {
  353. // Write the record.
  354. if(line is StockMovementTimberlineDirectCost dc)
  355. {
  356. csv.WriteRecord(dc);
  357. }
  358. else if(line is StockMovementTimberlineGL gl)
  359. {
  360. csv.WriteRecord(gl);
  361. }
  362. }
  363. }
  364. }
  365. else
  366. {
  367. throw new PostCancelledException();
  368. }
  369. return result;
  370. }
  371. public void AfterPost(IDataModel<StockMovement> model, IPostResult<StockMovement> result)
  372. {
  373. Script?.Execute(methodname: "AfterPost", parameters: new object[] { model });
  374. }
  375. }
  376. public class StockMovementTimberlinePosterEngine<T> : TimberlinePosterEngine<StockMovement, StockMovementTimberlineSettings>
  377. {
  378. }
  379. }