KissCount

KissCount Git Source Tree

Root/src/view/grid/GridAccount.cpp

1/*
2 Copyright 2010-2012 Grégory Soutadé
3
4 This file is part of KissCount.
5
6 KissCount is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 KissCount is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with KissCount. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include <QtGui>
21
22#include "GridAccount.hpp"
23#include "TableViewDelegate.hpp"
24#include "ChoiceDelegate.hpp"
25#include "DateDelegate.hpp"
26#include "FloatDelegate.hpp"
27#include "FormulaDelegate.hpp"
28#include "TabDelegate.hpp"
29
30#define SET_ROW_COLOR(row, backcolor, forecolor) for(int i=0; i<NUMBER_COLS_OPS; i++) \
31 {\
32if (!this->item(row, i)) setItem(row, i, new QTableWidgetItem("")); \
33this->item(row, i)->setBackground(backcolor);\
34this->item(row, i)->setForeground(forecolor);\
35 }
36
37#define SET_ROW_FONT(row, font) for(int i=0; i<NUMBER_COLS_OPS; i++)\
38 {\
39if (!this->item(row, i)) setItem(row, i, new QTableWidgetItem("")); \
40this->item(row, i)->setFont(font);\
41 }
42
43#define SET_READ_ONLY(item) item->setFlags(item->flags() & ~Qt::ItemIsEditable);
44
45GridAccount::GridAccount(KissCount* kiss, QWidget *parent,
46 bool canAddOperation, bool setWeek, bool synchronizeWithDatabase)
47 : QTableWidget(parent), _fixCosts(0), _week1(0),
48 _week2(0), _week3(0), _week4(0), _week5(0), _canAddOperation(canAddOperation),
49 _parent(parent), _kiss(kiss), _setWeek(setWeek),
50 _databaseSynchronization(synchronizeWithDatabase), _loadOperations(false),
51 _curMonth(0), _curYear(0), _treeSignalMapper(this), _checkSignalMapper(this),
52 _deleteSignalMapper(this), _inModification(false), _completer(0),
53 _transfertCompletionIndex(0)
54{
55 DEFAULT_FONT(font);
56 int i;
57 User* user = _kiss->GetUser();
58 std::vector<Account>::iterator accountIt;
59 std::vector<Category>::iterator categoryIt;
60 QTableWidgetItem* item;
61 QLabel* label;
62
63 setColumnCount(NUMBER_COLS_OPS);
64 setRowCount(1);
65 verticalHeader()->setHidden(true);
66 horizontalHeader()->setHidden(true);
67 setShowGrid(false);
68
69 setColumnWidth (DESCRIPTION, columnWidth(DESCRIPTION)*3);
70 setFont(font);
71
72 font.setBold(true);
73 QString colsName[] = {"", _("Description"), _("Date"), _("Debit"), _("Credit"), _("Category"), _("Account"), "", ""};
74 for(i=0; i<NUMBER_COLS_OPS; i++)
75 {
76item = new QTableWidgetItem(colsName[i]);
77item->setBackground(view::OWN_CYAN);
78item->setFont(font);
79SET_READ_ONLY(item);
80item->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
81setItem(0, i, item);
82 }
83 label = new QLabel();
84 label->setPixmap(QPixmap(DELETE_ICON));
85 label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
86 setCellWidget(0, OP_DELETE, label);
87
88 label = new QLabel();
89 label->setPixmap(QPixmap(CHECKED_ICON));
90 label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
91 setCellWidget(0, CHECKED, label);
92
93 _nbAccounts = 0;
94 for (accountIt = user->_accounts.begin();
95 accountIt != user->_accounts.end();
96 accountIt++)
97 {
98if (!accountIt->hidden)
99 _nbAccounts++;
100 }
101
102 _accounts = new QString[_nbAccounts];
103 for (i=0,
104 accountIt = user->_accounts.begin();
105 accountIt != user->_accounts.end();
106 accountIt++, i++)
107 {
108if (!accountIt->hidden)
109 _accounts[i] = accountIt->name;
110else
111 i--;
112 }
113
114 _categories = new QString[user->GetCategoriesNumber()-1] ;
115 for(i=0, categoryIt = user->_categories.begin()+1;
116 categoryIt != user->_categories.end();
117 categoryIt++, i++)
118 {
119 _categories[i] = _(categoryIt->name.toStdString().c_str()) ;
120 }
121
122 resizeColumnToContents(TREE);
123 resizeColumnToContents(CATEGORY);
124 resizeColumnToContents(OP_DATE);
125 resizeColumnToContents(ACCOUNT);
126 resizeColumnToContents(OP_DELETE);
127 resizeColumnToContents(CHECKED);
128
129 connect(&_treeSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnMetaClicked(int)));
130 connect(&_checkSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnCheckClicked(int)));
131 connect(&_deleteSignalMapper, SIGNAL(mapped(int)), this, SLOT(OnDeleteClicked(int)));
132 setItemDelegate(new TableViewDelegate(this));
133 connect(this, SIGNAL(cellChanged(int, int)), this, SLOT(OnOperationModified(int, int)));
134}
135
136GridAccount::~GridAccount()
137{
138 delete[] _categories;
139 delete[] _accounts;
140 if (_completer)
141delete _completer;
142}
143
144void GridAccount::ResetWeeks()
145{
146 _week1 = _week2 = _week3 = _week4 = _week5 = 0;
147}
148
149void GridAccount::SetWeek(int week, int line) {
150 switch (week) {
151 case 1: _week1 = line; break;
152 case 2: _week2 = line; break;
153 case 3: _week3 = line; break;
154 case 4: _week4 = line; break;
155 case 5: _week5 = line; break;
156 }
157}
158
159Operation& GridAccount::GetOperation(int id) throw (OperationNotFound)
160{
161 std::vector<Operation>::iterator it = std::find(_operations->begin(), _operations->end(), id);
162
163 if (it != _operations->end()) return *it;
164
165 throw OperationNotFound();
166}
167
168void GridAccount::UpdateOperation(Operation& op)
169{
170 std::vector<Operation>::iterator it = std::find(_operations->begin(), _operations->end(), op.id);
171
172 if (it != _operations->end())
173 {
174if (_databaseSynchronization)
175 _kiss->UpdateOperation(op);
176*it = op;
177 }
178}
179
180int GridAccount::GetDisplayedRow(int id) throw (OperationNotFound)
181{
182 std::vector<Operation>::iterator it = std::find(_displayedOperations.begin(), _displayedOperations.end(), id);
183
184 if (it != _displayedOperations.end()) return it-_displayedOperations.begin();
185
186 throw OperationNotFound();
187}
188
189void GridAccount::ClearGrid()
190{
191 std::vector<Operation> operations;
192
193 LoadOperations(&operations, 0, 0);
194}
195
196void GridAccount::LoadOperations(std::vector<Operation>* operations, int month, int year)
197{
198 std::vector<Operation>::iterator it;
199 User* user = _kiss->GetUser();
200 int curLine = 0;
201 Operation NULLop;
202 QStringList list;
203
204 NULLop.id = 0;
205
206 _loadOperations = true;
207 _operations = operations;
208 _curMonth = month;
209 _curYear = year;
210 _displayedOperations.clear();
211 _displayedOperations.push_back(NULLop); // Header
212 _fixCosts = 0;
213 it = _operations->begin();
214
215 if (rowCount() > 1)
216setRowCount(1);
217
218 if (_completer)
219 {
220delete _completer;
221_completer = 0;
222 }
223
224 if (_canAddOperation)
225 {
226_kiss->GetHistory(month, year, list);
227_completer = new QCompleter(list);
228_completer->setCaseSensitivity(Qt::CaseInsensitive);
229//_completer->setCompletionMode(QCompleter::InlineCompletion);
230 }
231
232 TabDelegate* descriptionEditor = new TabDelegate(this, &_displayedOperations, _completer);
233 setItemDelegateForColumn(DESCRIPTION, descriptionEditor);
234
235 ChoiceDelegate* categoryEditor = new ChoiceDelegate(this, _categories, user->GetCategoriesNumber()-1);
236 setItemDelegateForColumn(CATEGORY, categoryEditor);
237 ChoiceDelegate* accountEditor = new ChoiceDelegate(this, _accounts, _nbAccounts);
238 setItemDelegateForColumn(ACCOUNT, accountEditor);
239
240 DateDelegate* dateEditor = new DateDelegate(this, month+1, year, _kiss->GetDateFormat());
241 setItemDelegateForColumn(OP_DATE, dateEditor);
242
243 FormulaDelegate* formulaEditor = new FormulaDelegate(this, &_displayedOperations);
244 setItemDelegateForColumn(DEBIT, formulaEditor);
245 setItemDelegateForColumn(CREDIT, formulaEditor);
246
247 if (_canAddOperation)
248 {
249for (;it != _operations->end() && it->fix_cost; it++)
250{
251 if (it->parent) continue;
252
253 if (_setWeek)
254InsertOperationWithWeek(user, *it, ++curLine, true, it->month, it->year);
255 else
256InsertOperation(user, *it, ++curLine, true, it->month, it->year);
257}
258
259InsertOperation(user, NULLop, ++curLine, true, month, year);
260
261for (; it != _operations->end(); it++)
262{
263 if (it->parent) continue;
264
265 if (_setWeek)
266InsertOperationWithWeek(user, *it, ++curLine, false, it->month, it->year);
267 else
268InsertOperation(user, *it, ++curLine, false, it->month, it->year);
269}
270
271InsertOperation(user, NULLop, ++curLine, false, month, year);
272 }
273 else
274 {
275for (;it != _operations->end(); it++)
276{
277 if (it->parent) continue;
278
279 InsertOperation(user, *it, ++curLine, it->fix_cost, it->month, it->year);
280}
281 }
282
283 resizeColumnToContents(TREE);
284 resizeColumnToContents(CATEGORY);
285 resizeColumnToContents(OP_DATE);
286 resizeColumnToContents(ACCOUNT);
287 resizeColumnToContents(OP_DELETE);
288 resizeColumnToContents(CHECKED);
289
290 _loadOperations = false;
291}
292
293void GridAccount::ComputeWeeks()
294{
295 std::vector<Operation>::iterator it;
296 int curLine, curWeek, week, i;
297
298 if (!_canAddOperation) return;
299
300 for (it = _displayedOperations.begin(), curLine=0;
301 it != _displayedOperations.end();
302 it++, curLine++)
303 {
304if (it->id && !it->fix_cost) break;
305 }
306
307 if (it == _displayedOperations.end()) return;
308
309 ResetWeeks();
310
311 curWeek = QDate(it->year, it->month+1, it->day+1).weekNumber();
312 it++;
313 for (i=1; it != _displayedOperations.end(); it++, curLine++)
314 {
315 if (!it->id || it->parent) continue;
316 week = QDate(it->year, it->month+1, it->day+1).weekNumber();
317 if (week != curWeek)
318 {
319 SetWeek(i++, curLine);
320 curWeek = week;
321 }
322 }
323}
324
325void GridAccount::InsertOperationWithWeek(User* user, Operation& op, int line, bool fix, int month, int year)
326{
327 InsertOperation(user, op, line, fix, month, year);
328
329 if (op.id && !fix)
330ComputeWeeks();
331}
332
333void GridAccount::InsertOperation(User* user, Operation& op, int line, bool fix, int month, int year)
334{
335 std::vector<Operation>::iterator it;
336 std::vector<QString>::iterator it2;
337 int r, g, b;
338 int amount;
339 QColor color;
340 QDate curDate = QDate::currentDate();
341 QString description, v;
342 DEFAULT_FONT(font);
343 Category cat ;
344 Operation op2;
345 QTableWidgetItem* item;
346 QCheckBox* checkBox;
347
348 // // First is header
349 // if (op.id)
350 _displayedOperations.insert(_displayedOperations.begin()+line, op);
351
352 if (!user->_accounts.size()) return;
353
354 _inModification = true;
355
356 insertRow(line);
357
358 if (fix)
359 {
360item = new QTableWidgetItem(_("Fix"));
361setItem(line, CATEGORY, item);
362SET_READ_ONLY(item);
363_fixCosts++;
364 }
365
366 if (op.id)
367 {
368 cat = user->GetCategory(op.category);
369
370 description = op.description;
371 UNESCAPE_CHARS(description);
372if (op.parent)
373 setItem(line, DESCRIPTION, new QTableWidgetItem(" " + description));
374else
375 setItem(line, DESCRIPTION, new QTableWidgetItem(description));
376item = new QTableWidgetItem();
377setItem(line, OP_DATE, new QTableWidgetItem(_kiss->FormatDate(op.day+1, month+1, year)));
378 if (op.amount < 0)
379{
380 setItem(line, DEBIT, new QTableWidgetItem(v.sprintf("%.2lf", (double)-op.amount/100)));
381}
382 else
383 setItem(line, CREDIT, new QTableWidgetItem(v.sprintf("%.2lf", (double)op.amount/100)));
384
385if (!op.meta)
386 setItem(line, ACCOUNT, new QTableWidgetItem(user->GetAccountName(op.account)));
387 if (!fix && !op.meta)
388 setItem(line, CATEGORY, new QTableWidgetItem(_(cat.name.toStdString().c_str())));
389
390checkBox = new QCheckBox();
391checkBox->setCheckState(Qt::Unchecked);
392setCellWidget(line, OP_DELETE, checkBox);
393_deleteSignalMapper.setMapping(checkBox, op.id);
394connect(checkBox, SIGNAL(stateChanged(int)), &_deleteSignalMapper, SLOT(map()));
395
396checkBox = new QCheckBox();
397checkBox->setCheckState((op.checked) ? Qt::Checked : Qt::Unchecked);
398setCellWidget(line, CHECKED, checkBox);
399_checkSignalMapper.setMapping(checkBox, op.id);
400connect(checkBox, SIGNAL(stateChanged(int)), &_checkSignalMapper, SLOT(map()));
401
402if (op.meta && !op.amount)
403{
404 amount = _kiss->MetaPositiveAmount(op.id);
405
406 setItem(line, DEBIT, new QTableWidgetItem(v.sprintf("%.2lf", (double)amount/100)));
407 setItem(line, CREDIT, new QTableWidgetItem(v.sprintf("%.2lf", (double)amount/100)));
408}
409
410if (op.category)
411 color = cat.backcolor;
412else
413 color = view::OWN_GREEN;
414
415 if (op.checked)
416{
417 r = ((color.red()*1.5) >= 0xFF) ? 0xFF : color.red()*1.5 ;
418 g = ((color.green()*1.5) >= 0xFF) ? 0xFF : color.green()*1.5 ;
419 b = ((color.blue()*1.5) >= 0xFF) ? 0xFF : color.blue()*1.5 ;
420 color.setRgb(r, g, b, color.alpha());
421}
422
423 SET_ROW_COLOR(line, color, cat.forecolor);
424SET_ROW_FONT(line, user->GetCategoryFont(cat.id));
425
426if (!_loadOperations)
427{
428 if (op.meta)
429resizeColumnToContents(TREE);
430 resizeColumnToContents(CATEGORY);
431 resizeColumnToContents(ACCOUNT);
432}
433 }
434 else
435 {
436// NULL Op
437item = new QTableWidgetItem("");
438setItem(line, DESCRIPTION, item);
439 if (fix)
440 {
441 SET_ROW_COLOR(line, view::OWN_YELLOW, Qt::black);
442 }
443 else
444 {
445 int day;
446 if (curDate.year() == year)
447 {
448if (curDate.month() > (month+1))
449 day = QDate(year, month+1, 1).daysInMonth();
450else if (curDate.month() < (month+1))
451 day = 1;
452else
453 day = curDate.day();
454
455 }
456 else if (curDate.year() > year)
457day = QDate(year, month+1, 1).daysInMonth();
458 else
459day = 1;
460
461 setItem(line, OP_DATE, new QTableWidgetItem(_kiss->FormatDate(day, month+1, year)));
462 SET_ROW_COLOR(line, view::OWN_GREEN, Qt::black);
463 }
464
465SET_READ_ONLY(this->item(line, CHECKED));
466SET_READ_ONLY(this->item(line, OP_DELETE));
467SET_ROW_FONT(line, user->GetCategoryFont(0));
468 }
469
470 this->item(line, OP_DATE)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
471 this->item(line, DEBIT)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
472 this->item(line, CREDIT)->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
473 this->item(line, OP_DELETE)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
474 this->item(line, CHECKED)->setTextAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
475
476 if (op.id && op.meta)
477 {
478int height = rowHeight(TREE);
479QPushButton* button = new QPushButton("+");
480_treeSignalMapper.setMapping(button, op.id);
481connect(button, SIGNAL(clicked()), &_treeSignalMapper, SLOT(map()));
482
483button->setMaximumSize(QSize(height, height));
484setCellWidget(line, TREE, button);
485
486SET_READ_ONLY(this->item(line, OP_DATE));
487SET_READ_ONLY(this->item(line, CREDIT));
488SET_READ_ONLY(this->item(line, DEBIT));
489SET_READ_ONLY(this->item(line, CATEGORY));
490SET_READ_ONLY(this->item(line, ACCOUNT));
491 }
492 else
493SET_READ_ONLY(this->item(line, TREE));
494
495 resizeRowToContents(line);
496
497 layout();
498
499 _inModification = false;
500}
501
502void GridAccount::DeleteOperation(const Operation& op) throw (OperationNotFound)
503{
504 std::vector<Operation>::iterator it = std::find(_operations->begin(), _operations->end(), op.id);
505 std::vector<int>::iterator it2;
506 Operation parent;
507
508 User* user = _kiss->GetUser();
509
510 if (it == _operations->end())
511throw OperationNotFound();
512
513 if (it->parent)
514user->UnGroup(*it);
515
516 if (it != _operations->end())
517 {
518if (_databaseSynchronization)
519 _kiss->DeleteOperation(*it);
520if (it->parent)
521{
522 parent = GetOperation(it->parent);
523 it2 = std::find(parent.childs.begin(), parent.childs.end(), it->id);
524
525 if (it2 != parent.childs.end())
526parent.childs.erase(it2);
527}
528_operations->erase(it);
529 }
530}
531
532void GridAccount::RemoveRow(const Operation& op, int line, bool deleteOp)
533{
534 QPushButton* button = qobject_cast<QPushButton*> (cellWidget(line, TREE));
535
536 if (button)
537button->disconnect(&_treeSignalMapper, SLOT(map()));
538 QCheckBox* checkBox = qobject_cast<QCheckBox*> (cellWidget(line, CHECKED));
539 if (checkBox)
540checkBox->disconnect(&_checkSignalMapper, SLOT(map()));
541 checkBox = qobject_cast<QCheckBox*> (cellWidget(line, OP_DELETE));
542 if (checkBox)
543checkBox->disconnect(&_deleteSignalMapper, SLOT(map()));
544 removeRow(line);
545 _displayedOperations.erase(_displayedOperations.begin()+line);
546 if (op.fix_cost) _fixCosts--;
547 if (deleteOp)
548DeleteOperation(op);
549}
550
551bool GridAccount::CheckDay(User* user, const Operation& op, int& i)
552{
553 if (user->_preferences["operation_order"] == "ASC")
554 {
555if ((_displayedOperations)[i].day > op.day)
556 return true;
557 }
558 else
559 {
560if ((_displayedOperations)[i].day < op.day)
561 return true;
562 }
563
564 return false;
565}
566
567int GridAccount::InsertIntoGrid(Operation& op)
568{
569 int i, a, start=0;
570 User* user = _kiss->GetUser();
571 Operation parent;
572
573 if (op.parent)
574parent = GetOperation(op.parent);
575
576 // No previous fix operations
577 if (op.fix_cost && !_displayedOperations[1].id)
578i = 1;
579 else
580 {
581if (op.parent)
582{
583 start = GetDisplayedRow(op.parent)+1;
584 for(i=start; i<(int)_displayedOperations.size(); i++)
585 {
586if (!_displayedOperations[i].parent) break;
587if (_displayedOperations[i].id == 0) break;
588if (CheckDay(user, op, i)) break;
589 }
590}
591else
592{
593 if (op.fix_cost)
594 {
595for(i=1; i<(int)_fixCosts; i++)
596{
597 if (_displayedOperations[i].parent) continue;
598 if (_displayedOperations[i].id == 0) break;
599 if (CheckDay(user, op, i)) break;
600}
601 }
602 else
603 {
604for(i=_fixCosts+1; i<(int)_displayedOperations.size()-1; i++)
605{
606 if (_displayedOperations[i].parent) continue;
607 if (_displayedOperations[i].fix_cost) continue;
608 if (_displayedOperations[i].id == 0) break;
609 if (CheckDay(user, op, i)) break;
610}
611 }
612}
613 }
614
615 for (a=0; a<(int)_operations->size(); a++)
616 {
617if ((*_operations)[a].fix_cost && !op.fix_cost) continue;
618if (!(*_operations)[a].fix_cost && op.fix_cost) break;
619if ((*_operations)[a].day > op.day) break;
620 }
621
622 _operations->insert(_operations->begin()+a, op);
623
624 InsertOperationWithWeek(user, op, i, op.fix_cost, _curMonth, _curYear);
625
626 return i;
627}
628
629void GridAccount::CheckOperation(Operation& op, int line, bool check, bool force)
630{
631 QColor color;
632 int r,g,b;
633 User* user = _kiss->GetUser();
634
635 if (!force)
636 {
637if (op.checked == check) return;
638
639op.checked = check;
640UpdateOperation(op);
641
642QCheckBox* checkBox = qobject_cast<QCheckBox*>(cellWidget(line, CHECKED));
643if (checkBox)
644 checkBox->setCheckState(check ? Qt::Checked : Qt::Unchecked);
645 }
646
647 color = user->GetCategory(op.category).backcolor;
648
649 if (check)
650 {
651r = ((color.red()*1.5) >= 0xFF) ? 0xFF : color.red()*1.5 ;
652g = ((color.green()*1.5) >= 0xFF) ? 0xFF : color.green()*1.5 ;
653b = ((color.blue()*1.5) >= 0xFF) ? 0xFF : color.blue()*1.5 ;
654color.setRgb(r, g, b);
655 }
656 SET_ROW_COLOR(line, color, user->GetCategory(op.category).forecolor);
657}
658
659int GridAccount::RemoveMeta(Operation op, int line, bool removeRoot, bool deleteOp)
660{
661 std::vector<Operation*>::iterator it, it2;
662 int i, deletedOperations = 0;
663 Operation op2;
664 QPushButton* button = qobject_cast<QPushButton*> (cellWidget(line, TREE));
665
666 for(i=0; i<(int)op.childs.size(); i++)
667 {
668 op2 = GetOperation(op.childs[i]);
669 if (op2.meta)
670 deletedOperations += RemoveMeta(op2, line+1, true, deleteOp);
671 else
672 {
673 if (button->text() == "-")
674 {
675RemoveRow(op2, line+1, deleteOp);
676deletedOperations++;
677 }
678 }
679 }
680
681 op.childs.clear();
682
683 if (removeRoot)
684 {
685RemoveRow(op, line, deleteOp);
686deletedOperations++;
687 }
688
689 return deletedOperations;
690}
691
692void GridAccount::CheckMeta(Operation& op, int line, bool check)
693{
694 int i;
695 Operation op2;
696
697 CheckOperation(op, line, check, false);
698
699 if (IsMetaOpened(op.id))
700 {
701for(i=0; i<(int)op.childs.size(); i++)
702{
703 op2 = GetOperation(op.childs[i]);
704
705 if (op2.meta)
706CheckMeta(op2, line+i+1, check);
707 else
708CheckOperation(op2, line+i+1, check, false);
709}
710 }
711 else
712 {
713for(i=0; i<(int)op.childs.size(); i++)
714{
715 op2 = GetOperation(op.childs[i]);
716
717 if (op2.meta)
718CheckMeta(op2, line+i+1, check);
719 else
720 {
721op2.checked = check;
722UpdateOperation(op2);
723 }
724}
725 }
726}
727
728bool GridAccount::IsMetaOpened(int id)
729{
730 int row = GetDisplayedRow(id);
731
732 QPushButton* button = qobject_cast<QPushButton*> (cellWidget(row, TREE));
733
734 return (button->text() == QString("-"));
735}
736
737void GridAccount::OpenMeta(const Operation& meta)
738{
739 if (!IsMetaOpened(meta.id))
740OnMetaClicked(meta.id);
741}
742
743void GridAccount::OnMetaClicked(int id)
744{
745 QPushButton* button = qobject_cast<QPushButton*> (_treeSignalMapper.mapping(id));
746 std::vector<Operation>::iterator it;
747 std::vector<int>::iterator it2;
748 int i, row;
749 Operation op, op2;
750 User* user = _kiss->GetUser();
751
752 it = std::find(_displayedOperations.begin(), _displayedOperations.end(), id);
753
754 if (it == _displayedOperations.end()) return ;
755
756 op = *it;
757
758 row = it - _displayedOperations.begin();
759
760 if (button->text() == QString("+"))
761 {
762for (i=1, it2=op.childs.begin(); it2!=op.childs.end(); it2++, i++)
763{
764 op2 = GetOperation(*it2);
765 InsertOperation(user, op2, row+i, op2.fix_cost, op2.month, op2.year);
766}
767button->setText("-");
768 }
769 else
770 {
771RemoveMeta(op, row, false, false);
772button->setText("+");
773 }
774
775 ComputeWeeks();
776}
777
778void GridAccount::OnCheckClicked(int id)
779{
780 std::vector<Operation>::iterator it;
781 std::vector<int>::iterator it2;
782 int row;
783 Operation op2, parent;
784 bool fullCheck = true;
785
786 if (_inModification || _loadOperations) return;
787
788 it = std::find(_displayedOperations.begin(), _displayedOperations.end(), id);
789
790 if (it == _displayedOperations.end()) return ;
791
792 _inModification = true;
793
794 row = it-_displayedOperations.begin();
795
796 if (it->meta)
797CheckMeta(*it, row, !it->checked);
798 else
799 {
800CheckOperation(*it, row, !it->checked, false);
801
802if (it->parent)
803{
804 parent = GetOperation(it->parent);
805 for(it2=parent.childs.begin(); it2!=parent.childs.end(); it2++)
806 {
807op2 = GetOperation(*it2);
808if (!op2.checked)
809{
810 fullCheck = false;
811 break;
812}
813 }
814 CheckOperation(parent, GetDisplayedRow(parent.id), fullCheck, false);
815}
816 }
817
818 _inModification = false;
819 _kiss->UpdateStats();
820}
821
822void GridAccount::OnDeleteClicked(int id)
823{
824 std::vector<Operation>::iterator it;
825 int row, parentRow;
826 Operation op, op2, parent;
827 User* user = _kiss->GetUser();
828
829 if (_inModification || _loadOperations) return;
830
831 QCheckBox* checkBox = qobject_cast<QCheckBox*> (_deleteSignalMapper.mapping(id));
832
833 if (checkBox->checkState() == Qt::Unchecked) return;
834
835 it = std::find(_displayedOperations.begin(), _displayedOperations.end(), id);
836
837 if (it == _displayedOperations.end()) return ;
838
839 if (QMessageBox::question(0, "KissCount", _("Are you sure want to delete : \n")+it->description, QMessageBox::Yes|QMessageBox::No) == QMessageBox::No)
840 {
841checkBox->setCheckState(Qt::Unchecked);
842return;
843 }
844
845 op = *it; // Make a copy
846
847 _inModification = true;
848
849 row = it-_displayedOperations.begin();
850
851 if (op.meta)
852RemoveMeta(op, row, true, true);
853 else
854 {
855RemoveRow(op, row, true);
856
857if (op.parent)
858{
859 user->UnGroup(op);
860
861 parent = GetOperation(op.parent);
862 parentRow = GetDisplayedRow(parent.id);
863
864 // One child remains
865 if (parent.childs.size() == 1)
866 {
867op = GetOperation(parent.childs[0]);
868user->UnGroup(op);
869// Remove parent
870RemoveRow(parent, parentRow, true);
871// Remove child
872op.parent = 0;
873UpdateOperation(op);
874RemoveRow(op, parentRow, false);
875InsertIntoGrid(op);
876 }
877 else
878UpdateMeta(parent);
879}
880 }
881
882 _kiss->UpdateStats();
883 _inModification = false;
884}
885
886void GridAccount::OnOperationModified(int row, int col)
887{
888 User* user = _kiss->GetUser();
889 Operation new_op, cur_op, op_tmp, op_tmp2;
890 int op_complete = 6;
891 QString value, v ;
892 QDate date;
893 bool need_insertion = false, transfertCompleted = false;
894 std::vector<int>::iterator it;
895 std::vector<Operation>::iterator it2;
896 Operation op, op2, parent;
897 QFont font;
898 Category cat ;
899 bool fix_cost;
900 Operation NULLop;
901
902 // Avoid recursives calls
903 if (_inModification || _loadOperations) return;
904
905 _inModification = true ;
906
907 cur_op = (_displayedOperations)[row] ;
908
909 new_op.id = 0;
910 new_op.parent = 0;
911 new_op.day = 0;
912 new_op.month = 0;
913 new_op.year = 0;
914 new_op.amount = 0.0;
915 new_op.description = "";
916 new_op.category = 0;
917 new_op.fix_cost = false;
918 new_op.account = 0;
919 new_op.checked = 0;
920 new_op.transfert = 0;
921 new_op.formula = "";
922 new_op.meta = false;
923 new_op._virtual = false;
924
925 if (col == DEBIT)
926 setItem(row, CREDIT, new QTableWidgetItem(""));
927 else if (col == CREDIT)
928 setItem(row, DEBIT, new QTableWidgetItem(""));
929
930 value = item(row, DESCRIPTION)->text();
931 if (value.length())
932 {
933 new_op.description = value.trimmed();
934 op_complete--;
935 }
936
937 value = item(row, OP_DATE)->text();
938 if (value.length())
939 {
940 date = QDate::fromString(value, _kiss->GetDateFormat());
941 new_op.day = date.day()-1;
942 new_op.month = date.month()-1;
943 new_op.year = date.year();
944 op_complete--;
945 }
946
947 if (!cur_op.meta && col == DESCRIPTION &&
948 (!item(row, CATEGORY)->text().length() ||
949 !item(row, ACCOUNT)->text().length()))
950 {
951 new_op.fix_cost = (row <= _fixCosts);
952 if (_kiss->SearchPreviousOperation(&op_tmp, new_op, _curMonth, _curYear, _canAddOperation, _transfertCompletionIndex))
953 {
954 if (!item(row, CATEGORY)->text().length())
955 setItem(row, CATEGORY, new QTableWidgetItem(_(user->GetCategoryName(op_tmp.category).toStdString().c_str())));
956
957 if (!item(row, ACCOUNT)->text().length())
958 setItem(row, ACCOUNT, new QTableWidgetItem(user->GetAccountName(op_tmp.account)));
959
960 col = CATEGORY;
961 new_op.fix_cost = (new_op.category == user->GetCategoryId("Fix"));
962 if (op_tmp.transfert != 0)
963transfertCompleted = true;
964 }
965 }
966
967 value = item(row, DEBIT)->text();
968 if (value.length())
969 {
970 new_op.amount = value.replace(".", "").toInt();
971 if (new_op.amount < 0)
972 {
973 new_op.amount *= -1.0;
974 setItem(row, DEBIT, new QTableWidgetItem(value.sprintf("%.2lf", (double)new_op.amount/100)));
975 }
976 if (new_op.amount != 0.0) new_op.amount *= -1.0;
977 op_complete--;
978 new_op.formula = _displayedOperations[row].formula;
979 }
980
981 value = item(row, CREDIT)->text();
982 if (value.length())
983 {
984 new_op.amount = value.replace(".", "").toInt();
985 if (new_op.amount < 0)
986 {
987 new_op.amount *= -1.0;
988 setItem(row, DEBIT, new QTableWidgetItem(value.sprintf("%.2lf", (double)new_op.amount/100)));
989 }
990 op_complete--;
991 new_op.formula = _displayedOperations[row].formula;
992 }
993
994 value = item(row, CATEGORY)->text();
995 if (value.length())
996 {
997 new_op.category = user->GetCategoryId(value);
998 op_complete--;
999 }
1000
1001 value = item(row, ACCOUNT)->text();
1002 if (value.length())
1003 {
1004 new_op.account = user->GetAccountId(value);
1005 op_complete--;
1006 }
1007
1008 if (cellWidget(row, CHECKED))
1009new_op.checked = _displayedOperations[row].checked;
1010 else
1011new_op.checked = false;
1012 op_complete--;
1013
1014 fix_cost = (row <= _fixCosts);
1015
1016 // Modify an operation
1017 if (!_canAddOperation || (row < _fixCosts ||
1018 (row > _fixCosts &&
1019 row < (int)(_displayedOperations.size()-1))))
1020 {
1021 new_op.id = cur_op.id;
1022 new_op.fix_cost = fix_cost;
1023 new_op.transfert = cur_op.transfert;
1024 new_op.meta = cur_op.meta;
1025 new_op.parent = cur_op.parent;
1026 new_op.childs = cur_op.childs;
1027 new_op._virtual = cur_op._virtual;
1028
1029UpdateOperation(new_op);
1030
1031 if (cur_op.day != new_op.day)
1032 {
1033 // Remove from _operation without DeleteOperation to avoid commit into database
1034 it2 = std::find(_operations->begin(), _operations->end(), new_op.id);
1035 if (it2 != _operations->end())
1036_operations->erase(it2);
1037 need_insertion = true;
1038 RemoveRow(new_op, row, false);
1039}
1040else
1041{
1042 (_displayedOperations)[row] = new_op;
1043 cat = user->GetCategory(new_op.category);
1044 CheckOperation(new_op, row, new_op.checked, true);
1045 SET_ROW_FONT(row, user->GetCategoryFont(cat.id));
1046}
1047 }
1048 // Add an operation
1049 else
1050 {
1051cat = user->GetCategory(new_op.category);
1052CheckOperation(new_op, row, new_op.checked, true);
1053SET_ROW_FONT(row, user->GetCategoryFont(cat.id));
1054
1055 if (op_complete) {
1056 _inModification = false ;
1057 return ;
1058 }
1059 need_insertion = true;
1060 new_op.fix_cost = fix_cost;
1061 new_op.meta = false;
1062 new_op._virtual = false;
1063new_op.parent = 0;
1064
1065RemoveRow(new_op, row, false);
1066NULLop.id = 0;
1067InsertOperation(user, NULLop, row, new_op.fix_cost, _curMonth, _curYear);
1068
1069 new_op.id = _kiss->AddOperation(new_op);
1070
1071if (transfertCompleted)
1072 _transfertCompletionIndex = (_transfertCompletionIndex + 1) % 2;
1073 }
1074
1075 if (!new_op.meta && user->GetAccount(new_op.account).blocked && new_op.amount < 0)
1076 QMessageBox::warning(0, _("Warning"), _("You made a debit on a blocked account"));
1077
1078 if (need_insertion)
1079InsertIntoGrid(new_op);
1080
1081 if (new_op.parent)
1082 {
1083 parent = GetOperation(new_op.parent);
1084 UpdateMeta(parent);
1085 }
1086
1087 _kiss->UpdateStats();
1088 _inModification = false ;
1089}
1090
1091void GridAccount::UpdateMeta(Operation& meta)
1092{
1093 std::vector<int>::iterator it;
1094 std::vector<Operation>::iterator it2;
1095 Operation op ;
1096 int category = 0;
1097 bool updateCat = false ;
1098 bool openMeta;
1099
1100 if (!meta.childs.size()) return ;
1101
1102 openMeta = IsMetaOpened(meta.id);
1103
1104 meta.category = 0;
1105 meta.amount = 0;
1106
1107 op = GetOperation(meta.childs[0]);
1108 meta.year = op.year;
1109 meta.month = op.month;
1110 meta.day = op.day;
1111 meta.description = "";
1112
1113 for(it=meta.childs.begin(); it!=meta.childs.end(); it++)
1114 {
1115op = GetOperation(*it);
1116if (op.year <= meta.year && op.month <= meta.month && op.day < meta.day)
1117{
1118 meta.year = op.year;
1119 meta.month = op.month;
1120 meta.day = op.day;
1121}
1122if (!meta.description.length() && op.description.length())
1123 meta.description = op.description;
1124if (!category)
1125{
1126 if (op.category)
1127 {
1128category = op.category;
1129updateCat = true;
1130 }
1131}
1132else
1133{
1134 if (op.category && op.category != category)
1135updateCat = false;
1136}
1137op.parent = meta.id;
1138 }
1139
1140 if (updateCat)
1141meta.category = category;
1142
1143 meta.amount = _kiss->MetaAmount(meta.id);
1144
1145 UpdateOperation(meta);
1146
1147 RemoveMeta(meta, GetDisplayedRow(meta.id), true, false);
1148 it2 = std::find(_operations->begin(), _operations->end(), meta.id);
1149 if (it2 != _operations->end())
1150_operations->erase(it2);
1151
1152 InsertIntoGrid(meta);
1153
1154 if (openMeta)
1155OpenMeta(meta);
1156}
1157
1158void GridAccount::Group()
1159{
1160 std::vector<int> rows;
1161 std::vector<int>::iterator it;
1162 std::vector<Operation> ops;
1163 std::vector<Operation>::iterator it2;
1164 std::vector<int>::iterator it3;
1165 int parent = 0, deletedRows;
1166 Operation op, op2;
1167 int fix = -1, i, a, row;
1168 User* user = _kiss->GetUser();
1169 QModelIndexList selected = selectedIndexes();
1170 bool fullCheck = true;
1171
1172 for (int i = 0; i < selected.size(); ++i)
1173 {
1174row = selected[i].row();
1175 op = _displayedOperations[row] ;
1176
1177 if (op.id)
1178 {
1179 if (!parent)
1180 {
1181 if (op.parent)
1182 parent = op.parent;
1183 else if(op.meta)
1184 parent = op.id;
1185 }
1186 else
1187 {
1188 if ((parent && op.parent && op.parent != parent))
1189 {
1190 QMessageBox::critical(0, _("Error"), _("Cannot group these operations"));
1191 return ;
1192 }
1193 }
1194
1195 if (op.parent)
1196 {
1197 row = GetDisplayedRow(op.parent);
1198 op = _displayedOperations[row] ;
1199 }
1200
1201 if (fix != -1 && ((!fix && op.fix_cost) || (fix && !op.fix_cost)))
1202 {
1203QMessageBox::critical(0, _("Error"), _("Cannot group these operations"));
1204 return ;
1205 }
1206
1207 if (fix == -1)
1208 fix = op.fix_cost ? 1 : 0;
1209
1210 ops.push_back(op);
1211 rows.push_back(row);
1212 }
1213 }
1214
1215 if (!ops.size()) return;
1216
1217 if (!parent)
1218 {
1219 if (rows.size() < 2) return;
1220
1221 op.parent = 0;
1222 op.day = ops[0].day;
1223 op.month = ops[0].month;
1224 op.year = ops[0].year;
1225 op.amount = 0;
1226 op.description = "";
1227 op.category = 0;
1228 op.fix_cost = fix;
1229 op.account = 0;
1230 op.checked = false;
1231 op.transfert = 0;
1232 op.formula = "";
1233 op.meta = true;
1234 op.childs.clear();
1235
1236 op.id = _kiss->AddOperation(op);
1237 }
1238 else
1239 {
1240 if (rows.size() < 1) return;
1241
1242 row = GetDisplayedRow(parent);
1243 op = _displayedOperations[row];
1244
1245 //if (op.id) return;
1246 }
1247
1248 for(i=0; i<(int)rows.size(); i++)
1249 {
1250 if (ops[i].meta)
1251 deletedRows = RemoveMeta(ops[i], rows[i], true, false);
1252 else
1253 {
1254 RemoveRow(ops[i], rows[i], false);
1255 deletedRows = 1;
1256 }
1257for(a=i+1; a<(int)rows.size(); a++)
1258 if (rows[a] >= rows[i])
1259rows[a] -= deletedRows;
1260 }
1261
1262 for(it2=ops.begin(); it2!=ops.end(); it2++)
1263 {
1264 if (it2->id == parent) continue;
1265
1266 for (i=0, it3=op.childs.begin(); it3!=op.childs.end(); it3++, i++)
1267 {
1268 op2 = GetOperation(*it3);
1269 if (*it3 == it2->id)
1270 {
1271if (user->_preferences["operation_order"] == "ASC")
1272{
1273 if (op2.day > it2->day)
1274break;
1275}
1276else
1277{
1278 if (op2.day < it2->day)
1279break;
1280}
1281 }
1282 }
1283
1284 if (i) i--;
1285
1286 if (it3 == op.childs.end())
1287 op.childs.push_back(it2->id);
1288 else if (*it3 == it2->id)
1289 continue;
1290 else
1291 op.childs.insert(op.childs.begin()+i, it2->id);
1292
1293 it2->parent = op.id;
1294 UpdateOperation(*it2);
1295if (!it2->checked)
1296 fullCheck = false;
1297 }
1298
1299 row = InsertIntoGrid(op);
1300
1301 if (fullCheck)
1302 {
1303op.checked = true;
1304CheckOperation(op, row, true, false);
1305 }
1306 else
1307 {
1308op.checked = false;
1309CheckOperation(op, row, false, false);
1310 }
1311
1312 UpdateMeta(op);
1313
1314}
1315
1316void GridAccount::GetSelectedOperations(std::vector<int>* rows)
1317{
1318 Operation op;
1319
1320 rows->clear();
1321
1322 QModelIndexList selected = selectedIndexes();
1323
1324 for (int i = 0; i < selected.size(); ++i)
1325 {
1326op = _displayedOperations[selected[i].row()] ;
1327
1328if (op.id)
1329 rows->push_back(selected[i].row());
1330 }
1331}
1332
1333void GridAccount::UnGroup()
1334{
1335 std::vector<int> rows;
1336 std::vector<int>::iterator it;
1337 std::vector<Operation> ops;
1338 std::vector<int> ops2;
1339 std::vector<Operation>::iterator it2;
1340 std::vector<int>::iterator it3;
1341 int parent = 0;
1342 Operation op, op2;
1343 int fix = -1, i, line;
1344
1345 QModelIndexList selected = selectedIndexes();
1346
1347 for (int i = 0; i < selected.size(); ++i)
1348 {
1349op = _displayedOperations[selected[i].row()] ;
1350
1351if (op.id)
1352{
1353 if ((parent && op.parent != parent)
1354|| (!op.parent && !op.meta))
1355 {
1356QMessageBox::critical(0, _("Error"), _("Cannot ungroup these operations"));
1357return ;
1358 }
1359
1360 if (fix != -1 && ((!fix && op.fix_cost) || (fix && !op.fix_cost)))
1361 {
1362QMessageBox::critical(0, _("Error"), _("Cannot ungroup these operations"));
1363return ;
1364 }
1365
1366 if (fix == -1)
1367fix = op.fix_cost ? 1 : 0;
1368
1369 if(op.meta)
1370 {
1371 parent = op.id;
1372continue;
1373 }
1374
1375 if (!parent && op.parent)
1376parent = op.parent;
1377
1378 ops.push_back(op);
1379 rows.push_back(selected[i].row());
1380}
1381 }
1382
1383 if (!ops.size() && !parent) return;
1384
1385 _inModification = true;
1386
1387removeLastGroup:
1388 // Only one meta is selected
1389 if (!ops.size())
1390 {
1391line = GetDisplayedRow(parent);
1392op = _displayedOperations[line];
1393ops2 = op.childs;
1394RemoveMeta(op, line, true, false);
1395
1396for(i=0; i<(int)ops2.size(); i++)
1397{
1398 op2 = GetOperation(ops2[i]);
1399 op2.parent = 0;
1400 UpdateOperation(op2);
1401 InsertIntoGrid(op2);
1402}
1403
1404DeleteOperation(op);
1405 }
1406 else
1407 {
1408if (!parent) return;
1409
1410line = GetDisplayedRow(parent);
1411op2 = _displayedOperations[line];
1412
1413for(i=0; i<(int)ops.size(); i++)
1414{
1415 op = ops[i];
1416 op.parent = 0;
1417 UpdateOperation(op);
1418 line = GetDisplayedRow(op.id);
1419 RemoveRow(op, line, false);
1420 InsertIntoGrid(op);
1421 it = std::find(op2.childs.begin(), op2.childs.end(), op.id);
1422 if (it != op2.childs.end())
1423op2.childs.erase(it);
1424}
1425
1426UpdateMeta(op2);
1427line = GetDisplayedRow(parent);
1428_displayedOperations[line] = op2;
1429
1430if (op2.childs.size() < 2)
1431{
1432 ops.clear();
1433 // Sorry ...
1434 goto removeLastGroup;
1435}
1436
1437UpdateOperation(op2);
1438 }
1439
1440 _inModification = false;
1441}
1442
1443void GridAccount::MassUpdate(std::vector<int>& rows, bool do_childs, updateOperationFunc func, void** params)
1444{
1445 int i, b;
1446 std::vector<Operation>::iterator it;
1447 Operation op, op2;
1448
1449 _kiss->setOverrideCursor(QCursor(Qt::WaitCursor));
1450 _parent->setEnabled(false);
1451 _parent->repaint();
1452
1453 if (rows.size())
1454 {
1455 for(i=0; i<(int)rows.size(); i++)
1456 {
1457 op = _displayedOperations[rows[i]];
1458
1459 func (&op, params);
1460
1461 UpdateOperation(op);
1462
1463 if (op.meta && do_childs)
1464 {
1465 for(b=0; b<(int)op.childs.size(); b++)
1466 {
1467 op2 = GetOperation(op.childs[b]);
1468 func (&op2, params);
1469 UpdateOperation(op2);
1470 }
1471 }
1472 }
1473 }
1474 else
1475 {
1476 for(it=_operations->begin(); it!=_operations->end(); it++)
1477 {
1478 func (&(*it), params);
1479 if (_databaseSynchronization)
1480 _kiss->UpdateOperation(*it);
1481
1482 if (it->meta && do_childs)
1483 {
1484 for(b=0; b<(int)it->childs.size(); b++)
1485 {
1486 op2 = GetOperation(it->childs[b]);
1487 func (&op2, params);
1488 UpdateOperation(op2);
1489 }
1490 }
1491 }
1492 }
1493
1494 LoadOperations(_operations, 0, 0);
1495
1496 layout();
1497
1498 _parent->setEnabled(true);
1499 _kiss->setOverrideCursor(QCursor(Qt::ArrowCursor));
1500}

Archive Download this file