kima2/src/core/database.cpp

748 lines
26 KiB
C++
Raw Normal View History

2018-07-06 13:30:23 +02:00
#include "database.h"
2018-07-17 10:19:41 +02:00
#include <filesystem>
2018-07-10 14:12:37 +02:00
#include <iostream>
2018-07-10 15:46:55 +02:00
#include <stdexcept>
2018-07-11 11:53:06 +02:00
#include <vector>
2018-07-06 13:30:23 +02:00
#include "boost/date_time/posix_time/posix_time.hpp"
2022-07-07 15:21:46 +02:00
Database::Database(const std::string &dbname)
2018-07-06 13:30:23 +02:00
{
2018-07-11 09:43:40 +02:00
dbname_ = dbname;
2018-07-17 10:19:41 +02:00
init();
}
Database::Database()
{
namespace fs = std::filesystem;
2018-07-18 08:17:30 +02:00
2018-07-23 08:57:35 +02:00
#if defined(__linux__) || defined(__APPLE__)
2018-07-18 15:08:20 +02:00
fs::path dbpath = fs::path(std::getenv("HOME")) / ".local/share/kima2";
2018-07-20 08:18:28 +02:00
#elif defined(_WIN64) || defined(_WIN32)
2018-07-31 12:31:57 +02:00
fs::path dbpath = fs::path(std::getenv("LOCALAPPDATA")) / "kima2";
2018-07-17 10:19:41 +02:00
#else
throw std::runtime_error("Platform not supported.");
#endif
2018-07-18 08:17:30 +02:00
2018-07-17 10:19:41 +02:00
if (!fs::exists(dbpath)) {
try {
fs::create_directories(dbpath);
2022-07-07 15:21:46 +02:00
} catch (fs::filesystem_error &err) {
2018-07-17 10:19:41 +02:00
throw err;
}
2018-07-06 13:30:23 +02:00
}
dbpath /= "kima2.db";
2018-07-17 10:19:41 +02:00
dbname_ = dbpath.string();
init();
2018-07-06 13:30:23 +02:00
}
void Database::newDb()
{
namespace fs = std::filesystem;
2018-10-09 08:04:47 +02:00
sqlite3_close(db_);
fs::path sourcePath = dbname_;
fs::path destPath = sourcePath.parent_path() / sourcePath.stem();
2018-10-09 12:01:58 +02:00
destPath += std::string("_") +=
boost::posix_time::to_iso_string(boost::posix_time::second_clock::local_time()) += ".db";
fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing);
fs::remove(sourcePath);
init();
}
2018-07-10 14:12:37 +02:00
Database::~Database() { sqlite3_close(db_); }
2018-07-06 13:30:23 +02:00
2022-07-07 15:21:46 +02:00
void Database::exec(const std::string &sql)
2018-07-06 13:30:23 +02:00
{
2022-07-07 15:21:46 +02:00
char *errMsg;
2018-07-11 08:33:40 +02:00
const int errCode = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &errMsg);
2018-07-09 13:03:03 +02:00
if (errCode) {
2018-07-11 08:33:40 +02:00
std::string errMsgString(errMsg); // Make a C++ string of the errMsg, so that we can call
// sqlite3_free() before throwing the exception
sqlite3_free(errMsg);
throw std::runtime_error("Error in SQL execution: " + errMsgString);
2018-07-06 13:30:23 +02:00
}
2018-07-09 21:03:59 +02:00
}
2018-07-11 09:43:40 +02:00
void Database::createNew()
2018-07-10 14:12:37 +02:00
{
2018-07-11 11:53:06 +02:00
std::vector<std::string> sqlStrings{};
2018-07-11 09:43:40 +02:00
std::string sqlCreateKima2{"CREATE TABLE IF NOT EXISTS kima2 ("
"version INTEGER NOT NULL);"
2019-10-04 15:15:43 +02:00
"INSERT INTO kima2 (version) VALUES (3);"};
2018-07-11 11:53:06 +02:00
sqlStrings.push_back(sqlCreateKima2);
2018-07-10 15:46:55 +02:00
std::string sqlCreateSellers{"CREATE TABLE IF NOT EXISTS sellers ("
2019-10-04 15:15:43 +02:00
"seller_no INTEGER PRIMARY KEY NOT NULL, "
2018-07-10 15:46:55 +02:00
"first_name TEXT, "
"last_name TEXT, "
2018-07-11 15:58:09 +02:00
"num_offered_articles INTEGER, "
2018-07-10 15:46:55 +02:00
"UNIQUE (seller_no)"
");"};
2018-07-11 11:53:06 +02:00
sqlStrings.push_back(sqlCreateSellers);
2018-07-10 14:12:37 +02:00
std::string sqlCreateArticles{
"CREATE TABLE IF NOT EXISTS articles ("
"id TEXT PRIMARY KEY NOT NULL, "
2019-10-04 15:15:43 +02:00
"seller_no TEXT NOT NULL, "
2018-07-10 14:12:37 +02:00
"source_no INTEGER NOT NULL, "
"article_no INTEGER NOT NULL, "
"description TEXT, "
"price INTEGER NOT NULL, "
"UNIQUE (source_no, article_no), "
2019-10-04 15:15:43 +02:00
"FOREIGN KEY (seller_no) REFERENCES sellers(seller_no) ON DELETE CASCADE, "
2018-07-10 14:12:37 +02:00
"CHECK (article_no BETWEEN 0 AND 99999)"
2018-07-10 15:46:55 +02:00
");"};
2018-07-11 11:53:06 +02:00
sqlStrings.push_back(sqlCreateArticles);
std::string sqlCreateSales{"CREATE TABLE IF NOT EXISTS sales ("
" id TEXT PRIMARY KEY NOT NULL,"
" source_no INTEGER NOT NULL,"
" sold_at TEXT"
");"};
sqlStrings.push_back(sqlCreateSales);
std::string sqlCreateSalesItems{
"CREATE TABLE IF NOT EXISTS sales_items("
" sale_id TEXT NOT NULL,"
" article_id TEXT NOT NULL,"
" FOREIGN KEY (sale_id) REFERENCES sales(id) ON DELETE CASCADE,"
" FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE"
");"};
sqlStrings.push_back(sqlCreateSalesItems);
2018-07-10 14:12:37 +02:00
2018-10-09 12:01:58 +02:00
std::string sqlInitialEntries{
2019-10-04 15:15:43 +02:00
"INSERT OR IGNORE INTO sellers (seller_no, first_name, last_name, "
2018-10-09 12:01:58 +02:00
"num_offered_articles) VALUES "
2019-10-04 15:15:43 +02:00
"(0, 'Sonderkonto', 'Sonderkonto', 0)"};
2018-10-08 14:30:42 +02:00
sqlStrings.push_back(sqlInitialEntries);
2018-07-10 14:12:37 +02:00
beginTransaction();
2022-07-07 15:21:46 +02:00
for (const auto &sql : sqlStrings) {
2018-07-11 11:53:06 +02:00
exec(sql);
}
2018-07-10 14:12:37 +02:00
endTransaction();
}
2018-08-07 07:57:47 +02:00
void Database::updateDbToVer2()
{
beginTransaction();
2019-10-04 15:15:43 +02:00
exec("INSERT OR IGNORE INTO sellers (seller_no, first_name, last_name, "
2018-08-07 08:02:27 +02:00
"num_offered_articles) VALUES "
2019-10-04 15:15:43 +02:00
"(0, 'Sonderkonto', 'Sonderkonto', 0)");
exec("UPDATE kima2 SET version = 3");
2018-08-07 07:57:47 +02:00
endTransaction();
}
2019-10-07 14:08:01 +02:00
void Database::updateDbToVer3() { newDb(); }
2019-10-04 15:15:43 +02:00
2018-07-11 09:43:40 +02:00
void Database::init()
{
2018-07-17 10:19:41 +02:00
const int errCode = sqlite3_open(dbname_.c_str(), &db_);
if (errCode) {
throw std::runtime_error("Could not open database file.");
}
2018-07-30 13:40:58 +02:00
2018-07-28 14:16:02 +02:00
// sqlite3_db_config(db_, SQLITE_DBCONFIG_ENABLE_FKEY);
exec("PRAGMA foreign_keys = ON;");
2018-07-17 10:19:41 +02:00
2018-07-11 09:43:40 +02:00
int version = getVersion();
switch (version) {
case 0:
createNew();
initResult_ = InitResult::OK;
2018-07-11 09:43:40 +02:00
break;
2018-08-07 07:57:47 +02:00
case 1:
2019-10-04 15:15:43 +02:00
updateDbToVer3();
initResult_ = InitResult::OUTDATED_REPLACED;
2019-10-04 15:15:43 +02:00
break;
case 2:
updateDbToVer3();
initResult_ = InitResult::OUTDATED_REPLACED;
2018-08-07 07:57:47 +02:00
break;
2018-07-11 09:43:40 +02:00
default:
// Do nothing because we are up-to-date.
initResult_ = InitResult::OK;
2018-07-11 09:43:40 +02:00
break;
}
}
int Database::getVersion()
{
int retCode{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-11 09:43:40 +02:00
// Check if there's already a kima2 table available.
// If not, return version == 0.
retCode = sqlite3_prepare_v2(
db_, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='kima2';", -1, &stmt,
nullptr);
if (retCode != SQLITE_OK)
throw std::string(sqlite3_errmsg(db_));
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_ROW && retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw errMsg;
} else if (retCode != SQLITE_DONE) {
int count = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
if (count == 0)
return 0; // no kima2 table, so version is 0
}
// Now that we know that the kima2 table is present, read and return the schema version.
retCode = sqlite3_prepare_v2(db_, "SELECT version FROM kima2", -1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::string(sqlite3_errmsg(db_));
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_ROW && retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw errMsg;
} else if (retCode == SQLITE_DONE) {
sqlite3_finalize(stmt);
return 0; // no version entry, so version is 0
}
int version = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
return version;
}
2018-07-10 12:51:23 +02:00
void Database::beginTransaction() { exec("BEGIN TRANSACTION"); }
2018-07-09 21:03:59 +02:00
2018-07-11 15:58:09 +02:00
void Database::endTransaction() { exec("END TRANSACTION"); }
2022-07-07 15:21:46 +02:00
unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>> &sellers, bool onlyDelete)
2018-07-11 15:58:09 +02:00
{
int retCode{};
int count{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-11 15:58:09 +02:00
beginTransaction();
2022-07-07 15:21:46 +02:00
for (auto &seller : sellers) {
if (seller->getState() == Seller::State::NEW && !onlyDelete) {
2018-07-11 15:58:09 +02:00
retCode = sqlite3_prepare_v2(
db_,
"INSERT INTO sellers"
2019-10-04 15:15:43 +02:00
" (seller_no, first_name, last_name, num_offered_articles)"
" VALUES (:seller_no, :first_name, :last_name, :num_offered_articles)",
2018-07-11 15:58:09 +02:00
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
seller->getSellerNo());
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
seller->getFirstName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":last_name"),
seller->getLastName().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":num_offered_articles"),
seller->numArticlesOffered());
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
} else if (seller->getState() == Seller::State::UPDATE && !onlyDelete) {
retCode = sqlite3_prepare_v2(
db_,
"UPDATE sellers SET"
" seller_no = :seller_no, first_name = :first_name,"
" last_name = :last_name, num_offered_articles = :num_offered_articles"
2019-10-04 15:15:43 +02:00
" WHERE seller_no = :id",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
2019-10-07 14:08:01 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":id"), seller->getId());
2018-07-11 15:58:09 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
seller->getSellerNo());
2018-07-11 15:58:09 +02:00
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
seller->getFirstName().c_str(), -1, SQLITE_TRANSIENT);
2018-07-11 15:58:09 +02:00
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":last_name"),
seller->getLastName().c_str(), -1, SQLITE_TRANSIENT);
2018-07-11 15:58:09 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":num_offered_articles"),
seller->numArticlesOffered());
2018-07-11 15:58:09 +02:00
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
} else if (seller->getState() == Seller::State::DELETE) {
count += static_cast<int>(seller->getArticles(false).size());
2018-07-13 13:58:32 +02:00
2019-10-07 14:08:01 +02:00
retCode = sqlite3_prepare_v2(db_, "DELETE FROM sellers WHERE seller_no = :id", -1,
&stmt, nullptr);
2018-07-13 13:58:32 +02:00
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
2019-10-07 14:08:01 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":id"), seller->getId());
2018-07-13 13:58:32 +02:00
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
}
if (seller->getState() != Seller::State::DELETE) {
2018-07-13 14:05:22 +02:00
count += storeArticles(seller->getArticles(false));
2018-07-11 15:58:09 +02:00
}
}
endTransaction();
2018-07-13 13:58:32 +02:00
2018-07-17 10:19:41 +02:00
// Everything went fine, so we can now update our objects
2018-07-16 15:30:24 +02:00
sellers.erase(std::remove_if(sellers.begin(), sellers.end(),
2022-07-07 15:21:46 +02:00
[](const std::unique_ptr<Seller> &seller) {
2018-07-16 15:30:24 +02:00
return (seller->getState() == Seller::State::DELETE);
}),
sellers.end());
2022-07-07 15:21:46 +02:00
for (auto &seller : sellers) {
2018-07-13 13:58:32 +02:00
seller->cleanupArticles();
seller->setState(Seller::State::OK);
}
return count;
}
2022-07-07 15:21:46 +02:00
unsigned int Database::storeArticles(std::vector<Article *> articles)
{
int retCode{};
int count{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2022-07-07 15:21:46 +02:00
for (auto &article : articles) {
if (article->getState() == Article::State::NEW) {
retCode = sqlite3_prepare_v2(
db_,
"INSERT INTO articles"
2019-10-04 15:15:43 +02:00
" (id, seller_no, source_no, article_no, description, price)"
" VALUES (:uuid, :seller_id, :source_no, :article_no, :desc, :price)",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
2018-07-13 13:14:08 +02:00
boost::uuids::to_string(article->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
2019-10-04 15:15:43 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"),
2019-10-07 14:08:01 +02:00
article->getSeller()->getId());
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
article->getSourceNo());
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"),
article->getArticleNo());
2018-07-13 13:14:08 +02:00
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":desc"),
article->getDescription().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":price"),
article->getPrice());
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
} else if (article->getState() == Article::State::UPDATE) {
2018-07-13 13:14:08 +02:00
retCode = sqlite3_prepare_v2(
db_,
"UPDATE articles SET"
2019-10-04 15:15:43 +02:00
" seller_no = :seller_id, source_no = :source_no, article_no = :article_no,"
2018-07-13 13:14:08 +02:00
" description = :desc, price = :price"
" WHERE id = :uuid",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(article->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
2019-10-04 15:15:43 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"),
2019-10-07 14:08:01 +02:00
article->getSeller()->getId());
2018-07-13 13:14:08 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
article->getSourceNo());
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"),
article->getArticleNo());
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":desc"),
article->getDescription().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":price"),
article->getPrice());
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
} else if (article->getState() == Article::State::DELETE) {
2018-07-13 13:58:32 +02:00
retCode = sqlite3_prepare_v2(db_, "DELETE FROM articles WHERE id = :uuid", -1, &stmt,
nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(article->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
}
}
2018-07-17 11:09:35 +02:00
return count;
}
2022-07-07 15:21:46 +02:00
unsigned int Database::storeSales(std::vector<std::unique_ptr<Sale>> &sales)
2018-07-25 09:31:17 +02:00
{
int retCode{};
int count{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-25 09:31:17 +02:00
if (sales.size() == 0)
return 0;
beginTransaction();
2022-07-07 15:21:46 +02:00
for (auto &sale : sales) {
2018-07-25 09:31:17 +02:00
if (sale->getState() == Sale::State::NEW) {
retCode = sqlite3_prepare_v2(db_,
"INSERT INTO sales"
" (id, source_no, sold_at)"
" VALUES (:uuid, :source_no, :sold_at)",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(sale->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
sale->getSourceNo());
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":sold_at"),
sale->getTimestamp().c_str(), -1, SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
2022-07-07 15:21:46 +02:00
for (const auto &article : sale->getArticles()) {
2018-07-25 09:31:17 +02:00
retCode = sqlite3_prepare_v2(db_,
"INSERT INTO sales_items"
" (sale_id, article_id)"
" VALUES (:sale_id, :article_id)",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":sale_id"),
sale->getUuidAsString().c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":article_id"),
article->getUuidAsString().c_str(), -1, SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
sqlite3_finalize(stmt);
}
} else if (sale->getState() == Sale::State::DELETE) {
retCode =
sqlite3_prepare_v2(db_, "DELETE FROM sales WHERE id = :uuid", -1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
sale->getUuidAsString().c_str(), -1, SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
++count;
sqlite3_finalize(stmt);
}
}
endTransaction();
2018-07-28 11:52:43 +02:00
// Everything went fine, so we can now update our objects
sales.erase(
std::remove_if(sales.begin(), sales.end(),
2022-07-07 15:21:46 +02:00
[](const auto &sale) { return (sale->getState() == Sale::State::DELETE); }),
2018-07-28 11:52:43 +02:00
sales.end());
2022-07-07 15:21:46 +02:00
for (auto &sale : sales) {
2018-07-25 09:31:17 +02:00
sale->setState(Sale::State::OK);
}
return count;
}
2022-07-07 15:21:46 +02:00
unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>> &sellers)
2018-07-17 11:09:35 +02:00
{
int retCode{};
int count{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-17 11:09:35 +02:00
retCode = sqlite3_prepare_v2(db_,
2019-10-04 15:15:43 +02:00
"SELECT seller_no, first_name, last_name, "
2018-07-17 11:09:35 +02:00
"num_offered_articles FROM sellers ORDER BY seller_no",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
retCode = sqlite3_step(stmt);
sellers.clear();
while (retCode != SQLITE_DONE) {
++count;
auto seller = std::make_unique<Seller>();
2019-10-04 15:15:43 +02:00
seller->setSellerNo(sqlite3_column_int(stmt, 0));
2022-07-07 15:21:46 +02:00
seller->setFirstName(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1)));
seller->setLastName(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2)));
2019-10-04 15:15:43 +02:00
seller->setNumArticlesOffered(sqlite3_column_int(stmt, 3));
2018-07-17 11:09:35 +02:00
seller->setState(Seller::State::OK);
sellers.push_back(std::move(seller));
retCode = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
2022-07-07 15:21:46 +02:00
for (auto &seller : sellers) {
2018-07-23 08:57:35 +02:00
retCode = sqlite3_prepare_v2(db_,
"SELECT id, source_no, article_no, description, price"
" FROM articles"
2019-10-04 15:15:43 +02:00
" WHERE seller_no = :seller_id"
2018-07-23 08:57:35 +02:00
" ORDER BY article_no",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
2019-10-07 14:08:01 +02:00
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"), seller->getId());
2018-07-23 08:57:35 +02:00
retCode = sqlite3_step(stmt);
while (retCode != SQLITE_DONE) {
++count;
auto article = std::make_unique<Article>();
2022-07-07 15:21:46 +02:00
article->setUuidFromString(
reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0)));
2018-07-23 08:57:35 +02:00
article->setSeller(seller.get());
article->setSourceNo(sqlite3_column_int(stmt, 1));
article->setArticleNo(sqlite3_column_int(stmt, 2));
2022-07-07 15:21:46 +02:00
article->setDescription(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 3)));
2018-07-23 08:57:35 +02:00
article->setPrice(sqlite3_column_int(stmt, 4));
article->setState(Article::State::OK);
seller->addArticle(std::move(article));
retCode = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
}
2018-07-25 09:31:17 +02:00
return count;
}
2022-07-07 15:21:46 +02:00
unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>> &sales,
std::vector<std::unique_ptr<Seller>> &sellers)
2018-07-25 09:31:17 +02:00
{
int retCode{};
int count{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-25 09:31:17 +02:00
retCode = sqlite3_prepare_v2(db_,
"SELECT id, source_no, sold_at"
2018-07-25 16:11:25 +02:00
" FROM sales ORDER BY sold_at DESC",
2018-07-25 09:31:17 +02:00
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
retCode = sqlite3_step(stmt);
sales.clear();
2022-07-07 15:21:46 +02:00
std::map<std::string, Sale *> saleMap;
2018-07-25 09:31:17 +02:00
while (retCode != SQLITE_DONE) {
++count;
auto sale = std::make_unique<Sale>();
2022-07-07 15:21:46 +02:00
sale->setUuidFromString(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0)));
2018-07-25 09:31:17 +02:00
sale->setSourceNo(sqlite3_column_int(stmt, 1));
2022-07-07 15:21:46 +02:00
sale->setTimestamp(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2)));
2018-07-25 09:31:17 +02:00
sale->setState(Sale::State::OK);
saleMap.insert(std::make_pair(sale->getUuidAsString(), sale.get()));
2018-07-25 09:31:17 +02:00
sales.push_back(std::move(sale));
retCode = sqlite3_step(stmt);
}
sqlite3_finalize(stmt);
2022-07-07 15:21:46 +02:00
std::map<std::string, Article *> artMap;
for (const auto &seller : sellers) {
for (const auto article : seller->getArticles(false)) {
artMap.insert(std::make_pair(article->getUuidAsString(), article));
}
}
2018-07-25 09:31:17 +02:00
retCode = sqlite3_prepare_v2(db_,
"SELECT sale_id, article_id"
" FROM sales_items",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_));
2018-07-25 09:31:17 +02:00
retCode = sqlite3_step(stmt);
2018-07-25 09:31:17 +02:00
while (retCode != SQLITE_DONE) {
2022-07-07 15:21:46 +02:00
saleMap[reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0))]->addArticle(
artMap[reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1))]);
2018-07-25 09:31:17 +02:00
retCode = sqlite3_step(stmt);
2018-07-25 09:31:17 +02:00
}
sqlite3_finalize(stmt);
2018-07-11 15:58:09 +02:00
return count;
2018-07-25 16:11:25 +02:00
}
2018-07-30 13:40:58 +02:00
void Database::updateCashPointNo(int oldCashPointNo, int newCashPointNo)
{
int retCode{};
2022-07-07 15:21:46 +02:00
sqlite3_stmt *stmt;
2018-07-30 13:40:58 +02:00
// Check if the new no ist already in use
retCode = sqlite3_prepare_v2(db_, "SELECT COUNT() FROM articles WHERE source_no = :source_no",
-1, &stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::string(sqlite3_errmsg(db_));
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"), newCashPointNo);
retCode = sqlite3_step(stmt);
int count{};
if (retCode != SQLITE_ROW) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
count = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
if (count > 0) {
throw std::runtime_error("The desired cashpoint no is aleady in use.");
}
beginTransaction();
retCode = sqlite3_prepare_v2(
db_, "UPDATE articles SET source_no = :new_source_no WHERE source_no = :old_source_no", -1,
&stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::string(sqlite3_errmsg(db_));
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":new_source_no"), newCashPointNo);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":old_source_no"), oldCashPointNo);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
sqlite3_finalize(stmt);
retCode = sqlite3_prepare_v2(
db_, "UPDATE sales SET source_no = :new_source_no WHERE source_no = :old_source_no", -1,
&stmt, nullptr);
if (retCode != SQLITE_OK)
throw std::string(sqlite3_errmsg(db_));
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":new_source_no"), newCashPointNo);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":old_source_no"), oldCashPointNo);
retCode = sqlite3_step(stmt);
if (retCode != SQLITE_DONE) {
std::string errMsg(sqlite3_errmsg(db_));
sqlite3_finalize(stmt);
throw std::runtime_error(errMsg);
}
sqlite3_finalize(stmt);
endTransaction();
2019-10-04 15:15:43 +02:00
}