Tag management : first version

This commit is contained in:
Grégory Soutadé 2014-11-10 11:54:28 +01:00
parent 899dc3db71
commit 57d4b90182
32 changed files with 868 additions and 60 deletions

View File

@ -3,8 +3,9 @@ CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR(255), password VARCHAR(2
CREATE TABLE account(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255), number VARCHAR(255), shared CHAR(1), blocked CHAR(1), default_account CHAR(1), virtual CHAR(1), hidden CHAR(1));
CREATE TABLE shared_account(account REFERENCES account(id), user REFERENCES user(id));
CREATE TABLE account_amount(account REFERENCES account(id), year INTEGER, month INTEGER, amount INTEGER, PRIMARY KEY(account, year, month));
CREATE TABLE operation(id INTEGER PRIMARY KEY, parent REFERENCES operation(id), user REFERENCES user(id), account REFERENCES account(id) ON DELETE SET NULL, year INTEGER, month INTEGER, day INTEGER, amount INTEGER, description VARCHAR(255), category REFERENCES category(id) ON DELETE SET NULL, fix_cost CHAR(1), checked CHAR(1), formula VARCHAR(255), transfert REFERENCES operation(id), meta CHAR(1), virtual CHAR(1));
CREATE TABLE operation(id INTEGER PRIMARY KEY, parent REFERENCES operation(id), user REFERENCES user(id), account REFERENCES account(id) ON DELETE SET NULL, year INTEGER, month INTEGER, day INTEGER, amount INTEGER, description VARCHAR(255), category REFERENCES category(id) ON DELETE SET NULL, fix_cost CHAR(1), checked CHAR(1), formula VARCHAR(255), transfert REFERENCES operation(id), meta CHAR(1), virtual CHAR(1), tag REFERENCES tag(id));
CREATE TABLE category(id INTEGER PRIMARY KEY, user REFERENCES user(id), parent REFERENCES category(id) ON DELETE SET NULL, name VARCHAR(255), backcolor VARCHAR(10), forecolor VARCHAR(10), font VARCHAR(255), fix_cost CHAR(1));
CREATE TABLE preference(user REFERENCES user(id), name VARCHAR(255), value VARCHAR(255), PRIMARY KEY(user, name));
CREATE TABLE import_pattern(user REFERENCES user(id), description VARCHAR(255), pattern VARCHAR(255), account REFERENCES account(id), category REFERENCES category(id), PRIMARY KEY(user, description));
INSERT INTO kisscount ("db_version") VALUES ("3");
CREATE TABLE tag(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255));
INSERT INTO kisscount ("db_version") VALUES ("4");

View File

