Database now uses objects to represent Accounts Earnings and Payments
This commit is contained in:
parent
0a19042c73
commit
7e19be33e9
10
.idea/dataSources.xml
generated
10
.idea/dataSources.xml
generated
@ -11,5 +11,15 @@
|
||||
<property name="foreign_keys" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
<data-source source="LOCAL" name="budgetDebug.sqlite" uuid="be7a7e56-dbe5-4daa-aaf6-89095191fe75">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/.local/share/budget/budgetDebug.sqlite</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
<driver-properties>
|
||||
<property name="foreign_keys" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -10,7 +10,7 @@ set(SOURCES
|
||||
src/optHandlers/accountOperation.cpp
|
||||
src/optHandlers/createOperation.cpp
|
||||
src/optHandlers/earnOperation.cpp
|
||||
src/optHandlers/PaymentOperation.cpp
|
||||
src/optHandlers/paymentOperation.cpp
|
||||
src/database.cpp
|
||||
src/utilities.cpp)
|
||||
|
||||
@ -20,13 +20,17 @@ set(HEADERS
|
||||
src/optHandlers/accountOperation.h
|
||||
src/optHandlers/createOperation.h
|
||||
src/optHandlers/earnOperation.h
|
||||
src/optHandlers/PaymentOperation.h
|
||||
src/optHandlers/paymentOperation.h
|
||||
src/database.h
|
||||
src/exceptions/helpRequested.h
|
||||
src/exceptions/badValue.h
|
||||
src/utilities.h
|
||||
src/sqliteDb.h
|
||||
src/main.h)
|
||||
src/main.h
|
||||
src/models/account.h
|
||||
src/models/transaction.h
|
||||
src/models/earning.h
|
||||
src/models/payment.h)
|
||||
|
||||
|
||||
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
|
||||
|
@ -43,6 +43,6 @@ budget -cAcct -eAcct -v10.00 -r"./receipt.pdf" -pAcct -v5.50 -r"./payment.pdf"
|
||||
````
|
||||
|
||||
Does the following in order:
|
||||
Creates an account named Acct with no description.
|
||||
Creates an accountName named Acct with no description.
|
||||
Earns 10.00 to it with a receipt.
|
||||
Pays 5.50 to it with a receipt.
|
||||
|
185
src/database.cpp
185
src/database.cpp
@ -6,72 +6,52 @@
|
||||
#include <sqlite3.h>
|
||||
#include <stdexcept>
|
||||
|
||||
bool Database::doesAccountExist(const std::string &account, sqlite3 *db) {
|
||||
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 account exist query.");
|
||||
throw std::runtime_error("Failed to prepare accountName exist query.");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 1, accountName.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
return true;
|
||||
Budget::Models::Account account(
|
||||
sqlite3_column_int64(stmt, 0),
|
||||
(char*)(sqlite3_column_text(stmt, 1)),
|
||||
(sqlite3_column_type(stmt,2) == SQLITE_NULL) ? "" : (char*)(sqlite3_column_text(stmt, 2)),
|
||||
(sqlite3_column_type(stmt, 3) == SQLITE_NULL) ? nullptr : new long double(sqlite3_column_double(stmt, 4))
|
||||
);
|
||||
sqlite3_finalize(stmt);
|
||||
return account;
|
||||
} else if (rc == SQLITE_DONE) {
|
||||
return false;
|
||||
sqlite3_finalize(stmt);
|
||||
throw std::runtime_error("Account " + accountName + " does not exist");
|
||||
} else {
|
||||
throw std::runtime_error("Failed to step account existing statement.");
|
||||
sqlite3_finalize(stmt);
|
||||
throw std::runtime_error("Failed to step accountName existing statement.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Database::deleteAccount(const std::string &account, sqlite3 *db) {
|
||||
void Database::deleteAccount(Budget::Models::Account *account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "DELETE FROM account WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed to delete account " + account);
|
||||
throw std::runtime_error("Failed to delete accountName " + account->name);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 1, account->name.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to delete account " + account);
|
||||
throw std::runtime_error("Failed to delete accountName " + account->name);
|
||||
}
|
||||
|
||||
double Database::getValue(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT id, cachedValue FROM account WHERE name = ?", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing cashedValue " + account);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
double value;
|
||||
if (sqlite3_column_type(stmt, 1) == SQLITE_NULL) {
|
||||
value = cacheAccountValue(sqlite3_column_int64(stmt, 0), db);
|
||||
sqlite3_finalize(stmt);
|
||||
return value;
|
||||
}
|
||||
value = sqlite3_column_double(stmt, 1);
|
||||
sqlite3_finalize(stmt);
|
||||
return value;
|
||||
} else if (rc == SQLITE_DONE)
|
||||
throw std::runtime_error("Account doesnt exist? Shouldn't be possible in this call.");
|
||||
else
|
||||
throw std::runtime_error("Failed to step getValue statement.");
|
||||
}
|
||||
|
||||
double Database::cacheAccountValue(long long accountId, sqlite3 *db) {
|
||||
void Database::cacheAccountValue(Budget::Models::Account *account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT SUM(value) FROM ("
|
||||
"SELECT value * -1 as value FROM payment WHERE accountId = ? "
|
||||
@ -79,10 +59,10 @@ double Database::cacheAccountValue(long long accountId, sqlite3 *db) {
|
||||
"SELECT value FROM earning WHERE accountId = ?);", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing get cashedValue " + std::to_string(accountId));
|
||||
throw std::runtime_error("Failed preparing get cashedValue " + account->name);
|
||||
|
||||
sqlite3_bind_int64(stmt, 1, accountId);
|
||||
sqlite3_bind_int64(stmt, 2, accountId);
|
||||
sqlite3_bind_int64(stmt, 1, account->id);
|
||||
sqlite3_bind_int64(stmt, 2, account->id);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
@ -92,79 +72,42 @@ double Database::cacheAccountValue(long long accountId, sqlite3 *db) {
|
||||
else if (rc == SQLITE_DONE)
|
||||
value = 0;
|
||||
else
|
||||
throw std::runtime_error("Failed get cashedValue " + std::to_string(accountId));
|
||||
throw std::runtime_error("Failed get cashedValue " + account->name);
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedValue = ? WHERE id = ?", -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing set cashedValue " + std::to_string(accountId));
|
||||
throw std::runtime_error("Failed preparing set cashedValue " + account->name);
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_int64(stmt, 2, accountId);
|
||||
sqlite3_bind_int64(stmt, 2, account->id);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_DONE)
|
||||
return value;
|
||||
throw std::runtime_error("Failed to set cashedValue for " + std::to_string(accountId));
|
||||
if (rc == SQLITE_DONE) {
|
||||
account->cachedValue = new long double(value);
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error("Failed to set cashedValue for " + std::to_string(account->id));
|
||||
}
|
||||
|
||||
double Database::cacheAccountValue(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "SELECT SUM(value) FROM ("
|
||||
"SELECT value * -1 as value FROM payment WHERE accountId = "
|
||||
"(SELECT id FROM account where name = ?) "
|
||||
"UNION ALL "
|
||||
"SELECT value FROM earning WHERE accountId = "
|
||||
"(SELECT id FROM account where name = ?));", -1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing get cashedValue " + account);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
|
||||
double value;
|
||||
if (rc == SQLITE_ROW)
|
||||
value = sqlite3_column_double(stmt, 0);
|
||||
else if (rc == SQLITE_DONE)
|
||||
value = 0;
|
||||
else
|
||||
throw std::runtime_error("Failed get cashedValue " + account);
|
||||
|
||||
sqlite3_reset(stmt);
|
||||
rc = sqlite3_prepare_v2(db, "UPDATE account SET cachedValue = ? WHERE name = ?", -1, &stmt, nullptr);
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing set cashedValue " + account);
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
if (rc == SQLITE_DONE)
|
||||
return value;
|
||||
throw std::runtime_error("Failed to set cashedValue for " + account);
|
||||
}
|
||||
|
||||
void Database::accountDescription(const std::string &account, const std::string &description, sqlite3 *db) {
|
||||
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, description.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
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);
|
||||
throw std::runtime_error("Failed to set description on " + account->name);
|
||||
}
|
||||
|
||||
void Database::createAccount(const std::string &account, const std::string &description, sqlite3 *db) {
|
||||
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);
|
||||
|
||||
@ -172,46 +115,33 @@ void Database::createAccount(const std::string &account, const std::string &desc
|
||||
throw std::runtime_error("Failed preparing createAccount statement");
|
||||
|
||||
sqlite3_bind_text(stmt, 1, account.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 2, description.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);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create account " + account);
|
||||
return sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
|
||||
void Database::createAccount(const std::string &account, sqlite3 *db) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc = sqlite3_prepare_v2(db, "INSERT INTO account (name) 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);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE)
|
||||
throw std::runtime_error("Failed to create account " + account);
|
||||
}
|
||||
|
||||
long long int Database::earn(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long date, sqlite3 *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 (value, description, receipt, accountId, date) VALUES "
|
||||
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);",
|
||||
"(?, ?, ?, ?, ?);",
|
||||
-1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing earn statement");
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 5, date);
|
||||
sqlite3_bind_double(stmt, 1, flags->value);
|
||||
sqlite3_bind_text(stmt, 2, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 4, account->id);
|
||||
sqlite3_bind_int64(stmt, 5, flags->date);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
@ -223,21 +153,20 @@ long long int Database::earn(const std::string &account, long double &value, std
|
||||
return sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
|
||||
long long int Database::pay(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long date, sqlite3 *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 (value, description, receipt, accountId, date) VALUES "
|
||||
"(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);",
|
||||
"(?, ?, ?, ?, ?);",
|
||||
-1, &stmt, nullptr);
|
||||
|
||||
if (rc != SQLITE_OK)
|
||||
throw std::runtime_error("Failed preparing pay statement");
|
||||
|
||||
sqlite3_bind_double(stmt, 1, value);
|
||||
sqlite3_bind_text(stmt, 2, (description.empty() ? nullptr : description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (receipt.empty() ? nullptr : receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 4, (account.empty() ? nullptr : account.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 5, date);
|
||||
sqlite3_bind_double(stmt, 1, flags->value);
|
||||
sqlite3_bind_text(stmt, 2, (flags->description.empty() ? nullptr : flags->description.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(stmt, 3, (flags->receipt.empty() ? nullptr : flags->receipt.c_str()), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(stmt, 4, account->id);
|
||||
sqlite3_bind_int64(stmt, 5, flags->date);
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
@ -5,6 +5,10 @@
|
||||
#ifndef BUDGET_DATABASE_H
|
||||
#define BUDGET_DATABASE_H
|
||||
|
||||
#include "models/account.h"
|
||||
#include "optHandlers/createOperation.h"
|
||||
#include "optHandlers/earnOperation.h"
|
||||
#include "optHandlers/paymentOperation.h"
|
||||
|
||||
#include <string>
|
||||
#include <sqlite3.h>
|
||||
@ -12,101 +16,67 @@
|
||||
class Database {
|
||||
public:
|
||||
/**
|
||||
* @brief Checks if an account exists in the database
|
||||
* @brief Checks if an accountName exists in the database
|
||||
*
|
||||
* @param account The name of the account to check for
|
||||
* @param accountName The name of the accountName to check for
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return True if the account exists, false otherwise
|
||||
* @return True if the accountName exists, false otherwise
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static bool doesAccountExist(const std::string &account, sqlite3 *db);
|
||||
static Budget::Models::Account getAccount(const std::string &accountName, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Deletes an account from the database
|
||||
* @brief Deletes an accountName from the database
|
||||
*
|
||||
* @param account The name of the account to delete
|
||||
* @param account The name of the accountName to delete
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return True if the account was successfully deleted, false otherwise
|
||||
* @return True if the accountName was successfully deleted, false otherwise
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static void deleteAccount(const std::string &account, sqlite3 *db);
|
||||
static void deleteAccount(Budget::Models::Account *account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the cached value of an account from the database
|
||||
* @brief Caches the value of an accountName by calculating the sum of all payments and earnings
|
||||
*
|
||||
* @param account The name of the account to retrieve the value of
|
||||
* @param accountId The ID of the accountName to cache the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails or the account doesn't exist
|
||||
*/
|
||||
static double getValue(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Caches the value of an account by calculating the sum of all payments and earnings
|
||||
*
|
||||
* @param accountId The ID of the account to cache the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
* @return The cached value of the accountName
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static double cacheAccountValue(long long accountId, sqlite3 *db);
|
||||
static void cacheAccountValue(Budget::Models::Account *account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Caches the value of an account by calculating the sum of all payments and earnings
|
||||
* @brief Sets a new description for an accountName
|
||||
*
|
||||
* @param account The name of the account to cache the value of
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @return The cached value of the account
|
||||
*
|
||||
* @throws std::runtime_error If the database query fails
|
||||
*/
|
||||
static double cacheAccountValue(const std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Sets a new description for an account
|
||||
*
|
||||
* @param account The account name
|
||||
* @param account The accountName name
|
||||
* @param description The new description
|
||||
* @param db The database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the statement fails to prepare or the step fails to execute
|
||||
*/
|
||||
static void accountDescription(const std::string &account, const std::string &description, sqlite3 *db);
|
||||
static void accountDescription(Budget::Models::Account *account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Creates a new account in the database with the given name and description
|
||||
* @brief Creates a new accountName in the database with the given name and description
|
||||
*
|
||||
* @param account The name of the account to create
|
||||
* @param description The description of the account to create
|
||||
* @param account The name of the accountName to create
|
||||
* @param description The description of the accountName to create
|
||||
* @param db The sqlite3 database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the account could not be created in the database
|
||||
* @throws std::runtime_error If the accountName could not be created in the database
|
||||
*/
|
||||
static void createAccount(const std::string &account, const std::string &description, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief Creates a new account in the database with the given name
|
||||
*
|
||||
* @param account The name of the account to create
|
||||
* @param db The sqlite3 database connection to use
|
||||
*
|
||||
* @throws std::runtime_error If the account could not be created in the database
|
||||
*/
|
||||
static void createAccount(const std::string &account, sqlite3 *db);
|
||||
static long long int createAccount(Budget::OptHandlers::CreateOperation::Flags *flags, std::string &account, sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief The function records an earning in the database
|
||||
*
|
||||
* @param account The name of the account to record the earning in
|
||||
* @param account The name of the accountName to record the earning in
|
||||
* @param value The value of the earning
|
||||
* @param description A description of the earning, can be an empty string
|
||||
* @param receipt A receipt of the earning, can be an empty string
|
||||
@ -116,18 +86,17 @@ public:
|
||||
* @throws std::runtime_error If the earning could not be created in the database
|
||||
*
|
||||
* This function records an earning in the database by inserting a new row in the 'earning' table with the provided information.
|
||||
* The accountId is retrieved from the 'account' table using the provided account name.
|
||||
* The accountId is retrieved from the 'accountName' table using the provided accountName name.
|
||||
* If the query fails to prepare or execute, the function throws a runtime_error.
|
||||
*/
|
||||
static long long int
|
||||
earn(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long int date,
|
||||
earn(Budget::Models::Account *account, Budget::OptHandlers::EarnOperation::Flags *flags,
|
||||
sqlite3 *db);
|
||||
|
||||
/**
|
||||
* @brief The function records an payment in the database
|
||||
*
|
||||
* @param account The name of the account to record the payment in
|
||||
* @param account The name of the accountName to record the payment in
|
||||
* @param value The value of the payment
|
||||
* @param description A description of the payment, can be an empty string
|
||||
* @param receipt A receipt of the payment, can be an empty string
|
||||
@ -137,13 +106,11 @@ public:
|
||||
* @throws std::runtime_error If the payment could not be created in the database
|
||||
*
|
||||
* This function records an payment in the database by inserting a new row in the 'payment' table with the provided information.
|
||||
* The accountId is retrieved from the 'account' table using the provided account name.
|
||||
* The accountId is retrieved from the 'accountName' table using the provided accountName name.
|
||||
* If the query fails to prepare or execute, the function throws a runtime_error.
|
||||
*/
|
||||
static long long int
|
||||
pay(const std::string &account, long double &value, std::string &description, std::string &receipt,
|
||||
long long int date,
|
||||
sqlite3 *db);
|
||||
pay(Budget::Models::Account *account, Budget::OptHandlers::PaymentOperation::Flags *flags, sqlite3 *db);
|
||||
};
|
||||
|
||||
|
||||
|
@ -16,11 +16,11 @@
|
||||
|
||||
using namespace Budget;
|
||||
|
||||
const char* createTables = "CREATE TABLE IF NOT EXISTS account (id INTEGER CONSTRAINT account_pk PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, cachedValue DOUBLE);"
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS account_name_uindex ON account (name);"
|
||||
"CREATE TABLE IF NOT EXISTS earning (id INTEGER CONSTRAINT earning_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES account ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
const char* createTables = "CREATE TABLE IF NOT EXISTS accountName (id INTEGER CONSTRAINT account_pk PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, cachedValue DOUBLE);"
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS account_name_uindex ON accountName (name);"
|
||||
"CREATE TABLE IF NOT EXISTS earning (id INTEGER CONSTRAINT earning_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES accountName ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
"CREATE INDEX IF NOT EXISTS earning_date_index ON earning (date DESC);"
|
||||
"CREATE TABLE IF NOT EXISTS payment (id INTEGER CONSTRAINT payment_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES account ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
"CREATE TABLE IF NOT EXISTS payment (id INTEGER CONSTRAINT payment_pk PRIMARY KEY AUTOINCREMENT, value DOUBLE NOT NULL, description TEXT, receipt TEXT, accountId INT NOT NULL REFERENCES accountName ON UPDATE CASCADE ON DELETE CASCADE, date INTEGER NOT NULL);"
|
||||
"CREATE INDEX IF NOT EXISTS payment_date_index ON payment (date DESC);";
|
||||
|
||||
void createRequiredFolders() {
|
||||
|
27
src/models/account.h
Normal file
27
src/models/account.h
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by quentin on 2/2/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_ACCOUNT_H
|
||||
#define BUDGET_ACCOUNT_H
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
namespace Budget::Models {
|
||||
class Account {
|
||||
public:
|
||||
Account(long long int id, std::string name, std::string description, long double* cachedValue)
|
||||
: id(id), name(std::move(name)), description(std::move(description)), cachedValue(new long double(3.3)) {}
|
||||
|
||||
// ~Account() {
|
||||
// delete cachedValue;
|
||||
// }
|
||||
public:
|
||||
long long id;
|
||||
std::string name;
|
||||
std::string description;
|
||||
long double* cachedValue;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BUDGET_ACCOUNT_H
|
14
src/models/earning.h
Normal file
14
src/models/earning.h
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Created by quentin on 2/2/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_EARNING_H
|
||||
#define BUDGET_EARNING_H
|
||||
|
||||
#include "transaction.h"
|
||||
|
||||
namespace Budget::Models {
|
||||
class Earning : Transaction {};
|
||||
}
|
||||
|
||||
#endif //BUDGET_EARNING_H
|
14
src/models/payment.h
Normal file
14
src/models/payment.h
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// Created by quentin on 2/2/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_EARNING_H
|
||||
#define BUDGET_EARNING_H
|
||||
|
||||
#include "transaction.h"
|
||||
|
||||
namespace Budget::Models {
|
||||
class Payment : Transaction {};
|
||||
}
|
||||
|
||||
#endif //BUDGET_EARNING_H
|
22
src/models/transaction.h
Normal file
22
src/models/transaction.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by quentin on 2/2/23.
|
||||
//
|
||||
|
||||
#ifndef BUDGET_TRANSACTION_H
|
||||
#define BUDGET_TRANSACTION_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Budget::Models {
|
||||
class Transaction {
|
||||
public:
|
||||
long long int id;
|
||||
long double value;
|
||||
std::string description;
|
||||
std::string receipt;
|
||||
long long int accountId;
|
||||
long long int date;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //BUDGET_TRANSACTION_H
|
@ -1,34 +0,0 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include "PaymentOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include "../main.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void PaymentOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
|
||||
long long id = Database::pay(account, flags.value, flags.description, flags.receipt, flags.date, db);
|
||||
|
||||
if (!flags.receipt.empty()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ifstream source(flags.receipt, std::ios::binary);
|
||||
std::ofstream dest(storageD + "receipts/payment/" + std::to_string(id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
}
|
||||
|
||||
PaymentOperation::PaymentOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
@ -13,23 +13,24 @@
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void AccountOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
Budget::Models::Account account = Database::getAccount(accountName, db);
|
||||
|
||||
if (flags.del) {
|
||||
if (flags.forceDel || Utilities::confirm("Are you sure you'd like to delete " + account)) {
|
||||
Database::deleteAccount(account, db);
|
||||
if (flags.forceDel || Utilities::confirm("Are you sure you'd like to delete " + accountName)) {
|
||||
Database::deleteAccount(&account, db);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags.value) {
|
||||
std::cout << Database::getValue(account, db) << std::endl;
|
||||
if (account.cachedValue == nullptr)
|
||||
Database::cacheAccountValue(&account, db);
|
||||
std::cout << *account.cachedValue << std::endl;
|
||||
}
|
||||
|
||||
if (!flags.description.empty()) {
|
||||
Database::accountDescription(account, flags.description, db);
|
||||
Database::accountDescription(&account, db);
|
||||
}
|
||||
}
|
||||
|
||||
AccountOperation::AccountOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
||||
AccountOperation::AccountOperation(sqlite3 *db, std::string account) : Operation(db), accountName(std::move(account)) {}
|
||||
|
@ -25,7 +25,7 @@ namespace Budget::OptHandlers {
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
std::string accountName;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,14 +9,7 @@
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void CreateOperation::commit() {
|
||||
if (Database::doesAccountExist(account, db)) {
|
||||
throw Budget::Exceptions::BadValue("Account already exists, cant create " + account);
|
||||
}
|
||||
if (flags.description.empty()) {
|
||||
Database::createAccount(account, db);
|
||||
return;
|
||||
}
|
||||
Database::createAccount(account, flags.description, db);
|
||||
long long int id = Database::createAccount(&flags, accountName, db);
|
||||
}
|
||||
|
||||
CreateOperation::CreateOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
||||
CreateOperation::CreateOperation(sqlite3 *db, std::string account) : Operation(db), accountName(std::move(account)) {}
|
||||
|
@ -21,7 +21,7 @@ namespace Budget::OptHandlers {
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
std::string accountName;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,24 +11,28 @@
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void EarnOperation::commit() {
|
||||
if (!Database::doesAccountExist(account, db))
|
||||
throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist");
|
||||
|
||||
long long id = Database::earn(account, flags.value, flags.description, flags.receipt, flags.date, db);
|
||||
Models::Account account = Database::getAccount(accountName, db);
|
||||
|
||||
if (!flags.receipt.empty()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ifstream source(flags.receipt, std::ios::binary);
|
||||
std::ofstream dest(storageD + "receipts/receipt/" + std::to_string(id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
if (source.good()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ofstream dest(storageD + "receipts/receipt/" + std::to_string(account.id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
else {
|
||||
throw Exceptions::BadValue("File " + flags.receipt + " does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
Database::earn(&account, &flags, db);
|
||||
}
|
||||
|
||||
EarnOperation::EarnOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {}
|
||||
EarnOperation::EarnOperation(sqlite3 *db, std::string account) : Operation(db), accountName(std::move(account)) {}
|
@ -26,7 +26,7 @@ namespace Budget::OptHandlers {
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
std::string accountName;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "accountOperation.h"
|
||||
#include "createOperation.h"
|
||||
#include "earnOperation.h"
|
||||
#include "PaymentOperation.h"
|
||||
#include "paymentOperation.h"
|
||||
#include "../exceptions/helpRequested.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include <iostream>
|
||||
@ -17,7 +17,7 @@ using namespace Budget::OptHandlers;
|
||||
MainOptHandler::MainOptHandler(const std::vector<char *> &_argv, sqlite3 *db) : argv(_argv), db(db) {
|
||||
struct option actionLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"accountName", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'}
|
||||
@ -56,7 +56,7 @@ MainOptHandler::MainOptHandler(const std::vector<char *> &_argv, sqlite3 *db) :
|
||||
void MainOptHandler::accountOptHandler(std::string account) {
|
||||
struct option accountLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"accountName", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
@ -77,7 +77,7 @@ void MainOptHandler::accountOptHandler(std::string account) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at account");
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at accountName");
|
||||
case 'a':
|
||||
case 'c':
|
||||
case 'e':
|
||||
@ -100,7 +100,7 @@ void MainOptHandler::accountOptHandler(std::string account) {
|
||||
break;
|
||||
case '?':
|
||||
help();
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at account, unknown argument.");
|
||||
throw Budget::Exceptions::HelpRequested("Help requested at accountName, unknown argument.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -111,7 +111,7 @@ void MainOptHandler::accountOptHandler(std::string account) {
|
||||
void MainOptHandler::createOptHandler(std::string account) {
|
||||
struct option createLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"accountName", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
@ -153,7 +153,7 @@ void MainOptHandler::createOptHandler(std::string account) {
|
||||
void MainOptHandler::earnOptHandler(std::string account) {
|
||||
struct option earnLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"accountName", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
@ -224,7 +224,7 @@ void MainOptHandler::earnOptHandler(std::string account) {
|
||||
void MainOptHandler::paymentOptHandler(std::string account) {
|
||||
struct option paymentLongOpts[] = {
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"account", required_argument, nullptr, 'a'},
|
||||
{"accountName", required_argument, nullptr, 'a'},
|
||||
{"create", required_argument, nullptr, 'c'},
|
||||
{"earn", required_argument, nullptr, 'e'},
|
||||
{"payment", required_argument, nullptr, 'p'},
|
||||
@ -298,17 +298,17 @@ void MainOptHandler::help() {
|
||||
" budget <action> [options] ...\n"
|
||||
"Actions:\n"
|
||||
" -h --help Prints this.\n"
|
||||
" -a --account<=STRING> Management tools for an account.\n"
|
||||
" -c --create<=STRING> Creates a new account with NAME.\n"
|
||||
" -e --earn<=STRING> Add an earning to an account.\n"
|
||||
" -p --payment<=STRING> Add a payment to an account.\n"
|
||||
" -a --accountName<=STRING> Management tools for an accountName.\n"
|
||||
" -c --create<=STRING> Creates a new accountName with NAME.\n"
|
||||
" -e --earn<=STRING> Add an earning to an accountName.\n"
|
||||
" -p --payment<=STRING> Add a payment to an accountName.\n"
|
||||
"Account Options: [-dvD]\n"
|
||||
" -d --delete Deletes specified account.\n"
|
||||
" --force-delete Deletes the specified account without confirmation.\n"
|
||||
" -v --value Gets the current value of the account.\n"
|
||||
" -D --description<=STRING> Changes the description of the account.\n"
|
||||
" -d --delete Deletes specified accountName.\n"
|
||||
" --force-delete Deletes the specified accountName without confirmation.\n"
|
||||
" -v --value Gets the current value of the accountName.\n"
|
||||
" -D --description<=STRING> Changes the description of the accountName.\n"
|
||||
"Create Options: [-d]\n"
|
||||
" -d --description<=STRING> Sets a description for an account.\n"
|
||||
" -d --description<=STRING> Sets a description for an accountName.\n"
|
||||
"Earn Options: -v [-drD]\n"
|
||||
" -v --value=<FlOAT> Value for earning.\n"
|
||||
" -d --description=<STRING> Description for earning.\n"
|
||||
@ -325,7 +325,7 @@ void MainOptHandler::help() {
|
||||
"budget -cAcct -eAcct -v10.00 -r\"./receipt.pdf\" -pAcct -v5.50 -r\"./payment.pdf\"\n"
|
||||
"\n"
|
||||
"Does the following in order:\n"
|
||||
"Creates an account named Acct with no description.\n"
|
||||
"Creates an accountName named Acct with no description.\n"
|
||||
"Earns 10.00 to it with a receipt.\n"
|
||||
"Pays 5.50 to it with a receipt." << std::endl;
|
||||
}
|
||||
|
38
src/optHandlers/paymentOperation.cpp
Normal file
38
src/optHandlers/paymentOperation.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by quentin on 1/17/23.
|
||||
//
|
||||
|
||||
#include <fstream>
|
||||
#include "paymentOperation.h"
|
||||
#include "../database.h"
|
||||
#include "../exceptions/badValue.h"
|
||||
#include "../main.h"
|
||||
|
||||
using namespace Budget::OptHandlers;
|
||||
|
||||
void PaymentOperation::commit() {
|
||||
Models::Account account = Database::getAccount(accountName, db);
|
||||
|
||||
if (!flags.receipt.empty()) {
|
||||
std::ifstream source(flags.receipt, std::ios::binary);
|
||||
if (source.good()) {
|
||||
std::string extension;
|
||||
size_t pos = flags.receipt.find_last_of('.');
|
||||
if (pos != std::string::npos) {
|
||||
extension = flags.receipt.substr(pos);
|
||||
}
|
||||
|
||||
std::ofstream dest(storageD + "receipts/receipt/" + std::to_string(account.id) + extension, std::ios::binary);
|
||||
dest << source.rdbuf();
|
||||
source.close();
|
||||
dest.close();
|
||||
}
|
||||
else {
|
||||
throw Exceptions::BadValue("File " + flags.receipt + " does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
Database::pay(&account, &flags, db);
|
||||
}
|
||||
|
||||
PaymentOperation::PaymentOperation(sqlite3 *db, std::string account) : Operation(db), accountName(std::move(account)) {}
|
@ -27,7 +27,7 @@ namespace Budget::OptHandlers {
|
||||
|
||||
Flags flags;
|
||||
private:
|
||||
std::string account;
|
||||
std::string accountName;
|
||||
};
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user