253 lines
9.6 KiB
C#
253 lines
9.6 KiB
C#
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;
|
|
} |