diff --git a/ChangeLog b/ChangeLog index bbf41af..ff45129 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -v0.2 (28/05/2011) +v0.2 (04/07/2011) ** User ** Better use of sizers (so better interface!) @@ -14,10 +14,12 @@ v0.2 (28/05/2011) Add Real mode Database is now at ~/.kisscount/kc.bdd Add Debian's packages !! - Add Import Panel, OFX and Grisbi imports + Add Import Panel, XML, OFX and Grisbi imports + Add Export Panel (only XML) Add update next months Change charts and real/virtual mode position Add "non fix" category to statistics (sum of all non fix categories) + Auto completion does not require date to be set ** Dev ** Use a factory to create panels (prepare for plug-in) diff --git a/src/controller/KissCount.cpp b/src/controller/KissCount.cpp index f75c109..54793d7 100644 --- a/src/controller/KissCount.cpp +++ b/src/controller/KissCount.cpp @@ -104,23 +104,27 @@ double KissCount::CalcAccountAmount(const wxString& id, int month, int year, boo return _db->CalcAccountAmount(id, month, year, had_values); } -void KissCount::UpdateOperation(Operation& op) +void KissCount::UpdateOperation(Operation& op, bool checkTransfert) { // Unlink - op.transfert = wxT(""); - _user->LinkOrUnlinkOperation(op); + if (checkTransfert) + { + op.transfert = wxT(""); + _user->LinkOrUnlinkOperation(op); + } - _db->UpdateOperation(_user, op); + _db->UpdateOperation(_user, op, checkTransfert); // Link - _user->LinkOrUnlinkOperation(op); + if (checkTransfert) + _user->LinkOrUnlinkOperation(op); } -wxString KissCount::AddOperation(Operation& op) +wxString KissCount::AddOperation(Operation& op, bool checkTransfert) { - wxString ret = _db->AddOperation(_user, op); + wxString ret = _db->AddOperation(_user, op, checkTransfert); - if (op.transfert.Length()) + if (checkTransfert && op.transfert.Length()) _user->LinkOrUnlinkOperation(op); return ret; @@ -159,9 +163,9 @@ double KissCount::MetaPositiveAmount(const wxString& id) return _db->MetaPositiveAmount(id); } -void KissCount::SetAccountAmount(int month, int year, const wxString& accountId, double amount) +void KissCount::SetAccountAmount(const wxString& accountId, int month, int year, double amount) { - _db->SetAccountAmount(month, year, accountId, amount); + _db->SetAccountAmount(accountId, month, year, amount); } wxString KissCount::AddAccount(Account& ac) @@ -173,7 +177,7 @@ wxString KissCount::AddAccount(Account& ac) curDate.SetToCurrent(); - SetAccountAmount((int)curDate.GetMonth(), curDate.GetYear(), ac.id, 0.0); + SetAccountAmount(ac.id, (int)curDate.GetMonth(), curDate.GetYear(), 0.0); return ac.id; } diff --git a/src/controller/KissCount.h b/src/controller/KissCount.h index 6b03553..a8d0c42 100644 --- a/src/controller/KissCount.h +++ b/src/controller/KissCount.h @@ -40,6 +40,10 @@ s.Replace(wxT("\'"), wxT("\\\'"), true); \ } +#define UNESCAPE_CHARS(s) { \ + s.Replace(wxT("\\\""), wxT("\""), true); \ + s.Replace(wxT("\\\'"), wxT("\'"), true); \ + } class wxUI; class Database; class ImportEngine; @@ -62,15 +66,15 @@ public: void LoadYear(int year, bool force=false); - wxString AddOperation(Operation& op); - void UpdateOperation(Operation& op); + wxString AddOperation(Operation& op, bool checkTransfert=true); + void UpdateOperation(Operation& op, bool checkTransfert=true); void DeleteOperation(Operation& op); void DeleteOperations(int month, int year); double MetaAmount(const wxString& id); double MetaPositiveAmount(const wxString& id); double GetAccountAmount(const wxString& id, int month, int year); - void SetAccountAmount(int month, int year, const wxString& accountId, double value); + void SetAccountAmount(const wxString& accountId, int month, int year, double value); double CalcAccountAmount(const wxString& id, int month, int year, bool* had_values); wxString AddAccount(Account& ac); diff --git a/src/model/AccountAmount.h b/src/model/AccountAmount.h new file mode 100644 index 0000000..79bd690 --- /dev/null +++ b/src/model/AccountAmount.h @@ -0,0 +1,44 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef ACCOUNTAMOUNT_H +#define ACCOUNTAMOUNT_H + +class AccountAmount { +public: + wxString account; + int month; + int year; + bool operator()(const AccountAmount& x, const AccountAmount& y) const + { + long x1, y1; + + if (x.account != y.account) + { + x.account.ToLong(&x1); + y.account.ToLong(&y1); + + return x1 < y1; + } + + return (x.year < y.year || ((x.year == y.year) && x.month < y.month)); + } +}; + +#endif diff --git a/src/model/Database.cpp b/src/model/Database.cpp index 6e0d8e1..9f6ec23 100644 --- a/src/model/Database.cpp +++ b/src/model/Database.cpp @@ -91,10 +91,13 @@ void Database::CreateDatabase() wxString message = _("No database found, would you like to create a new one ?\n\n"); message += _("!! Warning !! If there was a bug, the old database will be suppressed !"); - wxMessageDialog dialog(NULL, message, wxT("KissCount"), wxYES_NO); + if (dirname.DirExists()) + { + wxMessageDialog dialog(NULL, message, wxT("KissCount"), wxYES_NO); - if (dialog.ShowModal() == wxID_NO) - throw std::string("No database") ; + if (dialog.ShowModal() == wxID_NO) + throw std::string("No database") ; + } init_script.open(INIT_SCRIPT); @@ -400,7 +403,7 @@ double Database::GetAccountAmount(const wxString& id, int month, int year) res = set.GetDouble(wxT("amount")); else { - SetAccountAmount(month, year, id, 0.0); + SetAccountAmount(id, month, year, 0.0); res = 0.0; } @@ -550,11 +553,12 @@ void Database::LinkOrUnlinkOperation(User* user, Operation& op) } } -void Database::UpdateOperation(User* user, Operation& op) +void Database::UpdateOperation(User* user, Operation& op, bool checkTransfert) { wxString req; - LinkOrUnlinkOperation(user, op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); ESCAPE_CHARS(op.description); @@ -589,10 +593,11 @@ void Database::UpdateOperation(User* user, Operation& op) EXECUTE_SQL_UPDATE(req, ); - LinkOrUnlinkOperation(user, op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); } -wxString Database::AddOperation(User* user, Operation& op) +wxString Database::AddOperation(User* user, Operation& op, bool checkTransfert) { wxString req, res; wxSQLite3ResultSet set; @@ -631,7 +636,8 @@ wxString Database::AddOperation(User* user, Operation& op) op.id = res; - LinkOrUnlinkOperation(user, op); + if (checkTransfert) + LinkOrUnlinkOperation(user, op); return res; } @@ -783,7 +789,7 @@ double Database::MetaPositiveAmount(const wxString& id) return res; } -void Database::SetAccountAmount(int month, int year, const wxString& accountId, double amount) +void Database::SetAccountAmount(const wxString& accountId, int month, int year, double amount) { wxString req; req = wxT("UPDATE account_amount SET ") ; diff --git a/src/model/Database.h b/src/model/Database.h index 7e7b15c..e741ef1 100644 --- a/src/model/Database.h +++ b/src/model/Database.h @@ -104,8 +104,8 @@ public: User* LoadUser(const wxString& name); void LoadYear(User* user, int year); - void UpdateOperation(User* user, Operation& op); - wxString AddOperation(User* user, Operation& op); + void UpdateOperation(User* user, Operation& op, bool checkTransfert=true); + wxString AddOperation(User* user, Operation& op, bool checkTransfert=true); void DeleteOperation(User* user, Operation& op); void DeleteOperations(User* user, int month, int year); bool LoadOperation(User* user, const wxString& id); @@ -113,7 +113,7 @@ public: double MetaPositiveAmount(const wxString& id); double GetAccountAmount(const wxString& id, int month, int year); - void SetAccountAmount(int month, int year, const wxString& accountId, double amount); + void SetAccountAmount(const wxString& accountId, int month, int year, double amount); double CalcAccountAmount(const wxString& id, int month, int year, bool* had_values); wxString AddAccount(User* user, Account& ac); diff --git a/src/model/export/ExportEngine.cpp b/src/model/export/ExportEngine.cpp index d276358..db65e10 100644 --- a/src/model/export/ExportEngine.cpp +++ b/src/model/export/ExportEngine.cpp @@ -42,6 +42,10 @@ bool ExportEngine::SaveFile(std::vector* operations) int i; wxString account, category; AccountAmount accountAmount; + int minMonth = -1, minYear = -1; + unsigned int maxMonth = -1, maxYear = -1; + unsigned int month, year; + std::map::iterator it; if (!operations) return NULL; @@ -54,23 +58,45 @@ bool ExportEngine::SaveFile(std::vector* operations) account = (*operations)[i].account; category = (*operations)[i].category; - accountAmount.account = account; - accountAmount.month = (*operations)[i].month; - accountAmount.year = (*operations)[i].year; + if (minYear == -1 || (int)(*operations)[i].year < minYear) + maxYear = minYear = (*operations)[i].year; - if (account.Length()) + if (minMonth == -1 || ((int)(*operations)[i].month < minMonth && (int)(*operations)[i].year == minYear)) + maxMonth = minMonth = (*operations)[i].month; + + if ((*operations)[i].year > maxYear) { - if (!_accounts.count(account)) - _accounts[account]++; - - if (!_accountAmounts.count(accountAmount)) - _accountAmounts[accountAmount] = _kiss->GetAccountAmount(accountAmount.account, accountAmount.month, accountAmount.year); + maxYear = (*operations)[i].year; + maxMonth = (*operations)[i].month; } + if ((*operations)[i].month > maxMonth && (*operations)[i].year == maxYear) + maxMonth = (*operations)[i].month; + + if (account.Length() && !_accounts.count(account)) + _accounts[account]++; + if (category.Length() && !_categories.count(category)) _categories[category]++; } + for(it=_accounts.begin(); it!=_accounts.end(); it++) + { + month = minMonth; + for (year = minYear; year <= maxYear; year++) + { + for (; !(month > maxMonth && year == maxYear) && month < 12; month++) + { + accountAmount.account = it->first; + accountAmount.month = month; + accountAmount.year = year; + + _accountAmounts[accountAmount] = _kiss->GetAccountAmount(accountAmount.account, accountAmount.month, accountAmount.year); + } + month = 0; + } + } + return true; } diff --git a/src/model/export/ExportEngine.h b/src/model/export/ExportEngine.h index 4ecf2f9..50976f9 100644 --- a/src/model/export/ExportEngine.h +++ b/src/model/export/ExportEngine.h @@ -22,30 +22,10 @@ #include #include +#include class KissCount; -class AccountAmount { -public: - wxString account; - int month; - int year; - bool operator()(const AccountAmount& x, const AccountAmount& y) const - { - long x1, y1; - - if (x.account != y.account) - { - x.account.ToLong(&x1); - y.account.ToLong(&y1); - - return x1 < y1; - } - - return (x.year < y.year && x.month < y.month); - } -}; - class ExportEngine { public: ExportEngine(); diff --git a/src/model/export/XMLExportEngine.cpp b/src/model/export/XMLExportEngine.cpp index 1cdf6e2..be78faf 100644 --- a/src/model/export/XMLExportEngine.cpp +++ b/src/model/export/XMLExportEngine.cpp @@ -48,15 +48,16 @@ bool XMLExportEngine::SaveAccounts() account = _user->GetAccount(it->first); ESCAPE_CHARS(account.name); + ESCAPE_CHARS(account.number); xmlTextWriterStartElement(_writer, (const xmlChar*) "account"); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "id", (const xmlChar*) account.id.utf8_str().data()); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "name", (const xmlChar*) account.name.utf8_str().data()); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "number", (const xmlChar*) account.number.utf8_str().data()); - xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "shared", (const xmlChar*) (account.shared ? "1" : "0")); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "shared", (const xmlChar*) (account.shared ? "1" : "0")); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "blocked", (const xmlChar*) (account.blocked ? "1" : "0")); - xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "default", (const xmlChar*) (account._default ? "1" : "0")); - xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "is_owner", (const xmlChar*) (account.is_owner ? "1" : "0")); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "default", (const xmlChar*) (account._default ? "1" : "0")); + // xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "is_owner", (const xmlChar*) (account.is_owner ? "1" : "0")); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "virtual", (const xmlChar*) (account._virtual ? "1" : "0")); xmlTextWriterEndElement(_writer); } @@ -100,11 +101,11 @@ bool XMLExportEngine::SaveCategories() rgb = category.backcolor.Blue(); rgb |= category.backcolor.Green() << 8; rgb |= category.backcolor.Red() << 16; - xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "backcolor", "%08X", rgb); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "backcolor", "0x%08X", rgb); rgb = category.forecolor.Blue(); rgb |= category.forecolor.Green() << 8; rgb |= category.forecolor.Red() << 16; - xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "forecolor", "%08X", rgb); + xmlTextWriterWriteFormatAttribute(_writer, (const xmlChar*) "forecolor", "0x%08X", rgb); xmlTextWriterWriteAttribute(_writer, (const xmlChar*) "fix_cost", (const xmlChar*) (category.fix_cost ? "1" : "0")); xmlTextWriterEndElement(_writer); } diff --git a/src/model/import/GrisbiImportEngine.cpp b/src/model/import/GrisbiImportEngine.cpp index d5d0de1..9fb267d 100644 --- a/src/model/import/GrisbiImportEngine.cpp +++ b/src/model/import/GrisbiImportEngine.cpp @@ -44,6 +44,9 @@ void GrisbiImportEngine::LoadAccount(GrisbiImportEngine* _this, const char** att account_number += key; + UNESCAPE_CHARS(name); + UNESCAPE_CHARS(account_number); + for (i=0; i<(int)_this->_user->_accounts.size(); i++) { if (_this->_user->_accounts[i].number == account_number) @@ -56,6 +59,11 @@ void GrisbiImportEngine::LoadAccount(GrisbiImportEngine* _this, const char** att _this->_accounts[id] = wxT("unknown-") + account_number; ac.number = account_number; ac.name = name; + ac.shared = false; + ac.blocked = false; + ac._default = false; + ac.is_owner = true; + ac._virtual = false; _this->_unresolvedAccounts.push_back(ac); } @@ -74,6 +82,8 @@ void GrisbiImportEngine::LoadCategory(GrisbiImportEngine* _this, const char** at id = wxString(attrs[i+1], wxConvUTF8); } + UNESCAPE_CHARS(name); + for (i=0; i<(int)_this->_user->_categories.size(); i++) { if (_this->_user->_categories[i].name == name) @@ -86,6 +96,10 @@ void GrisbiImportEngine::LoadCategory(GrisbiImportEngine* _this, const char** at _this->_categories[id] = wxT("unknown-") + name; cat.id = id; cat.name = name; + cat.parent = wxT("0"); + cat.backcolor = OWN_GREEN ; + cat.forecolor = *wxBLACK; + cat.fix_cost = false; _this->_unresolvedCategories.push_back(cat); } @@ -134,6 +148,8 @@ void GrisbiImportEngine::LoadOperation(GrisbiImportEngine* _this, const char** a op.description = wxString(attrs[i+1], wxConvUTF8); } + UNESCAPE_CHARS(op.description); + _this->_operations.push_back(op); _this->_descriptions[op.id] = op.description; @@ -193,7 +209,19 @@ GrisbiImportEngine::~GrisbiImportEngine() bool GrisbiImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) { + int res = -1; + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; - return xmlSAXUserParseFile(&_sax, this, path.mb_str()) >= 0; + try + { + res = xmlSAXUserParseFile(&_sax, this, path.mb_str()); + } + catch (const char* s) + { + std::cout << "GrisbiImportEngine :: " << s << std::endl; + res = -1; + } + + return res >= 0; } diff --git a/src/model/import/GrisbiImportEngine.h b/src/model/import/GrisbiImportEngine.h index 4d1c75b..ec238ea 100644 --- a/src/model/import/GrisbiImportEngine.h +++ b/src/model/import/GrisbiImportEngine.h @@ -30,8 +30,6 @@ public: ~GrisbiImportEngine(); virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); - /* virtual std::vector ParseFile(); */ - /* virtual std::vector* GetOperations(std::map& accounts); */ private: xmlSAXHandler _sax; diff --git a/src/model/import/ImportEngine.cpp b/src/model/import/ImportEngine.cpp index c68e642..11fc6ca 100644 --- a/src/model/import/ImportEngine.cpp +++ b/src/model/import/ImportEngine.cpp @@ -38,6 +38,7 @@ bool ImportEngine::HandleFile(const wxString& path, User* user, Database* db, Ki _unresolvedAccounts.clear(); _operations.clear(); _descriptions.clear(); + _accountAmounts.clear(); return path.EndsWith(_shortExt) || path.EndsWith(_shortExt.Upper()); } @@ -307,3 +308,8 @@ std::vector* ImportEngine::GetOperations(std::map return &_operations; } + +const std::map& ImportEngine::GetAccountAmounts() +{ + return _accountAmounts; +} diff --git a/src/model/import/ImportEngine.h b/src/model/import/ImportEngine.h index b6949d5..9db452e 100644 --- a/src/model/import/ImportEngine.h +++ b/src/model/import/ImportEngine.h @@ -22,6 +22,7 @@ #include #include +#include class KissCount; @@ -52,10 +53,13 @@ public: // Final Step virtual std::vector* GetOperations(std::map& accounts, std::map& categories); + const std::map& GetAccountAmounts(); + void MatchPattern(wxString& key, Operation& op); int UpdatePattern(int pos); static wxString RemoveUnused(const wxString& s); + protected: Database* _db; User* _user; @@ -71,6 +75,7 @@ protected: std::vector _unresolvedCategories; std::vector _operations; std::map _descriptions; + std::map _accountAmounts; void ApplyPattern(ImportPattern& pattern, Operation& op); wxString FindPattern(wxString& s1, wxString& s2); diff --git a/src/model/import/OFXImportEngine.cpp b/src/model/import/OFXImportEngine.cpp index a5039c5..25e6073 100644 --- a/src/model/import/OFXImportEngine.cpp +++ b/src/model/import/OFXImportEngine.cpp @@ -30,6 +30,8 @@ int OFXImportEngine::account_cb(const struct OfxAccountData data, void * account _this->_curAccount = wxT(""); + UNESCAPE_CHARS(account_number); + for (i=0; i<(int)_this->_user->_accounts.size(); i++) { if (_this->_user->_accounts[i].number == account_number) @@ -46,6 +48,11 @@ int OFXImportEngine::account_cb(const struct OfxAccountData data, void * account { _this->_curAccount = _this->_accounts[account_number] = wxT("unknown-") + account_number; ac.number = account_number; + ac.shared = false; + ac.blocked = false; + ac._default = false; + ac.is_owner = true; + ac._virtual = false; _this->_unresolvedAccounts.push_back(ac); } @@ -94,6 +101,8 @@ int OFXImportEngine::transaction_cb(const struct OfxTransactionData data, void * op.description += wxString(data.memo, wxConvUTF8); } + UNESCAPE_CHARS(op.description); + _this->_operations.push_back(op); _this->_descriptions[op.id] = op.description; diff --git a/src/model/import/XMLImportEngine.cpp b/src/model/import/XMLImportEngine.cpp new file mode 100644 index 0000000..692b587 --- /dev/null +++ b/src/model/import/XMLImportEngine.cpp @@ -0,0 +1,294 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#include "XMLImportEngine.h" + +static XMLImportEngine xmlImportEngine; + +void XMLImportEngine::LoadAccount(XMLImportEngine* _this, const char** attrs) +{ + int i; + wxString account_number, name, id; + Account ac; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "name")) + ac.name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "id")) + ac.id = id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "number")) + ac.number = account_number = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "blocked")) + ac.blocked = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "virtual")) + ac._virtual = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(ac.name); + UNESCAPE_CHARS(ac.number); + + ac._default = false; + ac.shared = false; + ac.is_owner = true; + + if (account_number.Length()) + { + for (i=0; i<(int)_this->_user->_accounts.size(); i++) + { + if (_this->_user->_accounts[i].number == account_number) + { + _this->_accounts[id] = _this->_user->_accounts[i].id; + return; + } + } + } + + _this->_accounts[id] = wxT("unknown-") + account_number; + _this->_unresolvedAccounts.push_back(ac); +} + +void XMLImportEngine::LoadAccountAmount(XMLImportEngine* _this, const char** attrs) +{ + AccountAmount accountAmount; + int i; + long v; + double amount; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "account")) + accountAmount.account = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "month")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + accountAmount.month = v; + } + + else if (!strcmp(attrs[i], "year")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + accountAmount.year = v; + } + + else if (!strcmp(attrs[i], "amount")) + wxString(attrs[i+1], wxConvUTF8).ToDouble(&amount); + } + + _this->_accountAmounts[accountAmount] = amount; +} + +void XMLImportEngine::LoadCategory(XMLImportEngine* _this, const char** attrs) +{ + wxString name, id; + int i; + long rgb; + Category cat; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "name")) + cat.name = name = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "id")) + cat.id = id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "font")) + cat.font = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "backcolor")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&rgb, 16); + cat.backcolor = wxColour((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + else if (!strcmp(attrs[i], "forecolor")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&rgb, 16); + cat.forecolor = wxColour((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF); + } + + else if (!strcmp(attrs[i], "fix_cost")) + cat.fix_cost = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(cat.name); + + for (i=0; i<(int)_this->_user->_categories.size(); i++) + { + if (_this->_user->_categories[i].name == name) + { + _this->_categories[id] = _this->_user->_categories[i].id; + return; + } + } + + _this->_categories[id] = wxT("unknown-") + name; + _this->_unresolvedCategories.push_back(cat); +} + +void XMLImportEngine::LoadOperation(XMLImportEngine* _this, const char** attrs) +{ + int i; + Operation op; + long v; + double amount; + wxString id; + + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "id")) + op.id = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "parent")) + op.parent = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "day")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.day = v; + } + + else if (!strcmp(attrs[i], "month")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.month = v; + } + + else if (!strcmp(attrs[i], "year")) + { + wxString(attrs[i+1], wxConvUTF8).ToLong(&v); + op.year = v; + } + + else if (!strcmp(attrs[i], "amount")) + { + wxString(attrs[i+1], wxConvUTF8).ToDouble(&amount); + op.amount = amount; + } + + else if (!strcmp(attrs[i], "description")) + op.description = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "category")) + op.category = _this->_categories[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "fix_cost")) + op.fix_cost = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "account")) + op.account = _this->_accounts[wxString(attrs[i+1], wxConvUTF8)]; + + else if (!strcmp(attrs[i], "checked")) + op.checked = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "transfert")) + op.transfert = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "formula")) + op.formula = wxString(attrs[i+1], wxConvUTF8); + + else if (!strcmp(attrs[i], "meta")) + op.meta = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + + else if (!strcmp(attrs[i], "virtual")) + op._virtual = (wxString(attrs[i+1], wxConvUTF8) == wxT("1")); + } + + UNESCAPE_CHARS(op.description); + + _this->_operations.push_back(op); + _this->_descriptions[op.id] = op.description; + + _this->MatchPattern(op.description, op); +} + +void XMLImportEngine::XmlStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_) +{ + XMLImportEngine* _this = (XMLImportEngine*) user_data; + int i; + const char** attrs = (const char**) attrs_; + const char* name = (const char*) name_; + + // if (!first && strcmp(name, "Xml")) + // { + // throw "Invalid file !"; + // } + // else + // first = 1; + + if (!strcmp(name, "kisscount")) + { + for (i=0; attrs[i]; i+=2) + { + if (!strcmp(attrs[i], "version") && strcmp(attrs[i+1], "1")) + throw "Unsupported version !"; + } + } + + else if (!strcmp(name, "account")) + LoadAccount(_this, attrs); + + else if (!strcmp(name, "account_amount")) + LoadAccountAmount(_this, attrs); + + else if (!strcmp(name, "category")) + LoadCategory(_this, attrs); + + else if (!strcmp(name, "operation")) + LoadOperation(_this, attrs); +} + +XMLImportEngine::XMLImportEngine() +{ + KissCount::RegisterImportEngine(this); + + _shortExt = wxT("xml"); + _longExt = _("KissCount xml files (*.xml)|*.xml"); + + _sax.startElement = XmlStartElement ; +} + +XMLImportEngine::~XMLImportEngine() +{ +} + +bool XMLImportEngine::HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss) +{ + int res = -1 ; + + if (!ImportEngine::HandleFile(path, user, db, kiss)) return false; + + try + { + res = xmlSAXUserParseFile(&_sax, this, path.mb_str()) >= 0; + } + catch (const char* s) + { + std::cout << "XMLImportEngine :: " << s << std::endl; + res = -1; + } + + return res >= 0; +} diff --git a/src/model/import/XMLImportEngine.h b/src/model/import/XMLImportEngine.h new file mode 100644 index 0000000..2ab355b --- /dev/null +++ b/src/model/import/XMLImportEngine.h @@ -0,0 +1,44 @@ +/* + Copyright 2010-2011 Grégory Soutadé + + This file is part of KissCount. + + KissCount is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + KissCount is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with KissCount. If not, see . +*/ + +#ifndef XMLIMPORTENGINE_H +#define XMLIMPORTENGINE_H + +#include + +#include "ImportEngine.h" + +class XMLImportEngine : public ImportEngine { +public: + XMLImportEngine(); + ~XMLImportEngine(); + + virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss); + +private: + xmlSAXHandler _sax; + + static void XmlStartElement(void* user_data, const xmlChar* name_, const xmlChar** attrs_); + static void LoadAccount(XMLImportEngine* _this, const char** attrs); + static void LoadAccountAmount(XMLImportEngine* _this, const char** attrs); + static void LoadCategory(XMLImportEngine* _this, const char** attrs); + static void LoadOperation(XMLImportEngine* _this, const char** attrs); +}; + +#endif diff --git a/src/view/AccountPanel.cpp b/src/view/AccountPanel.cpp index e86beda..25c216a 100644 --- a/src/view/AccountPanel.cpp +++ b/src/view/AccountPanel.cpp @@ -715,7 +715,7 @@ void AccountPanel::OnAccountModified(wxGridEvent& event) _accountsGrid->GetCellValue(row, event.GetCol()).ToDouble(&amount); - _kiss->SetAccountAmount(_curMonth, _curYear, id, amount); + _kiss->SetAccountAmount(id, _curMonth, _curYear, amount); _accountsInitValues[id] = amount; UpdateStats(); @@ -1082,7 +1082,7 @@ void AccountPanel::OnUpdateNextMonths(wxCommandEvent& event) if ((cur_amounts[i] - amount) != deltas[i]) continue; cur_amounts[i] = amount + deltas[i]; - _kiss->SetAccountAmount(last_month, last_year, user->_accounts[i].id, cur_amounts[i]); + _kiss->SetAccountAmount(user->_accounts[i].id, last_month, last_year, cur_amounts[i]); cur_amounts[i] += _kiss->CalcAccountAmount(user->_accounts[i].id, last_month, last_year, &had_values); account_updated++; diff --git a/src/view/ImportPanel.cpp b/src/view/ImportPanel.cpp index 9b4b9e0..92d246c 100644 --- a/src/view/ImportPanel.cpp +++ b/src/view/ImportPanel.cpp @@ -150,8 +150,6 @@ void ImportPanel::OnFileEnter(wxCommandEvent& WXUNUSED(event)) void ImportPanel::ProcessFile() { - std::vector accounts; - std::vector categories; User* user = _kiss->GetUser(); int i; wxGridCellChoiceEditor* accountEditor; @@ -179,9 +177,9 @@ void ImportPanel::ProcessFile() return ; } - _importEngine->ParseFile(accounts, categories); + _importEngine->ParseFile(_unresolvedAccounts, _unresolvedCategories); - if (accounts.size()) + if (_unresolvedAccounts.size()) { int nb_accounts = user->GetAccountsNumber(); userAccounts = new wxString[nb_accounts+1]; @@ -195,13 +193,13 @@ void ImportPanel::ProcessFile() _buttonLoadOperations->Enable(); - _accountsGrid->AppendRows(accounts.size()); + _accountsGrid->AppendRows(_unresolvedAccounts.size()); - for (i=0; i<(int)accounts.size(); i++) + for (i=0; i<(int)_unresolvedAccounts.size(); i++) { - _accountsGrid->SetCellValue(i, 0, accounts[i].number); + _accountsGrid->SetCellValue(i, 0, _unresolvedAccounts[i].number); _accountsGrid->SetReadOnly(i, 0); - _accountsGrid->SetCellValue(i, 1, accounts[i].name); + _accountsGrid->SetCellValue(i, 1, _unresolvedAccounts[i].name); _accountsGrid->SetCellValue(i, 2, userAccounts[0]); _accountsGrid->SetCellEditor(i, 2, accountEditor); @@ -211,7 +209,7 @@ void ImportPanel::ProcessFile() _accountsGrid->Layout(); } - if (categories.size()) + if (_unresolvedCategories.size()) { int nb_categories = user->GetCategoriesNumber(); userCategories = new wxString[nb_categories+1]; @@ -225,11 +223,11 @@ void ImportPanel::ProcessFile() _buttonLoadOperations->Enable(); - _categoriesGrid->AppendRows(categories.size()); + _categoriesGrid->AppendRows(_unresolvedCategories.size()); - for (i=0; i<(int)categories.size(); i++) + for (i=0; i<(int)_unresolvedCategories.size(); i++) { - _categoriesGrid->SetCellValue(i, 0, categories[i].name); + _categoriesGrid->SetCellValue(i, 0, _unresolvedCategories[i].name); _categoriesGrid->SetReadOnly(i, 0); _categoriesGrid->SetCellValue(i, 2, userCategories[0]); @@ -240,7 +238,7 @@ void ImportPanel::ProcessFile() _categoriesGrid->Layout(); } - if (!accounts.size() && !categories.size()) + if (!_unresolvedAccounts.size() && !_unresolvedCategories.size()) { OnLoadOperations(event); } @@ -249,19 +247,20 @@ void ImportPanel::ProcessFile() void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) { - std::map resolvedAccounts; - std::map resolvedCategories; int i, nbAccounts=0, nbCategories=0; User* user = _kiss->GetUser(); Account account; Category category; + std::map accounts; + std::map categories; + wxString oldid; for(i=0; i<_accountsGrid->GetNumberRows(); i++) { if (_accountsGrid->GetCellValue(i, 2) == _("Create one")) nbAccounts++; else - resolvedAccounts[_accountsGrid->GetCellValue(i, 0)] = + accounts[_accountsGrid->GetCellValue(i, 0)] = user->GetAccountId(_accountsGrid->GetCellValue(i, 1)); } @@ -270,7 +269,7 @@ void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) if (_categoriesGrid->GetCellValue(i, 2) == _("Create one")) nbCategories++; else - resolvedCategories[_categoriesGrid->GetCellValue(i, 0)] = + categories[_categoriesGrid->GetCellValue(i, 0)] = user->GetAccountId(_categoriesGrid->GetCellValue(i, 1)); } @@ -297,18 +296,15 @@ void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) { if (_accountsGrid->GetCellValue(i, 2) == _("Create one")) { + account = _unresolvedAccounts[i] ; if (_accountsGrid->GetCellValue(i, 1).Length()) account.name = _accountsGrid->GetCellValue(i, 1); else account.name = _accountsGrid->GetCellValue(i, 0); account.number = _accountsGrid->GetCellValue(i, 0); - account.shared = false; - account.blocked = false; - account._default = false; - account.is_owner = true; - account._virtual = false; - resolvedAccounts[_accountsGrid->GetCellValue(i, 0)] = _kiss->AddAccount(account); + oldid = account.id; + _resolvedAccounts[oldid] = accounts[_accountsGrid->GetCellValue(i, 0)] = _kiss->AddAccount(account); } } @@ -318,16 +314,14 @@ void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) { if (_categoriesGrid->GetCellValue(i, 2) == _("Create one")) { + category = _unresolvedCategories[i] ; if (_categoriesGrid->GetCellValue(i, 1).Length()) category.name = _categoriesGrid->GetCellValue(i, 1); else category.name = _categoriesGrid->GetCellValue(i, 0); - category.parent = wxT("0"); - category.backcolor = OWN_GREEN ; - category.forecolor = *wxBLACK; - category.fix_cost = false; - - resolvedCategories[_categoriesGrid->GetCellValue(i, 0)] = category.id = _kiss->AddCategory(category); + + oldid = category.id; + _resolvedCategories[oldid] = categories[_categoriesGrid->GetCellValue(i, 0)] = category.id = _kiss->AddCategory(category); } } @@ -336,7 +330,7 @@ void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) _wxUI->NeedReload(); } - _operations = _importEngine->GetOperations(resolvedAccounts, resolvedCategories); + _operations = _importEngine->GetOperations(accounts, categories); if (_operations->size()) { @@ -361,6 +355,12 @@ void ImportPanel::OnLoadOperations(wxCommandEvent& WXUNUSED(event)) void ImportPanel::OnIntegrate(wxCommandEvent& WXUNUSED(event)) { int i; + std::map mapid; + wxString oldid, account; + bool update; + std::map accountAmounts; + std::map::iterator it; + double amount; if (!_operations->size()) return; @@ -371,9 +371,48 @@ void ImportPanel::OnIntegrate(wxCommandEvent& WXUNUSED(event)) _buttonIntegrate->Disable(); for(i=0; i<(int)_operations->size(); i++) - _kiss->AddOperation((*_operations)[i]); + { + oldid = (*_operations)[i].id; + _kiss->AddOperation((*_operations)[i], false); + mapid[oldid] = (*_operations)[i].id; + } - // if (_checkSaveImportPatterns->IsChecked()) + for(i=0; i<(int)_operations->size(); i++) + { + update = false; + + if ((*_operations)[i].parent.Length()) + { + (*_operations)[i].parent = mapid[(*_operations)[i].parent]; + update = true; + } + + if ((*_operations)[i].transfert.Length()) + { + (*_operations)[i].transfert = mapid[(*_operations)[i].transfert]; + update = true; + } + + if (update) + _kiss->UpdateOperation((*_operations)[i], false); + } + + accountAmounts = _importEngine->GetAccountAmounts(); + + for(it=accountAmounts.begin(); it!=accountAmounts.end(); it++) + { + account = it->first.account; + + if (_resolvedAccounts.count(account)) + account = _resolvedAccounts[account]; + + amount = _kiss->GetAccountAmount(account, it->first.month, it->first.year); + + if (!amount) + _kiss->SetAccountAmount(account, it->first.month, it->first.year, it->second); + } + + if (_checkSaveImportPatterns->IsChecked()) _kiss->UpdateImportPattern(); _operations->clear(); diff --git a/src/view/ImportPanel.h b/src/view/ImportPanel.h index 89b460a..8ef365c 100644 --- a/src/view/ImportPanel.h +++ b/src/view/ImportPanel.h @@ -61,6 +61,11 @@ private: wxCheckBox *_checkSaveImportPatterns; std::vector* _operations; + std::vector _unresolvedAccounts; + std::vector _unresolvedCategories; + std::map _resolvedAccounts; + std::map _resolvedCategories; + void ProcessFile(); DECLARE_EVENT_TABLE(); diff --git a/src/view/SearchBanner.cpp b/src/view/SearchBanner.cpp index 3ffd8d9..4ee0d91 100644 --- a/src/view/SearchBanner.cpp +++ b/src/view/SearchBanner.cpp @@ -26,11 +26,6 @@ EVT_CALENDAR_SEL_CHANGED(CALENDAR_TO_ID, SearchBanner::OnCalendarToChange) EVT_TEXT_ENTER(DESCRIPTION_ID, SearchBanner::OnEnter) END_EVENT_TABLE() -#define UNESCAPE_CHARS(s) { \ - s.Replace(wxT("\\\""), wxT("\""), true); \ - s.Replace(wxT("\\\'"), wxT("\'"), true); \ - } - SearchBanner::SearchBanner(KissCount* kiss, wxPanel *parent, void* caller, OnButtonEnter enterCallback) : wxPanel(parent), _kiss(kiss), _caller(caller), _enterCallback(enterCallback), _operations(NULL) { DEFAULT_FONT(font); @@ -38,7 +33,6 @@ SearchBanner::SearchBanner(KissCount* kiss, wxPanel *parent, void* caller, OnBut std::vector::iterator accountIt; std::vector::iterator categoryIt; wxDateTime firstOfMonth; - wxRect rect = wxDisplay().GetGeometry(); wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); diff --git a/src/view/grid/GridAccount.cpp b/src/view/grid/GridAccount.cpp index 1f52762..a07d7d4 100644 --- a/src/view/grid/GridAccount.cpp +++ b/src/view/grid/GridAccount.cpp @@ -30,11 +30,6 @@ SetCellFont(row, i, font); \ } -#define UNESCAPE_CHARS(s) { \ - s.Replace(wxT("\\\""), wxT("\""), true); \ - s.Replace(wxT("\\\'"), wxT("\'"), true); \ - } - BEGIN_EVENT_TABLE(GridAccount, wxGrid) EVT_GRID_CELL_LEFT_CLICK(GridAccount::OnCellLeftClick ) END_EVENT_TABLE() @@ -665,7 +660,7 @@ void GridAccount::OnOperationModified(wxGridEvent& event) int op_complete = 6, i, last_day; wxString value ; wxDateTime date; - bool need_insertion = false, fix_op=false; + bool need_insertion = false; static bool inModification = false ; wxColour color ; unsigned char r, g, b; @@ -946,8 +941,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) UpdateOperation(new_op); (_displayedOperations)[row] = new_op; } - - fix_op = true; } // Add a fixCost else if (row == _fixCosts) @@ -957,7 +950,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) return ; } need_insertion = true; - fix_op = true; new_op.fix_cost = true; new_op.meta = false; new_op._virtual = false; @@ -1058,7 +1050,6 @@ void GridAccount::OnOperationModified(wxGridEvent& event) return ; } need_insertion = true; - fix_op = false; new_op.fix_cost = false; new_op.meta = false; new_op._virtual = false;