@ -251,6 +251,33 @@ void KissCount::DeleteCategory(Category& category, int replacement)
LoadYear(it->first, true);
}
int KissCount::AddTag(Tag& tag)
{
tag.id = _db->AddTag(_user, tag);
_user->AddTag(tag);
return tag.id;
}
void KissCount::UpdateTag(Tag& tag)
{
_db->UpdateTag(tag);
_user->UpdateTag(tag);
}
void KissCount::DeleteTag(Tag& tag, int replacement)
{
std::map<unsigned int, std::map<unsigned int, std::vector<Operation> >* >::iterator it;
_db->DeleteTag(_user, tag, replacement);
_user->DeleteTag(tag);
for (it= _user->_operations.begin();
it != _user->_operations.end();
it++)
LoadYear(it->first, true);
}
std::map<int, std::vector<int> > KissCount::GetAllOperations()
{
return _db->GetAllOperations(_user);
@ -411,10 +438,11 @@ const QString& KissCount::GetOperationOrder()
std::vector<Operation>* KissCount::Search(QString* description, QDate* dateFrom, QDate* dateTo,
int* amountFrom, int* amountTo,
std::vector<int> categories, int types, std::vector<int> accounts)
std::vector<int> categories,
int types, std::vector<int> accounts, std::vector<int> tags)
{
return _db->Search(_user, description, dateFrom, dateTo, amountFrom, amountTo, categories, types, accounts, true);
return _db->Search(_user, description, dateFrom, dateTo, amountFrom, amountTo, categories, types, accounts, true, tags);
}
bool KissCount::SearchPreviousOperation(Operation* res, Operation& op, int month, int year, bool limitToType, int index)
@ -434,9 +462,9 @@ bool KissCount::SearchPreviousOperation(Operation* res, Operation& op, int month
QDate date = QDate(year, month, 0);
if (limitToType)
operations = _db->Search(_user, &op.description, &date, 0, 0, 0, v, op.fix_cost ? +Database::FIX_OP : +Database::NON_FIX_OP, v, false);
operations = _db->Search(_user, &op.description, &date, 0, 0, 0, v, op.fix_cost ? +Database::FIX_OP : +Database::NON_FIX_OP, v, false, v);
else
operations = _db->Search(_user, &op.description, &date, 0, 0, 0, v, Database::ALL_OP, v, false);
operations = _db->Search(_user, &op.description, &date, 0, 0, 0, v, Database::ALL_OP, v, false, v);
if (!operations->size())
{
@ -471,16 +499,16 @@ void KissCount::GetHistory(int month, int year, QStringList& list)
void KissCount::GetStats(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>* categories, std::map<int, int>* tags)
{
_db->GetStats(_user, monthFrom, yearFrom, monthTo, yearTo, accountAmounts, categories);
_db->GetStats(_user, monthFrom, yearFrom, monthTo, yearTo, accountAmounts, categories, tags);
}
void KissCount::GetMonthStats(int month, int year, int nbDays,
std::map<int, std::vector<int> >* operations,
std::map<int, int>* categories)
std::map<int, int>* categories, std::map<int, int>* tags)
{
_db->GetMonthStats(_user, month, year, nbDays, operations, categories);
_db->GetMonthStats(_user, month, year, nbDays, operations, categories, tags);
}
void KissCount::UpdateStats()

View File

@ -93,6 +93,10 @@ public:
void UpdateCategory(Category& category);
void DeleteCategory(Category& category, int replacement);
int AddTag(Tag& tag);
void UpdateTag(Tag& tag);
void DeleteTag(Tag& tag, int replacement);
std::map<int, std::vector<int> > GetAllOperations();
int GenerateMonth(int monthFrom, int yearFrom, int monthTo, int yearTo);
@ -104,17 +108,18 @@ public:
std::vector<Operation>* Search(QString* description, QDate* dateFrom, QDate* dateTo,
int* amountFrom, int* amountTo,
std::vector<int> categories, int types, std::vector<int> accounts);
std::vector<int> categories,
int types, std::vector<int> accounts, std::vector<int> tags);
bool SearchPreviousOperation(Operation* res, Operation& op, int month, int year, bool limitToType, int index);
void GetStats(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>* categories, std::map<int, int>* tags);
void GetMonthStats(int month, int year, int nbDays,
std::map<int, std::vector<int> >* operations,
std::map<int, int>* categories);
std::map<int, int>* categories, std::map<int, int>* tags);
void UpdateStats();
std::map<int, int>* GetNotChecked(int month, int year);

View File

