Skip to content

Commit

Permalink
✨ Add monthly option for income/expenses
Browse files Browse the repository at this point in the history
  • Loading branch information
Hona committed Aug 24, 2023
1 parent b5ccd0c commit 8b038c5
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 91 deletions.
3 changes: 3 additions & 0 deletions src/UpBlazor.ApiClient/UpBlazorClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,13 @@ public async Task<IReadOnlyList<ForecastDto>> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,10 @@ private static void GetExactSubTotals(IncomePlannerDto output, IReadOnlyList<Sav

var proRataAmount = recurringExpense.Amount * income.IntervalUnits * income.Interval switch
{
Interval.Days => 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()
};

Expand Down
29 changes: 29 additions & 0 deletions src/UpBlazor.Core/Helpers/IntervalExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};

Expand All @@ -36,6 +37,29 @@ public static DateTime FindFirstCycle(this DateTime start, Interval interval, in
return currentCycle;
}

private static List<DateOnly> GetAllCyclesInMonthlyRange(this DateTime start, DateTime rangeStart,
DateTime rangeEnd, int units)
{
var dayOfMonth = start.Day;

var output = new List<DateOnly>();

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<DateOnly> GetAllCyclesInRange(this DateTime start, DateTime rangeStart, DateTime rangeEnd, Interval interval, int units)
{
var startOfRange = start.FindFirstCycle(interval, units, rangeStart);
Expand All @@ -45,6 +69,11 @@ public static List<DateOnly> GetAllCyclesInRange(this DateTime start, DateTime r
{
return Enumerable.Empty<DateOnly>().ToList();
}

if (interval is Interval.Monthly)
{
return GetAllCyclesInMonthlyRange(start, rangeStart, rangeEnd, units);
}

var output = new List<DateOnly>
{
Expand Down
3 changes: 2 additions & 1 deletion src/UpBlazor.Core/Models/Enums/Interval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ public enum Interval
{
Days,
Weeks,
Fortnights
Fortnights,
Monthly
}
}
42 changes: 0 additions & 42 deletions src/UpBlazor.Core/Models/Income.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
43 changes: 0 additions & 43 deletions src/UpBlazor.Core/Models/RecurringExpense.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

}
}
5 changes: 4 additions & 1 deletion src/UpBlazor.WebUI/Pages/Insights/Forecast/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ else
<ApexPointSeries TItem="ForecastDto"
Items="@_totalForecastData.Where(x => x.AccountName == incomeName)"
Name="@incomeName"
XValue="@(x => DateTime.ParseExact(x.Cycle, "dd/MM/yyyy", CultureInfo.InvariantCulture))"
XValue="@(x =>
{
var date = DateOnly.ParseExact(x.Cycle, "dd/MM/yyyy", CultureInfo.InvariantCulture); return date.AddDays(1); })"
YValue="x => x.Balance"/>
}

Expand Down Expand Up @@ -196,6 +198,7 @@ else
Xaxis = new XAxis()
{
Type = XAxisType.Datetime,
TickPlacement = TickPlacement.On,
},
Yaxis = new List<YAxis>()
{
Expand Down

0 comments on commit 8b038c5

Please sign in to comment.