PageRenderTime 59ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/Main/Source/BillsPresentation/PaymentReminderPresenter.cs

#
C# | 388 lines | 257 code | 37 blank | 94 comment | 37 complexity | ca29bcd5e2ac46319f14dc94b9b479bb MD5 | raw file
  1. using System;
  2. using BillsDomain;
  3. using System.ComponentModel;
  4. using System.Globalization;
  5. using System.Xml.Serialization;
  6. using System.IO;
  7. namespace BillsPresentation
  8. {
  9. /// <summary>
  10. /// Business logic for the payment reminder view
  11. /// </summary>
  12. public class PaymentReminderPresenter
  13. {
  14. /// <summary>
  15. /// Gets or sets the controller.
  16. /// </summary>
  17. /// <value>The controller.</value>
  18. public IScheduledPaymentController Controller { get; set; }
  19. /// <summary>
  20. /// The view
  21. /// </summary>
  22. private readonly IPaymentReminderView view;
  23. /// <summary>
  24. /// The list of all days and their balances for the graph on the view
  25. /// </summary>
  26. private readonly BindingList<CalendarDay> allDaysInARangeForGraph = new BindingList<CalendarDay>();
  27. /// <summary>
  28. /// The list of days with payments for the monthly calendar on the view
  29. /// </summary>
  30. private readonly BindingList<CalendarDay> allDaysInARangeForCalendar = new BindingList<CalendarDay>();
  31. /// <summary>
  32. /// The list of upcoming payments
  33. /// </summary>
  34. private readonly UpcomingPaymentsCollection upcomingPayments = new UpcomingPaymentsCollection();
  35. /// <summary>
  36. /// Initializes a new instance of the PaymentReminderPresenter class.
  37. /// </summary>
  38. /// <param name="view">The view.</param>
  39. /// <param name="controller">The controller.</param>
  40. public PaymentReminderPresenter(IPaymentReminderView view, IScheduledPaymentController controller)
  41. {
  42. this.view = view;
  43. this.Controller = controller;
  44. this.Controller.LoadData();
  45. this.SubscribeViewToEvents();
  46. }
  47. /// <summary>
  48. /// Subscribes the view to events.
  49. /// </summary>
  50. public void SubscribeViewToEvents()
  51. {
  52. this.view.Load += view_Load;
  53. this.view.AsOfDateChanged += this.OnAsOfDateChanged;
  54. this.view.AsOfDateChanged += this.OnProjectedBalanceItemsChanged;
  55. this.view.FormClose += this.OnFormClosing;
  56. this.view.ScheduledPaymentChanged += this.OnScheduledPaymentChanged;
  57. this.Controller.ListChanged += this.OnControllerListChanged;
  58. this.view.DeleteClicked += this.OnDeleteClicked;
  59. this.view.ProjectedBalanceItemsChanged += this.OnProjectedBalanceItemsChanged;
  60. this.view.SpecificDateSelected += this.OnSpecificDateSelected;
  61. this.view.DataExporting += this.OnDataExporting;
  62. this.view.DataImporting += this.OnDataImporting;
  63. }
  64. private void OnDataExporting(object sender, EventArgs e)
  65. {
  66. this.Controller.SaveData(this.view.PathToData);
  67. }
  68. private void OnDataImporting(object sender, EventArgs e)
  69. {
  70. this.Controller.LoadData(this.view.PathToData);
  71. this.view_Load(sender, e);
  72. }
  73. /// <summary>
  74. /// Called when a specific date is selected.
  75. /// </summary>
  76. /// <param name="sender">The sender.</param>
  77. /// <param name="e">The <see cref="BillsPresentation.DateSelectedEventArgs"/> instance containing the event data.</param>
  78. private void OnSpecificDateSelected(object sender, DateSelectedEventArgs e)
  79. {
  80. this.PopulateCalendar();
  81. int i = 0;
  82. CalendarDay currentCalendarDay = this.allDaysInARangeForCalendar[i];
  83. while (e.SelectedDate.Date != currentCalendarDay.Date.Date && i < this.allDaysInARangeForCalendar.Count)
  84. {
  85. i++;
  86. currentCalendarDay = this.allDaysInARangeForCalendar[i] as CalendarDay;
  87. }
  88. this.view.PaymentsForADay = currentCalendarDay.Payments;
  89. }
  90. /// <summary>
  91. /// Called when projected balance items changed.
  92. /// </summary>
  93. /// <param name="sender">The sender.</param>
  94. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  95. private void OnProjectedBalanceItemsChanged(object sender, EventArgs e)
  96. {
  97. DateTime projectDateAsOfMidnight = new DateTime(this.view.DateForProjection.Year, this.view.DateForProjection.Month, this.view.DateForProjection.Day);
  98. this.view.ProjectedBalance = String.Empty;
  99. this.PopulateGraph();
  100. if (projectDateAsOfMidnight >= this.allDaysInARangeForGraph[0].Date && projectDateAsOfMidnight <= this.allDaysInARangeForGraph[this.allDaysInARangeForGraph.Count - 1].Date)
  101. {
  102. TimeSpan span = projectDateAsOfMidnight - this.allDaysInARangeForGraph[0].Date;
  103. int index = span.Days;
  104. this.view.ProjectedBalance = this.allDaysInARangeForGraph[index].EndOfDayBalance.ToString(CultureInfo.CurrentCulture);
  105. }
  106. else
  107. {
  108. TimeSpan span = projectDateAsOfMidnight - this.view.AsOfDay.Date;
  109. BindingList<CalendarDay> fromAsOfDayUntilProjectionDate = new BindingList<CalendarDay>();
  110. this.GenerateDayRangeOfPayments(this.view.AsOfDay.Date, span.Days + 1, fromAsOfDayUntilProjectionDate);
  111. this.view.ProjectedBalance = fromAsOfDayUntilProjectionDate[span.Days].EndOfDayBalance.ToString(CultureInfo.CurrentCulture);
  112. }
  113. this.UpdateUpcomingPaymentsBalances();
  114. this.view.UpcomingPayments = this.upcomingPayments;
  115. }
  116. /// <summary>
  117. /// Handles the DeleteClicked event of the view control.
  118. /// </summary>
  119. /// <param name="sender">The source of the event.</param>
  120. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  121. private void OnDeleteClicked(object sender, EventArgs e)
  122. {
  123. this.Controller.DeleteCurrentPayment();
  124. }
  125. /// <summary>
  126. /// Handles the ListChanged event of the controller control.
  127. /// </summary>
  128. /// <param name="sender">The source of the event.</param>
  129. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  130. private void OnControllerListChanged(object sender, EventArgs e)
  131. {
  132. this.LoadViewOnModel();
  133. }
  134. /// <summary>
  135. /// Called when a scheduled payment is changed or added.
  136. /// </summary>
  137. /// <param name="sender">The sender.</param>
  138. /// <param name="e">The <see cref="BillsPresentation.ScheduledPaymentChangedEventArgs"/> instance containing the event data.</param>
  139. private void OnScheduledPaymentChanged(object sender, ScheduledPaymentChangedEventArgs e)
  140. {
  141. this.Controller.SetScheduledPayment(e.SelectedPayment.ScheduledPaymentId);
  142. }
  143. /// <summary>
  144. /// Handles the FormClosing event of the view control.
  145. /// </summary>
  146. /// <param name="sender">The source of the event.</param>
  147. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  148. private void OnFormClosing(object sender, EventArgs e)
  149. {
  150. this.Controller.SaveData();
  151. }
  152. /// <summary>
  153. /// Called when the as of date is changed.
  154. /// </summary>
  155. /// <param name="sender">The sender.</param>
  156. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  157. private void OnAsOfDateChanged(object sender, EventArgs e)
  158. {
  159. if (this.view.DateForProjection < this.view.AsOfDay)
  160. {
  161. this.view.DateForProjection = this.view.AsOfDay;
  162. }
  163. this.LoadViewOnModel();
  164. }
  165. /// <summary>
  166. /// Populates the graph.
  167. /// </summary>
  168. private void PopulateGraph()
  169. {
  170. this.GenerateDayRangeOfPayments(this.view.AsOfDay, 90, this.allDaysInARangeForGraph);
  171. this.view.DaysAndBalances = this.allDaysInARangeForGraph;
  172. #if DEBUG
  173. XmlSerializer scheduledPaymentsSerializer = new XmlSerializer(typeof(BindingList<CalendarDay>));
  174. using (TextWriter fileForWriting = new StreamWriter("cal.xml"))
  175. {
  176. scheduledPaymentsSerializer.Serialize(fileForWriting, this.allDaysInARangeForGraph);
  177. }
  178. #endif
  179. }
  180. /// <summary>
  181. /// Generates the upcoming payments.
  182. /// </summary>
  183. private void GenerateUpcomingPayments()
  184. {
  185. this.upcomingPayments.Clear();
  186. foreach (ScheduledPayment scheduledPayment in this.Controller.ScheduledPayments)
  187. {
  188. if (scheduledPayment.PaymentRecurrence == Recurrence.None && scheduledPayment.FirstDate < this.view.AsOfDay)
  189. {
  190. continue;
  191. }
  192. this.upcomingPayments.Add(new PaymentWithDate()
  193. {
  194. Amount = scheduledPayment.Amount * (scheduledPayment.IsDebit ? -1 : 1),
  195. IsDebit = String.CompareOrdinal(scheduledPayment.PaymentType.Name, PaymentType.Debit.Name) == 0,
  196. Payee = scheduledPayment.Payee,
  197. ScheduledPaymentId = scheduledPayment.Id,
  198. PaymentDate = CalculateNextPaymentDate(this.view.AsOfDay, scheduledPayment),
  199. IncludeInProjection = scheduledPayment.IncludeInProjection
  200. });
  201. }
  202. this.upcomingPayments.Sort();
  203. this.UpdateUpcomingPaymentsBalances();
  204. }
  205. /// <summary>
  206. /// Updates the upcoming payments balances.
  207. /// </summary>
  208. private void UpdateUpcomingPaymentsBalances()
  209. {
  210. decimal currentBalance = this.view.CurrentBalance ?? 0;
  211. int i = 0;
  212. foreach (PaymentWithDate paymentOnDate in this.upcomingPayments)
  213. {
  214. if (paymentOnDate.IncludeInProjection)
  215. {
  216. currentBalance = currentBalance + paymentOnDate.Amount;
  217. }
  218. paymentOnDate.Balance = currentBalance;
  219. this.upcomingPayments.ResetItem(i);
  220. i++;
  221. }
  222. }
  223. /// <summary>
  224. /// Generates the day range of payments.
  225. /// </summary>
  226. /// <param name="startDate">The start date.</param>
  227. /// <param name="numberOfDaysToProject">The number of days to project.</param>
  228. /// <param name="output">The output.</param>
  229. private void GenerateDayRangeOfPayments(DateTime startDate, int numberOfDaysToProject, BindingList<CalendarDay> output)
  230. {
  231. output.Clear();
  232. decimal currentBalance = this.view.CurrentBalance ?? 0;
  233. for (int i = 0; i < numberOfDaysToProject; i++)
  234. {
  235. DateTime currentDate = startDate.AddDays(i).Date;
  236. CalendarDay currentCalendarDay = new CalendarDay() { Date = currentDate };
  237. output.Add(currentCalendarDay);
  238. currentCalendarDay.EndOfDayBalance = currentBalance;
  239. foreach (ScheduledPayment scheduledPayment in this.Controller.ScheduledPayments)
  240. {
  241. if (scheduledPayment.PaymentRecurrence == Recurrence.None && scheduledPayment.FirstDate != currentDate)
  242. {
  243. continue;
  244. }
  245. DateTime nextPaymentDate = CalculateNextPaymentDate(currentDate, scheduledPayment);
  246. if (nextPaymentDate == currentDate)
  247. {
  248. currentCalendarDay.Payments.Add(new Payment()
  249. {
  250. Amount = scheduledPayment.Amount * (scheduledPayment.IsDebit ? -1 : 1),
  251. IsDebit = String.CompareOrdinal(scheduledPayment.PaymentType.Name, PaymentType.Debit.Name) == 0,
  252. Payee = scheduledPayment.Payee,
  253. ScheduledPaymentId = scheduledPayment.Id,
  254. IncludeInProjection = scheduledPayment.IncludeInProjection
  255. });
  256. if (scheduledPayment.IncludeInProjection)
  257. {
  258. if (scheduledPayment.IsDebit)
  259. {
  260. currentCalendarDay.EndOfDayBalance -= scheduledPayment.Amount;
  261. }
  262. else
  263. {
  264. currentCalendarDay.EndOfDayBalance += scheduledPayment.Amount;
  265. }
  266. }
  267. }
  268. }
  269. currentCalendarDay.Payments.Sort();
  270. currentBalance = currentCalendarDay.EndOfDayBalance;
  271. }
  272. }
  273. /// <summary>
  274. /// Populates the calendar.
  275. /// </summary>
  276. private void PopulateCalendar()
  277. {
  278. this.GenerateDayRangeOfPayments(this.view.DayOnCalendar.AddMonths(-2), 180, this.allDaysInARangeForCalendar);
  279. BindingList<CalendarDay> datesWithPayments = new BindingList<CalendarDay>();
  280. foreach (CalendarDay item in allDaysInARangeForCalendar)
  281. {
  282. if (item.Payments.Count > 0)
  283. {
  284. datesWithPayments.Add(item);
  285. }
  286. }
  287. this.view.DatesWithPayments = datesWithPayments;
  288. }
  289. /// <summary>
  290. /// Loads the view on model.
  291. /// </summary>
  292. private void LoadViewOnModel()
  293. {
  294. this.GenerateUpcomingPayments();
  295. this.PopulateGraph();
  296. this.PopulateCalendar();
  297. this.view.UpcomingPayments = this.upcomingPayments;
  298. this.OnSpecificDateSelected(this, new DateSelectedEventArgs() { SelectedDate = this.view.DayOnCalendar });
  299. }
  300. /// <summary>
  301. /// Handles the Load event of the view control.
  302. /// </summary>
  303. /// <param name="sender">The source of the event.</param>
  304. /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  305. private void view_Load(object sender, EventArgs e)
  306. {
  307. this.view.AsOfDay = DateTime.Today;
  308. LoadViewOnModel();
  309. }
  310. /// <summary>
  311. /// Calculates the next payment date.
  312. /// </summary>
  313. /// <param name="asOfDate">As of date.</param>
  314. /// <param name="scheduledPayment">The scheduled payment.</param>
  315. /// <returns></returns>
  316. private static DateTime CalculateNextPaymentDate(DateTime asOfDate, ScheduledPayment scheduledPayment)
  317. {
  318. DateTime result = scheduledPayment.FirstDate;
  319. DateTime asOfDateAtMidnight = new DateTime(asOfDate.Year, asOfDate.Month, asOfDate.Day);
  320. if (scheduledPayment.PaymentRecurrence == Recurrence.Biweekly)
  321. {
  322. while (result < asOfDateAtMidnight)
  323. {
  324. result = result.AddDays(14);
  325. }
  326. }
  327. else if (scheduledPayment.PaymentRecurrence == Recurrence.Bimonthly)
  328. {
  329. while (result < asOfDateAtMidnight)
  330. {
  331. result = result.AddMonths(2);
  332. }
  333. }
  334. else if (scheduledPayment.PaymentRecurrence == Recurrence.Monthly)
  335. {
  336. while (result < asOfDateAtMidnight)
  337. {
  338. result = result.AddMonths(1);
  339. }
  340. }
  341. else if (scheduledPayment.PaymentRecurrence == Recurrence.Weekly)
  342. {
  343. while (result < asOfDateAtMidnight)
  344. {
  345. result = result.AddDays(7);
  346. }
  347. }
  348. return result;
  349. }
  350. }
  351. }