@ -187,6 +187,7 @@ static inline void fillOperation(Operation* op, const QSqlRecord& set)
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();
@ -219,11 +220,18 @@ static inline void fillCategory(Category* category, const QSqlRecord& set)
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;
@ -311,6 +319,19 @@ User* Database::LoadUser(const QString& name)
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;});
@ -549,9 +570,9 @@ void Database::UpdateOperation(User* user, Operation& op, bool checkTransfert)
ESCAPE_CHARS(op.description);
req = "UPDATE operation SET parent='%1', account='%2', year='%3', month='%4', day='%5', amount='%6', description=\"%7\", category='%8'" ;
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.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));
@ -571,11 +592,11 @@ int Database::AddOperation(User* user, Operation& op, bool checkTransfert)
ESCAPE_CHARS(op.description);
req = "INSERT INTO operation ('user', 'parent', 'account', 'year', 'month', 'day', 'amount', 'description', 'category', 'fix_cost', 'formula', 'transfert', 'meta', 'virtual', 'checked') VALUES ('%1', '%2', '%3', '%4', '%5', '%6', '%7', \"%8\"" ;
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')";
req = req.arg(QString::number(op.category), QString::number(op.fix_cost), op.formula, (op.transfert) ? QString::number(op.transfert): "",
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))
@ -1001,6 +1022,80 @@ bool Database::LoadCategory(int id, const QString& name, Category& category)
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;
@ -1211,6 +1306,9 @@ void Database::KillMe(User* user)
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, );
}
@ -1228,7 +1326,7 @@ void Database::UpdatePreference(User* user, const QString& preference)
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> categories, int types, std::vector<int> accounts, bool wildcards, std::vector<int> tags)
{
QSqlRecord set;
QSqlQuery query(_db);
@ -1318,6 +1416,20 @@ std::vector<Operation>* Database::Search(User* user, QString* description, QDate
req += "')";
}
if (tags.size())
{
if (firstCond) req += " AND " ; else firstCond = true;
req += "category 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)) ||
@ -1398,13 +1510,14 @@ std::vector<Operation>* Database::Search(User* user, QString* description, QDate
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>* 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())
{
@ -1473,12 +1586,59 @@ void Database::GetStats(User* user, int monthFrom, int yearFrom, int monthTo,
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>* categories, std::map<int, int>* tags)
{
QSqlRecord set;
QSqlQuery query(_db);
@ -1535,7 +1695,7 @@ void Database::GetMonthStats(User* user, int month, int year, int nbDays,
}
// Fill categories
GetStats(user, month, year, month, year, 0, categories) ;
GetStats(user, month, year, month, year, 0, categories, tags) ;
}
std::map<int, int>* Database::GetNotChecked(User* user, int month, int year)

View File

@ -88,7 +88,7 @@ public:
static const int NOT_CHECKED_OP = (1 << 3);
static const int ALL_OP = (~0);
static const int VERSION = 3;
static const int VERSION = 4;
Database(const char* filename, KissCount* kiss);
@ -128,6 +128,11 @@ public:
void DeleteCategory(User* user, Category& category, int replacement);
bool LoadCategory(int id, const QString& name, Category& category);
int AddTag(User* user, Tag& tag);
void UpdateTag(Tag& tag);
void DeleteTag(User* user, Tag& tag, int replacement);
bool LoadTag(int id, const QString& name, Tag& tag);
std::map<int, std::vector<int> > GetAllOperations(User* user);
void GenerateMonth(User* user, int monthFrom, int yearFrom, int monthTo, int yearTo);
@ -140,15 +145,15 @@ public:
std::vector<Operation>* 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> categories, int types, std::vector<int> accounts, bool wildcards, std::vector<int> tags);
void 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>* categories, std::map<int, int>* tags);
void 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>* categories, std::map<int, int>* tags);
void KillMe(User* user);
bool GetOperation(int id, Operation* op);

View File

@ -94,9 +94,28 @@ static void Version_2_to_3(QSqlDatabase& _db)
ON_ERROR(_("Cannot update database version 2 to version 3 because some columns needs to be deleted."));
}
static void Version_3_to_4(QSqlDatabase& _db)
{
QString req ;
req = "CREATE TABLE tag(id INTEGER PRIMARY KEY, user REFERENCES user(id), name VARCHAR(255));";
UPDATE_TABLE("2", "3", "1");
/* Account */
req = "ALTER TABLE operation ADD tag REFERENCES tag(id)";
UPDATE_TABLE("2", "3", "2");
req = "UPDATE operation SET tag='0'";
UPDATE_TABLE("2", "3", "3");
}
static update_func updates[] = {
Version_1_to_2,
Version_2_to_3
Version_2_to_3,
Version_3_to_4
};
void Database::CheckDatabaseVersion()

View File

