KissCount/src/model/Database.cpp

1976 lines
54 KiB
C++

/*
Copyright 2010-2016 Grégory Soutadé
This file is part of KissCount.
KissCount is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
KissCount is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with KissCount. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <iostream>
#include <QString>
#include <QMessageBox>
#include <QSqlQuery>
#include <QSqlError>
#include <QVariant>
#include <QSqlRecord>
#include <QCryptographicHash>
#include "Database.hpp"
Database::Database(const char* filename, KissCount* kiss) : _kiss(kiss)
{
std::ifstream bdd_file;
QString sPath = GetDatabaseHome() + GetDatabaseFile();
_db = QSqlDatabase::addDatabase("QSQLITE");
if (filename)
{
bdd_file.open(filename, std::ifstream::in);
if (!bdd_file.good())
{
QMessageBox::critical(0, _("Error"), _("Unable to open database"));
throw std::string("Unable to open ") + filename;
}
_db.setDatabaseName(filename);
if (!_db.open())
{
QMessageBox::critical(0, _("Error"), _("Unable to open database"));
throw std::string("Unable to open ") + filename;
}
}
else
{
// If default BDD file, assume this can be the first load
bdd_file.open(sPath.toStdString().c_str(), std::ifstream::in);
if (!bdd_file.good())
{
CreateDatabase();
}
else
{
_db.setDatabaseName(sPath);
if (!_db.open())
{
QMessageBox::critical(0, _("Error"), _("Unable to open database"));
throw std::string("Unable to open ") + sPath.toStdString();
}
}
}
bdd_file.close();
CheckDatabaseVersion();
}
void Database::CreateDatabase()
{
QFile init_script(INIT_SCRIPT);
QString sPath = GetDatabaseHome() + GetDatabaseFile();
QDir dirname( GetDatabaseHome());
QFile file(sPath);
QString message;
message = _("A new database will be created, continue ?");
if (QMessageBox::question(0, "KissCount", message, QMessageBox::Yes|QMessageBox::No) == QMessageBox::No)
throw std::string("Abort") ;
if (!init_script.exists() || !init_script.open(QIODevice::ReadOnly | QIODevice::Text))
{
QMessageBox::critical(0, _("Error"), INIT_SCRIPT + _(" not found, aborting"));
throw "init.sql not found, aborting";
}
if (!dirname.exists() && !dirname.mkdir(dirname.absolutePath()))
{
QMessageBox::critical(0, _("Error"), _("Unable to Create ") + dirname.absolutePath() );
throw std::string("Unable to create ") + dirname.absolutePath().toStdString();
}
if (!file.exists() && !file.open(QIODevice::WriteOnly | QIODevice::Truncate))
{
QMessageBox::critical(0, _("Error"), _("Unable to Create ") + QString(sPath));
throw std::string("Unable to create ") + sPath.toStdString();
}
file.close();
_db.setDatabaseName(sPath);
if (!_db.open())
{
QMessageBox::critical(0, _("Error"), _("Unable to open Database"));
throw std::string("Unable to open ") + sPath.toStdString();
}
while (!init_script.atEnd())
{
QByteArray line = init_script.readLine().trimmed();
QSqlQuery query;
if (line.isEmpty() || line.startsWith("--")) continue;
// if (!query.isValid())
// {
// std::cout << QString(line).toStdString() << " is invalid !\n" ;
// continue;
// }
if (!query.exec(QString(line)))
{
QMessageBox::critical(0, _("Error"), _("Error creating original database"));
init_script.close();
file.remove();
std::cout << QString(line).toStdString() << " is invalid !\n" ;
throw QString(line);
}
}
init_script.close();
}
QString Database::HashPassword(const QString& password)
{
return QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha1).toHex();
}
std::list<QString> Database::GetUsers()
{
std::list<QString> res;
QSqlQuery query(_db);
EXECUTE_SQL_QUERY(QString("SELECT name FROM user ORDER BY name"), res);
while (query.next())
res.push_back(query.value(0).toString());
return res;
}
bool Database::IsValidUser(const QString& user, const QString& password)
{
QSqlQuery query(_db);
query.prepare("SELECT name FROM user WHERE name=:id AND password=:password");
query.bindValue(":id", user);
query.bindValue(":password", HashPassword(password));
EXECUTE_PREPARED_SQL_QUERY(false);
return query.next();
}
static inline void fillOperation(Operation* op, const QSqlRecord& set)
{
op->id = set.value("id").toInt();
op->parent = set.value("parent").toInt();
op->account = set.value("account").toInt();
op->day = set.value("day").toInt();
op->month = set.value("month").toInt();
op->year = set.value("year").toInt();
op->amount = set.value("amount").toInt();
op->description = set.value("description").toString();
op->category = set.value("category").toInt();
op->tag = set.value("tag").toInt();
op->fix_cost = set.value("fix_cost").toBool();
op->checked = set.value("checked").toBool();
op->transfert = set.value("transfert").toInt();
op->formula = set.value("formula").toString();
op->meta = set.value("meta").toBool();
op->_virtual = set.value("virtual").toBool();
}
static inline void fillAccount(Account* account, const QSqlRecord& set)
{
account->id = set.value("id").toInt();
account->name = set.value("name").toString();
account->number = set.value("number").toString();
account->shared = set.value("shared").toBool();
account->blocked = set.value("blocked").toBool();
account->_default = set.value("default_account").toBool();
account->_virtual = set.value("virtual").toBool();
account->is_owner = true;
account->hidden = set.value("hidden").toBool();
account->start_date = set.value("start").toDate();
account->end_date = set.value("end").toDate();
}
static inline void fillCategory(Category* category, const QSqlRecord& set)
{
category->id = set.value("id").toInt();
category->parent = set.value("parent").toInt();
category->name = set.value("name").toString();
category->backcolor = QColor(set.value("backcolor").toString());
category->forecolor = QColor(set.value("forecolor").toString());
category->font = set.value("font").toString();
category->fix_cost = set.value("fix_cost").toBool();
}
static inline void fillTag(Tag* tag, const QSqlRecord& set)
{
tag->id = set.value("id").toInt();
tag->name = set.value("name").toString();
}
User* Database::LoadUser(const QString& name)
{
User* user;
Account account;
Category category;
Tag tag;
ImportPattern importPattern;
QString req;
QSqlRecord set;
QSqlQuery query(_db);
std::vector<Account>::iterator it;
query.prepare("SELECT * FROM user WHERE name=:name");
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(0);
if (!query.next())
return 0;
user = new User(this);
set = query.record();
user->_id = set.value("id").toInt();
user->_name = set.value("name").toString();
user->_password = "" ; // Security reasons set.value("password").toString();
query.clear();
req = QString("SELECT * FROM account WHERE user='%1' ORDER BY default_account DESC, hidden, blocked, virtual, name ASC").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
fillAccount(&account, set);
user->_accounts.push_back(account);
}
query.clear();
req = QString("SELECT * FROM account WHERE id IN (SELECT account FROM shared_account WHERE user='%1') ORDER BY default_account DESC, hidden, blocked, virtual, name ASC").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
fillAccount(&account, set);
account.is_owner = false;
user->_accounts.push_back(account);
}
query.clear();
if (!user->_accounts.empty())
{
it = user->_accounts.begin();
req = "SELECT DISTINCT year FROM operation WHERE account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " OR user='" + QString::number(user->_id) + "'";
req += " ORDER BY year ASC";
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
user->_operations[set.value("year").toInt()] = 0;
}
}
req = QString("SELECT * FROM category WHERE user='%1' ORDER BY fix_cost DESC, name ASC").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
fillCategory(&category, set);
user->_categories.push_back(category);
user->_categoriesFonts.push_back(_kiss->ExtractFont(category.font));
}
query.clear();
req = QString("SELECT * FROM tag WHERE user='%1' ORDER BY name ASC").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
fillTag(&tag, set);
user->_tags.push_back(tag);
}
query.clear();
req = QString("SELECT name, value FROM preference WHERE user='%1' ORDER BY value ASC").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
user->_preferences[set.value("name").toString()] = set.value("value").toString();
}
query.clear();
req = QString("SELECT * FROM import_pattern WHERE user='%1'").arg(user->_id);
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, {delete user;}, {delete user;});
while (query.next())
{
set = query.record();
importPattern.pattern = set.value("pattern").toString();
importPattern.account = set.value("account").toInt();
importPattern.category = set.value("category").toInt();
user->_importPatterns[set.value("description").toString()] = importPattern;
}
query.clear();
return user;
}
void Database::LoadYear(User* user, int year)
{
QSqlRecord set;
QString req;
std::vector<Account>::iterator it;
QSqlQuery query(_db);
if (user->_operations[year] == 0)
user->_operations[year] = new std::map<unsigned int, std::vector<Operation> >();
if (!user->_accounts.size()) return;
it = user->_accounts.begin();
req = "SELECT * FROM operation WHERE (account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " OR user='" + QString::number(user->_id) + "')";
req += " AND year='" + QString::number(year) + "'";
req += " ORDER BY fix_cost DESC, year, month ASC, day ";
req += user->_preferences["operation_order"];
EXECUTE_SQL_QUERY(req, );
while (query.next())
{
Operation op;
set = query.record();
fillOperation(&op, set);
(*user->_operations[op.year])[op.month].push_back(op);
}
user->ResolveGroups(year);
query.clear();
}
int Database::GetAccountAmount(int id, int month, int year, bool* had_value, bool create_if_not_exsits)
{
QSqlRecord set;
QString req;
int res = 0;
QSqlQuery query(_db);
req = QString("SELECT amount FROM account_amount WHERE account='%1' AND month='%2' AND year='%3'")
.arg(QString::number(id), QString::number(month), QString::number(year)) ;
EXECUTE_SQL_QUERY(req, 0);
if (query.next())
{
set = query.record();
res = set.value("amount").toInt();
if (had_value)
*had_value = true;
}
else
{
if (create_if_not_exsits)
SetAccountAmount(id, month, year, 0);
if (had_value)
*had_value = false;
}
query.clear();
return res;
}
int Database::CalcAccountAmount(int id, int month, int year, bool* had_values)
{
QSqlRecord set;
QString req;
int res = 0;
QSqlQuery query(_db);
req = QString("SELECT SUM(id) AS id, SUM(amount) AS amount FROM operation WHERE account='%1' AND month='%2' AND year='%3' AND meta='0'").
arg(QString::number(id), QString::number(month), QString::number(year)) ;
EXECUTE_SQL_QUERY(req, 0);
if (query.next())
{
set = query.record();
res = set.value("amount").toInt();
if (had_values)
*had_values = set.value("id").toInt() > 0 ;
}
else
{
if (had_values)
*had_values = false;
}
query.clear();
return res;
}
bool Database::GetOperation(int id, Operation* op)
{
QSqlRecord set;
QString req;
QSqlQuery query(_db);
req = QString("SELECT * FROM operation WHERE id='%1'").arg(QString::number(id));
EXECUTE_SQL_QUERY(req, false);
if (!query.next()) return false;
set = query.record();
fillOperation(op, set);
return true;
}
void Database::LinkOrUnlinkOperation(User* user, Operation& op)
{
Operation linked;
QString req, v;
QSqlRecord set;
Account account, account2;
bool _virtual;
QSqlQuery query(_db);
if (op.meta) return;
account = user->GetAccount(op.account);
if (op.transfert)
{
// No one or not linked
if (!GetOperation(op.transfert, &linked) || op.description != linked.description || op.amount != -linked.amount || op.account == linked.account)
{
op._virtual = account._virtual;
req = QString("UPDATE operation SET transfert='', virtual='%1' WHERE id='%2'").arg(op._virtual?"1":"0", op.id);
EXECUTE_SQL_UPDATE(req, );
op.transfert = 0;
return;
}
}
// Not Linked
else
{
req = QString("SELECT id FROM operation WHERE transfert='%1'").arg(op.id);
EXECUTE_SQL_QUERY(req, );
if (query.next())
{
op._virtual = account._virtual;
set = query.record();
req = QString("UPDATE operation SET transfert='', virtual='%1' WHERE id='%2'").arg(op._virtual?"1":"0", set.value("id").toString());
query.clear();
EXECUTE_SQL_UPDATE(req, );
}
else
query.clear();
req = QString("SELECT id, account FROM operation WHERE description=\"%1\" AND month='%2' AND year='%3' AND amount='%4' AND meta='0' AND account !='%5' AND transfert=''")
.arg(op.description, QString::number(op.month), QString::number(op.year), v.sprintf("%d", -op.amount), QString::number(op.account));
EXECUTE_SQL_QUERY(req, );
// Don't need to link
if (!query.next()) return ;
set = query.record();
// Link
linked.id = set.value("id").toInt();
op.transfert = linked.id;
account2 = user->GetAccount(set.value("account").toInt());
_virtual = account._virtual || account2._virtual;
query.clear();
req = QString("UPDATE operation SET transfert='%1', virtual='%2' WHERE id='%3'")
.arg((linked.id) ? QString::number(linked.id) : "", QString::number(_virtual), QString::number(op.id));
EXECUTE_SQL_UPDATE(req, );
query.clear();
req = QString("UPDATE operation SET transfert='%1', virtual='%2' WHERE id='%3'")
.arg((op.id) ? QString::number(op.id) : "", QString::number(_virtual), QString::number(linked.id));
EXECUTE_SQL_UPDATE(req, );
op._virtual = _virtual;
query.clear();
}
}
void Database::UpdateOperation(User* user, Operation& op, bool checkTransfert)
{
QString req, v;
QSqlQuery query(_db);
if (checkTransfert)
LinkOrUnlinkOperation(user, op);
ESCAPE_CHARS(op.description);
req = "UPDATE operation SET parent='%1', account='%2', year='%3', month='%4', day='%5', amount='%6', description=:description, category='%8', tag='%9'" ;
req = req.arg((op.parent) ? QString::number(op.parent) : "", QString::number(op.account), QString::number(op.year), QString::number(op.month),
QString::number(op.day), v.sprintf("%d", op.amount), QString::number(op.category), QString::number(op.tag));
req += ", fix_cost='%1', checked='%2', transfert='%3', meta='%4', virtual='%5', formula=:formula WHERE id='%7'";
req = req.arg(QString::number(op.fix_cost), QString::number(op.checked), (op.transfert) ? QString::number(op.transfert): "",
QString::number(op.meta), QString::number(op._virtual), QString::number(op.id));
query.prepare(req);
query.bindValue(":description", op.description);
query.bindValue(":formula", op.formula);
EXECUTE_PREPARED_SQL_UPDATE();
if (checkTransfert)
LinkOrUnlinkOperation(user, op);
}
int Database::AddOperation(User* user, Operation& op, bool checkTransfert)
{
QString req, v;
QSqlRecord set;
QSqlQuery query(_db);
ESCAPE_CHARS(op.description);
req = "INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'tag', 'fix_cost', 'formula', 'transfert', 'meta', 'virtual', 'checked') VALUES ('%1', '%2', '%3', '%4', '%5', '%6', '%7', :description" ;
req = req.arg(QString::number(user->_id), (op.parent) ? QString::number(op.parent): "", QString::number(op.account), QString::number(op.year),
QString::number(op.month), QString::number(op.day), v.sprintf("%d", op.amount));
req += ", '%1', '%2', '%3', :formula, '%4', '%5', '%6', '%7')";
req = req.arg(QString::number(op.category), QString::number(op.tag), QString::number(op.fix_cost), (op.transfert) ? QString::number(op.transfert): "",
QString::number(op.meta), QString::number(op._virtual), QString::number(op.checked));
query.prepare(req);
query.bindValue(":description", op.description);
query.bindValue(":formula", op.formula);
if (!query.exec())
{
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
return 0;
}
op.id = query.lastInsertId().toInt();
if (checkTransfert)
LinkOrUnlinkOperation(user, op);
return op.id;
}
void Database::DeleteOperation(User* user, Operation& op)
{
QSqlQuery query(_db);
QString req = QString("DELETE FROM operation WHERE id='%1'").arg(op.id);
EXECUTE_SQL_UPDATE(req, );
LinkOrUnlinkOperation(user, op);
}
void Database::DeleteOperations(User* user, int month, int year)
{
QString req;
std::vector<Account>::iterator it;
it = user->_accounts.begin();
req = "DELETE FROM account_amount WHERE account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " AND year='" + QString::number(year) + "'";
if (month != -1)
req += " AND month='" + QString::number(month) + "'";
EXECUTE_SQL_UPDATE(req, );
it = user->_accounts.begin();
req = "DELETE FROM operation WHERE (account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " OR user='" + QString::number(user->_id) + "')";
req += " AND year='" + QString::number(year) + "'";
if (month != -1)
req += " AND month='" + QString::number(month) + "'";
EXECUTE_SQL_UPDATE(req, );
}
bool Database::LoadOperation(User* user, int id)
{
QSqlRecord set;
QString req;
bool ret = false;
std::vector<Operation>::iterator it;
QSqlQuery query(_db);
req = QString("SELECT * FROM operation WHERE id='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
if (query.next())
{
Operation op;
set = query.record();
fillOperation(&op, set);
if (user->_operations[op.year])
{
for (it = (*user->_operations[op.year])[op.month].begin();
it != (*user->_operations[op.year])[op.month].end();
it++)
{
if (!op.fix_cost && it->fix_cost) continue;
if (op.fix_cost && !it->fix_cost)
{
it--;
break;
}
if (it->day > op.day)
{
it--;
break;
}
}
if (it == (*user->_operations[op.year])[op.month].end())
(*user->_operations[op.year])[op.month].push_back(op);
else
(*user->_operations[op.year])[op.month].insert(it, op);
// if (op.fix_cost)
// else
// (*user->_operations[op.year])[op.month].push_back(op);
ret = true;
}
}
query.clear();
return ret;
}
// We may not have access to all operations if we have a shared account
int Database::MetaAmount(int id)
{
QSqlRecord set;
QString req;
int res = 0;
QSqlQuery query(_db);
req = QString("SELECT SUM(amount) as amount FROM operation WHERE parent='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
if (query.next())
{
set = query.record();
res = set.value("amount").toInt();
}
query.clear();
return res;
}
// Idem
int Database::MetaPositiveAmount(int id)
{
QSqlRecord set;
QString req;
int res = 0;
QSqlQuery query(_db);
req = QString("SELECT SUM(amount) as amount FROM operation WHERE amount > 0 AND parent='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
if (query.next())
{
set = query.record();
res = set.value("amount").toInt();
}
query.clear();
return res;
}
void Database::SetAccountAmount(int accountId, int month, int year, int amount)
{
QString req, v;
QSqlQuery query(_db);
req = "INSERT or REPLACE INTO account_amount ('account', 'year', 'month', 'amount') VALUES ('%1', '%2', '%3', '%4')" ;
req = req.arg(QString::number(accountId), QString::number(year), QString::number(month), v.sprintf("%d", amount));
EXECUTE_SQL_UPDATE(req, );
}
int Database::AddAccount(User* user, Account& ac)
{
QString req;
QSqlQuery query(_db);
int id;
req = "INSERT INTO account ('user', 'name', 'number', 'shared', 'blocked', 'default_account', 'virtual', 'hidden') VALUES " ;
req += "('%1', '%2', '%3', '%4', '%5', '%6', '%7', '%8')";
req = req.arg(QString::number(user->_id), ac.name, ac.number, QString::number(ac.shared), QString::number(ac.blocked),
QString::number(ac._default), QString::number(ac._virtual), QString::number(ac.hidden));
if (!query.exec(req))
{
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
std::cerr << __FUNCTION__ << "\n" ;
std::cerr << req.toStdString() << "\n" ;
std::cerr << query.lastError().text().toStdString() << "\n" ;
return 0;
}
id = query.lastInsertId().toInt();
req = "UPDATE account SET start='%1', end='%2' WHERE id='%3'";
req = req.arg(ac.start_date.toString("yyyy-MM-dd"), ac.end_date.toString("yyyy-MM-dd"), QString::number(id));
EXECUTE_SQL_UPDATE(req, 0);
return id;
}
void Database::UpdateAccount(Account& ac)
{
QString req;
req = "UPDATE account SET name='%1', number='%2', shared='%3', blocked='%4', default_account='%5', virtual='%6', hidden='%7' WHERE id='%8'" ;
req = req.arg(ac.name, ac.number, QString::number(ac.shared), QString::number(ac.blocked), QString::number(ac._default),
QString::number(ac._virtual), QString::number(ac.hidden), QString::number(ac.id));
EXECUTE_SQL_UPDATE(req, );
req = "UPDATE account SET start='%1', end='%2' WHERE id='%3'" ;
req = req.arg(ac.start_date.toString("yyyy-MM-dd"), ac.end_date.toString("yyyy-MM-dd"),
QString::number(ac.id));
EXECUTE_SQL_UPDATE(req, );
if (!ac.shared && ac.is_owner)
{
req = QString("DELETE FROM shared_account WHERE account='%1'").arg(ac.id);
EXECUTE_SQL_UPDATE(req, );
}
}
void Database::DeleteAccount(User* user, Account& ac, int replacement)
{
QString req;
if (ac.is_owner)
{
if (ac.shared)
{
req = QString("DELETE FROM shared_account WHERE account='%1'").arg(ac.id);
EXECUTE_SQL_UPDATE(req, );
}
req = QString("DELETE FROM account WHERE id='%1'").arg(ac.id);
EXECUTE_SQL_UPDATE(req, );
req = QString("DELETE FROM account_amount WHERE account='%1'").arg(ac.id);
EXECUTE_SQL_UPDATE(req, );
req = QString("UPDATE operation SET account='%1' WHERE account='%2'").arg(QString::number(replacement), QString::number(ac.id));
// req += wxT(", transfert='0'");
EXECUTE_SQL_UPDATE(req, );
}
else
RemoveSharedAccount(ac, user->_id);
}
void Database::AddSharedAccount(Account& ac, const QString& granted)
{
QString req;
QSqlRecord set;
QSqlQuery query(_db);
req = QString("SELECT id FROM user WHERE name=:name");
query.prepare(req);
query.bindValue(":name", granted);
EXECUTE_PREPARED_SQL_QUERY();
query.next();
set = query.record();
req = QString("INSERT INTO shared_account ('account', 'user') VALUES ('%1', '%2')").arg(QString::number(ac.id), set.value("id").toString());
query.clear();
EXECUTE_SQL_UPDATE(req, );
if (!ac.shared)
{
ac.shared = true;
UpdateAccount(ac);
}
}
void Database::RemoveSharedAccount(Account& ac, int granted)
{
QString req;
QSqlRecord set;
QSqlQuery query(_db);
req = QString("DELETE FROM shared_account WHERE user='%1' AND account='%2'").arg(QString::number(granted), QString::number(ac.id));
EXECUTE_SQL_UPDATE(req, );
req = QString("SELECT COUNT(user) AS cnt FROM shared_account WHERE account='%1'").arg(ac.id);
EXECUTE_SQL_QUERY(req, );
query.next();
set = query.record();
if (!set.value("cnt").toInt())
{
ac.shared = false;
UpdateAccount(ac);
}
}
int Database::AddCategory(User* user, Category& category)
{
QString req;
QString backcolor, forecolor;
QSqlQuery query(_db);
backcolor = "#" ;
backcolor += QString::number(category.backcolor.red(), 16);
backcolor += QString::number(category.backcolor.green(), 16);
backcolor += QString::number(category.backcolor.blue(), 16);
forecolor = "#" ;
forecolor += QString::number(category.forecolor.red(), 16);
forecolor += QString::number(category.forecolor.green(), 16);
forecolor += QString::number(category.forecolor.blue(), 16);
req = "INSERT INTO category ('user', 'parent', 'name', 'backcolor', 'forecolor', 'font', 'fix_cost') VALUES ('" ;
req += QString::number(user->_id) + "'";
req += ", '" + QString::number(category.parent) + "'";
req += ", :name";
req += ", '" + backcolor + "'";
req += ", '" + forecolor + "'";
req += ", '" + category.font + "'";
if (category.fix_cost)
req += ", '1'";
else
req += ", '0'";
req += ")";
query.prepare(req);
query.bindValue(":name", category.name);
if (!query.exec())
{
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
std::cerr << __FUNCTION__ << "\n" ;
std::cerr << req.toStdString() << "\n" ;
std::cerr << query.lastError().text().toStdString() << "\n" ;
return 0;
}
return query.lastInsertId().toInt();
}
void Database::UpdateCategory(Category& category)
{
QString req, tmp;
QString backcolor, forecolor;
QSqlQuery query(_db);
backcolor = "#" ;
tmp = QString::number(category.backcolor.red(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
backcolor += tmp;
tmp = QString::number(category.backcolor.green(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
backcolor += tmp;
tmp = QString::number(category.backcolor.blue(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
backcolor += tmp;
forecolor = "#" ;
tmp = QString::number(category.forecolor.red(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
forecolor += tmp;
tmp = QString::number(category.forecolor.green(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
forecolor += tmp;
tmp = QString::number(category.forecolor.blue(), 16);
if (tmp.length() == 1) tmp = "0" + tmp;
forecolor += tmp;
req = "UPDATE category SET" ;
req += " parent='" + QString::number(category.parent) + "'";
req += ", name=:name";
req += ", backcolor='" + backcolor + "'";
req += ", forecolor='" + forecolor + "'";
req += ", font='" + category.font + "'";
if (category.fix_cost)
req += ", fix_cost='1'";
else
req += ", fix_cost='0'";
req += " WHERE id='" + QString::number(category.id) + "'";
query.prepare(req);
query.bindValue(":name", category.name);
EXECUTE_PREPARED_SQL_UPDATE();
}
void Database::DeleteCategory(User* user, Category& category, int replacement)
{
QString req;
req = QString("DELETE FROM category WHERE id='%1'").arg(category.id);
EXECUTE_SQL_UPDATE(req, );
req = QString("UPDATE category SET parent='' WHERE parent='%1'").arg(category.id) ;
EXECUTE_SQL_UPDATE(req, );
req = QString("UPDATE operation SET category='%1' WHERE category='%2' AND user='%3'")
.arg(QString::number(replacement), QString::number(category.id), QString::number(user->_id));
EXECUTE_SQL_UPDATE(req, );
if (replacement == user->_categories[0].id)
{
req = QString("UPDATE operation SET fix_cost='1' WHERE category='%1'").arg(replacement);
EXECUTE_SQL_UPDATE(req, );
}
}
bool Database::LoadCategory(int id, const QString& name, Category& category)
{
QSqlRecord set;
QString req;
QSqlQuery query(_db);
bool ret = false ;
if (id)
{
req = QString("SELECT * FROM category WHERE id='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
}
else
{
req = QString("SELECT * FROM category WHERE name=:name");
query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
}
if (query.next())
{
set = query.record();
fillCategory(&category, set);
ret = true;
}
query.clear();
return ret;
}
int Database::AddTag(User* user, Tag& tag)
{
QString req;
QString backcolor, forecolor;
QSqlQuery query(_db);
req = "INSERT INTO tag ('user', 'name') VALUES ('" ;
req += QString::number(user->_id) + "'";
req += ", :name";
req += ")";
query.prepare(req);
query.bindValue(":name", tag.name);
if (!query.exec())
{
QMessageBox::critical(0, _("Error"), _("Update failed !\n") + req);
std::cerr << __FUNCTION__ << "\n" ;
std::cerr << req.toStdString() << "\n" ;
std::cerr << query.lastError().text().toStdString() << "\n" ;
return 0;
}
return query.lastInsertId().toInt();
}
void Database::UpdateTag(Tag& tag)
{
QString req;
QSqlQuery query(_db);
req = "UPDATE tag SET" ;
req += "name=:name";
req += " WHERE id='" + QString::number(tag.id) + "'";
query.prepare(req);
query.bindValue(":name", tag.name);
EXECUTE_PREPARED_SQL_UPDATE();
}
void Database::DeleteTag(User* user, Tag& tag, int replacement)
{
QString req;
req = QString("DELETE FROM tag WHERE id='%1'").arg(tag.id);
EXECUTE_SQL_UPDATE(req, );
req = QString("UPDATE operation SET tag='%1' WHERE tag='%2' AND user='%3'")
.arg(QString::number(replacement), QString::number(tag.id), QString::number(user->_id));
EXECUTE_SQL_UPDATE(req, );
}
bool Database::LoadTag(int id, const QString& name, Tag& tag)
{
QSqlRecord set;
QString req;
QSqlQuery query(_db);
bool ret = false ;
if (id)
{
req = QString("SELECT * FROM tag WHERE id='%1'").arg(id);
EXECUTE_SQL_QUERY(req, false);
}
else
{
req = QString("SELECT * FROM tag WHERE name=:name");
query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
}
if (query.next())
{
set = query.record();
fillTag(&tag, set);
ret = true;
}
query.clear();
return ret;
}
std::map<int, std::vector<int> > Database::GetAllOperations(User* user)
{
QString req, req2, reqUnion;
QSqlRecord set;
QSqlQuery query(_db),query2(_db);
std::vector<Account>::iterator it;
std::map<int, std::vector<int> > res;
int year;
if (!user->_accounts.empty())
{
it = user->_accounts.begin();
req = "SELECT DISTINCT year FROM account_amount WHERE account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
it = user->_accounts.begin();
req2 = "SELECT DISTINCT year FROM operation WHERE account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req2 += "', '" + QString::number(it->id) ;
}
req2 += "')";
req2 += " OR user='" + QString::number(user->_id) + "'";
req2 += " ORDER BY year ASC";
reqUnion = req + " UNION " + req2;
EXECUTE_SQL_QUERY(reqUnion, res);
query2 = query;
while (query2.next())
{
set = query2.record();
year = set.value("year").toInt();
it = user->_accounts.begin();
req = "SELECT DISTINCT month FROM account_amount WHERE account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " AND year='" + QString::number(year) + "'";
it = user->_accounts.begin();
req2 = "SELECT DISTINCT month FROM operation WHERE (account IN('" + QString::number(it->id);
it++;
for (;it != user->_accounts.end(); it++)
{
req2 += "', '" + QString::number(it->id) ;
}
req2 += "')";
req2 += " OR user='" + QString::number(user->_id) + "')";
req2 += " AND year='" + QString::number(year) + "'";
req2 += " ORDER BY month ASC";
reqUnion = req + " UNION " + req2;
query.clear();
EXECUTE_SQL_QUERY(reqUnion, res);
while (query.next())
{
set = query.record();
res[year].push_back(set.value("month").toInt());
}
query.clear();
}
query2.clear();
}
return res;
}
void Database::GenerateMonth(User* user, int monthFrom, int yearFrom, int monthTo, int yearTo)
{
std::vector<Account>::iterator it;
QString req, v;
QSqlRecord set;
int amount;
QSqlQuery query(_db);
for (it = user->_accounts.begin(); it != user->_accounts.end(); it++)
{
amount = 0;
req = "SELECT SUM(amount) AS total FROM operation WHERE account='%1' AND year='%2' AND month='%3' AND meta='0'" ;
req = req.arg(QString::number(it->id), QString::number(yearFrom), QString::number(monthFrom));
EXECUTE_SQL_QUERY(req, );
if (query.next())
{
set = query.record();
amount += set.value("total").toInt();
}
query.clear();
req = "SELECT amount FROM account_amount WHERE account='%1' AND year='%2' AND month='%3'" ;
req = req.arg(QString::number(it->id), QString::number(yearFrom), QString::number(monthFrom));
EXECUTE_SQL_QUERY(req, );
if (query.next())
{
set = query.record();
amount += set.value("amount").toInt();
}
query.clear();
req = "INSERT INTO account_amount ('account', 'year', 'month', 'amount') VALUES " ;
req += "('%1', '%2', '%3', '%4')";
req = req.arg(QString::number(it->id), QString::number(yearTo), QString::number(monthTo), v.sprintf("%d", amount));
EXECUTE_SQL_UPDATE(req, );
}
}
void Database::ChangePassword(User* user, const QString& password)
{
QString req;
QSqlQuery query(_db);
req = QString("UPDATE user SET password=:password WHERE name=:name");
query.prepare(req);
query.bindValue(":name", user->_name);
query.bindValue(":password", HashPassword(password));
EXECUTE_PREPARED_SQL_UPDATE();
}
bool Database::UserExists(const QString& name)
{
QSqlRecord set;
QSqlQuery query(_db);
QString req;
bool res=false;
req = QString("SELECT name FROM user WHERE name=:name");
query.prepare(req);
query.bindValue(":name", name);
EXECUTE_PREPARED_SQL_QUERY(false);
if (query.next())
res = true;
else
res = false;
query.clear();
return res;
}
void Database::ChangeName(User* user, const QString& name)
{
QString req;
QSqlQuery query(_db);
req = QString("UPDATE user SET name=:name WHERE name=:name2");
query.prepare(req);
query.bindValue(":name", name);
query.bindValue(":name2", user->_name);
EXECUTE_PREPARED_SQL_UPDATE();
}
void Database::NewUser(const QString& name)
{
QString req;
QSqlQuery query(_db);
req = QString("INSERT INTO user ('name', 'password') VALUES (:name, :password)");
query.prepare(req);
query.bindValue(":name", name);
query.bindValue(":password", HashPassword(""));
EXECUTE_PREPARED_SQL_UPDATE();
}
/*
Shared accounts not currently supported
*/
void Database::KillMe(User* user)
{
QString req;
std::vector<Account>::iterator it;
req = QString("DELETE FROM preference WHERE user='%1'").arg(user->_id);
EXECUTE_SQL_UPDATE(req, );
if (!user->_accounts.empty())
{
for (it = user->_accounts.begin(); it != user->_accounts.end(); it++)
DeleteAccount(user, *it, 0);
it = user->_accounts.begin();
if (it->is_owner)
req = "DELETE FROM operation WHERE account IN('" + QString::number(it->id);
else
req = "DELETE FROM operation WHERE account IN('-1";
it++;
for (;it != user->_accounts.end(); it++)
{
if (it->is_owner)
req += "', '" + QString::number(it->id) ;
}
req += "')";
req += " OR (user='" + QString::number(user->_id) + "' AND account='')";
EXECUTE_SQL_UPDATE(req, );
}
req = QString("DELETE FROM category WHERE user='%1'").arg(user->_id);
EXECUTE_SQL_UPDATE(req, );
req = QString("DELETE FROM tag WHERE user='%1'").arg(user->_id);
EXECUTE_SQL_UPDATE(req, );
req = QString("DELETE FROM user WHERE id='%1'").arg(user->_id);
EXECUTE_SQL_UPDATE(req, );
}
void Database::UpdatePreference(User* user, const QString& preference)
{
QString req;
QString value = user->_preferences[preference];
QSqlQuery query(_db);
req = "INSERT or REPLACE INTO preference ('user', 'name', 'value') VALUES ('%1', '%2', '%3')" ;
req = req.arg(QString::number(user->_id), preference, value);
EXECUTE_SQL_UPDATE(req, );
}
std::vector<Operation>* Database::Search(User* user, QString* description, QDate* dateFrom, QDate* dateTo,
int* amountFrom, int* amountTo,
std::vector<int> categories, int types, std::vector<int> accounts, bool wildcards, std::vector<int> tags)
{
QSqlRecord set;
QSqlQuery query(_db);
QString req, v;
bool firstCond = false;
std::vector<int>::iterator it;
std::vector<Account>::iterator accountIt;
std::vector<Operation>* res = new std::vector<Operation>;
int i;
QString dayFrom, monthFrom, yearFrom;
QString dayTo, monthTo, yearTo;
QString desc;
if (dateFrom)
{
dayFrom = QString::number(dateFrom->day()-1);
monthFrom = QString::number(dateFrom->month()-1);
yearFrom = QString::number(dateFrom->year());
}
if (dateTo)
{
dayTo = QString::number(dateTo->day()-1);
monthTo = QString::number(dateTo->month()-1);
yearTo = QString::number(dateTo->year());
}
req = "SELECT * from operation WHERE ";
if (description)
{
desc = *description;
ESCAPE_CHARS(desc);
if (wildcards)
req += "UPPER(description) LIKE UPPER(\"%" + desc + "%\")";
else
req += "description=\"" + desc + "\"";
firstCond = true;
}
if (dateFrom)
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "(";
req += "year >= " + yearFrom ;
req += " AND (month > '" + monthFrom + "' OR (month == '" + monthFrom + "' AND day >= '" + dayFrom + "')";
req += " OR (month < '" + monthFrom + "' AND year > '" + yearFrom + "'))";
req += ")";
}
if (dateTo)
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "(";
req += "year <= " + yearTo ;
req += " AND (month < '" + monthTo + "' OR (month == '" + monthTo + "' AND day <= '" + dayTo + "')";
req += " OR (month > '" + monthTo + "' AND year < '" + yearTo + "'))";
req += ")";
}
if (amountFrom)
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "ABS(amount) >= " + v.sprintf("%d", *amountFrom);
}
if (amountTo)
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "ABS(amount) <= " + v.sprintf("%d", *amountTo);
}
if (categories.size())
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "category IN ('";
it = categories.begin();
req += QString::number(*it);
it++;
for (; it != categories.end(); it++)
req += "', '" + QString::number(*it) ;
req += "')";
}
if (tags.size())
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "tag IN ('";
it = tags.begin();
req += QString::number(*it);
it++;
for (; it != tags.end(); it++)
req += "', '" + QString::number(*it) ;
req += "')";
}
if (types)
{
if (((types & Database::FIX_OP) && !(types & Database::NON_FIX_OP)) ||
(!(types & Database::FIX_OP) && (types & Database::NON_FIX_OP)))
{
if (firstCond) req += " AND " ; else firstCond = true;
if (types & Database::FIX_OP)
req += " fix_cost='1'";
else
req += " fix_cost='0'";
}
if (((types & Database::CHECKED_OP) && !(types & Database::NOT_CHECKED_OP)) ||
(!(types & Database::CHECKED_OP) && (types & Database::NOT_CHECKED_OP)))
{
if (firstCond) req += " AND " ; else firstCond = true;
if (types & Database::CHECKED_OP)
req += " checked='1'";
else
req += " checked='0'";
}
}
if (firstCond) req += " AND " ; else firstCond = true;
if (accounts.size())
{
req += "(account IN ('";
it = accounts.begin();
req += QString::number(*it);
it++;
for (; it != accounts.end(); it++)
req += "', '" + QString::number(*it) ;
req += "'))";
// req += wxT(" OR user ='") + user->_id + "')";
}
else
{
req += "(account IN ('";
accountIt = user->_accounts.begin();
req += QString::number(accountIt->id);
accountIt++;
for (;accountIt != user->_accounts.end(); accountIt++)
{
req += "', '" + QString::number(accountIt->id) ;
}
req += "')";
req += " OR user ='" + QString::number(user->_id) + "')";
}
req += " ORDER BY year " ;
req += user->_preferences["operation_order"] ;
req += ", month " + user->_preferences["operation_order"] ;
req += ", day " + user->_preferences["operation_order"] ;
// std::cout << req.toStdString() << "\n";
EXECUTE_SQL_QUERY(req, res);
while (query.next())
{
Operation op;
set = query.record();
fillOperation(&op, set);
res->push_back(op);
}
for(i=0; i < (int)res->size(); i++)
if ((*res)[i].parent)
user->Group(res, (*res)[i]);
return res;
}
void Database::GetStats(User* user, int monthFrom, int yearFrom, int monthTo,
int yearTo, std::map<int, std::map<int, std::map<int, int> > >* accountAmounts,
std::map<int, int>* categories, std::map<int, int>* tags)
{
QSqlRecord set;
QSqlQuery query(_db);
QString req, req2;
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
std::vector<Tag>::iterator tagIt;
if (!user->_accounts.empty())
{
if (accountAmounts)
{
for (accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++)
{
req = "SELECT month, year, amount FROM account_amount WHERE account ='" + QString::number(accountIt->id) + "'";
req += " AND (year > '" + QString::number(yearFrom) + "' OR (year == '" + QString::number(yearFrom) + "' AND month >= '" + QString::number(monthFrom) + "'))";
req += " AND (year < '" + QString::number(yearTo) + "' OR (year == '" + QString::number(yearTo) + "' AND month <= '" + QString::number(monthTo) + "'))";
EXECUTE_SQL_QUERY(req, );
while (query.next())
{
set = query.record();
(*accountAmounts)[accountIt->id][set.value("year").toInt()][set.value("month").toInt()] = set.value("amount").toInt();
}
query.clear();
}
}
if (categories)
{
for (categoryIt = user->_categories.begin(); categoryIt != user->_categories.end(); categoryIt++)
{
if (categoryIt->id == 1)
req = "SELECT SUM(amount) as amount FROM operation AS o1 WHERE (category='" + QString::number(categoryIt->id) + "' OR fix_cost='1')";
else
req = "SELECT SUM(amount) as amount FROM operation AS o1 WHERE category='" + QString::number(categoryIt->id) + "'";
accountIt = user->_accounts.begin();
req += " AND (account IN('" + QString::number(accountIt->id);
accountIt++;
for (;accountIt != user->_accounts.end(); accountIt++)
{
req += "', '" + QString::number(accountIt->id) ;
}
req += "')";
req += " OR user='" + QString::number(user->_id) + "')";
req += " AND (year > '" + QString::number(yearFrom) + "' OR (year == '" + QString::number(yearFrom) + "' AND month >= '" + QString::number(monthFrom) + "'))";
req += " AND (year < '" + QString::number(yearTo) + "' OR (year == '" + QString::number(yearTo) + "' AND month <= '" + QString::number(monthTo) + "'))";
req += " AND meta='0'";
req2 = req + " AND (transfert='' OR transfert IS 0)";
req2 += " AND amount < 0";
EXECUTE_SQL_QUERY(req2, );
if (query.next())
{
set = query.record();
(*categories)[categoryIt->id] = -set.value("amount").toInt();
}
query.clear();
// Transfert on blocked accounts must be computed
req2 = req + " AND transfert != ''";
req2 += " AND (SELECT blocked FROM account WHERE id=o1.account)";
req2 += " AND amount > 0";
EXECUTE_SQL_QUERY(req2, );
if (query.next())
{
set = query.record();
(*categories)[categoryIt->id] += set.value("amount").toInt();
}
query.clear();
}
}
if (tags)
{
for (tagIt = user->_tags.begin(); tagIt != user->_tags.end(); tagIt++)
{
req = "SELECT SUM(amount) as amount FROM operation AS o1 WHERE tag='" + QString::number(tagIt->id) + "'";
accountIt = user->_accounts.begin();
req += " AND (account IN('" + QString::number(accountIt->id);
accountIt++;
for (;accountIt != user->_accounts.end(); accountIt++)
{
req += "', '" + QString::number(accountIt->id) ;
}
req += "')";
req += " OR user='" + QString::number(user->_id) + "')";
req += " AND (year > '" + QString::number(yearFrom) + "' OR (year == '" + QString::number(yearFrom) + "' AND month >= '" + QString::number(monthFrom) + "'))";
req += " AND (year < '" + QString::number(yearTo) + "' OR (year == '" + QString::number(yearTo) + "' AND month <= '" + QString::number(monthTo) + "'))";
req += " AND meta='0'";
req2 = req + " AND (transfert='' OR transfert IS 0)";
req2 += " AND amount < 0";
EXECUTE_SQL_QUERY(req2, );
if (query.next())
{
set = query.record();
(*tags)[tagIt->id] = -set.value("amount").toInt();
}
query.clear();
// Transfert on blocked accounts must be computed
req2 = req + " AND transfert != ''";
req2 += " AND (SELECT blocked FROM account WHERE id=o1.account)";
req2 += " AND amount > 0";
EXECUTE_SQL_QUERY(req2, );
if (query.next())
{
set = query.record();
(*tags)[tagIt->id] += set.value("amount").toInt();
}
query.clear();
}
}
}
}
void Database::GetMonthStats(User* user, int month, int year, int nbDays,
std::map<int, std::vector<int> >* operations,
std::map<int, int>* categories, std::map<int, int>* tags)
{
QSqlRecord set;
QSqlQuery query(_db);
QString req;
std::vector<Account>::iterator accountIt;
int previous_amount, previous_day, cur_day;
if (!user->_accounts.empty())
{
for (accountIt = user->_accounts.begin(); accountIt != user->_accounts.end(); accountIt++)
{
req = "SELECT amount FROM account_amount WHERE account ='%1' AND year='%2' AND month='%3'";
req = req.arg(QString::number(accountIt->id), QString::number(year), QString::number(month));
EXECUTE_SQL_QUERY(req, );
while (query.next())
{
set = query.record();
(*operations)[accountIt->id].clear();
previous_amount = set.value("amount").toInt();
}
query.clear();
req = "SELECT day, amount FROM operation WHERE account='%1' AND year='%2' AND month='%3' AND meta='0' ORDER BY day ASC";
req = req.arg(QString::number(accountIt->id), QString::number(year), QString::number(month));
EXECUTE_SQL_QUERY(req, );
cur_day = previous_day = -1;
while (query.next())
{
set = query.record();
cur_day = set.value("day").toInt();
while (cur_day != previous_day)
{
(*operations)[accountIt->id].push_back(previous_amount);
previous_day++;
}
previous_amount += set.value("amount").toInt();
(*operations)[accountIt->id][cur_day] = previous_amount;
}
query.clear();
while (cur_day < nbDays)
{
(*operations)[accountIt->id].push_back(previous_amount);
cur_day++;
}
}
}
// Fill categories
GetStats(user, month, year, month, year, 0, categories, tags) ;
}
std::map<int, int>* Database::GetNotChecked(User* user, int month, int year)
{
std::vector<Account>::iterator accountIt;
std::map<int, int>* res = new std::map<int, int>;
QSqlRecord set;
QSqlQuery query(_db);
QString req;
for (accountIt = user->_accounts.begin() ;accountIt != user->_accounts.end(); accountIt++)
{
req = "SELECT SUM(amount) AS amount FROM operation WHERE account='" + QString::number(accountIt->id) + "'";
req += " AND checked='0'";
req += " AND meta='0'";
req += " AND virtual='0'";
req += " AND (year < '" + QString::number(year) + "'" ;
req += " OR (year == '" + QString::number(year) + "'" ;
req += " AND month < '" + QString::number(month) + "'" ;
req += "))";
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, delete res, delete res);
if (query.next())
{
set = query.record();
(*res)[accountIt->id] = set.value("amount").toInt();
}
query.clear();
}
return res;
}
std::map<int, int>* Database::GetVirtualAmount(User* user, int month, int year)
{
std::vector<Account>::iterator accountIt;
std::map<int, int>* res = new std::map<int, int>;
QSqlRecord set;
QSqlQuery query(_db);
QString req;
for (accountIt = user->_accounts.begin() ;accountIt != user->_accounts.end(); accountIt++)
{
req = "SELECT SUM(amount) AS amount FROM operation WHERE account='" + QString::number(accountIt->id) + "'";
req += " AND virtual='1'";
req += " AND meta='0'";
req += " AND (year < '" + QString::number(year) + "'" ;
req += " OR (year == '" + QString::number(year) + "'" ;
req += " AND month < '" + QString::number(month) + "'" ;
req += "))";
EXECUTE_SQL_QUERY_WITH_CODE(req, 0, delete res, delete res);
if (query.next())
{
set = query.record();
(*res)[accountIt->id] = set.value("amount").toInt();
}
query.clear();
}
return res;
}
std::map<QString, QString> Database::getSharedAccountOwners(int account)
{
std::map<QString, QString> res;
QSqlRecord set, set2;
QSqlQuery query(_db);
QString req, user;
req = QString("SELECT user FROM shared_account WHERE account='%1'").arg(account);
EXECUTE_SQL_QUERY(req, res);
while(query.next())
{
set = query.record();
user = set.value("user").toString();
req = QString("SELECT name FROM user WHERE id='%1'").arg(set.value("user").toString());
query.clear();
EXECUTE_SQL_QUERY(req, res);
if (query.next())
{
set2 = query.record();
res[set2.value("name").toString()] = user;
}
}
return res;
}
QString Database::getSharedAccountOwner(int account)
{
QSqlRecord set ;
QSqlQuery query(_db);
QString req;
req = QString("SELECT user FROM account WHERE id='%1'").arg(account);
EXECUTE_SQL_QUERY(req, "");
if (query.next())
{
set = query.record();
req = QString("SELECT name FROM user WHERE id='%1'").arg(set.value("user").toString());
query.clear();
EXECUTE_SQL_QUERY(req, "");
if (query.next())
{
set = query.record();
return set.value("name").toString();
}
}
return "";
}
void Database::UpdateImportPattern(User* user)
{
std::map<QString, ImportPattern>::iterator it;
QString req, key;
QSqlQuery query(_db);
for (it = user->_importPatterns.begin();
it != user->_importPatterns.end();
it++)
{
if (it->second.pattern == ImportEngine::NULL_IMPORT_PATTERN) continue;
key = ImportEngine::RemoveUnused(it->first);
req = "INSERT or REPLACE INTO import_pattern ('user', 'description', 'pattern', 'account', 'category') VALUES " ;
req += "('%1', '%2', '%3', '%4', '%5')";
req = req.arg(QString::number(user->_id), key, it->second.pattern, QString::number(it->second.account), QString::number(it->second.category));
EXECUTE_SQL_UPDATE(req, );
}
}
void Database::GetHistory(int month, int year, QStringList& list)
{
QSqlRecord set ;
QSqlQuery query(_db);
QString req;
req = QString("SELECT DISTINCT description AS d FROM operation");
req += " WHERE (year > '" + QString::number(year) + "' OR (year == '" + QString::number(year) + "' AND month >= '" + QString::number(month) + "'))";
req += " ORDER by description";
EXECUTE_SQL_QUERY(req, );
while (query.next())
{
set = query.record();
list << set.value("d").toString();
}
}
bool Database::ChangeDatabase(QString filename)
{
QString oldFilename = _db.databaseName();
_db.close();
_db.setDatabaseName(filename);
if (!_db.open())
{
QMessageBox::critical(0, _("Error"), _("Unable to open database"));
_db.setDatabaseName(oldFilename);
_db.open();
return false;
}
try {
CheckDatabaseVersion();
}
catch(...)
{
_db.setDatabaseName(oldFilename);
_db.open();
return false;
}
return true;
}