189 lines
6.8 KiB
C++
189 lines
6.8 KiB
C++
//
|
|
// Created by quentin on 1/18/23.
|
|
//
|
|
|
|
#include "database.h"
|
|
#include <sqlite3.h>
|
|
#include <stdexcept>
|
|
|
|
Budget::Models::Account Database::getAccount(const std::string &accountName, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "SELECT * FROM account WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed to prepare accountName exist query.");
|
|
|
|
sqlite3_bind_text(stmt, 1, accountName.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
if (rc == SQLITE_ROW) {
|
|
long long id = sqlite3_column_int64(stmt, 0);
|
|
char* name = (char*)(sqlite3_column_text(stmt, 1));
|
|
char* description = (char*)sqlite3_column_text(stmt, 2);
|
|
if (sqlite3_column_type(stmt, 3) == SQLITE_NULL || sqlite3_column_type(stmt, 4) == SQLITE_NULL) {
|
|
sqlite3_finalize(stmt);
|
|
return {id, name, description, nullptr};
|
|
}
|
|
auto* money = new Budget::Models::Money(sqlite3_column_int64(stmt, 3), sqlite3_column_int64(stmt, 4));
|
|
sqlite3_finalize(stmt);
|
|
return {id, name, description, money};
|
|
} else if (rc == SQLITE_DONE) {
|
|
sqlite3_finalize(stmt);
|
|
throw std::runtime_error("Account " + accountName + " does not exist");
|
|
} else {
|
|
sqlite3_finalize(stmt);
|
|
throw std::runtime_error("Failed to step accountName existing statement.");
|
|
}
|
|
}
|
|
|
|
void Database::deleteAccount(Budget::Models::Account *account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "DELETE FROM account WHERE id = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed to delete accountName " + account->name);
|
|
|
|
sqlite3_bind_int64(stmt, 1, account->id);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to delete accountName " + account->name);
|
|
}
|
|
|
|
void Database::cacheAccountValue(Budget::Models::Account *account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "SELECT SUM(dollars * 100 + cents) / 100 as nDollars, SUM(dollars * 100 + cents) % 100 as nCents FROM("
|
|
"SELECT SUM(dollars * 100 + cents) / 100 AS dollars, SUM(dollars * 100 + cents) % 100 AS cents FROM earning WHERE accountId = ? "
|
|
"UNION ALL "
|
|
"SELECT SUM(dollars * 100 + cents) / 100 * -1 AS dollars, SUM(dollars * 100 + cents) % 100 * -1 AS cents FROM payment WHERE accountId = ?);", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing get cashedValue " + account->name);
|
|
|
|
sqlite3_bind_int64(stmt, 1, account->id);
|
|
sqlite3_bind_int64(stmt, 2, account->id);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
long long int dollars;
|
|
long long int cents;
|
|
if (rc == SQLITE_ROW) {
|
|
dollars = sqlite3_column_int64(stmt, 0);
|
|
cents = sqlite3_column_int64(stmt, 1);
|
|
}
|
|
else if (rc == SQLITE_DONE) {
|
|
dollars = 0;
|
|
cents = 0;
|
|
}
|
|
else
|
|
throw std::runtime_error("Failed get cashedValue " + account->name);
|
|
|
|
sqlite3_reset(stmt);
|
|
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedDollars = ?, cachedCents = ? WHERE id = ?", -1, &stmt, nullptr);
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing set cashedValue " + account->name);
|
|
|
|
sqlite3_bind_int64(stmt, 1, dollars);
|
|
sqlite3_bind_int64(stmt, 2, cents);
|
|
sqlite3_bind_int64(stmt, 3, account->id);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
if (rc == SQLITE_DONE) {
|
|
account->cachedValue = new Budget::Models::Money(dollars, cents);
|
|
return;
|
|
}
|
|
throw std::runtime_error("Failed to set cashedValue for " + std::to_string(account->id));
|
|
}
|
|
|
|
void Database::accountDescription(Budget::Models::Account *account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "UPDATE account SET description = ? WHERE name = ?", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing accountDescription statement");
|
|
|
|
sqlite3_bind_text(stmt, 1, account->description.c_str(), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 2, account->name.c_str(), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to set description on " + account->name);
|
|
}
|
|
|
|
long long int Database::createAccount(Budget::OptHandlers::CreateOperation::Flags *flags, std::string &account, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name, description) VALUES (?, ?)", -1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing createAccount statement");
|
|
|
|
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 2, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
|
|
if (rc != SQLITE_DONE) {
|
|
throw std::runtime_error("Failed to create accountName " + account);
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
}
|
|
|
|
long long int Database::earn(Budget::Models::Account *account, Budget::OptHandlers::EarnOperation::Flags *flags, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO earning (dollars, cents, description, receipt, accountId, date) VALUES "
|
|
"(?, ?, ?, ?, ?, ?);",
|
|
-1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing earn statement");
|
|
|
|
sqlite3_bind_int64(stmt, 1, flags->dollars);
|
|
sqlite3_bind_int64(stmt, 2, flags->cents);
|
|
sqlite3_bind_text(stmt, 3, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 4, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(stmt, 5, account->id);
|
|
sqlite3_bind_int64(stmt, 6, flags->date);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create earning");
|
|
cacheAccountValue(account, db);
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
}
|
|
|
|
long long int Database::pay(Budget::Models::Account *account, Budget::OptHandlers::PaymentOperation::Flags *flags, sqlite3 *db) {
|
|
sqlite3_stmt *stmt;
|
|
int rc = sqlite3_prepare_v2(db, "INSERT INTO payment (dollars, cents, description, receipt, accountId, date) VALUES "
|
|
"(?, ?, ?, ?, ?, ?);",
|
|
-1, &stmt, nullptr);
|
|
|
|
if (rc != SQLITE_OK)
|
|
throw std::runtime_error("Failed preparing pay statement");
|
|
|
|
sqlite3_bind_int64(stmt, 1, flags->dollars);
|
|
sqlite3_bind_int64(stmt, 2, flags->cents);
|
|
sqlite3_bind_text(stmt, 3, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_text(stmt, 4, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(stmt, 5, account->id);
|
|
sqlite3_bind_int64(stmt, 6, flags->date);
|
|
|
|
rc = sqlite3_step(stmt);
|
|
sqlite3_finalize(stmt);
|
|
|
|
if (rc != SQLITE_DONE)
|
|
throw std::runtime_error("Failed to create payment");
|
|
cacheAccountValue(account, db);
|
|
|
|
return sqlite3_last_insert_rowid(db);
|
|
} |