Add XMLImportEngine

Fix some bugs in ImportPanel and ImportEngines
This commit is contained in:
Grégory Soutadé 2011-07-04 20:23:00 +02:00
parent ebe9ef4a62
commit 753b499b48
21 changed files with 597 additions and 117 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

44
src/model/AccountAmount.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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

View File

@ -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 ") ;

View File

@ -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);

View File

@ -42,6 +42,10 @@ bool ExportEngine::SaveFile(std::vector<Operation>* 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<wxString, int>::iterator it;
if (!operations) return NULL;
@ -54,23 +58,45 @@ bool ExportEngine::SaveFile(std::vector<Operation>* 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;
}

View File

@ -22,30 +22,10 @@
#include <model/model.h>
#include <controller/KissCount.h>
#include <model/AccountAmount.h>
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();

View File

@ -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);
}

View File

@ -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;
}

View File

@ -30,8 +30,6 @@ public:
~GrisbiImportEngine();
virtual bool HandleFile(const wxString& path, User* user, Database* db, KissCount* kiss);
/* virtual std::vector<wxString> ParseFile(); */
/* virtual std::vector<Operation>* GetOperations(std::map<wxString, wxString>& accounts); */
private:
xmlSAXHandler _sax;

View File

@ -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<Operation>* ImportEngine::GetOperations(std::map<wxString, wxString>
return &_operations;
}
const std::map<AccountAmount, double, AccountAmount>& ImportEngine::GetAccountAmounts()
{
return _accountAmounts;
}

View File

@ -22,6 +22,7 @@
#include <model/model.h>
#include <controller/KissCount.h>
#include <model/AccountAmount.h>
class KissCount;
@ -52,10 +53,13 @@ public:
// Final Step
virtual std::vector<Operation>* GetOperations(std::map<wxString, wxString>& accounts, std::map<wxString, wxString>& categories);
const std::map<AccountAmount, double, AccountAmount>& 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<Category> _unresolvedCategories;
std::vector<Operation> _operations;
std::map<wxString, wxString> _descriptions;
std::map<AccountAmount, double, AccountAmount> _accountAmounts;
void ApplyPattern(ImportPattern& pattern, Operation& op);
wxString FindPattern(wxString& s1, wxString& s2);

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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;
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef XMLIMPORTENGINE_H
#define XMLIMPORTENGINE_H
#include <libxml/parser.h>
#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

View File

@ -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++;

View File

@ -150,8 +150,6 @@ void ImportPanel::OnFileEnter(wxCommandEvent& WXUNUSED(event))
void ImportPanel::ProcessFile()
{
std::vector<Account> accounts;
std::vector<Category> 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<wxString, wxString> resolvedAccounts;
std::map<wxString, wxString> resolvedCategories;
int i, nbAccounts=0, nbCategories=0;
User* user = _kiss->GetUser();
Account account;
Category category;
std::map<wxString, wxString> accounts;
std::map<wxString, wxString> 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<wxString, wxString> mapid;
wxString oldid, account;
bool update;
std::map<AccountAmount, double, AccountAmount> accountAmounts;
std::map<AccountAmount, double, AccountAmount>::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();

View File

@ -61,6 +61,11 @@ private:
wxCheckBox *_checkSaveImportPatterns;
std::vector<Operation>* _operations;
std::vector<Account> _unresolvedAccounts;
std::vector<Category> _unresolvedCategories;
std::map<wxString, wxString> _resolvedAccounts;
std::map<wxString, wxString> _resolvedCategories;
void ProcessFile();
DECLARE_EVENT_TABLE();

View File

@ -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<Account>::iterator accountIt;
std::vector<Category>::iterator categoryIt;
wxDateTime firstOfMonth;
wxRect rect = wxDisplay().GetGeometry();
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);

View File

@ -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;