From 0d6a22fb8b437a4d0b77fc44c1ecb1e701573672 Mon Sep 17 00:00:00 2001 From: Quentin Snow Date: Tue, 31 Jan 2023 18:51:22 -0600 Subject: [PATCH] Payments and earns now store their receipts. The database tables are now created on startup --- CMakeLists.txt | 4 +++- src/database.cpp | 19 ++++++++++++------- src/database.h | 4 ++-- src/main.cpp | 22 +++++++++++++++------- src/main.h | 17 +++++++++++++++++ src/optHandlers/PaymentOperation.cpp | 18 +++++++++++++++++- src/optHandlers/earnOperation.cpp | 18 +++++++++++++++++- 7 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 src/main.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a10e96..2ddd354 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,9 @@ set(HEADERS src/database.h src/exceptions/helpRequested.h src/exceptions/badValue.h - src/utilities.h src/sqliteDb.h) + src/utilities.h + src/sqliteDb.h + src/main.h) add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS}) diff --git a/src/database.cpp b/src/database.cpp index 7169d01..8619adf 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -197,12 +197,12 @@ void Database::createAccount(const std::string &account, sqlite3 *db) { throw std::runtime_error("Failed to create account " + account); } -void 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(const std::string &account, long double &value, std::string &description, std::string &receipt, + long long date, 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 = ?), ?);" - "SELECT cachedValue FROM account WHERE name = ?;", -1, &stmt, nullptr); + "(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);", + -1, &stmt, nullptr); if (rc != SQLITE_OK) throw std::runtime_error("Failed preparing earn statement"); @@ -219,13 +219,16 @@ void Database::earn(const std::string &account, long double &value, std::string if (rc != SQLITE_DONE) throw std::runtime_error("Failed to create earning"); cacheAccountValue(account, db); + + return sqlite3_last_insert_rowid(db); } -void 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(const std::string &account, long double &value, std::string &description, std::string &receipt, + long long date, 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); + "(?, ?, ?, (SELECT id FROM account WHERE name = ?), ?);", + -1, &stmt, nullptr); if (rc != SQLITE_OK) throw std::runtime_error("Failed preparing pay statement"); @@ -242,4 +245,6 @@ void Database::pay(const std::string &account, long double &value, std::string & if (rc != SQLITE_DONE) throw std::runtime_error("Failed to create payment"); cacheAccountValue(account, db); + + return sqlite3_last_insert_rowid(db); } \ No newline at end of file diff --git a/src/database.h b/src/database.h index 4b98ee2..51ad163 100644 --- a/src/database.h +++ b/src/database.h @@ -119,7 +119,7 @@ public: * The accountId is retrieved from the 'account' table using the provided account name. * If the query fails to prepare or execute, the function throws a runtime_error. */ - static void + static long long int earn(const std::string &account, long double &value, std::string &description, std::string &receipt, long long int date, sqlite3 *db); @@ -140,7 +140,7 @@ public: * The accountId is retrieved from the 'account' table using the provided account name. * If the query fails to prepare or execute, the function throws a runtime_error. */ - static void + static long long int pay(const std::string &account, long double &value, std::string &description, std::string &receipt, long long int date, sqlite3 *db); diff --git a/src/main.cpp b/src/main.cpp index 6b7db62..1119f59 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,9 +6,8 @@ #include "exceptions/helpRequested.h" #include "exceptions/badValue.h" #include "sqliteDb.h" +#include "main.h" -#include -#include #include #include #include @@ -17,19 +16,24 @@ using namespace Budget; -const std::string homeDirectory = getpwuid(getuid())->pw_dir; -const std::string configD = homeDirectory + "/.config/budget/"; -const std::string storageD = homeDirectory + "/.local/share/budget/"; -const std::string databaseFile = homeDirectory + "/.local/share/budget/budget.sqlite"; - +const char* createTables = "CREATE TABLE account (id INTEGER CONSTRAINT account_pk PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, cachedValue DOUBLE);" + "CREATE UNIQUE INDEX account_name_uindex ON account (name);" + "CREATE TABLE 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);" + "CREATE INDEX earning_date_index ON earning (date DESC);" + "CREATE TABLE 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 INDEX payment_date_index ON payment (date DESC);"; void createRequiredFolders() { std::filesystem::create_directory(configD); std::filesystem::create_directories(storageD); std::filesystem::create_directories(storageD + "receipts"); + std::filesystem::create_directories(storageD + "receipts/payment"); + std::filesystem::create_directories(storageD + "receipts/earn"); } int main(int argc, char *argv[]) { + createRequiredFolders(); + sqlite3 *db; SqliteDb dbRAII(db); @@ -47,6 +51,10 @@ int main(int argc, char *argv[]) { if (rc != SQLITE_OK) throw std::runtime_error("Couldn't begin transaction"); + rc = sqlite3_exec(db, createTables, nullptr, nullptr, nullptr); + if (rc != SQLITE_OK) + throw std::runtime_error("Couldn't create the tables"); + std::vector args(argv, argv + argc); try { OptHandlers::MainOptHandler moh(args, db); diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..f04b1e2 --- /dev/null +++ b/src/main.h @@ -0,0 +1,17 @@ +// +// Created by quentin on 1/31/23. +// + +#ifndef BUDGET_MAIN_H +#define BUDGET_MAIN_H + +#include +#include + +const static std::string homeDirectory = getpwuid(getuid())->pw_dir; +const static std::string configD = homeDirectory + "/.config/budget/"; +const static std::string storageD = homeDirectory + "/.local/share/budget/"; +const static std::string databaseFile = homeDirectory + "/.local/share/budget/budget.sqlite"; + + +#endif //BUDGET_MAIN_H diff --git a/src/optHandlers/PaymentOperation.cpp b/src/optHandlers/PaymentOperation.cpp index 7415936..9bd9213 100644 --- a/src/optHandlers/PaymentOperation.cpp +++ b/src/optHandlers/PaymentOperation.cpp @@ -2,9 +2,11 @@ // Created by quentin on 1/17/23. // +#include #include "PaymentOperation.h" #include "../database.h" #include "../exceptions/badValue.h" +#include "../main.h" using namespace Budget::OptHandlers; @@ -12,7 +14,21 @@ void PaymentOperation::commit() { if (!Database::doesAccountExist(account, db)) throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist"); - Database::pay(account, flags.value, flags.description, flags.receipt, flags.date, db); + 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)) {} \ No newline at end of file diff --git a/src/optHandlers/earnOperation.cpp b/src/optHandlers/earnOperation.cpp index 12c871a..a736a4a 100644 --- a/src/optHandlers/earnOperation.cpp +++ b/src/optHandlers/earnOperation.cpp @@ -2,9 +2,11 @@ // Created by quentin on 1/17/23. // +#include #include "earnOperation.h" #include "../database.h" #include "../exceptions/badValue.h" +#include "../main.h" using namespace Budget::OptHandlers; @@ -12,7 +14,21 @@ void EarnOperation::commit() { if (!Database::doesAccountExist(account, db)) throw Budget::Exceptions::BadValue("Account " + account + " doesn't exist"); - Database::earn(account, flags.value, flags.description, flags.receipt, flags.date, db); + 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/receipt/" + std::to_string(id) + extension, std::ios::binary); + dest << source.rdbuf(); + source.close(); + dest.close(); + } } EarnOperation::EarnOperation(sqlite3 *db, std::string account) : Operation(db), account(std::move(account)) {} \ No newline at end of file