@ -40,6 +40,7 @@ struct Operation {
bool meta;
bool _virtual;
std::vector<int> childs;
int tag;
bool operator == (int opId)
{

View File

@ -149,6 +149,76 @@ void User::DeleteCategory(const Category& cat)
}
}
Tag User::GetTag(int tagId)
{
Tag tag;
std::vector<Tag>::iterator it = std::find(_tags.begin(), _tags.end(), tagId);
if (it !=_tags.end()) return *it;
if (_db->LoadTag(tagId, "", tag))
return tag;
tag.id = 0;
tag.name = "";
return tag;
}
QString User::GetTagName(int tagId)
{
Tag tag;
std::vector<Tag>::iterator it = std::find(_tags.begin(), _tags.end(), tagId);
if (it !=_tags.end()) return it->name;
if (_db->LoadTag(tagId, "", tag))
return tag.name;
return _("Unknown") ;
}
int User::GetTagId(const QString& tagName) throw (TagNotFound)
{
std::vector<Tag>::iterator it;
Tag tag;
for (it=_tags.begin(); it !=_tags.end(); it++)
if (_(it->name.toStdString().c_str()) == tagName)
return it->id;
if ( _db->LoadTag(0, tagName, tag))
return tag.id;
throw TagNotFound();
}
void User::AddTag(const Tag& tag)
{
_tags.push_back(tag);
}
void User::UpdateTag(const Tag& tag)
{
std::vector<Tag>::iterator it = std::find(_tags.begin(), _tags.end(), tag.id);
if (it !=_tags.end())
{
*it = tag;
}
}
void User::DeleteTag(const Tag& tag)
{
std::vector<Tag>::iterator it = std::find(_tags.begin(), _tags.end(), tag.id);
if (it !=_tags.end())
{
int pos = it - _tags.begin();
_tags.erase(_tags.begin()+pos);
}
}
Account User::GetAccount(int accountId) throw (AccountNotFound)
{
std::vector<Account>::iterator it = std::find(_accounts.begin(), _accounts.end(), accountId);
@ -214,6 +284,11 @@ int User::GetCategoriesNumber()
return _categories.size();
}
int User::GetTagsNumber()
{
return _tags.size();
}
int User::GetAccountsNumber()
{
return _accounts.size();

View File

@ -26,6 +26,7 @@
#include <QFont>
#include "Category.hpp"
#include "Tag.hpp"
#include "Account.hpp"
#include "Operation.hpp"
#include "Database.hpp"
@ -47,12 +48,14 @@ public:
std::vector<Account> _accounts;
std::map<unsigned int, std::map<unsigned int, std::vector<Operation> >* > _operations;
std::vector<Category> _categories;
std::vector<Tag> _tags;
std::vector<QFont> _categoriesFonts;
std::map<QString, QString> _preferences;
std::map<QString, ImportPattern> _importPatterns;
class AccountNotFound {};
class CategoryNotFound {};
class TagNotFound {};
Category GetCategory(int catId);
QString GetCategoryName(int catId);
@ -61,6 +64,13 @@ public:
void AddCategory(const Category& cat);
void UpdateCategory(const Category& cat);
void DeleteCategory(const Category& cat);
Tag GetTag(int tagId);
QString GetTagName(int tagId);
int GetTagId(const QString& tagName) throw (TagNotFound);
void AddTag(const Tag& tag);
void UpdateTag(const Tag& tag);
void DeleteTag(const Tag& tag);
Account GetAccount(int accountId) throw (AccountNotFound);
@ -73,6 +83,7 @@ public:
int GetCategoriesNumber();
int GetTagsNumber();
int GetAccountsNumber();
int GetOperationsNumber(int month, int year);

View File

@ -145,13 +145,38 @@ bool CSVExportEngine::SaveCategories()
return true;
}
bool CSVExportEngine::SaveTags()
{
Tag tag;
std::map<int, int>::iterator it;
QString v;
*_writer << "Tags" << endl << endl;
*_writer << "id;name" << endl;
for(it=_tags.begin(); it!=_tags.end(); it++)
{
tag = _user->GetTag(it->first);
ESCAPE_CHARS(tag.name);
*_writer << QString::number(tag.id) << ";";
*_writer << "\"" << tag.name << "\"" << ";";
*_writer << endl;
}
*_writer << endl << endl;
return true;
}
bool CSVExportEngine::SaveOperations(std::vector<Operation>* operations)
{
std::vector<Operation>::iterator it;
QString v;
*_writer << "Operations" << endl << endl;
*_writer << "id;parent;day;month;year;amount;description;category;fix_cost;account;checked;transfert;formula;meta;virtual" << endl;
*_writer << "id;parent;day;month;year;amount;description;category;fix_cost;account;checked;transfert;formula;meta;virtual;tag" << endl;
for(it=operations->begin(); it!=operations->end(); it++)
{
@ -172,6 +197,7 @@ bool CSVExportEngine::SaveOperations(std::vector<Operation>* operations)
*_writer << it->formula << ";";
*_writer << (it->meta ? "1" : "0") << ";";
*_writer << (it->_virtual ? "1" : "0");
*_writer << QString::number(it->tag) << ";";
*_writer << endl;
}
@ -202,6 +228,7 @@ bool CSVExportEngine::SaveFile(std::vector<Operation>* operations)
SaveAccounts();
SaveAccountAmounts();
SaveCategories();
SaveTags();
SaveOperations(operations);
file.flush();

View File

@ -38,6 +38,7 @@ private:
bool SaveAccounts();
bool SaveAccountAmounts();
bool SaveCategories();
bool SaveTags();
bool SaveOperations(std::vector<Operation>* operations);
};

