153 lines
5.4 KiB
D
153 lines
5.4 KiB
D
module analytics.data_impl_sqlite;
|
|
|
|
import d2sqlite3;
|
|
import std.array;
|
|
import std.datetime;
|
|
|
|
import util.money;
|
|
import util.data;
|
|
import util.sqlite;
|
|
import account.model : AccountJournalEntryType, AccountType;
|
|
import analytics.modules.balances;
|
|
import analytics.data;
|
|
|
|
class SqliteAnalyticsRepository : AnalyticsRepository {
|
|
private Database db;
|
|
this(Database db) {
|
|
this.db = db;
|
|
}
|
|
|
|
JournalEntryStub[] getJournalEntries(
|
|
in Currency currency,
|
|
in TimeRange timeRange
|
|
) {
|
|
QueryBuilder qb = QueryBuilder("account_journal_entry je")
|
|
.select("je.timestamp,je.account_id,je.amount,je.type,account.type")
|
|
.join("LEFT JOIN account ON account.id = je.account_id")
|
|
.where("UPPER(je.currency) = ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, currency.codeString);
|
|
});
|
|
if (timeRange.fromTime) {
|
|
qb.where("je.timestamp >= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
if (timeRange.toTime) {
|
|
qb.where("je.timestamp <= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
string query = qb.build() ~ " ORDER BY je.timestamp";
|
|
Statement stmt = db.prepare(query);
|
|
qb.applyArgBindings(stmt);
|
|
ResultRange result = stmt.execute();
|
|
Appender!(JournalEntryStub[]) app;
|
|
foreach (row; result) {
|
|
auto journalEntryTypeStr = row.peek!(string, PeekMode.slice)(3);
|
|
AccountJournalEntryType type;
|
|
if (journalEntryTypeStr == AccountJournalEntryType.CREDIT) {
|
|
type = AccountJournalEntryType.CREDIT;
|
|
} else {
|
|
type = AccountJournalEntryType.DEBIT;
|
|
}
|
|
app ~= JournalEntryStub(
|
|
SysTime.fromISOExtString(row.peek!(string, PeekMode.slice)(0)),
|
|
row.peek!ulong(1),
|
|
AccountType.fromId(row.peek!(string, PeekMode.slice)(4)),
|
|
row.peek!ulong(2),
|
|
type
|
|
);
|
|
}
|
|
return app[];
|
|
}
|
|
|
|
BalanceRecordStub[] getBalanceRecords(
|
|
in Currency currency,
|
|
in TimeRange timeRange
|
|
) {
|
|
QueryBuilder qb = QueryBuilder("account_value_record")
|
|
.select("timestamp,account_id,value,type")
|
|
.where("UPPER(currency) = ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, currency.codeString);
|
|
})
|
|
.where("UPPER(type) = 'BALANCE'");
|
|
if (timeRange.fromTime) {
|
|
qb.where("timestamp >= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
if (timeRange.toTime) {
|
|
qb.where("timestamp <= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
string query = qb.build() ~ " ORDER BY timestamp";
|
|
Statement stmt = db.prepare(query);
|
|
qb.applyArgBindings(stmt);
|
|
ResultRange result = stmt.execute();
|
|
Appender!(BalanceRecordStub[]) app;
|
|
foreach (row; result) {
|
|
app ~= BalanceRecordStub(
|
|
SysTime.fromISOExtString(row.peek!(string, PeekMode.slice)(0)),
|
|
row.peek!ulong(1),
|
|
row.peek!long(2)
|
|
);
|
|
}
|
|
return app[];
|
|
}
|
|
|
|
CategorySpendData[] getCategorySpendData(
|
|
in Currency currency,
|
|
in TimeRange timeRange
|
|
) {
|
|
QueryBuilder qb = QueryBuilder("transaction_category c")
|
|
.select("c.id,c.name,c.color,c.parent_id")
|
|
.select(`
|
|
(
|
|
SUM(CASE WHEN je.type LIKE 'DEBIT' THEN je.amount ELSE 0 END)
|
|
- SUM(CASE WHEN je.type LIKE 'CREDIT' THEN je.amount ELSE 0 END)
|
|
)
|
|
`)
|
|
.join("LEFT JOIN \"transaction\" t ON t.category_id = c.id")
|
|
.join("LEFT JOIN account_journal_entry je ON je.transaction_id = t.id");
|
|
qb.where("t.internal_transfer = false");
|
|
qb.where("t.currency = ?");
|
|
qb.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, currency.codeString);
|
|
});
|
|
if (timeRange.fromTime) {
|
|
qb.where("t.timestamp >= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
if (timeRange.toTime) {
|
|
qb.where("t.timestamp <= ?")
|
|
.withArgBinding((ref stmt, ref idx) {
|
|
stmt.bind(idx++, timeRange.fromTime.value);
|
|
});
|
|
}
|
|
string query = qb.build() ~ " GROUP BY c.id ORDER BY c.id";
|
|
Statement stmt = db.prepare(query);
|
|
qb.applyArgBindings(stmt);
|
|
ResultRange result = stmt.execute();
|
|
Appender!(CategorySpendData[]) app;
|
|
foreach (row; result) {
|
|
import std.typecons : Nullable;
|
|
app ~= CategorySpendData(
|
|
row.peek!ulong(0),
|
|
row.peek!string(1),
|
|
row.peek!string(2),
|
|
toOptional(row.peek!(Nullable!ulong)(3)),
|
|
row.peek!long(4)
|
|
);
|
|
}
|
|
return app[];
|
|
}
|
|
} |