Version 1.0.1
This commit is contained in:
3
WheresMyMoney.Maui.Core/Balance.cs
Normal file
3
WheresMyMoney.Maui.Core/Balance.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
public record Balance(int Id, decimal Amount, DateTime Date);
|
||||
78
WheresMyMoney.Maui.Core/PlannedPayment.cs
Normal file
78
WheresMyMoney.Maui.Core/PlannedPayment.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
public enum ReoccurenceType
|
||||
{
|
||||
Days,
|
||||
Weeks,
|
||||
Months,
|
||||
Years,
|
||||
EveryLastDayOfMonth
|
||||
}
|
||||
|
||||
public record PlannedPayment(
|
||||
int Id,
|
||||
decimal Amount,
|
||||
string Name,
|
||||
DateTime DateStart,
|
||||
DateTime? DateEnd,
|
||||
bool IsSubscription,
|
||||
int? Reoccurences,
|
||||
ReoccurenceType? ReoccurenceType)
|
||||
{
|
||||
public List<PlannedPayment> ExpandSubscription(DateTime startDate, DateTime endDate)
|
||||
{
|
||||
var expandedPayments = new List<PlannedPayment>();
|
||||
|
||||
if (!IsSubscription || Reoccurences is null || Reoccurences < 1 || ReoccurenceType is null)
|
||||
return DateStart >= startDate && DateStart <= endDate ? [this] : [];
|
||||
|
||||
var occurrence = DateStart;
|
||||
|
||||
while (occurrence <= endDate && (DateEnd == null || occurrence <= DateEnd))
|
||||
{
|
||||
if (occurrence >= startDate)
|
||||
{
|
||||
expandedPayments.Add(new PlannedPayment(
|
||||
0, // New ID not assigned
|
||||
Amount,
|
||||
Name,
|
||||
occurrence,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
|
||||
occurrence = GetNextOccurence(occurrence, Reoccurences.Value, ReoccurenceType.Value);
|
||||
}
|
||||
|
||||
return expandedPayments;
|
||||
|
||||
DateTime GetNextOccurence(DateTime dateStart, int reoccurences, ReoccurenceType reoccurenceType)
|
||||
{
|
||||
switch (reoccurenceType)
|
||||
{
|
||||
case Core.ReoccurenceType.Days:
|
||||
return dateStart.AddDays(reoccurences);
|
||||
case Core.ReoccurenceType.Weeks:
|
||||
return dateStart.AddDays(reoccurences * 7);
|
||||
break;
|
||||
case Core.ReoccurenceType.Months:
|
||||
return dateStart.AddMonths(reoccurences);
|
||||
break;
|
||||
case Core.ReoccurenceType.Years:
|
||||
return dateStart.AddYears(reoccurences);
|
||||
break;
|
||||
case Core.ReoccurenceType.EveryLastDayOfMonth:
|
||||
var result = new DateTime(startDate.Year, startDate.Month,
|
||||
DateTime.DaysInMonth(dateStart.Year, dateStart.Month));
|
||||
if (result.Date != dateStart.Date) return result;
|
||||
result = result.AddDays(1);
|
||||
return new DateTime(result.Year, result.Month, DateTime.DaysInMonth(result.Year, result.Month));
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(reoccurenceType), reoccurenceType, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
// All the code in this file is only included on Android.
|
||||
public class PlatformClass1
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
// All the code in this file is only included on Mac Catalyst.
|
||||
public class PlatformClass1
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace WheresMyMoney.Maui.Core
|
||||
{
|
||||
// All the code in this file is only included on Tizen.
|
||||
public class PlatformClass1
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
// All the code in this file is only included on Windows.
|
||||
public class PlatformClass1
|
||||
{
|
||||
}
|
||||
6
WheresMyMoney.Maui.Core/Platforms/iOS/PlatformClass1.cs
Normal file
6
WheresMyMoney.Maui.Core/Platforms/iOS/PlatformClass1.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
// All the code in this file is only included on iOS.
|
||||
public class PlatformClass1
|
||||
{
|
||||
}
|
||||
253
WheresMyMoney.Maui.Core/Repository.cs
Normal file
253
WheresMyMoney.Maui.Core/Repository.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
using Microsoft.Data.Sqlite;
|
||||
|
||||
namespace WheresMyMoney.Maui.Core;
|
||||
|
||||
public class Repository
|
||||
{
|
||||
private static Repository? _instance;
|
||||
|
||||
public static Repository Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance is not null) return _instance;
|
||||
string dbPath = Path.Combine(FileSystem.AppDataDirectory, "wheresmymoney.db");
|
||||
_instance = new Repository(new SqliteConnection($"Data Source={dbPath};"));
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly SqliteConnection _connection;
|
||||
|
||||
private Repository(SqliteConnection connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_connection.Open();
|
||||
CreateTables();
|
||||
}
|
||||
|
||||
~Repository()
|
||||
{
|
||||
_connection.Close();
|
||||
}
|
||||
|
||||
public void CreateTables()
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
CREATE TABLE IF NOT EXISTS planned_payment (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
amount INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
date_start TEXT NOT NULL,
|
||||
date_end TEXT NULL,
|
||||
is_subscription BOOLEAN NOT NULL,
|
||||
reoccurences INTEGER NULL,
|
||||
reoccurence_type INTEGER NULL CHECK(CASE reoccurences IS NOT NULL AND reoccurence_type IS NOT NULL WHEN TRUE THEN is_subscription ELSE NOT is_subscription END)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS balance (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
amount INTEGER NOT NULL,
|
||||
date TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
payday TINYINT NOT NULL CHECK(payday BETWEEN 1 AND 31)
|
||||
);
|
||||
""";
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
public void InsertPlannedPayment(decimal amount, string name, DateTime dateStart, DateTime? dateEnd,
|
||||
bool isSubscription,
|
||||
int? reoccurences, ReoccurenceType? reoccurenceType)
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
INSERT INTO planned_payment (amount, name, date_start, date_end, is_subscription, reoccurences, reoccurence_type)
|
||||
VALUES (@amount, @name, @date_start, @date_end, @is_subscription, @reoccurences, @reoccurence_type);
|
||||
""";
|
||||
|
||||
command.Parameters.AddWithValue("@amount", (int)(amount * 100));
|
||||
command.Parameters.AddWithValue("@name", name);
|
||||
command.Parameters.AddWithValue("@date_start", dateStart);
|
||||
command.Parameters.AddWithValue("@date_end", dateEnd.HasValue ? dateEnd.Value : DBNull.Value);
|
||||
command.Parameters.AddWithValue("@is_subscription", isSubscription);
|
||||
command.Parameters.AddWithValue("@reoccurences", reoccurences is null ? DBNull.Value : reoccurences.Value);
|
||||
command.Parameters.AddWithValue("@reoccurence_type",
|
||||
reoccurenceType is null ? DBNull.Value : (int)reoccurenceType.Value);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
DataChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void InsertBalance(decimal amount, DateTime date)
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
INSERT INTO balance (amount, date)
|
||||
VALUES (@amount, @date);
|
||||
""";
|
||||
|
||||
command.Parameters.AddWithValue("@amount", (int)(amount * 100));
|
||||
command.Parameters.AddWithValue("@date", date);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
DataChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
|
||||
public decimal GetNewestBalance()
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = @"SELECT amount FROM balance ORDER BY date DESC LIMIT 1;";
|
||||
var result = command.ExecuteScalar();
|
||||
return result != null ? Convert.ToDecimal(result) / 100m : 0m;
|
||||
}
|
||||
|
||||
public List<Balance> GetAllBalances()
|
||||
{
|
||||
var balances = new List<Balance>();
|
||||
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = @"SELECT id, amount, date FROM balance ORDER BY date;";
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
balances.Add(new Balance(
|
||||
reader.GetInt32(0),
|
||||
reader.GetDecimal(1) / 100m,
|
||||
reader.GetDateTime(2)
|
||||
));
|
||||
}
|
||||
|
||||
return balances;
|
||||
}
|
||||
|
||||
public byte GetPayday()
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = "SELECT payday FROM settings LIMIT 1;";
|
||||
var result = command.ExecuteScalar();
|
||||
return result != null ? Convert.ToByte(result) : (byte)10;
|
||||
}
|
||||
|
||||
public void ReplacePayday(byte newPayday)
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
INSERT INTO settings (id, payday)
|
||||
VALUES (1, @payday)
|
||||
ON CONFLICT(id) DO UPDATE SET payday = excluded.payday;
|
||||
""";
|
||||
|
||||
command.Parameters.AddWithValue("@payday", newPayday);
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
DataChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public List<PlannedPayment> GetAllPlannedPayments()
|
||||
{
|
||||
var payments = new List<PlannedPayment>();
|
||||
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText =
|
||||
"SELECT id, amount, name, date_start, date_end, is_subscription, reoccurences, reoccurence_type FROM planned_payment ORDER BY DATETIME(date_start);";
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
payments.Add(new PlannedPayment(
|
||||
reader.GetInt32(0),
|
||||
reader.GetDecimal(1) / 100m,
|
||||
reader.GetString(2),
|
||||
reader.GetDateTime(3),
|
||||
reader.IsDBNull(4) ? null : reader.GetDateTime(4),
|
||||
reader.GetBoolean(5),
|
||||
reader.IsDBNull(6) ? null : reader.GetInt32(6),
|
||||
reader.IsDBNull(7) ? null : (ReoccurenceType)reader.GetInt32(7)
|
||||
));
|
||||
}
|
||||
|
||||
return payments;
|
||||
}
|
||||
|
||||
public List<PlannedPayment> GetFilteredPlannedPayments()
|
||||
{
|
||||
var filteredPayments = new List<PlannedPayment>();
|
||||
var now = DateTime.Now;
|
||||
|
||||
var nearestPayday = GetNearestPayday(now);
|
||||
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
SELECT id, amount, name, date_start, date_end, is_subscription, reoccurences, reoccurence_type
|
||||
FROM planned_payment
|
||||
WHERE (
|
||||
(is_subscription = 0 AND DATETIME(date_start) >= DATETIME(@currentDate) AND DATETIME(date_start) <= DATETIME(@nearestPayday)) OR
|
||||
(is_subscription = 1 AND (date_end IS NULL OR DATETIME(date_end) >= DATETIME(@currentDate)) AND DATETIME(date_start) <= DATETIME(@nearestPayday))
|
||||
)
|
||||
ORDER BY DATETIME(date_start);
|
||||
""";
|
||||
|
||||
command.Parameters.AddWithValue("@currentDate", now);
|
||||
command.Parameters.AddWithValue("@nearestPayday", nearestPayday);
|
||||
|
||||
using var reader = command.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
filteredPayments.Add(new PlannedPayment(
|
||||
reader.GetInt32(0),
|
||||
reader.GetDecimal(1) / 100m,
|
||||
reader.GetString(2),
|
||||
reader.GetDateTime(3),
|
||||
reader.IsDBNull(4) ? null : reader.GetDateTime(4),
|
||||
reader.GetBoolean(5),
|
||||
reader.IsDBNull(6) ? null : reader.GetInt32(6),
|
||||
reader.IsDBNull(7) ? null : (ReoccurenceType)reader.GetInt32(7)
|
||||
));
|
||||
}
|
||||
|
||||
return filteredPayments;
|
||||
}
|
||||
|
||||
public DateTime GetNearestPayday(DateTime now)
|
||||
{
|
||||
var payday = GetPayday();
|
||||
var nearestPayday =
|
||||
new DateOnly(now.Year, now.Month,
|
||||
payday > DateTime.DaysInMonth(now.Year, now.Month)
|
||||
? DateTime.DaysInMonth(now.Year, now.Month)
|
||||
: payday)
|
||||
.ToDateTime(TimeOnly.MinValue);
|
||||
if (now.Day <= payday) return nearestPayday;
|
||||
var newDate = new DateOnly(now.Year, now.Month, DateTime.DaysInMonth(now.Year, now.Month)).AddDays(1);
|
||||
nearestPayday =
|
||||
new DateOnly(newDate.Year, newDate.Month,
|
||||
payday > DateTime.DaysInMonth(newDate.Year, newDate.Month)
|
||||
? DateTime.DaysInMonth(newDate.Year, newDate.Month)
|
||||
: payday).ToDateTime(TimeOnly.MinValue);
|
||||
|
||||
return nearestPayday;
|
||||
}
|
||||
|
||||
public void RemovePlannedPayment(int id)
|
||||
{
|
||||
using var command = _connection.CreateCommand();
|
||||
command.CommandText = """
|
||||
DELETE FROM planned_payment
|
||||
WHERE id = @id;
|
||||
""";
|
||||
command.Parameters.AddWithValue("@id", id);
|
||||
command.ExecuteNonQuery();
|
||||
DataChanged(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler DataChanged;
|
||||
}
|
||||
26
WheresMyMoney.Maui.Core/WheresMyMoney.Maui.Core.csproj
Normal file
26
WheresMyMoney.Maui.Core/WheresMyMoney.Maui.Core.csproj
Normal file
@@ -0,0 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||
<UseMaui>true</UseMaui>
|
||||
<SingleProject>true</SingleProject>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user