FreeDebks  1.0.3
 All Classes Files Functions Variables Friends Pages
FdModelCoa.cpp
Go to the documentation of this file.
1 // --------------------------------------------------------------------
2 // Copyright © 2011-2013 Mathieu Schopfer
3 //
4 // This file is part of FreeDebks.
5 //
6 // FreeDebks 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 // FreeDebks 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 FreeDebks. If not, see <http://www.gnu.org/licenses/>.
18 // --------------------------------------------------------------------
19 
20 #include "FdModelCoa.hpp"
21 #include "FdItemCoa.hpp"
22 #include "FdCommandsCoa.hpp"
23 #include "FdModelJournal.hpp"
24 #include "../gui/FdSubWindow.hpp"
25 
29 FdModelCoa::FdModelCoa(FdSubWindow* parent) : QAbstractItemModel(0), mSubwindow(parent)
30 {
31  mRootItem = FdItemCoa_p(new FdItemCoa());
32  init();
33 }
34 
38 FdModelCoa::FdModelCoa(FdSubWindow* parent, QDomElement& coa) : QAbstractItemModel(0), mSubwindow(parent)
39 {
40  mRootItem = FdItemCoa::pointer(coa);
41  init();
42 }
43 
49 FdModelCoa::FdModelCoa(FdSubWindow* parent, const FdModelCoa* model) : QAbstractItemModel(0), mSubwindow(parent)
50 {
51  mRootItem = FdItemCoa::pointer(model->getRootItem());
52  init();
53 }
54 
55 void FdModelCoa::init()
56 {
57  mStack = new QUndoStack();
58  connect(mStack, SIGNAL(cleanChanged(bool)), mSubwindow, SLOT(cleanStateChanged(bool)));
59  // Insert items to mIndexes.
60  QList<FdItemCoa_p> items = mRootItem->children();
61  for(int i = 0; i < items.size(); ++i)
62  {
63  mIndexes.insert(i, items[i]);
64  items << items[i]->children();
65  }
66 }
67 
68 FdItemCoa_p FdModelCoa::getRootItem() const
69 {
70  return mRootItem;
71 }
72 
73 QVariant FdModelCoa::data(FdItemCoa_p item, CoaColumn column) const
74 {
75  switch(column)
76  {
77  case CoaId: return item->id();
78  case CoaLabel: return item->label();
79  case CoaSigns:
80  if(item->type() == CategoryType)
81  return QVariant();
82  if(item->signature() == -1)
83  return "-+";
84  return "+-";
85  case CoaBalanceSheet:
86  if(item->onBalanceSheet())
87  return Qt::Checked;
88  return Qt::Unchecked;
89  case CoaInitial: return item->initialBalance();
90  case CoaBalance: return item->balance();
91  }
92 
93  return QVariant();
94 }
95 
96 QVariant FdModelCoa::data(const QModelIndex &index, int role) const
97 {
98  if(!index.isValid())
99  return QVariant();
100  if(role == Qt::TextAlignmentRole)
101  {
102  if(index.column() == CoaBalanceSheet || index.column() == CoaSigns)
103  return Qt::AlignCenter;
104  if(index.column() == CoaInitial || index.column() == CoaBalance)
105  return Qt::AlignRight;
106  }
107  if(index.column() != CoaBalanceSheet && role != Qt::DisplayRole && role != Qt::EditRole)
108  return QVariant();
109 
110  FdItemCoa_p item = this->item(index);
111 
112  if(index.column() == CoaBalanceSheet && (role != Qt::CheckStateRole || item->parent()->type() != RootType))
113  return QVariant();
114 
115  return data(item, CoaColumn(index.column()));
116 }
117 
118 void FdModelCoa::setData(FdItemCoa_p item, CoaColumn column, const QVariant &value)
119 {
120  switch(column)
121  {
122  case CoaId:
123  item->setId(value.toString());
124  break;
125  case CoaLabel:
126  item->setLabel(value.toString());
127  break;
128  case CoaBalanceSheet:
129  item->setOnBalanceSheet(value.toBool());
130  break;
131  case CoaSigns:
132  item->setSignature(AccountSignature(value.toInt()));
133  break;
134  case CoaInitial:
135  item->setInitialBalance(value.toDouble());
136  break;
137  }
138  QModelIndex index = this->index(item, column);
139 
140  emit dataChanged(index, index);
141 }
142 
152 bool FdModelCoa::setData(const QModelIndex &index, const QVariant &value, int role)
153 {
154  if (!index.isValid() || (role != Qt::EditRole && role != Qt::CheckStateRole) || value == QVariant(""))
155  return false;
156 
157  if(value == data(index, Qt::DisplayRole))
158  return true;
159 
160  switch(index.column())
161  {
162  case CoaId:
163  if(idExists(value.toString()))
164  return false;
165  break;
166  case CoaLabel:
167  if(labelExists(value.toString()))
168  return false;
169  break;
170  }
171 
172  FdItemCoa_p item = this->item(index);
173  mStack->push(new FdCoaSetData(this, item, CoaColumn(index.column()), value));
174  return true;
175 }
176 
177 QVariant FdModelCoa::headerData(int section, Qt::Orientation orientation, int role) const
178 {
179  if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
180  {
181  switch(section)
182  {
183  case CoaId: return tr("Id", "Chart of accounts column header.");
184  case CoaLabel: return tr("Label", "Chart of accounts column header.");
185  case CoaSigns: return tr("Signs", "Chart of accounts column header.");
186  case CoaBalanceSheet: return tr("On balance sheet", "Chart of accounts column header, a column with tick boxes to decide whether or not the category belongs to the balance sheet.");
187  case CoaInitial: return tr("Initial balance", "Chart of accounts column header.");
188  case CoaBalance: return tr("Balance", "Chart of accounts column header.");
189  }
190  }
191 
192  return QVariant();
193 }
194 
206 Qt::ItemFlags FdModelCoa::flags(const QModelIndex &index) const
207 {
208  FdItemCoa_p item = this->item(index);
209 
210  Qt::ItemFlags flags;
211  if(!(item->type() == CategoryType && (index.column() == CoaInitial || index.column() == CoaSigns)) && index.column() != CoaBalanceSheet && index.column() != CoaBalance)
212  flags |= Qt::ItemIsEditable;
213 
214  if(!(item->type() == AccountType && mSubwindow->journal()->isUsed(item)))
215  flags |= Qt::ItemIsDropEnabled;
216 
217  if(index.column() == CoaBalanceSheet && item->parent()->type() == RootType)
218  flags |= Qt::ItemIsUserCheckable;
219 
220  flags |= Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
221  return flags;
222 }
223 
224 Qt::DropActions FdModelCoa::supportedDragActions() const
225 {
226  return Qt::MoveAction;
227 }
228 
229 Qt::DropActions FdModelCoa::supportedDropActions() const
230 {
231  return Qt::MoveAction;
232 }
233 
239 QModelIndex FdModelCoa::index(int row, int column, const QModelIndex &parent) const
240 {
241  if (parent.isValid() && parent.column() != 0)
242  return QModelIndex();
243 
244  FdItemCoa_p parentItem = item(parent);
245  if(FdItemCoa_p childItem = parentItem->child(row))
246  return createIndex(row, column, mIndexes.key(childItem));
247  else
248  return QModelIndex();
249 }
250 
254 QModelIndex FdModelCoa::index(FdItemCoa_p item) const
255 {
256  return index(item, CoaId);
257 }
258 
264 QModelIndex FdModelCoa::index(FdItemCoa_p item, CoaColumn column) const
265 {
266  QList<FdItemCoa_p> items;
267  while(item != mRootItem)
268  {
269  items.prepend(item);
270  item = item->parent();
271  }
272 
273  QModelIndex index = QModelIndex();
274  for(int i = 0; i < items.size(); ++i)
275  index = this->index(items[i]->row(),column,index);
276 
277  return index;
278 }
279 
280 QModelIndex FdModelCoa::parent(const QModelIndex &index) const
281 {
282  if (!index.isValid())
283  return QModelIndex();
284 
285  FdItemCoa_p parentItem = item(index)->parent();
286 
287  if (parentItem == mRootItem)
288  return QModelIndex();
289 
290  return createIndex(parentItem->row(), 0, mIndexes.key(parentItem));
291 }
292 
293 QStringList FdModelCoa::mimeTypes() const
294 {
295  return QStringList() << "application/freedebks";
296 }
297 
303 QMimeData* FdModelCoa::mimeData(const QModelIndexList &indexes) const
304 {
305  QMimeData* mimeData = new QMimeData();
306  QByteArray encodedData;
307  QDataStream stream(&encodedData, QIODevice::WriteOnly);
308 
309  qint32 index;
310  for(int i = 0; i < indexes.size(); ++i)
311  {
312  if(indexes[i].column() == 0)
313  {
314  index = indexes[i].internalId();
315  stream << index;
316  }
317  }
318 
319  mimeData->setData("application/freedebks", encodedData);
320  return mimeData;
321 }
322 
326 bool FdModelCoa::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
327 {
328  if(action != Qt::MoveAction)
329  return false;
330 
331  // Retrieves a list of items identifiers, see mIndexes.
332  QByteArray encodedData = data->data("application/freedebks");
333  QDataStream stream(&encodedData, QIODevice::ReadOnly);
334  QList<FdItemCoa_p> items;
335 
336  FdItemCoa_p newParent = this->item(parent);
337 
338  if(row == -1)
339  {
340  if(parent.isValid())
341  row = 0;
342  else
343  row = newParent->childrenCount();
344  }
345 
346  while(!stream.atEnd())
347  {
348  qint32 index;
349  stream >> index;
350  FdItemCoa_p item = mIndexes.value(index);
351  items << item;
352  }
353 
354  mStack->push(new FdCoaDragDrop(this, items, newParent, row));
355  return true;
356 }
357 
358 int FdModelCoa::rowCount(const QModelIndex &parent) const
359 {
360  return item(parent)->childrenCount();
361 }
362 
363 int FdModelCoa::columnCount(const QModelIndex &parent) const
364 {
365  return CoaColumnCount;
366 }
367 
368 bool FdModelCoa::insertItem(FdItemCoa_p item, int row, FdItemCoa_p parent)
369 {
370  return insertItems(QList<FdItemCoa_p>() << item, row, parent);
371 }
372 
376 bool FdModelCoa::insertItems(QList<FdItemCoa_p> items, int row, FdItemCoa_p parent)
377 {
378  QModelIndex parentIndex = index(parent);
379  int count = items.size();
380  beginInsertRows(parentIndex, row, row + count - 1);
381  qint32 key = 0;
382  for(int i = 0; i < count; ++i)
383  {
384  if(!parent->insertChild(items[i],row+i))
385  return false;
386  items[i]->setParent(parent);
387  while(mIndexes.contains(key))
388  ++key;
389  mIndexes.insert(key, items[i]);
390  }
391  endInsertRows();
392  return true;
393 }
394 
398 bool FdModelCoa::removeItem(FdItemCoa_p item, FdItemCoa_p parent)
399 {
400  QModelIndex parentIndex = index(parent);
401  int row = item->row();
402  beginRemoveRows(parentIndex, row, row);
403  if(!parent->removeChild(row))
404  return false;
405  mIndexes.remove(mIndexes.key(item));
406  item->setParent(FdItemCoa_p(0));
407  endRemoveRows();
408  return true;
409 }
410 
414 void FdModelCoa::moveItem(FdItemCoa_p item, FdItemCoa_p parent, int row)
415 {
416  removeItem(item, parent);
417  insertItem(item, row, parent);
418 }
419 
423 void FdModelCoa::orderItems(FdItemCoa_p parent)
424 {
425  if(parent.isNull())
426  parent = mRootItem;
427 
428  int count = parent->childrenCount()-1;
429  int j;
430  for(int i = 0; i < count; ++i)
431  {
432  j = i-1;
433  if(parent->child(i+1)->id() < parent->child(i)->id())
434  {
435  // We search where to place the item
436 
437  while(j >= 0 && parent->child(i+1)->id() < parent->child(j)->id())
438  --j;
439 
440  moveItem(parent->child(i+1), parent, j+1);
441  }
442 
443  j += 1;
444  if(parent->child(j)->type() == CategoryType)
445  orderItems(parent->child(j));
446  }
447 
448  if(count > 0 && parent->child(count)->type() == CategoryType)
449  orderItems(parent->child(count));
450 }
451 
457 FdItemCoa_p FdModelCoa::item(const QModelIndex &index) const
458 {
459  if(index.isValid())
460  {
461  if(FdItemCoa_p item = mIndexes.value(index.internalId()))
462  return item;
463  }
464  return mRootItem;
465 }
466 
470 QAction* FdModelCoa::redo()
471 {
472  return mStack->createRedoAction(this);
473 }
474 
478 QAction* FdModelCoa::undo()
479 {
480  return mStack->createUndoAction(this);
481 }
482 
487 bool FdModelCoa::idExists(QString id) const
488 {
489  if(itemById(id).isNull())
490  return false;
491 
492  return true;
493 }
494 
499 bool FdModelCoa::labelExists(QString label) const
500 {
501  if(itemByLabel(label).isNull())
502  return false;
503 
504  return true;
505 }
506 
510 bool FdModelCoa::isAccountId(QString id) const
511 {
512  FdItemCoa_p item = itemById(id);
513  if(item.isNull() || item->type() != AccountType)
514  return false;
515 
516  return true;
517 }
518 
522 bool FdModelCoa::isAccountLabel(QString label) const
523 {
524  FdItemCoa_p item = itemByLabel(label);
525  if(item.isNull() || item->type() != AccountType)
526  return false;
527 
528  return true;
529 }
530 
541 bool FdModelCoa::isRemovable(FdItemCoa_p item) const
542 {
543  if(item->type() == AccountType)
544  {
545  if(mSubwindow->journal()->isUsed(item) || item->wasUsed())
546  return false;
547  }
548  else if(item->type() == CategoryType)
549  {
550  QList<FdItemCoa_p> children = item->children();
551  for(int i = 0; i < children.size(); ++i)
552  {
553  if(!isRemovable(children[i]))
554  return false;
555  }
556  }
557  return true;
558 }
559 
560 bool FdModelCoa::isRemovable(const QModelIndex &index) const
561 {
562  if(index.isValid())
563  return isRemovable(item(index));
564  return false;
565 }
566 
572 QStringList FdModelCoa::accountsIds() const
573 {
574  return mRootItem->childrenAccountsIds();
575 }
576 
582 QStringList FdModelCoa::accountsLabels() const
583 {
584  return mRootItem->childrenAccountsLabels();
585 }
586 
592 QList<FdItemCoa_p> FdModelCoa::accounts() const
593 {
594  return mRootItem->childrenAccounts();
595 }
596 
602 FdItemCoa_p FdModelCoa::itemById(QString id) const
603 {
604  return mRootItem->childById(id);
605 }
606 
612 FdItemCoa_p FdModelCoa::itemByLabel(QString label) const
613 {
614  return mRootItem->childByLabel(label);
615 }
616 
617 
621 QModelIndexList FdModelCoa::itemByPartialText(QString text) const
622 {
623  QList<FdItemCoa_p> items = mRootItem->findChildren(text);
624  QModelIndexList results;
625  for(int i = 0; i < items.size(); ++i)
626  results << index(items[i]);
627 
628  return results;
629 }
630 
631 QDomElement FdModelCoa::toXml(QDomDocument &document) const
632 {
634  mStack->setClean();
635  return mRootItem->toXml(document);
636 }
637 
643 QModelIndex FdModelCoa::addItem(const QModelIndex &selected)
644 {
645  FdItemCoa_p parentItem = item(selected);
646  int row;
647  if(parentItem->type() == AccountType && (mSubwindow->journal()->isUsed(parentItem) || parentItem->wasUsed()))
648  {
649  parentItem = parentItem->parent();
650  row = selected.row()+1;
651  }
652  else
653  row = parentItem->childrenCount();
654 
655  // Automatically chooses an id depending on parent's id.
656  int i = 0;
657  QString id;
658  do
659  {
660  id = parentItem->id()+QString::number(i);
661  ++i;
662  }
663  while(idExists(id));
664 
665  // Automatically defines an unused name
666  i = 0;
667  QString label;
668  do
669  {
670  label = "Empty"+QString::number(i);
671  ++i;
672  }
673  while(labelExists(label));
674 
675  FdItemCoa_p item = FdItemCoa_p(new FdItemCoa(parentItem, id, label));
676  mStack->push(new FdCoaAddItem(this, parentItem, item, row));
677  return index(item);
678 }
679 
687 void FdModelCoa::removeSelected(QModelIndexList selected)
688 {
689  FdItemCoa_p item;
690  QList<FdItemCoa_p> items;
691  for(int i = 0; i < selected.size(); ++i)
692  {
693  item = this->item(selected[i]);
694  if(isRemovable(item))
695  items << item;
696  }
697 
698  if(items.size())
699  mStack->push(new FdCoaRemove(this, items));
700 }
701 
706 {
707  mRootItem->resetBalance();
709  mRootItem->computeBalances();
710  emit(dataChanged(index(0,0),index(rowCount()-1,columnCount()-1)));
711 }