diff --git a/src/UpBlazor.ApiClient/UpBlazorClient.cs b/src/UpBlazor.ApiClient/UpBlazorClient.cs index 13104a5..e7a0b42 100644 --- a/src/UpBlazor.ApiClient/UpBlazorClient.cs +++ b/src/UpBlazor.ApiClient/UpBlazorClient.cs @@ -4398,6 +4398,9 @@ public enum Interval [System.Runtime.Serialization.EnumMember(Value = @"Fortnights")] Fortnights = 2, + [System.Runtime.Serialization.EnumMember(Value = @"Monthly")] + Monthly = 3, + } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")] diff --git a/src/UpBlazor.Application/Features/Forecast/GetExpenseForecastQuery.cs b/src/UpBlazor.Application/Features/Forecast/GetExpenseForecastQuery.cs index 242956b..fd9ab6d 100644 --- a/src/UpBlazor.Application/Features/Forecast/GetExpenseForecastQuery.cs +++ b/src/UpBlazor.Application/Features/Forecast/GetExpenseForecastQuery.cs @@ -117,7 +117,13 @@ public async Task> Handle(GetExpenseForecastQuery req { if (expense.FromIncomeId is not null) { - var income = incomeCycleRanges.Keys.First(x => x == expense.FromIncomeId.Value); + var income = incomeCycleRanges.Keys.FirstOrDefault(x => x == expense.FromIncomeId.Value); + + if (income == default) + { + // TODO: log this or delette the record + continue; + } var cycleCollision = incomeCycleRanges[income] .FirstOrDefault(x => x == currentDay); diff --git a/src/UpBlazor.Application/Features/Planner/GetIncomePlannerQuery.cs b/src/UpBlazor.Application/Features/Planner/GetIncomePlannerQuery.cs index 0706e31..21c451b 100644 --- a/src/UpBlazor.Application/Features/Planner/GetIncomePlannerQuery.cs +++ b/src/UpBlazor.Application/Features/Planner/GetIncomePlannerQuery.cs @@ -183,9 +183,10 @@ private static void GetExactSubTotals(IncomePlannerDto output, IReadOnlyList 1, - Interval.Weeks => 7, - Interval.Fortnights => 14, + Interval.Days => 1M, + Interval.Weeks => 7M, + Interval.Fortnights => 14M, + Interval.Monthly => 30.437M, // https://www.britannica.com/science/time/Lengths-of-years-and-months _ => throw new ArgumentOutOfRangeException() }; diff --git a/src/UpBlazor.Core/Helpers/IntervalExtensions.cs b/src/UpBlazor.Core/Helpers/IntervalExtensions.cs index 322f171..7c86ebc 100644 --- a/src/UpBlazor.Core/Helpers/IntervalExtensions.cs +++ b/src/UpBlazor.Core/Helpers/IntervalExtensions.cs @@ -13,6 +13,7 @@ interval switch Interval.Days => TimeSpan.FromDays(units), Interval.Weeks => TimeSpan.FromDays(units * 7), Interval.Fortnights => TimeSpan.FromDays(units * 14), + Interval.Monthly => TimeSpan.FromDays(units * 30.437), // https://www.britannica.com/science/time/Lengths-of-years-and-months), _ => throw new ArgumentOutOfRangeException(nameof(interval), interval, null) }; @@ -36,6 +37,29 @@ public static DateTime FindFirstCycle(this DateTime start, Interval interval, in return currentCycle; } + private static List GetAllCyclesInMonthlyRange(this DateTime start, DateTime rangeStart, + DateTime rangeEnd, int units) + { + var dayOfMonth = start.Day; + + var output = new List(); + + var potentialHit = new DateTime(start.Year, start.Month, dayOfMonth); + + while (potentialHit <= rangeEnd) + { + if (potentialHit >= rangeStart) + { + output.Add(DateOnly.FromDateTime(potentialHit)); + } + + // Increment by the number of months specified in units + potentialHit = potentialHit.AddMonths(units); + } + + return output; + } + public static List GetAllCyclesInRange(this DateTime start, DateTime rangeStart, DateTime rangeEnd, Interval interval, int units) { var startOfRange = start.FindFirstCycle(interval, units, rangeStart); @@ -45,6 +69,11 @@ public static List GetAllCyclesInRange(this DateTime start, DateTime r { return Enumerable.Empty().ToList(); } + + if (interval is Interval.Monthly) + { + return GetAllCyclesInMonthlyRange(start, rangeStart, rangeEnd, units); + } var output = new List { diff --git a/src/UpBlazor.Core/Models/Enums/Interval.cs b/src/UpBlazor.Core/Models/Enums/Interval.cs index ef61846..d4c0cf7 100644 --- a/src/UpBlazor.Core/Models/Enums/Interval.cs +++ b/src/UpBlazor.Core/Models/Enums/Interval.cs @@ -4,6 +4,7 @@ public enum Interval { Days, Weeks, - Fortnights + Fortnights, + Monthly } } \ No newline at end of file diff --git a/src/UpBlazor.Core/Models/Income.cs b/src/UpBlazor.Core/Models/Income.cs index 6cfe45a..7239356 100644 --- a/src/UpBlazor.Core/Models/Income.cs +++ b/src/UpBlazor.Core/Models/Income.cs @@ -15,47 +15,5 @@ public class Income public int IntervalUnits { get; set; } public override string ToString() => $"{Name} (${ExactMoney:F2} every {IntervalUnits} {Interval})"; - - public bool FallsOn(DateTime dateTime, DateTime startDate, out int totalCyclesSinceStart) - { - var date = dateTime.Date; - - if (date < startDate) - { - totalCyclesSinceStart = default; - return false; - } - - if (date == startDate) - { - totalCyclesSinceStart = 0; - return true; - } - - var loopDate = startDate.Date; - - totalCyclesSinceStart = 0; - - do - { - totalCyclesSinceStart++; - var toAdd = Interval switch - { - Interval.Days => TimeSpan.FromDays(IntervalUnits), - Interval.Fortnights => TimeSpan.FromDays(IntervalUnits * 14), - Interval.Weeks => TimeSpan.FromDays(7), - _ => throw new ArgumentOutOfRangeException() - }; - - loopDate = loopDate.Add(toAdd); - - if (loopDate.Date == date.Date) - { - return true; - } - } while (loopDate < date); - - return false; - } } } \ No newline at end of file diff --git a/src/UpBlazor.Core/Models/RecurringExpense.cs b/src/UpBlazor.Core/Models/RecurringExpense.cs index f1c81fd..20450d3 100644 --- a/src/UpBlazor.Core/Models/RecurringExpense.cs +++ b/src/UpBlazor.Core/Models/RecurringExpense.cs @@ -23,48 +23,5 @@ public string SaverId get => FromSaverId; set => FromSaverId = value; } - - public bool FallsOn(DateTime dateTime, DateTime startDate, out int totalCyclesSinceStart) - { - var date = dateTime.Date; - - if (date < startDate) - { - totalCyclesSinceStart = default; - return false; - } - - if (date == startDate) - { - totalCyclesSinceStart = 0; - return true; - } - - var loopDate = startDate.Date; - - totalCyclesSinceStart = 0; - - do - { - totalCyclesSinceStart++; - var toAdd = Interval switch - { - Interval.Days => TimeSpan.FromDays(IntervalUnits), - Interval.Fortnights => TimeSpan.FromDays(IntervalUnits * 14), - Interval.Weeks => TimeSpan.FromDays(7), - _ => throw new ArgumentOutOfRangeException() - }; - - loopDate = loopDate.Add(toAdd); - - if (loopDate.Date == date.Date) - { - return true; - } - } while (loopDate < date); - - return false; - } - } } \ No newline at end of file diff --git a/src/UpBlazor.WebUI/Pages/Insights/Forecast/Index.razor b/src/UpBlazor.WebUI/Pages/Insights/Forecast/Index.razor index dd53679..a3eacee 100644 --- a/src/UpBlazor.WebUI/Pages/Insights/Forecast/Index.razor +++ b/src/UpBlazor.WebUI/Pages/Insights/Forecast/Index.razor @@ -155,7 +155,9 @@ else } @@ -196,6 +198,7 @@ else Xaxis = new XAxis() { Type = XAxisType.Datetime, + TickPlacement = TickPlacement.On, }, Yaxis = new List() {