1899 lines
52 KiB
C++
1899 lines
52 KiB
C++
/*
|
|
Copyright 2010-2012 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);
|
|
|
|
EXECUTE_SQL_QUERY(QString("SELECT name FROM user WHERE name='%1' AND password='%2'").arg(user, HashPassword(password)), 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();
|
|
}
|
|
|
|
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;
|
|
|
|
EXECUTE_SQL_QUERY(QString("SELECT * FROM user WHERE name='%1'").arg(name), 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();
|
|
|
|
user->_preferences["operation_order"] = "ASC" ;
|
|
|
|
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.transfert)
|
|
{
|
|
// No one or not linked
|
|
if (!GetOperation(op.transfert, &linked) || op.description != linked.description || op.amount != -linked.amount || op.account == linked.account)
|
|
{
|
|
req = QString("UPDATE operation SET transfert='', virtual='0' WHERE id='%1'").arg(op.id);
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
op.transfert = 0;
|
|
op._virtual = false;
|
|
return;
|
|
}
|
|
}
|
|
// Not Linked
|
|
else
|
|
{
|
|
req = QString("SELECT id FROM operation WHERE transfert='%1'").arg(op.id);
|
|
|
|
EXECUTE_SQL_QUERY(req, );
|
|
|
|
if (query.next())
|
|
{
|
|
set = query.record();
|
|
req = QString("UPDATE operation SET transfert='', virtual='0' WHERE id='%1'").arg(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, );
|
|
|
|
op._virtual = false;
|
|
|
|
// Don't need to link
|
|
if (!query.next()) return ;
|
|
|
|
set = query.record();
|
|
|
|
// Link
|
|
linked.id = set.value("id").toInt();
|
|
|
|
op.transfert = linked.id;
|
|
|
|
account = user->GetAccount(op.account);
|
|
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=\"%7\", 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), op.description, QString::number(op.category), QString::number(op.tag));
|
|
req += ", fix_cost='%1', checked='%2', transfert='%3', meta='%4', virtual='%5', formula='%6' 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), op.formula, QString::number(op.id));
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
|
|
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', \"%8\"" ;
|
|
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), op.description);
|
|
req += ", '%1', '%2', '%3', '%4', '%5', '%6', '%7', '%8')";
|
|
req = req.arg(QString::number(op.category), QString::number(op.tag), QString::number(op.fix_cost), op.formula, (op.transfert) ? QString::number(op.transfert): "",
|
|
QString::number(op.meta), QString::number(op._virtual), QString::number(op.checked));
|
|
|
|
if (!query.exec(req))
|
|
{
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
return query.lastInsertId().toInt();
|
|
}
|
|
|
|
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, );
|
|
|
|
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='%1'").arg(granted);
|
|
|
|
EXECUTE_SQL_QUERY(req, );
|
|
|
|
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 += ", '" + category.name + "'";
|
|
req += ", '" + backcolor + "'";
|
|
req += ", '" + forecolor + "'";
|
|
req += ", '" + category.font + "'";
|
|
if (category.fix_cost)
|
|
req += ", '1'";
|
|
else
|
|
req += ", '0'";
|
|
req += ")";
|
|
|
|
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;
|
|
}
|
|
|
|
return query.lastInsertId().toInt();
|
|
}
|
|
|
|
void Database::UpdateCategory(Category& category)
|
|
{
|
|
QString req, tmp;
|
|
QString backcolor, forecolor;
|
|
|
|
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='" + category.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) + "'";
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
}
|
|
|
|
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);
|
|
else
|
|
req = QString("SELECT * FROM category WHERE name='%1'").arg(name);
|
|
|
|
EXECUTE_SQL_QUERY(req, 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 += ", '" + tag.name + "'";
|
|
req += ")";
|
|
|
|
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;
|
|
}
|
|
|
|
return query.lastInsertId().toInt();
|
|
}
|
|
|
|
void Database::UpdateTag(Tag& tag)
|
|
{
|
|
QString req;
|
|
|
|
req = "UPDATE tag SET" ;
|
|
req += "name='" + tag.name + "'";
|
|
req += " WHERE id='" + QString::number(tag.id) + "'";
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
}
|
|
|
|
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);
|
|
else
|
|
req = QString("SELECT * FROM tag WHERE name='%1'").arg(name);
|
|
|
|
EXECUTE_SQL_QUERY(req, 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;
|
|
|
|
req = QString("UPDATE user SET password='%1' WHERE name='%2'").arg(HashPassword(password), user->_name) ;
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
}
|
|
|
|
bool Database::UserExists(const QString& name)
|
|
{
|
|
QSqlRecord set;
|
|
QSqlQuery query(_db);
|
|
QString req;
|
|
bool res=false;
|
|
|
|
req = QString("SELECT name FROM user WHERE name='%1'").arg(name);
|
|
|
|
EXECUTE_SQL_QUERY(req, false);
|
|
|
|
if (query.next())
|
|
res = true;
|
|
else
|
|
res = false;
|
|
|
|
query.clear();
|
|
|
|
return res;
|
|
}
|
|
|
|
void Database::ChangeName(User* user, const QString& name)
|
|
{
|
|
QString req;
|
|
|
|
req = QString("UPDATE user SET name='%1' WHERE name='%2'").arg(name, user->_name) ;
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
}
|
|
|
|
void Database::NewUser(const QString& name)
|
|
{
|
|
QString req;
|
|
|
|
req = QString("INSERT INTO user ('name', 'password') VALUES ('%1', '%2')").arg(name, HashPassword("")) ;
|
|
|
|
EXECUTE_SQL_UPDATE(req, );
|
|
}
|
|
|
|
/*
|
|
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++)
|
|
{
|
|
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 (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;
|
|
}
|