Payments and earns now store their receipts.

The database tables are now
created on startup
This commit is contained in:
Quentin Snow 2023-01-31 18:51:22 -06:00
parent c81bddf79a
commit 0d6a22fb8b
7 changed files with 83 additions and 19 deletions

View File

@ -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})

View File

@ -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);
}

View File

@ -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);

View File

@ -6,9 +6,8 @@
#include "exceptions/helpRequested.h"
#include "exceptions/badValue.h"
#include "sqliteDb.h"
#include "main.h"
#include <pwd.h>
#include <unistd.h>
#include <string>
#include <filesystem>
#include <sqlite3.h>
@ -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<char *> args(argv, argv + argc);
try {
OptHandlers::MainOptHandler moh(args, db);

17
src/main.h Normal file
View File

@ -0,0 +1,17 @@
//
// Created by quentin on 1/31/23.
//
#ifndef BUDGET_MAIN_H
#define BUDGET_MAIN_H
#include <unistd.h>
#include <pwd.h>
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

View File

@ -2,9 +2,11 @@
// 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;
@ -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)) {}

View File

@ -2,9 +2,11 @@
// Created by quentin on 1/17/23.
//
#include <fstream>
#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)) {}