View File

@ -55,6 +55,7 @@ protected:
std::map<int, int> _accounts;
std::map<int, int> _categories;
std::map<int, int> _tags;
std::map<AccountAmount, int, AccountAmount> _accountAmounts;
};

View File

@ -133,6 +133,27 @@ bool XMLExportEngine::SaveCategories()
return true;
}
bool XMLExportEngine::SaveTags()
{
Tag tag;
std::map<int, int>::iterator it;
QString v;
for(it=_tags.begin(); it!=_tags.end(); it++)
{
tag = _user->GetTag(it->first);
ESCAPE_CHARS(tag.name);
_writer->writeStartElement("tag");
_writer->writeAttribute("id", QString::number(tag.id));
_writer->writeAttribute("name", tag.name);
_writer->writeEndElement();
}
return true;
}
bool XMLExportEngine::SaveOperations(std::vector<Operation>* operations)
{
std::vector<Operation>::iterator it;
@ -151,6 +172,7 @@ bool XMLExportEngine::SaveOperations(std::vector<Operation>* operations)
_writer->writeAttribute("amount", v.sprintf("%d", it->amount));
_writer->writeAttribute("description", it->description);
_writer->writeAttribute("category", QString::number(it->category));
_writer->writeAttribute("tag", QString::number(it->tag));
_writer->writeAttribute("fix_cost", (it->fix_cost ? "1" : "0"));
_writer->writeAttribute("account", QString::number(it->account));
_writer->writeAttribute("checked", (it->checked ? "1" : "0"));
@ -191,6 +213,7 @@ bool XMLExportEngine::SaveFile(std::vector<Operation>* operations)
SaveAccounts();
SaveAccountAmounts();
SaveCategories();
SaveTags();
SaveOperations(operations);
_writer->writeEndElement();

View File

@ -38,6 +38,7 @@ private:
bool SaveAccounts();
bool SaveAccountAmounts();
bool SaveCategories();
bool SaveTags();
bool SaveOperations(std::vector<Operation>* operations);
};

View File

@ -101,6 +101,7 @@ void GrisbiImportEngine::LoadOperation(const QXmlAttributes& attrs)
op.parent = 0;
op.account = 0;
op.category = 0;
op.tag = 0;
op.fix_cost = false;
op.checked = false;
op.transfert = 0;

View File

