From 57d4b90182b0cadc310ebe9e7f28b1205f8a22ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Mon, 10 Nov 2014 11:54:28 +0100 Subject: [PATCH] Tag management : first version --- ressources/init.sql | 5 +- src/controller/KissCount.cpp | 44 ++++- src/controller/KissCount.hpp | 11 +- src/model/Database.cpp | 178 ++++++++++++++++++- src/model/Database.hpp | 13 +- src/model/DatabaseUpdate.cpp | 21 ++- src/model/Operation.hpp | 1 + src/model/User.cpp | 75 ++++++++ src/model/User.hpp | 11 ++ src/model/export/CSVExportEngine.cpp | 29 ++- src/model/export/CSVExportEngine.hpp | 1 + src/model/export/ExportEngine.hpp | 1 + src/model/export/XMLExportEngine.cpp | 23 +++ src/model/export/XMLExportEngine.hpp | 1 + src/model/import/GrisbiImportEngine.cpp | 1 + src/model/import/ImportEngine.cpp | 13 +- src/model/import/ImportEngine.hpp | 7 +- src/model/import/OFXImportEngine.cpp | 1 + src/model/import/XMLImportEngine.cpp | 30 ++++ src/model/import/XMLImportEngine.hpp | 1 + src/view/AccountPanel.cpp | 14 ++ src/view/AccountPanel.hpp | 5 +- src/view/ImportPanel.cpp | 102 ++++++++++- src/view/ImportPanel.hpp | 4 +- src/view/PreferencesPanel.cpp | 224 +++++++++++++++++++++++- src/view/PreferencesPanel.hpp | 7 +- src/view/SearchBanner.cpp | 30 +++- src/view/SearchBanner.hpp | 2 +- src/view/StatsPanel.cpp | 5 +- src/view/grid/ChoiceDelegate.hpp | 1 + src/view/grid/GridAccount.cpp | 63 ++++++- src/view/grid/GridAccount.hpp | 4 +- 32 files changed, 868 insertions(+), 60 deletions(-) diff --git a/ressources/init.sql b/ressources/init.sql index 11df5de..0e28a23 100644 --- a/ressources/init.sql +++ b/ressources/init.sql @@ -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"); diff --git a/src/controller/KissCount.cpp b/src/controller/KissCount.cpp index 6324a3a..eef9c7f 100644 --- a/src/controller/KissCount.cpp +++ b/src/controller/KissCount.cpp @@ -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 >* >::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 > KissCount::GetAllOperations() { return _db->GetAllOperations(_user); @@ -411,10 +438,11 @@ const QString& KissCount::GetOperationOrder() std::vector* KissCount::Search(QString* description, QDate* dateFrom, QDate* dateTo, int* amountFrom, int* amountTo, - std::vector categories, int types, std::vector accounts) + std::vector categories, + int types, std::vector accounts, std::vector 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 > >* accountAmounts, - std::map* categories) + std::map* categories, std::map* 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 >* operations, - std::map* categories) + std::map* categories, std::map* tags) { - _db->GetMonthStats(_user, month, year, nbDays, operations, categories); + _db->GetMonthStats(_user, month, year, nbDays, operations, categories, tags); } void KissCount::UpdateStats() diff --git a/src/controller/KissCount.hpp b/src/controller/KissCount.hpp index cd1222a..14ee33e 100644 --- a/src/controller/KissCount.hpp +++ b/src/controller/KissCount.hpp @@ -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 > GetAllOperations(); int GenerateMonth(int monthFrom, int yearFrom, int monthTo, int yearTo); @@ -104,17 +108,18 @@ public: std::vector* Search(QString* description, QDate* dateFrom, QDate* dateTo, int* amountFrom, int* amountTo, - std::vector categories, int types, std::vector accounts); + std::vector categories, + int types, std::vector accounts, std::vector 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 > >* accountAmounts, - std::map* categories); + std::map* categories, std::map* tags); void GetMonthStats(int month, int year, int nbDays, std::map >* operations, - std::map* categories); + std::map* categories, std::map* tags); void UpdateStats(); std::map* GetNotChecked(int month, int year); diff --git a/src/model/Database.cpp b/src/model/Database.cpp index d20a29f..594b22d 100644 --- a/src/model/Database.cpp +++ b/src/model/Database.cpp @@ -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 > 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* Database::Search(User* user, QString* description, QDate* dateFrom, QDate* dateTo, int* amountFrom, int* amountTo, - std::vector categories, int types, std::vector accounts, bool wildcards) + std::vector categories, int types, std::vector accounts, bool wildcards, std::vector tags) { QSqlRecord set; QSqlQuery query(_db); @@ -1318,6 +1416,20 @@ std::vector* 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* Database::Search(User* user, QString* description, QDate void Database::GetStats(User* user, int monthFrom, int yearFrom, int monthTo, int yearTo, std::map > >* accountAmounts, - std::map* categories) + std::map* categories, std::map* tags) { QSqlRecord set; QSqlQuery query(_db); QString req, req2; std::vector::iterator accountIt; std::vector::iterator categoryIt; + std::vector::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 >* operations, - std::map* categories) + std::map* categories, std::map* 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* Database::GetNotChecked(User* user, int month, int year) diff --git a/src/model/Database.hpp b/src/model/Database.hpp index 4ac3797..f382619 100644 --- a/src/model/Database.hpp +++ b/src/model/Database.hpp @@ -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 > GetAllOperations(User* user); void GenerateMonth(User* user, int monthFrom, int yearFrom, int monthTo, int yearTo); @@ -140,15 +145,15 @@ public: std::vector* Search(User* user, QString* description, QDate* dateFrom, QDate* dateTo, int* amountFrom, int* amountTo, - std::vector categories, int types, std::vector accounts, bool wildcards); + std::vector categories, int types, std::vector accounts, bool wildcards, std::vector tags); void GetStats(User* user, int monthFrom, int yearFrom, int monthTo, int yearTo, std::map > >* accountAmounts, - std::map* categories); + std::map* categories, std::map* tags); void GetMonthStats(User* user, int month, int year, int nbDays, std::map >* operations, - std::map* categories); + std::map* categories, std::map* tags); void KillMe(User* user); bool GetOperation(int id, Operation* op); diff --git a/src/model/DatabaseUpdate.cpp b/src/model/DatabaseUpdate.cpp index 2a8cec8..3665f9c 100644 --- a/src/model/DatabaseUpdate.cpp +++ b/src/model/DatabaseUpdate.cpp @@ -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() diff --git a/src/model/Operation.hpp b/src/model/Operation.hpp index 35c1e73..6c57278 100644 --- a/src/model/Operation.hpp +++ b/src/model/Operation.hpp @@ -40,6 +40,7 @@ struct Operation { bool meta; bool _virtual; std::vector childs; + int tag; bool operator == (int opId) { diff --git a/src/model/User.cpp b/src/model/User.cpp index 40f6792..804687a 100644 --- a/src/model/User.cpp +++ b/src/model/User.cpp @@ -149,6 +149,76 @@ void User::DeleteCategory(const Category& cat) } } +Tag User::GetTag(int tagId) +{ + Tag tag; + std::vector::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::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::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::iterator it = std::find(_tags.begin(), _tags.end(), tag.id); + + if (it !=_tags.end()) + { + *it = tag; + } +} + +void User::DeleteTag(const Tag& tag) +{ + std::vector::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::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(); diff --git a/src/model/User.hpp b/src/model/User.hpp index f40f5e6..a1886e4 100644 --- a/src/model/User.hpp +++ b/src/model/User.hpp @@ -26,6 +26,7 @@ #include #include "Category.hpp" +#include "Tag.hpp" #include "Account.hpp" #include "Operation.hpp" #include "Database.hpp" @@ -47,12 +48,14 @@ public: std::vector _accounts; std::map >* > _operations; std::vector _categories; + std::vector _tags; std::vector _categoriesFonts; std::map _preferences; std::map _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); diff --git a/src/model/export/CSVExportEngine.cpp b/src/model/export/CSVExportEngine.cpp index 54586e5..29293fd 100644 --- a/src/model/export/CSVExportEngine.cpp +++ b/src/model/export/CSVExportEngine.cpp @@ -145,13 +145,38 @@ bool CSVExportEngine::SaveCategories() return true; } +bool CSVExportEngine::SaveTags() +{ + Tag tag; + std::map::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* operations) { std::vector::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* 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* operations) SaveAccounts(); SaveAccountAmounts(); SaveCategories(); + SaveTags(); SaveOperations(operations); file.flush(); diff --git a/src/model/export/CSVExportEngine.hpp b/src/model/export/CSVExportEngine.hpp index 54dd88d..2efa698 100644 --- a/src/model/export/CSVExportEngine.hpp +++ b/src/model/export/CSVExportEngine.hpp @@ -38,6 +38,7 @@ private: bool SaveAccounts(); bool SaveAccountAmounts(); bool SaveCategories(); + bool SaveTags(); bool SaveOperations(std::vector* operations); }; diff --git a/src/model/export/ExportEngine.hpp b/src/model/export/ExportEngine.hpp index df4ea4c..0b9eca1 100644 --- a/src/model/export/ExportEngine.hpp +++ b/src/model/export/ExportEngine.hpp @@ -55,6 +55,7 @@ protected: std::map _accounts; std::map _categories; + std::map _tags; std::map _accountAmounts; }; diff --git a/src/model/export/XMLExportEngine.cpp b/src/model/export/XMLExportEngine.cpp index 082f3f4..483534c 100644 --- a/src/model/export/XMLExportEngine.cpp +++ b/src/model/export/XMLExportEngine.cpp @@ -133,6 +133,27 @@ bool XMLExportEngine::SaveCategories() return true; } +bool XMLExportEngine::SaveTags() +{ + Tag tag; + std::map::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* operations) { std::vector::iterator it; @@ -151,6 +172,7 @@ bool XMLExportEngine::SaveOperations(std::vector* 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* operations) SaveAccounts(); SaveAccountAmounts(); SaveCategories(); + SaveTags(); SaveOperations(operations); _writer->writeEndElement(); diff --git a/src/model/export/XMLExportEngine.hpp b/src/model/export/XMLExportEngine.hpp index 05d091d..4e4b6b2 100644 --- a/src/model/export/XMLExportEngine.hpp +++ b/src/model/export/XMLExportEngine.hpp @@ -38,6 +38,7 @@ private: bool SaveAccounts(); bool SaveAccountAmounts(); bool SaveCategories(); + bool SaveTags(); bool SaveOperations(std::vector* operations); }; diff --git a/src/model/import/GrisbiImportEngine.cpp b/src/model/import/GrisbiImportEngine.cpp index ef596fa..7525a4e 100644 --- a/src/model/import/GrisbiImportEngine.cpp +++ b/src/model/import/GrisbiImportEngine.cpp @@ -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; diff --git a/src/model/import/ImportEngine.cpp b/src/model/import/ImportEngine.cpp index 84d2e3a..187e5bc 100644 --- a/src/model/import/ImportEngine.cpp +++ b/src/model/import/ImportEngine.cpp @@ -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& accounts, std::vector& categories) +void ImportEngine::ParseFile(std::vector& accounts, std::vector& categories, std::vector& tags) { accounts = _unresolvedAccounts; categories = _unresolvedCategories; + tags = _unresolvedTags; } -std::vector* ImportEngine::GetOperations(std::map& accounts, std::map& categories) +std::vector* ImportEngine::GetOperations(std::map& accounts, std::map& categories, std::map& tags) { int i; std::map::iterator it; @@ -320,6 +324,11 @@ std::vector* ImportEngine::GetOperations(std::map& 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++) diff --git a/src/model/import/ImportEngine.hpp b/src/model/import/ImportEngine.hpp index ee2964f..980c7dc 100644 --- a/src/model/import/ImportEngine.hpp +++ b/src/model/import/ImportEngine.hpp @@ -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& accounts, std::vector& categories); + virtual void ParseFile(std::vector& accounts, std::vector& categories, std::vector& tags); // Final Step - virtual std::vector* GetOperations(std::map& accounts, std::map& categories); + virtual std::vector* GetOperations(std::map& accounts, std::map& categories, std::map& tags); const std::map& GetAccountAmounts(); @@ -73,8 +74,10 @@ protected: std::map _accounts; std::map _categories; + std::map _tags; std::vector _unresolvedAccounts; std::vector _unresolvedCategories; + std::vector _unresolvedTags; std::vector _operations; std::map _descriptions; std::map _accountAmounts; diff --git a/src/model/import/OFXImportEngine.cpp b/src/model/import/OFXImportEngine.cpp index 4fa40d5..1fb8009 100644 --- a/src/model/import/OFXImportEngine.cpp +++ b/src/model/import/OFXImportEngine.cpp @@ -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; diff --git a/src/model/import/XMLImportEngine.cpp b/src/model/import/XMLImportEngine.cpp index a713701..f5eb25c 100644 --- a/src/model/import/XMLImportEngine.cpp +++ b/src/model/import/XMLImportEngine.cpp @@ -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"); diff --git a/src/model/import/XMLImportEngine.hpp b/src/model/import/XMLImportEngine.hpp index 55eb00f..7b2a8ff 100644 --- a/src/model/import/XMLImportEngine.hpp +++ b/src/model/import/XMLImportEngine.hpp @@ -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); }; diff --git a/src/view/AccountPanel.cpp b/src/view/AccountPanel.cpp index cf6690a..2430f27 100644 --- a/src/view/AccountPanel.cpp +++ b/src/view/AccountPanel.cpp @@ -53,6 +53,7 @@ void AccountPanel::Init(KissCount* kiss, wxUI *parent, int curMode) User* user = _kiss->GetUser(); std::vector::iterator accountIt; std::vector::iterator categoryIt; + std::vector::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; } diff --git a/src/view/AccountPanel.hpp b/src/view/AccountPanel.hpp index 251719b..b4ffb38 100644 --- a/src/view/AccountPanel.hpp +++ b/src/view/AccountPanel.hpp @@ -77,11 +77,12 @@ private: GridAccount* _grid; QTableWidget* _accountsGrid, *_statsGrid; CostRepartitionBanner* _costRepartitionBanner; - int *_categoriesValues; + int *_categoriesValues, *_tagsValues; // wxRadioBox *_radioMode; std::map _categoriesIndexes; + std::map _tagsIndexes; std::vector* _curOperations; - QString* _categories, *_accounts; + QString* _categories, *_tags, *_accounts; std::map _accountsInitValues; int _fixCosts; QRadioButton *_virtual, *_real, *_check; diff --git a/src/view/ImportPanel.cpp b/src/view/ImportPanel.cpp index 6a4251b..680e6cc 100644 --- a/src/view/ImportPanel.cpp +++ b/src/view/ImportPanel.cpp @@ -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 resolvedAccounts; QString* userCategories; std::map resolvedCategories; + QString* userTags; + std::map 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_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 accounts; std::map categories; + std::map 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) { QString message, v; @@ -277,6 +338,9 @@ void ImportPanel::OnLoadOperations() if (nbCategories) message += v.sprintf(_("%d categories").toStdString().c_str(), nbCategories); + if (nbTags) + message += v.sprintf(_("%d tags").toStdString().c_str(), nbTags); + message += _(" will be created, is it ok ?"); if (QMessageBox::question(0, "KissCount", message, QMessageBox::Yes|QMessageBox::No) == QMessageBox::No) @@ -328,10 +392,33 @@ void ImportPanel::OnLoadOperations() _categoriesGrid->setRowCount(0); + for(i=0; i<_tagsGrid->rowCount(); i++) + { + if (_tagsGrid->item(i, 2)->text() == _("Create one")) + { + tag = _unresolvedTags[i] ; + if (_tagsGrid->item(i, 1)->text().length()) + tag.name = _tagsGrid->item(i, 1)->text(); + else + tag.name = _tagsGrid->item(i, 0)->text(); + + if (tag.name.length() == 0) + { + QMessageBox::critical(0, _("Error"), _("Tag ") + QString::number(i) + _(" must have a name")); + return; + } + + oldid = tag.id; + tags[oldid] = tag.id = _kiss->AddTag(tag); + } + } + + _tagsGrid->setRowCount(0); + _wxUI->NeedReload(); } - _operations = _importEngine->GetOperations(accounts, categories); + _operations = _importEngine->GetOperations(accounts, categories, tags); if (_operations->size()) { @@ -431,8 +518,9 @@ void ImportPanel::OnOperationModified(int row, int col) { static bool update = false; - if (col != GridAccount::DESCRIPTION && - col != GridAccount::CATEGORY && + if (col != GridAccount::DESCRIPTION && + col != GridAccount::CATEGORY && + col != GridAccount::TAG && col != GridAccount::ACCOUNT) return ; diff --git a/src/view/ImportPanel.hpp b/src/view/ImportPanel.hpp index f9ba797..7708c4e 100644 --- a/src/view/ImportPanel.hpp +++ b/src/view/ImportPanel.hpp @@ -48,7 +48,7 @@ private slots: private: QHBoxLayout *_hbox; - QTableWidget* _accountsGrid, *_categoriesGrid; + QTableWidget* _accountsGrid, *_categoriesGrid, *_tagsGrid; QLineEdit* _fileTxt; GridAccount* _operationsGrid; ImportEngine* _importEngine; @@ -58,8 +58,10 @@ private: std::vector _unresolvedAccounts; std::vector _unresolvedCategories; + std::vector _unresolvedTags; std::map _resolvedAccounts; std::map _resolvedCategories; + std::map _resolvedTags; void ProcessFile(); }; diff --git a/src/view/PreferencesPanel.cpp b/src/view/PreferencesPanel.cpp index 7dd2fe8..6f02252 100644 --- a/src/view/PreferencesPanel.cpp +++ b/src/view/PreferencesPanel.cpp @@ -25,14 +25,16 @@ enum {ACCOUNT_NAME, ACCOUNT_NUMBER, ACCOUNT_DEFAULT, ACCOUNT_VIRTUAL, ACCOUNT_BLOCKED, ACCOUNT_DELETE, ACCOUNT_HIDDEN, NUMBER_COLS_ACCOUNT}; enum {CATEGORY_NAME, CATEGORY_BACKGROUND_COLOR, CATEGORY_FOREGROUND_COLOR, CATEGORY_FONT, CATEGORY_DELETE, NUMBER_COLS_CATEGORY}; +enum {TAG_NAME, TAG_DELETE, NUMBER_COLS_TAG}; PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent, bool lowResolution) : KissPanel(kiss, parent, lowResolution), _sharedWith(0), _curAccountRow(-1), _defaultSignalMapper(this), _virtualSignalMapper(this), _blockedSignalMapper(this), _deleteAccountSignalMapper(this), _deleteCategorySignalMapper(this), _backgroundColorSignalMapper(this), _foregroundColorSignalMapper(this), _fontSignalMapper(this), _inModification(false) { QVBoxLayout *vbox = new QVBoxLayout; QHBoxLayout *hbox1 = new QHBoxLayout; QHBoxLayout *hbox2 = new QHBoxLayout; + QHBoxLayout *hbox3 = new QHBoxLayout; //QHBoxLayout *hbox = new QHBoxLayout; - QGroupBox* staticUser, *staticAccount, *staticCategories, *staticLanguage, *staticOperationOrder, *staticSharedWith; + QGroupBox* staticUser, *staticAccount, *staticCategories, *staticTags, *staticLanguage, *staticOperationOrder, *staticSharedWith; User* user = _kiss->GetUser(); QGridLayout *gridBagSizer; QLabel* label; @@ -49,6 +51,7 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent, bool lowResolu staticUser = new QGroupBox(_("User")); staticAccount = new QGroupBox(_("Accounts")); staticCategories = new QGroupBox(_("Categories")); + staticTags = new QGroupBox(_("Tags")); staticLanguage = new QGroupBox(_("Language")); staticOperationOrder = new QGroupBox(_("Operation order")); staticSharedWith = new QGroupBox(_("Shared with")); @@ -138,7 +141,21 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent, bool lowResolu connect(_categoriesGrid, SIGNAL(cellChanged(int, int)), this, SLOT(OnCategoryModified(int, int))); - vbox->addWidget(staticCategories); + hbox2->addWidget(staticCategories); + + // Tags + staticBoxSizer = new QVBoxLayout (); + staticTags->setLayout(staticBoxSizer); + + _tagsGrid = new QTableWidget(); + + staticBoxSizer->addWidget(_tagsGrid); + + InitTags(user); + + connect(_tagsGrid, SIGNAL(cellChanged(int, int)), this, SLOT(OnTagModified(int, int))); + + hbox2->addWidget(staticTags); // Operation Order staticBoxSizer = new QVBoxLayout (); @@ -151,7 +168,7 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent, bool lowResolu staticBoxSizer->addWidget(_operationOrder); - hbox2->addWidget(staticOperationOrder); + hbox3->addWidget(staticOperationOrder); connect(_operationOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(OnOperationOrderChange(int))); @@ -166,9 +183,10 @@ PreferencesPanel::PreferencesPanel(KissCount* kiss, wxUI *parent, bool lowResolu staticBoxSizer->addWidget(_language); - hbox2->addWidget(staticLanguage); + hbox3->addWidget(staticLanguage); vbox->addLayout(hbox2); + vbox->addLayout(hbox3); connect(&_defaultSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnAccountDefaultClicked(int))); connect(&_virtualSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnAccountVirtualClicked(int))); @@ -436,6 +454,75 @@ void PreferencesPanel::AddCategory(int line, Category cat) } } +#define SET_READ_ONLY_TAG(row, line) _tagsGrid->item(row, line)->setFlags(_tagsGrid->item(row, line)->flags() & ~Qt::ItemIsEditable); + +void PreferencesPanel::InitTags(User* user) +{ + std::vector::iterator it; + int curLine = 0; + DEFAULT_FONT(font); + Tag tag; + + _tagsGrid->setColumnCount(NUMBER_COLS_TAG); + _tagsGrid->verticalHeader()->setHidden(true); + _tagsGrid->setFont(font); + + _tagsGrid->setHorizontalHeaderItem(TAG_NAME, new QTableWidgetItem(_("Name"))); + _tagsGrid->setHorizontalHeaderItem(TAG_DELETE, new QTableWidgetItem(_("Delete"))); + + _tagsGrid->setRowCount(user->GetTagsNumber()+1); + + font.setBold(true); + for(int i=0; ihorizontalHeaderItem(i)->setFont(font); + } + + for (it=user->_tags.begin(); it!=user->_tags.end(); it++, curLine++) + { + AddTag(curLine, *it); + } + + for(int i=0; iitem(curLine, i)) + _tagsGrid->setItem(curLine, i, new QTableWidgetItem("")); + + SET_READ_ONLY_TAG(curLine, TAG_DELETE); + + tag.id = 0; + AddTag(curLine, tag); +} + +void PreferencesPanel::AddTag(int line, Tag tag) +{ + QCheckBox* checkBox; + + if (tag.id != 0) + { + _tagsGrid->setItem(line, TAG_NAME, new QTableWidgetItem(_(tag.name.toStdString().c_str()))); + + checkBox = new QCheckBox(); + checkBox->setCheckState(Qt::Unchecked); + _tagsGrid->setCellWidget(line, TAG_DELETE, checkBox); + _deleteTagSignalMapper.setMapping(checkBox, tag.id); + connect(checkBox, SIGNAL(stateChanged(int)), &_deleteTagSignalMapper, SLOT(map())); + + for(int i=0; iitem(line, i) == 0) + _tagsGrid->setItem(line, i, new QTableWidgetItem("")); + } + else + { + for(int i=0; iitem(line, i)) + _tagsGrid->setItem(line, i, new QTableWidgetItem("")); + + SET_READ_ONLY_TAG(line, TAG_DELETE); + + _tagsGrid->resizeColumnsToContents(); + } +} + void PreferencesPanel::InitLanguage(User* user) { int i, select=0; @@ -722,6 +809,53 @@ void PreferencesPanel::OnCategoryDeleteClicked(int id) _inModification = false; } +void PreferencesPanel::OnTagDeleteClicked(int id) +{ + QStringList tags; + int i, row; + QString res; + User* user = _kiss->GetUser(); + bool ok; + QCheckBox* checkBox = qobject_cast (_deleteTagSignalMapper.mapping(id)); + Tag tag; + std::vector::iterator it; + + if (_inModification) return; + + it = std::find(user->_tags.begin(), user->_tags.end(), id); + + if (it == user->_tags.end()) return ; + + _inModification = true; + + row = it-user->_tags.begin(); + tag = user->_tags[row]; + + tags << _("None"); + + for(i=0; i < user->GetTagsNumber(); i++) + if (user->_tags[i].id != id) + tags << _(user->_tags[i].name.toStdString().c_str()); + + res = QInputDialog::getItem(0, "KissCount", _("Wich tag will replace this one ?"), tags, 0, false, &ok); + + if (!ok) + { + checkBox->setCheckState(Qt::Unchecked); + _inModification = false; + return; + } + else + { + i = tags.indexOf(res); + _kiss->DeleteTag(tag, (!i) ? 0 : user->GetTagId(tags[i])); + _tagsGrid->removeRow(row); + + _wxUI->NeedReload(); + } + _inModification = false; +} + void PreferencesPanel::OnBackgroundColorClicked(int id) { User* user = _kiss->GetUser(); @@ -1080,6 +1214,88 @@ void PreferencesPanel::OnCategoryModified(int row, int col) _inModification = false; } +void PreferencesPanel::OnTagModified(int row, int col) +{ + int op_complete = 1; + QString value; + User* user = _kiss->GetUser(); + Tag new_tag, old_tag; + int new_id; + QTableWidgetItem* item = _tagsGrid->item(row, col); + + if (_inModification) return; + + _inModification = true; + + value = item->text(); + if (value.size()) + { + new_tag.name = value; + op_complete--; + } + + // Tags modification + if (user->GetTagsNumber() && row < user->GetTagsNumber()) + { + old_tag = user->_tags[row]; + new_tag.id = user->_tags[row].id; + + if (new_tag.name.length() == 0) + { + QMessageBox::critical(0, _("Error"), _("Tag must have a name")); + _tagsGrid->setItem(row, TAG_NAME, new QTableWidgetItem(_(user->_tags[row].name.toStdString().c_str()))); + _inModification = false; + return ; + } + + try { + new_id = user->GetTagId(new_tag.name); + if (new_id != new_tag.id) + { + QMessageBox::critical(0, _("Error"), _("Tag ")+new_tag.name+_(" already exists")); + _tagsGrid->setItem(row, TAG_NAME, new QTableWidgetItem(_(user->_tags[row].name.toStdString().c_str()))); + _inModification = false; + return ; + } + } + catch(User::TagNotFound e) + {} + + _kiss->UpdateTag(new_tag); + } + // New tag + else + { + if (op_complete) + { + _inModification = false; + return ; + } + + try { + user->GetTagId(new_tag.name); + QMessageBox::critical(0, _("Error"), _("Tag ")+new_tag.name+_(" already exists")); + _inModification = false; + return ; + } + catch(User::TagNotFound e) + {} + + _kiss->AddTag(new_tag); + AddTag(row, new_tag); + + SET_READ_ONLY_TAG(row, TAG_DELETE); + + new_tag.id = 0; + _tagsGrid->setRowCount(row+2); + AddTag(++row, new_tag); + } + + _wxUI->NeedReload(); + + _inModification = false; +} + void PreferencesPanel::OnChangeName() { User* user = _kiss->GetUser(); diff --git a/src/view/PreferencesPanel.hpp b/src/view/PreferencesPanel.hpp index 3a25569..c1986eb 100644 --- a/src/view/PreferencesPanel.hpp +++ b/src/view/PreferencesPanel.hpp @@ -46,6 +46,7 @@ private slots: void OnAccountDeleteClicked(int id); void OnAccountHiddenClicked(int id); void OnCategoryDeleteClicked(int id); + void OnTagDeleteClicked(int id); void OnBackgroundColorClicked(int id); void OnForegroundClicked(int id); void OnFontClicked(int id); @@ -53,6 +54,7 @@ private slots: void OnAccountCellChanged(int row, int col, int, int); void OnSharedChange(QListWidgetItem *item); void OnCategoryModified(int row, int col); + void OnTagModified(int row, int col); void OnChangeName(); void OnChangePassword(); void OnOperationOrderChange(int index); @@ -62,21 +64,24 @@ private slots: private: QTableWidget* _accountsGrid; QTableWidget* _categoriesGrid; + QTableWidget* _tagsGrid; QLineEdit* _name; QListWidget* _language; QComboBox* _operationOrder; QListWidget* _sharedWith; int _curAccountRow; std::map _sharedOwners; - QSignalMapper _defaultSignalMapper, _virtualSignalMapper, _blockedSignalMapper, _deleteAccountSignalMapper, _hiddenAccountSignalMapper, _deleteCategorySignalMapper, _backgroundColorSignalMapper, _foregroundColorSignalMapper, _fontSignalMapper; + QSignalMapper _defaultSignalMapper, _virtualSignalMapper, _blockedSignalMapper, _deleteAccountSignalMapper, _hiddenAccountSignalMapper, _deleteCategorySignalMapper, _deleteTagSignalMapper, _backgroundColorSignalMapper, _foregroundColorSignalMapper, _fontSignalMapper; bool _inModification; void InitAccounts(User* user); void InitCategories(User* user); + void InitTags(User* user); void InitLanguage(User* user); void InitOperationOrder(User* user); void AddAccount(int line, Account ac); void AddCategory(int line, Category cat); + void AddTag(int line, Tag tag); }; #endif diff --git a/src/view/SearchBanner.cpp b/src/view/SearchBanner.cpp index a8772a6..e58f344 100644 --- a/src/view/SearchBanner.cpp +++ b/src/view/SearchBanner.cpp @@ -25,6 +25,7 @@ SearchBanner::SearchBanner(KissCount* kiss, QFrame *parent, void* caller, OnButt User* user = _kiss->GetUser(); std::vector::iterator accountIt; std::vector::iterator categoryIt; + std::vector::iterator tagIt; QDate firstOfMonth = QDate::currentDate(); _checkDateFrom = new QCheckBox(_("Date from")); @@ -74,6 +75,16 @@ SearchBanner::SearchBanner(KissCount* kiss, QFrame *parent, void* caller, OnButt } _category->setMaximumSize(QSize(_category->contentsRect().width()*1.5, _category->sizeHint().height())); + _tag = new QListWidget(this); + item = new QListWidgetItem(_("Unknown"), _tag); + item->setCheckState (Qt::Unchecked); + for(tagIt = user->_tags.begin(); tagIt != user->_tags.end(); tagIt++) + { + item = new QListWidgetItem(_(tagIt->name.toStdString().c_str()), _tag); + item->setCheckState (Qt::Unchecked); + } + _tag->setMaximumSize(QSize(_tag->contentsRect().width()*1.5, _tag->sizeHint().height())); + _optype = new QListWidget(this); item = new QListWidgetItem(_("Fix"), _optype); item->setCheckState (Qt::Unchecked); @@ -99,6 +110,7 @@ SearchBanner::SearchBanner(KissCount* kiss, QFrame *parent, void* caller, OnButt QLabel* labelAmountFrom = new QLabel(_("Amount from")); QLabel* labelAmountTo = new QLabel(_("Amount to")); QLabel* labelCategory = new QLabel(_("Category")); + QLabel* labelTag = new QLabel(_("Tag")); QLabel* labelOperations = new QLabel(_("Operations")); QLabel* labelAccount = new QLabel(_("Account")); @@ -114,10 +126,12 @@ SearchBanner::SearchBanner(KissCount* kiss, QFrame *parent, void* caller, OnButt gridBagSizer->addWidget(_amountTo, 1, 4); gridBagSizer->addWidget(labelCategory, 0, 5); gridBagSizer->addWidget(_category, 1, 5); - gridBagSizer->addWidget(labelOperations, 0, 6); - gridBagSizer->addWidget(_optype, 1, 6); - gridBagSizer->addWidget(labelAccount, 0, 7); - gridBagSizer->addWidget(_account, 1, 7); + gridBagSizer->addWidget(labelTag, 0, 6); + gridBagSizer->addWidget(_tag, 1, 6); + gridBagSizer->addWidget(labelOperations, 0, 7); + gridBagSizer->addWidget(_optype, 1, 7); + gridBagSizer->addWidget(labelAccount, 0, 8); + gridBagSizer->addWidget(_account, 1, 8); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); } @@ -141,7 +155,7 @@ std::vector * SearchBanner::Search() { QString *description=0; int *amountFrom=0, *amountTo=0; - std::vector categories, accounts; + std::vector categories, tags, accounts; QDate *dateFrom=0, *dateTo=0; User* user= _kiss->GetUser(); int i, types=0; @@ -214,6 +228,10 @@ std::vector * SearchBanner::Search() if (_category->item(i)->checkState() == Qt::Checked) categories.push_back((i) ? user->_categories[i-1].id : 0); + for(i=0; iGetTagsNumber()+1; i++) + if (_tag->item(i)->checkState() == Qt::Checked) + tags.push_back((i) ? user->_tags[i-1].id : 0); + types |= (_optype->item(0)->checkState() == Qt::Checked) ? Database::FIX_OP : 0; types |= (_optype->item(1)->checkState() == Qt::Checked) ? Database::NON_FIX_OP : 0; types |= (_optype->item(2)->checkState() == Qt::Checked) ? Database::CHECKED_OP : 0; @@ -223,7 +241,7 @@ std::vector * SearchBanner::Search() if (_account->item(i)->checkState() == Qt::Checked) accounts.push_back((i) ? user->_accounts[i-1].id : 0); - _operations = _kiss->Search(description, dateFrom, dateTo, amountFrom, amountTo, categories, types, accounts); + _operations = _kiss->Search(description, dateFrom, dateTo, amountFrom, amountTo, categories, types, accounts, tags); end: delete dateFrom; diff --git a/src/view/SearchBanner.hpp b/src/view/SearchBanner.hpp index 0f3028e..3194fc4 100644 --- a/src/view/SearchBanner.hpp +++ b/src/view/SearchBanner.hpp @@ -51,7 +51,7 @@ private: QCalendarWidget* _calendarFrom, *_calendarTo; QCheckBox *_checkDateFrom, *_checkDateTo; QLineEdit* _description, *_amountFrom, *_amountTo; - QListWidget* _category, *_account, *_optype; + QListWidget* _category, *_tag, *_account, *_optype; }; #endif diff --git a/src/view/StatsPanel.cpp b/src/view/StatsPanel.cpp index 874145d..4692dcb 100644 --- a/src/view/StatsPanel.cpp +++ b/src/view/StatsPanel.cpp @@ -147,6 +147,7 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT { std::map > > accountAmounts; std::map categories; + std::map tags; std::map > operations; std::vector::iterator accountIt; std::map::iterator categoriesIt; @@ -173,7 +174,7 @@ void StatsPanel::UpdateStats(int monthFrom, int yearFrom, int monthTo, int yearT date.setDate(yearFrom, monthFrom+1, 1); nbDays = date.daysInMonth(); - _kiss->GetMonthStats(monthFrom, yearFrom, nbDays, &operations, &categories); + _kiss->GetMonthStats(monthFrom, yearFrom, nbDays, &operations, &categories, &tags); // Line on 0 all over the years for (a=0; aGetStats(monthFrom, yearFrom, monthTo, yearTo, &accountAmounts, &categories); + _kiss->GetStats(monthFrom, yearFrom, monthTo, yearTo, &accountAmounts, &categories, &tags); // Line on 0 all over the years nbDays = ((yearTo - yearFrom) + 1) * 12; diff --git a/src/view/grid/ChoiceDelegate.hpp b/src/view/grid/ChoiceDelegate.hpp index 0126260..adf1243 100644 --- a/src/view/grid/ChoiceDelegate.hpp +++ b/src/view/grid/ChoiceDelegate.hpp @@ -38,6 +38,7 @@ public: private: QString* _values; int _nbValues; + bool _editable; }; #endif diff --git a/src/view/grid/GridAccount.cpp b/src/view/grid/GridAccount.cpp index d5a1827..0dbec39 100644 --- a/src/view/grid/GridAccount.cpp +++ b/src/view/grid/GridAccount.cpp @@ -57,6 +57,7 @@ GridAccount::GridAccount(KissCount* kiss, QWidget *parent, User* user = _kiss->GetUser(); std::vector::iterator accountIt; std::vector::iterator categoryIt; + std::vector::iterator tagIt; QTableWidgetItem* item; QLabel* label; @@ -70,7 +71,7 @@ GridAccount::GridAccount(KissCount* kiss, QWidget *parent, setFont(font); font.setBold(true); - QString colsName[] = {"", _("Description"), _("Date"), _("Debit"), _("Credit"), _("Category"), _("Account"), "", ""}; + QString colsName[] = {"", _("Description"), _("Date"), _("Debit"), _("Credit"), _("Category"), _("Tag"), _("Account"), "", ""}; for(i=0; iname.toStdString().c_str()) ; } + _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()) ; + } + resizeColumnToContents(TREE); resizeColumnToContents(CATEGORY); + resizeColumnToContents(TAG); resizeColumnToContents(OP_DATE); resizeColumnToContents(ACCOUNT); resizeColumnToContents(OP_DELETE); @@ -136,6 +146,7 @@ GridAccount::GridAccount(KissCount* kiss, QWidget *parent, GridAccount::~GridAccount() { delete[] _categories; + delete[] _tags; delete[] _accounts; if (_completer) delete _completer; @@ -234,6 +245,10 @@ void GridAccount::LoadOperations(std::vector* operations, int month, ChoiceDelegate* categoryEditor = new ChoiceDelegate(this, _categories, user->GetCategoriesNumber()-1); setItemDelegateForColumn(CATEGORY, categoryEditor); + + ChoiceDelegate* tagEditor = new ChoiceDelegate(this, _tags, user->GetTagsNumber()); + setItemDelegateForColumn(TAG, tagEditor); + ChoiceDelegate* accountEditor = new ChoiceDelegate(this, _accounts, _nbAccounts); setItemDelegateForColumn(ACCOUNT, accountEditor); @@ -281,6 +296,7 @@ void GridAccount::LoadOperations(std::vector* operations, int month, } resizeColumnToContents(TREE); + resizeColumnToContents(TAG); resizeColumnToContents(CATEGORY); resizeColumnToContents(OP_DATE); resizeColumnToContents(ACCOUNT); @@ -341,6 +357,7 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, QString description, v; DEFAULT_FONT(font); Category cat ; + Tag tag; Operation op2; QTableWidgetItem* item; QCheckBox* checkBox; @@ -366,6 +383,7 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, if (op.id) { cat = user->GetCategory(op.category); + tag = user->GetTag(op.tag); description = op.description; UNESCAPE_CHARS(description); @@ -386,6 +404,8 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, setItem(line, ACCOUNT, new QTableWidgetItem(user->GetAccountName(op.account))); if (!fix && !op.meta) setItem(line, CATEGORY, new QTableWidgetItem(_(cat.name.toStdString().c_str()))); + + setItem(line, TAG, new QTableWidgetItem(_(tag.name.toStdString().c_str()))); checkBox = new QCheckBox(); checkBox->setCheckState(Qt::Unchecked); @@ -487,6 +507,7 @@ void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, SET_READ_ONLY(this->item(line, CREDIT)); SET_READ_ONLY(this->item(line, DEBIT)); SET_READ_ONLY(this->item(line, CATEGORY)); + SET_READ_ONLY(this->item(line, TAG)); SET_READ_ONLY(this->item(line, ACCOUNT)); } else @@ -896,6 +917,7 @@ void GridAccount::OnOperationModified(int row, int col) Operation op, op2, parent; QFont font; Category cat ; + Tag tag; bool fix_cost; Operation NULLop; @@ -914,6 +936,7 @@ void GridAccount::OnOperationModified(int row, int col) new_op.amount = 0.0; new_op.description = ""; new_op.category = 0; + new_op.tag = 0; new_op.fix_cost = false; new_op.account = 0; new_op.checked = 0; @@ -954,6 +977,9 @@ void GridAccount::OnOperationModified(int row, int col) if (!item(row, CATEGORY)->text().length()) setItem(row, CATEGORY, new QTableWidgetItem(_(user->GetCategoryName(op_tmp.category).toStdString().c_str()))); + if (!item(row, TAG)->text().length() && op_tmp.tag) + setItem(row, TAG, new QTableWidgetItem(_(user->GetTagName(op_tmp.tag).toStdString().c_str()))); + if (!item(row, ACCOUNT)->text().length()) setItem(row, ACCOUNT, new QTableWidgetItem(user->GetAccountName(op_tmp.account))); @@ -998,6 +1024,20 @@ void GridAccount::OnOperationModified(int row, int col) op_complete--; } + value = item(row, TAG)->text(); + if (value.length()) + { + try + { + new_op.tag = user->GetTagId(value); + } + catch (User::TagNotFound e) + { + op_complete++; + } + op_complete--; + } + value = item(row, ACCOUNT)->text(); if (value.length()) { @@ -1052,7 +1092,7 @@ void GridAccount::OnOperationModified(int row, int col) CheckOperation(new_op, row, new_op.checked, true); SET_ROW_FONT(row, user->GetCategoryFont(cat.id)); - if (op_complete) { + if (op_complete > 0) { _inModification = false ; return ; } @@ -1095,6 +1135,8 @@ void GridAccount::UpdateMeta(Operation& meta) Operation op ; int category = 0; bool updateCat = false ; + int tag = 0; + bool updateTag = false ; bool openMeta; if (!meta.childs.size()) return ; @@ -1133,12 +1175,28 @@ void GridAccount::UpdateMeta(Operation& meta) if (op.category && op.category != category) updateCat = false; } + if (!tag) + { + if (op.tag) + { + tag = op.tag; + updateCat = true; + } + } + else + { + if (op.tag && op.tag != tag) + updateCat = false; + } op.parent = meta.id; } if (updateCat) meta.category = category; + if (updateTag) + meta.tag = tag; + meta.amount = _kiss->MetaAmount(meta.id); UpdateOperation(meta); @@ -1224,6 +1282,7 @@ void GridAccount::Group() op.amount = 0; op.description = ""; op.category = 0; + op.tag = 0; op.fix_cost = fix; op.account = 0; op.checked = false; diff --git a/src/view/grid/GridAccount.hpp b/src/view/grid/GridAccount.hpp index b029c80..8113d49 100644 --- a/src/view/grid/GridAccount.hpp +++ b/src/view/grid/GridAccount.hpp @@ -42,7 +42,7 @@ class GridAccount : public QTableWidget public: class OperationNotFound {}; - enum {TREE, DESCRIPTION, OP_DATE, DEBIT, CREDIT, CATEGORY, ACCOUNT, OP_DELETE, CHECKED, NUMBER_COLS_OPS}; + enum {TREE, DESCRIPTION, OP_DATE, DEBIT, CREDIT, CATEGORY, TAG, ACCOUNT, OP_DELETE, CHECKED, NUMBER_COLS_OPS}; GridAccount(KissCount* kiss, QWidget *parent, bool canAddOperation, bool setWeek, bool synchronizeWithDatabase); @@ -77,7 +77,7 @@ private: bool _displayLines; bool _setWeek; bool _databaseSynchronization; - QString* _categories, *_accounts; + QString* _categories, *_accounts, *_tags; int _nbAccounts; std::vector* _operations; bool _loadOperations;