diff --git a/subtrack.MAUI/MauiProgram.cs b/subtrack.MAUI/MauiProgram.cs index 0ec2770..37bc39a 100644 --- a/subtrack.MAUI/MauiProgram.cs +++ b/subtrack.MAUI/MauiProgram.cs @@ -2,9 +2,6 @@ using Microsoft.EntityFrameworkCore; using subtrack.MAUI.Services.Abstractions; using subtrack.MAUI.Services; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Infrastructure; -using subtrack.DAL.Migrations; using System.Runtime.CompilerServices; using subtrack.MAUI.Shared.JsInterop; @@ -49,16 +46,17 @@ public static IServiceCollection AddSubtrackServices(this IServiceCollection ser .AddScoped() .AddScoped() .AddScoped() + .AddScoped() .AddScoped() .AddTransient(); } private static void SeedDb(SubtrackDbContext dbContext) { - var todayLastMonth = DateTime.Now.AddMonths(-1); _ = dbContext.Database.EnsureDeleted(); dbContext.Database.Migrate(); + var todayLastMonth = DateTime.Now.AddMonths(-1); dbContext.Subscriptions.AddRange( new DAL.Entities.Subscription() { Name = "paramount", LastPayment = todayLastMonth.AddDays(-1), FirstPaymentDay = todayLastMonth.AddDays(-1).Day, Cost = 3m, BillingOccurrence = DAL.Entities.BillingOccurrence.Week, BillingInterval = 2 }, new DAL.Entities.Subscription() { Name = "Disney+", LastPayment = todayLastMonth, FirstPaymentDay = todayLastMonth.Day, Cost = 3m, BillingOccurrence = DAL.Entities.BillingOccurrence.Month, BillingInterval = 1 }, diff --git a/subtrack.MAUI/Pages/Monthly.razor b/subtrack.MAUI/Pages/Monthly.razor index 4169c21..4c81e20 100644 --- a/subtrack.MAUI/Pages/Monthly.razor +++ b/subtrack.MAUI/Pages/Monthly.razor @@ -1,45 +1,37 @@ @page "/monthly" + @inject subtrack.MAUI.Services.Abstractions.ISubscriptionService SubscriptionService -@inject subtrack.MAUI.Services.Abstractions.ISubscriptionsCalculator SubscriptionCalculator +@inject subtrack.MAUI.Services.Abstractions.IDateProvider DateProvider +@inject subtrack.MAUI.Services.Abstractions.IMonthlyPageCalculator MonthlyPageCalculator -@foreach (var subMonth in _currentYearSubscriptionMonths) +@foreach (var subs in _monthlySubscriptions) { - -} - -
@(_currentMonthDate.AddYears(1).Year)
- -@foreach (var subMonth in _nextYearSubscriptionMonths) -{ - -} - -
@(_currentMonthDate.AddYears(2).Year)
+ var displayYear = subs.Key != _currentYear; + @if (displayYear) + { +
@(subs.Key)
+ } + @foreach (var subMonth in subs.Value) + { + -@foreach (var subMonth in _nextYearSubscriptionMonths) -{ - + } } @code { - IEnumerable _subscriptions = new List(); - DateTime _currentMonthDate = DateTime.Now; - - IEnumerable _currentYearSubscriptionMonths = null!; - IEnumerable _nextYearSubscriptionMonths = null!; - IEnumerable _twoYearsForwardSubscriptionMonths = null!; - + int _currentYear; + IDictionary> _monthlySubscriptions = default!; protected override async Task OnInitializedAsync() { - _subscriptions = await SubscriptionService.GetAllAsync(); + var subscriptions = await SubscriptionService.GetAllAsync(); - var twoYearsForwardDate = _currentMonthDate.AddYears(2); - var paymentsTwoYearsForward = SubscriptionCalculator.GetMonthlySubscriptionLists(_subscriptions, _currentMonthDate.FirstDayOfMonthDate(), twoYearsForwardDate.LastDayOfMonthDate()); + var fromDate = DateProvider.Today; + _currentYear = fromDate.Year; + var futureYearsToDisplay = 2; - _currentYearSubscriptionMonths = paymentsTwoYearsForward.Where(m => m.MonthDate.Year == _currentMonthDate.Year).ToList(); - _nextYearSubscriptionMonths = paymentsTwoYearsForward.Where(m => m.MonthDate.Year == _currentMonthDate.AddYears(1).Year).ToList(); - _twoYearsForwardSubscriptionMonths = paymentsTwoYearsForward.Where(m => m.MonthDate.Year == _currentMonthDate.AddYears(2).Year).ToList(); + var toDate = fromDate.AddYears(futureYearsToDisplay); + _monthlySubscriptions = MonthlyPageCalculator.GetMonthlySubscriptionLists(subscriptions, fromDate.FirstDayOfMonthDate(), toDate.LastDayOfMonthDate()); } } \ No newline at end of file diff --git a/subtrack.MAUI/Services/Abstractions/IMonthlyPageCalculator.cs b/subtrack.MAUI/Services/Abstractions/IMonthlyPageCalculator.cs new file mode 100644 index 0000000..64bcaba --- /dev/null +++ b/subtrack.MAUI/Services/Abstractions/IMonthlyPageCalculator.cs @@ -0,0 +1,17 @@ +using subtrack.DAL.Entities; +using subtrack.MAUI.Responses; + +namespace subtrack.MAUI.Services.Abstractions +{ + public interface IMonthlyPageCalculator + { + /// + /// Fetches all subscriptions that are active between the given dates grouped by month and year + /// + /// + /// Starting date for fetching the subscriptions + /// Ending date for fetching the subscriptions + /// A dictionary where key is the year and value is the list of subscriptions grouped by month for that year + IDictionary> GetMonthlySubscriptionLists(IEnumerable subscriptions, DateTime fromIncludedDate, DateTime toIncludedDate); + } +} diff --git a/subtrack.MAUI/Services/Abstractions/ISubscriptionsCalculator.cs b/subtrack.MAUI/Services/Abstractions/ISubscriptionsCalculator.cs index 8d6ee9d..d80d02e 100644 --- a/subtrack.MAUI/Services/Abstractions/ISubscriptionsCalculator.cs +++ b/subtrack.MAUI/Services/Abstractions/ISubscriptionsCalculator.cs @@ -1,22 +1,17 @@ using subtrack.DAL.Entities; -using subtrack.MAUI.Responses; namespace subtrack.MAUI.Services.Abstractions { public interface ISubscriptionsCalculator { int GetDueDays(Subscription subscription); + decimal GetTotalCost(IEnumerable subscriptions); + decimal GetYearlyCost(Subscription subscription); - /// - /// Gets lists of due subs for each month up until-including a specific month date - /// - /// - /// - /// Subscriptions due for a specific Month Date - IEnumerable GetMonthlySubscriptionLists(IEnumerable subscriptions, DateTime fromIncludedDate, DateTime toIncludedDate); DateTime GetNextPaymentDate(Subscription subscription); + (bool IsDue, DateTime NextPaymentDate) IsDue(Subscription subscription); decimal GetAverageMonthlyCost(IEnumerable subscriptions); diff --git a/subtrack.MAUI/Services/MonthlyPageCalculator.cs b/subtrack.MAUI/Services/MonthlyPageCalculator.cs new file mode 100644 index 0000000..d16c1d8 --- /dev/null +++ b/subtrack.MAUI/Services/MonthlyPageCalculator.cs @@ -0,0 +1,42 @@ +using subtrack.DAL.Entities; +using subtrack.MAUI.Responses; +using subtrack.MAUI.Services.Abstractions; + +namespace subtrack.MAUI.Services +{ + internal class MonthlyPageCalculator : IMonthlyPageCalculator + { + private readonly ISubscriptionsCalculator _subscriptionsCalculator; + + public MonthlyPageCalculator(ISubscriptionsCalculator subscriptionsCalculator) + { + _subscriptionsCalculator = subscriptionsCalculator; + } + + private IEnumerable GetPaymentsUntilMonth(Subscription subscription, DateTime fromIncludedDate, DateTime toIncludedDate) + { + subscription.LastPayment = _subscriptionsCalculator.GetNextPaymentDate(subscription); + + while (subscription.LastPayment.Date >= fromIncludedDate.Date + && subscription.LastPayment.Date <= toIncludedDate.Date) + { + yield return (Subscription)subscription.Clone(); + subscription.LastPayment = _subscriptionsCalculator.GetNextPaymentDate(subscription); + } + } + + public IDictionary> GetMonthlySubscriptionLists(IEnumerable subscriptions, DateTime fromIncludedMonthDate, DateTime finalIncludedMonthDate) + { + return subscriptions + .SelectMany(s => GetPaymentsUntilMonth(s, fromIncludedMonthDate, finalIncludedMonthDate)) + .GroupBy(s => (s.LastPayment.Year, s.LastPayment.Month)) + .Select(g => + new SubscriptionsMonthResponse + { + MonthDate = new DateTime(g.Key.Year, g.Key.Month, 1), + Subscriptions = g.OrderBy(s => s.LastPayment).ToList(), + Cost = _subscriptionsCalculator.GetTotalCost(g) + }).GroupBy(r => r.MonthDate.Year).ToDictionary(k => k.Key, v => v.ToList()); + } + } +} diff --git a/subtrack.MAUI/Services/SubscriptionsCalculator.cs b/subtrack.MAUI/Services/SubscriptionsCalculator.cs index 97034c1..3bc8961 100644 --- a/subtrack.MAUI/Services/SubscriptionsCalculator.cs +++ b/subtrack.MAUI/Services/SubscriptionsCalculator.cs @@ -1,5 +1,4 @@ using subtrack.DAL.Entities; -using subtrack.MAUI.Responses; using subtrack.MAUI.Services.Abstractions; using subtrack.MAUI.Utilities; using System.Globalization; @@ -50,32 +49,6 @@ public decimal GetYearlyCost(Subscription subscription) return subscription.Cost * yearlyPaymentsCount; } - private IEnumerable GetPaymentsUntilMonth(Subscription subscription, DateTime fromIncludedDate, DateTime toIncludedDate) - { - subscription.LastPayment = GetNextPaymentDate(subscription); - - while (subscription.LastPayment.Date >= fromIncludedDate.Date - && subscription.LastPayment.Date <= toIncludedDate.Date) - { - yield return (Subscription)subscription.Clone(); - subscription.LastPayment = GetNextPaymentDate(subscription); - } - } - - public IEnumerable GetMonthlySubscriptionLists(IEnumerable subscriptions, DateTime fromIncludedMonthDate, DateTime finalIncludedMonthDate) - { - return subscriptions - .SelectMany(s => GetPaymentsUntilMonth(s, fromIncludedMonthDate, finalIncludedMonthDate)) - .GroupBy(s => (s.LastPayment.Year, s.LastPayment.Month)) - .Select(g => - new SubscriptionsMonthResponse - { - MonthDate = new DateTime(g.Key.Year, g.Key.Month, 1), - Subscriptions = g.OrderBy(s => s.LastPayment).ToList(), - Cost = GetTotalCost(g) - }).ToList(); - } - public DateTime GetNextPaymentDate(Subscription subscription) { int startDay = subscription.FirstPaymentDay; diff --git a/subtrack.Tests/SubscriptionCalculatorTests/GetSubscriptionListByMonthTests.cs b/subtrack.Tests/SubscriptionCalculatorTests/GetSubscriptionListByMonthTests.cs index 22a78f6..2b37f4c 100644 --- a/subtrack.Tests/SubscriptionCalculatorTests/GetSubscriptionListByMonthTests.cs +++ b/subtrack.Tests/SubscriptionCalculatorTests/GetSubscriptionListByMonthTests.cs @@ -5,17 +5,17 @@ namespace subtrack.Tests.SubscriptionCalculatorTests { public class GetSubscriptionListByMonthTests { - private readonly ISubscriptionsCalculator _sut; - private readonly IDateProvider _dateTimeProvider = Substitute.For(); + private readonly IMonthlyPageCalculator _sut; private readonly int _numberOfMonths = 3; private readonly DateTime _fromIncludedDate = new(2023, 4, 1), _toIncludedDate; - + public GetSubscriptionListByMonthTests() { _toIncludedDate = _fromIncludedDate.AddMonths(_numberOfMonths - 1); - _sut = new SubscriptionsCalculator(_dateTimeProvider); - _dateTimeProvider.Today.Returns(_fromIncludedDate); + var dateProvider = Substitute.For(); + var subscriptionsCalculator = new SubscriptionsCalculator(dateProvider); + _sut = new MonthlyPageCalculator(subscriptionsCalculator); } [Theory] @@ -32,7 +32,7 @@ public void GetSubscriptionListByMonth_MonthProvided_Returns_UnpaidSubscriptions var collectedSubscriptions = CollectSubscriptions(result); // Assert - Assert.Equal(_numberOfMonths, result.Count()); + Assert.Equal(_numberOfMonths, result.First().Value.Count); Assert.Equal(_numberOfMonths, collectedSubscriptions.Count(sub => sub.Id.Equals(subscription.Id))); } @@ -59,7 +59,7 @@ public void GetSubscriptionListByMonth_MonthProvided_ShouldNotReturn_PaidSubscri var result = _sut.GetMonthlySubscriptionLists(subscriptions, _fromIncludedDate, _toIncludedDate); // Assert - Assert.Equal(_numberOfMonths, result.Count()); + Assert.Equal(_numberOfMonths, result.First().Value.Count); } [Theory] @@ -76,7 +76,7 @@ public void GetSubscriptionListByMonth_MonthProvided_ShouldReturnWeeklySubscript var collectedSubscriptions = CollectSubscriptions(result); // Assert - Assert.Equal(_numberOfMonths, result.Count()); + Assert.Equal(_numberOfMonths, result.First().Value.Count); Assert.Equal(expectedNumberOfIterationsInMonth, collectedSubscriptions.Count(sub => sub.Id.Equals(subscription.Id))); } @@ -90,7 +90,7 @@ public void GetSubscriptionListByMonth_WeeklySub_ShouldNotContainLastPayment() var result = _sut.GetMonthlySubscriptionLists(new[] { sub }, _fromIncludedDate, _toIncludedDate); // Assert - Assert.Equal(2, result.Count()); + Assert.Equal(2, result.First().Value.Count); } [Fact] @@ -104,8 +104,8 @@ public void GetSubscriptionListByMonth_MonthProvided_SubscriptionsHavingLongerTh var result = _sut.GetMonthlySubscriptionLists(subscriptions, _fromIncludedDate, _toIncludedDate); // Assert - Assert.Equal(_numberOfMonths, result.Count()); - Assert.Contains(result.Last().Subscriptions, (sub) => sub.Id.Equals(subscription.Id)); + Assert.Equal(_numberOfMonths, result.First().Value.Count); + Assert.Contains(result.Last().Value.Last().Subscriptions, (sub) => sub.Id.Equals(subscription.Id)); } private IEnumerable CreateSubscriptions() @@ -202,9 +202,9 @@ private IEnumerable CreateSubscriptions() }; } - private static IEnumerable CollectSubscriptions(IEnumerable subscriptionsMonthResponses) + private static IEnumerable CollectSubscriptions(IDictionary> subscriptionsMonthResponses) { - return subscriptionsMonthResponses.SelectMany(response => response.Subscriptions); + return subscriptionsMonthResponses.SelectMany(response => response.Value.SelectMany(r => r.Subscriptions)); } } }