@ -196,6 +196,7 @@ void ImportEngine::ApplyPattern(ImportPattern& pattern, Operation& op)
op.account = pattern.account;
op.category = pattern.category;
op.tag = pattern.tag;
if (pattern.pattern == NULL_IMPORT_PATTERN) return;
@ -250,6 +251,7 @@ int ImportEngine::UpdatePattern(int pos)
pattern.pattern = FindPattern(_descriptions[op.id], op.description);
pattern.account = op.account;
pattern.category = op.category;
pattern.tag = op.tag;
_user->_importPatterns[key1] = pattern;
@ -281,6 +283,7 @@ void ImportEngine::MatchPattern(QString& originalKey, Operation& op)
pattern.pattern = FindPattern(originalKey, op.description);
pattern.account = op.account;
pattern.category = op.category;
pattern.tag = op.tag;
_user->_importPatterns[key1] = pattern;
@ -293,13 +296,14 @@ void ImportEngine::MatchPattern(QString& originalKey, Operation& op)
}
}
void ImportEngine::ParseFile(std::vector<Account>& accounts, std::vector<Category>& categories)
void ImportEngine::ParseFile(std::vector<Account>& accounts, std::vector<Category>& categories, std::vector<Tag>& tags)
{
accounts = _unresolvedAccounts;
categories = _unresolvedCategories;
tags = _unresolvedTags;
}
std::vector<Operation>* ImportEngine::GetOperations(std::map<int, int>& accounts, std::map<int, int>& categories)
std::vector<Operation>* ImportEngine::GetOperations(std::map<int, int>& accounts, std::map<int, int>& categories, std::map<int, int>& tags)
{
int i;
std::map<AccountAmount, int, AccountAmount>::iterator it;
@ -320,6 +324,11 @@ std::vector<Operation>* ImportEngine::GetOperations(std::map<int, int>& accounts
else
_operations[i].category = _categories[_operations[i].category];
if (_tags[_operations[i].tag] < 0)
_operations[i].tag = tags[_operations[i].tag];
else
_operations[i].tag = _tags[_operations[i].tag];
}
for(it=_accountAmounts.begin(); it!=_accountAmounts.end(); it++)

View File

@ -31,6 +31,7 @@ public:
QString pattern;
int account;
int category;
int tag;
} ;
class ImportEngine {
@ -50,10 +51,10 @@ public:
virtual bool HandleFile(const QString& path, User* user, Database* db, KissCount* kiss)=0;
// Parse the file and return accounts that doesn't match
virtual void ParseFile(std::vector<Account>& accounts, std::vector<Category>& categories);
virtual void ParseFile(std::vector<Account>& accounts, std::vector<Category>& categories, std::vector<Tag>& tags);
// Final Step
virtual std::vector<Operation>* GetOperations(std::map<int, int>& accounts, std::map<int, int>& categories);
virtual std::vector<Operation>* GetOperations(std::map<int, int>& accounts, std::map<int, int>& categories, std::map<int, int>& tags);
const std::map<AccountAmount, int, AccountAmount>& GetAccountAmounts();
@ -73,8 +74,10 @@ protected:
std::map<int, int> _accounts;
std::map<int, int> _categories;
std::map<int, int> _tags;
std::vector<Account> _unresolvedAccounts;
std::vector<Category> _unresolvedCategories;
std::vector<Tag> _unresolvedTags;
std::vector<Operation> _operations;
std::map<int, QString> _descriptions;
std::map<AccountAmount, int, AccountAmount> _accountAmounts;

View File

@ -81,6 +81,7 @@ int OFXImportEngine::transaction_cb(const struct OfxTransactionData data, void *
op.id = ++id;
op.parent = 0;
op.category = 0;
op.tag = 0;
op.fix_cost = false;
op.account = _this->_curAccount;
op.checked = false;

View File

@ -84,6 +84,9 @@ bool XMLImportEngine::startElement (const QString& namespaceURI, const QString&
else if (qName == "category")
LoadCategory(attrs);
else if (qName == "tag")
LoadTag(attrs);
else if (qName == "operation")
LoadOperation(attrs);
else
@ -201,6 +204,32 @@ void XMLImportEngine::LoadCategory(const QXmlAttributes& attrs)
_unresolvedCategories.push_back(cat);
}
void XMLImportEngine::LoadTag(const QXmlAttributes& attrs)
{
QString name;
int id;
Tag tag;
static int unknownTag = 0;
tag.name = name = attrs.value("name");
tag.id = id = attrs.value("id").toInt();
UNESCAPE_CHARS(tag.name);
try
{
int tagId = _user->GetTagId(name);
_tags[id] = tagId;
return;
}
catch(User::TagNotFound e)
{}
_tags[id] = --unknownTag;
_unresolvedTags.push_back(tag);
}
void XMLImportEngine::LoadOperation(const QXmlAttributes& attrs)
{
Operation op;
@ -213,6 +242,7 @@ void XMLImportEngine::LoadOperation(const QXmlAttributes& attrs)
op.amount = attrs.value("amount").toInt();
op.description = attrs.value("description");
op.category = attrs.value("category").toInt();
op.tag = attrs.value("tag").toInt();
op.fix_cost = (attrs.value("fix_cost") == "1");
op.account = attrs.value("account").toInt();
op.checked = (attrs.value("checked") == "1");

View File

@ -40,6 +40,7 @@ private:
void LoadAccount(const QXmlAttributes& atts);
void LoadAccountAmount(const QXmlAttributes& atts);
void LoadCategory(const QXmlAttributes& atts);
void LoadTag(const QXmlAttributes& atts);
void LoadOperation(const QXmlAttributes& atts);
};

View File

@ -53,6 +53,7 @@ void AccountPanel::Init(KissCount* kiss, wxUI *parent, int curMode)
User* user = _kiss->GetUser();
std::vector<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
std::vector<Tag>::iterator tagIt;
_icons[KissPanel::LOW_RES_ICON] = USER_LOW_ICON;
_icons[KissPanel::HIGH_RES_ICON] = USER_ICON;
@ -92,6 +93,17 @@ void AccountPanel::Init(KissCount* kiss, wxUI *parent, int curMode)
_categoriesValues = new int[user->GetCategoriesNumber()];
_tags = new QString[user->GetTagsNumber()] ;
for(i=0, tagIt = user->_tags.begin();
tagIt != user->_tags.end();
tagIt++, i++)
{
_tags[i] = _(tagIt->name.toStdString().c_str()) ;
_tagsIndexes[tagIt->name] = i;
}
_tagsValues = new int[user->GetTagsNumber()];
_grid = new GridAccount(_kiss, this, true, true, true);
_grid->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@ -179,6 +191,8 @@ AccountPanel::~AccountPanel()
{
delete[] _categoriesValues;
delete[] _categories;
delete[] _tagsValues;
delete[] _tags;
delete[] _accounts;
}

View File

@ -77,11 +77,12 @@ private:
GridAccount* _grid;
QTableWidget* _accountsGrid, *_statsGrid;
CostRepartitionBanner* _costRepartitionBanner;
int *_categoriesValues;
int *_categoriesValues, *_tagsValues;
// wxRadioBox *_radioMode;
std::map<QString, int> _categoriesIndexes;
std::map<QString, int> _tagsIndexes;
std::vector<Operation>* _curOperations;
QString* _categories, *_accounts;
QString* _categories, *_tags, *_accounts;
std::map<int, int> _accountsInitValues;
int _fixCosts;
QRadioButton *_virtual, *_real, *_check;

View File

@ -32,6 +32,7 @@ ImportPanel::ImportPanel(KissCount* kiss, wxUI *parent, bool lowResolution) :
QPushButton* buttonOpen;
QGroupBox* staticAccount = new QGroupBox(_("Unresolved accounts"));
QGroupBox* staticCategory = new QGroupBox(_("Unresolved categories"));
QGroupBox* staticTag = new QGroupBox(_("Unresolved tags"));
_icons[KissPanel::LOW_RES_ICON] = IMPORT_LOW_ICON;
_icons[KissPanel::HIGH_RES_ICON] = IMPORT_ICON;
@ -76,6 +77,13 @@ ImportPanel::ImportPanel(KissCount* kiss, wxUI *parent, bool lowResolution) :
_categoriesGrid->setHorizontalHeaderLabels(labels);
_categoriesGrid->resizeColumnsToContents();
_tagsGrid = new QTableWidget(0, 3);
_tagsGrid->verticalHeader()->setHidden(true);
labels.clear();
labels << _("File tag") << _("Tag name") << _("Internal tag");
_tagsGrid->setHorizontalHeaderLabels(labels);
_tagsGrid->resizeColumnsToContents();
{
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(_accountsGrid);
@ -90,7 +98,14 @@ ImportPanel::ImportPanel(KissCount* kiss, wxUI *parent, bool lowResolution) :
staticCategory->setLayout(vbox);
}
{
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(_tagsGrid);
staticTag->setLayout(vbox);
}
vbox2->addWidget(staticCategory);
vbox2->addWidget(staticTag);
_operationsGrid = new GridAccount(kiss, this, false, false, false);
connect(_operationsGrid, SIGNAL(cellChanged(int, int)), this, SLOT(OnOperationModified(int, int)));
@ -145,6 +160,8 @@ void ImportPanel::ProcessFile()
std::map<int, int> resolvedAccounts;
QString* userCategories;
std::map<int, int> resolvedCategories;
QString* userTags;
std::map<int, int> resolvedTags;
QTableWidgetItem* item;
QString path = _fileTxt->text();
@ -153,6 +170,7 @@ void ImportPanel::ProcessFile()
_buttonIntegrate->setEnabled(false);
_accountsGrid->setRowCount(0);
_categoriesGrid->setRowCount(0);
_tagsGrid->setRowCount(0);
_operationsGrid->setRowCount(0);
_importEngine = _kiss->GetImportEngine(path);
@ -163,7 +181,7 @@ void ImportPanel::ProcessFile()
return ;
}
_importEngine->ParseFile(_unresolvedAccounts, _unresolvedCategories);
_importEngine->ParseFile(_unresolvedAccounts, _unresolvedCategories, _unresolvedTags);
if (_unresolvedAccounts.size())
{
@ -225,7 +243,37 @@ void ImportPanel::ProcessFile()
_categoriesGrid->layout();
}
if (!_unresolvedAccounts.size() && !_unresolvedCategories.size())
if (_unresolvedTags.size())
{
int nb_tags = user->GetTagsNumber();
userTags = new QString[nb_tags+1];
userTags[0] = _("Create one");
for(i=0; i<nb_tags; i++)
userTags[i+1] = user->_tags[i].name;
ChoiceDelegate* tagEditor = new ChoiceDelegate(this, userTags, nb_tags+1);
_tagsGrid->setItemDelegateForColumn(2, tagEditor);
_buttonLoadOperations->setEnabled(true);
_tagsGrid->setRowCount(_unresolvedTags.size());
for (i=0; i<(int)_unresolvedTags.size(); i++)
{
item = new QTableWidgetItem(_unresolvedTags[i].name);
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
_tagsGrid->setItem(i, 0, item);
_tagsGrid->setItem(i, 1, new QTableWidgetItem(""));
_tagsGrid->setItem(i, 2, new QTableWidgetItem(userTags[0]));
}
_tagsGrid->resizeColumnsToContents();
_tagsGrid->layout();
}
if (!_unresolvedAccounts.size() && !_unresolvedCategories.size() && !_unresolvedTags.size())
{
OnLoadOperations();
}
@ -234,12 +282,14 @@ void ImportPanel::ProcessFile()
void ImportPanel::OnLoadOperations()
{
int i, nbAccounts=0, nbCategories=0;
int i, nbAccounts=0, nbCategories=0, nbTags=0;
User* user = _kiss->GetUser();
Account account;
Category category;
Tag tag;
std::map<int, int> accounts;
std::map<int, int> categories;
std::map<int, int> tags;
int oldid;
for(i=0; i<_accountsGrid->rowCount(); i++)
@ -264,7 +314,18 @@ void ImportPanel::OnLoadOperations()
}
}
if (nbAccounts || nbCategories)
for(i=0; i<_tagsGrid->rowCount(); i++)
{
if (_tagsGrid->item(i, 2)->text() == _("Create one"))
nbTags++;
else
{
oldid = _unresolvedTags[i].id;
tags[oldid] = user->GetTagId(_tagsGrid->item(i, 2)->text());;
}
}
if (nbAccounts || nbCategories || nbTags)