FreeDebks  1.0.3
 All Classes Files Functions Variables Friends Pages
FdModelJournal.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 "FdModelJournal.hpp"
21 #include "FdItemJournal.hpp"
22 #include "FdCommandsJournal.hpp"
23 #include "FdModelCoa.hpp"
24 #include "FdItemCoa.hpp"
25 #include "../gui/FdSubWindow.hpp"
26 
31  QAbstractTableModel(0), mSubwindow(parent)
32 {
33  init();
34 }
35 
39 FdModelJournal::FdModelJournal(FdSubWindow *parent, QDomElement &journal) :
40  QAbstractTableModel(0), mSubwindow(parent)
41 {
42  mEntries = QList<FdItemJournal_p>();
43  QDomElement entry = journal.firstChildElement();
44  while(!entry.isNull())
45  {
46  mEntries << FdItemJournal::pointer(mSubwindow, entry);
47  entry = entry.nextSiblingElement();
48  }
49 
50  init();
51 }
52 
54 {
55  mStack = new QUndoStack();
56  connect(mStack, SIGNAL(cleanChanged(bool)), mSubwindow, SLOT(cleanStateChanged(bool)));
57 }
58 
59 QVariant FdModelJournal::data(FdItemJournal_p entry, JournalColumn column) const
60 {
61  switch(column)
62  {
63  case JournalDate: return entry->date();
64  case JournalObject: return entry->object();
65  case JournalLabel: return entry->label();
66  case JournalDebitId:
67  if(!entry->debit().isNull())
68  return entry->debit()->id();
69  break;
70  case JournalDebitLabel:
71  if(!entry->debit().isNull())
72  return entry->debit()->label();
73  break;
74  case JournalCreditId:
75  if(!entry->credit().isNull())
76  return entry->credit()->id();
77  break;
78  case JournalCreditLabel:
79  if(!entry->credit().isNull())
80  return entry->credit()->label();
81  break;
82  case JournalAmount: return entry->amount();
83  }
84 
85  return QVariant();
86 }
87 
88 QVariant FdModelJournal::data(const QModelIndex &index, int role) const
89 {
90  if(!index.isValid())
91  return QVariant();
92  if(role == ValidityRole)
93  return mEntries[index.row()]->isValid();
94  if(role == Qt::TextAlignmentRole && index.column() == JournalAmount)
95  return Qt::AlignRight;
96  if (role != Qt::DisplayRole && role != Qt::EditRole)
97  return QVariant();
98 
99  return data(mEntries[index.row()], JournalColumn(index.column()));
100 
101 }
102 
103 void FdModelJournal::setData(FdItemJournal_p entry, JournalColumn column, const QVariant &value)
104 {
105  int row = mEntries.indexOf(entry);
106  int columnEnd = column;
107  switch(column)
108  {
109  case JournalDate:
110  entry->setDate(value.toDate());
111  break;
112  case JournalObject:
113  entry->setObject(value.toString());
114  break;
115  case JournalLabel:
116  entry->setLabel(value.toString());
117  break;
118  case JournalDebitId:
119  entry->setDebit(mSubwindow->coa()->itemById(value.toString()));
120  columnEnd = JournalDebitLabel;
121  break;
122  case JournalDebitLabel:
123  entry->setDebit(mSubwindow->coa()->itemByLabel(value.toString()));
124  column = JournalDebitId;
125  break;
126  case JournalCreditId:
127  entry->setCredit(mSubwindow->coa()->itemById(value.toString()));
128  columnEnd = JournalCreditLabel;
129  break;
130  case JournalCreditLabel:
131  entry->setCredit(mSubwindow->coa()->itemByLabel(value.toString()));
132  column = JournalCreditId;
133  break;
134  case JournalAmount:
135  entry->setAmount(value.toDouble());
136  break;
137  }
138 
139  emit(dataChanged(index(row, column), index(row, columnEnd)));
140 }
141 
151 bool FdModelJournal::setData(const QModelIndex &index, const QVariant &value, int role)
152 {
153  if(!index.isValid() || role != Qt::EditRole)
154  return false;
155 
156  if(value == data(index, Qt::DisplayRole))
157  return true;
158 
159  int column = index.column();
160  switch(column)
161  {
162  case JournalDate:
163  if(value.toDate() < mSubwindow->initialDate() || mSubwindow->finalDate() < value.toDate())
164  return false;
165  break;
166  case JournalDebitId:
167  case JournalCreditId:
168  if(!mSubwindow->coa()->isAccountId(value.toString()))
169  return false;
170  break;
171  case JournalDebitLabel:
172  if(mSubwindow->coa()->isAccountId(value.toString()))
173  {
174  column = JournalDebitId;
175  break;
176  }
177  case JournalCreditLabel:
178  if(mSubwindow->coa()->isAccountId(value.toString()))
179  {
180  column = JournalCreditId;
181  break;
182  }
183  if(!mSubwindow->coa()->isAccountLabel(value.toString()))
184  return false;
185  break;
186  }
187 
188  FdItemJournal_p entry = mEntries.at(index.row());
189 
190  if(column == JournalLabel)
191  {
192  FdItemJournal_p lastEntry = lastEntryByLabel(value.toString(), index.row()-1);
193  if(!lastEntry.isNull())
194  {
195  QUndoCommand* copyEntry = new QUndoCommand();
196  copyEntry->setText(QObject::tr("copy entry", "Will appear like this : Undo/Redo copy entry."));
197  new FdJournalSetData(this, entry, JournalDebitId, lastEntry->debit()->id(), copyEntry);
198  new FdJournalSetData(this, entry, JournalCreditId, lastEntry->credit()->id(), copyEntry);
199  new FdJournalSetData(this, entry, JournalLabel, value, copyEntry);
200  mStack->push(copyEntry);
201  return true;
202  }
203  }
204 
205  mStack->push(new FdJournalSetData(this, entry, JournalColumn(column), value));
206  return true;
207 }
208 
209 QVariant FdModelJournal::headerData(int section, Qt::Orientation orientation, int role) const
210 {
211  if(role == Qt::DisplayRole)
212  {
213  if (orientation == Qt::Horizontal)
214  {
215  switch(section)
216  {
217  case JournalDate: return tr("Date", "Journal column header.");
218  case JournalObject: return tr("Object", "Journal column header.");
219  case JournalLabel: return tr("Label", "Journal column header.");
220  case JournalDebitId: return tr("Debit", "Journal column header.");
221  case JournalDebitLabel: return tr("Debit account", "Journal column header.");
222  case JournalCreditId: return tr("Credit", "Journal column header.");
223  case JournalCreditLabel: return tr("Credit account", "Journal column header.");
224  case JournalAmount: return tr("Amount", "Journal column header.");
225  }
226  }
227  else
228  return section+1;
229  }
230 
231  return QVariant();
232 }
233 
234 Qt::ItemFlags FdModelJournal::flags(const QModelIndex &index) const
235 {
236  if(index.isValid())
237  return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
238  else
239  return QAbstractTableModel::flags(index);
240 }
241 
242 bool FdModelJournal::removeRows(int row, int count, const QModelIndex &parent)
243 {
244  beginRemoveRows(parent, row, row+count-1);
245  for(int i = 0; i < count; ++i)
246  {
247  mEntries.removeAt(row);
248  }
249  endRemoveRows();
250  return true;
251 }
252 
253 int FdModelJournal::rowCount(const QModelIndex &parent) const
254 {
255  return mEntries.size();
256 }
257 
258 int FdModelJournal::columnCount(const QModelIndex &parent) const
259 {
260  return JournalColumnCount;
261 }
262 
263 void FdModelJournal::insertEntry(FdItemJournal_p entry, int row)
264 {
265  insertEntries(QList<FdItemJournal_p>() << entry, row);
266 }
267 
268 void FdModelJournal::insertEntries(QList<FdItemJournal_p> entries, int row)
269 {
270  int count = entries.size();
271  beginInsertRows(QModelIndex(), row, row+count-1);
272  FdItemCoa_p account;
273  for(int i = 0; i < count; ++i)
274  {
275  account = entries[i]->debit();
276  if(!account.isNull() && !mSubwindow->coa()->idExists(account->id()))
277  entries[i]->setDebit(FdItemCoa_p(0));
278 
279  account = entries[i]->credit();
280  if(!account.isNull() && !mSubwindow->coa()->idExists(account->id()))
281  entries[i]->setCredit(FdItemCoa_p(0));
282 
283  mEntries.insert(row+i, entries[i]);
284  }
285  endInsertRows();
286 }
287 
288 void FdModelJournal::removeEntries(QList<FdItemJournal_p> entries)
289 {
290  for(int i = 0; i < entries.size(); ++i)
291  removeRow(mEntries.indexOf(entries[i]));
292 }
293 
297 QModelIndex FdModelJournal::itemIndex(FdItemJournal_p entry)
298 {
299  for(int i = 0; i < mEntries.size(); ++i)
300  {
301  if(entry == mEntries[i])
302  return index(i, 0);
303  }
304 
305  return QModelIndex();
306 }
307 
311 QAction* FdModelJournal::redo()
312 {
313  return mStack->createRedoAction(this);
314 }
315 
319 QAction* FdModelJournal::undo()
320 {
321  return mStack->createUndoAction(this);
322 }
323 
329 QStringList FdModelJournal::labels() const
330 {
331  QStringList labels;
332  for(int i = 0; i < rowCount(); ++i)
333  {
334  QString label = mEntries[i]->label();
335  if(!labels.contains(label))
336  labels << label;
337  }
338  return labels;
339 }
340 
344 FdItemJournal_p FdModelJournal::lastEntryByLabel(QString label) const
345 {
346  return lastEntryByLabel(label, rowCount()-1);
347 }
348 
352 FdItemJournal_p FdModelJournal::lastEntryByLabel(QString label, int row) const
353 {
354  for(int i = row; i >= 0; --i)
355  {
356  if(mEntries[i]->label() == label)
357  return mEntries[i];
358  }
359 
360  return FdItemJournal_p(0);
361 }
362 
366 QModelIndexList FdModelJournal::entryByPartialText(QString text) const
367 {
368  QModelIndexList results;
369  for(int i = 0; i < rowCount(); ++i)
370  {
371  if(mEntries[i]->label().contains(text, Qt::CaseInsensitive) || mEntries[i]->amount() == text.toDouble())
372  results << index(i, 0);
373  }
374  return results;
375 }
376 
380 FdItemCoa_p FdModelJournal::selectedAccount(const QModelIndex &index) const
381 {
382  FdItemJournal_p entry = mEntries[index.row()];
383  if(index.column() == JournalDebitId || index.column() == JournalDebitLabel)
384  return entry->debit();
385  else if (index.column() == JournalCreditId || index.column() == JournalCreditLabel)
386  return entry->credit();
387 
388  return FdItemCoa_p(0);
389 }
390 
394 QList<FdItemJournal_p> FdModelJournal::accountEntries(FdItemCoa_p account) const
395 {
396  QList<FdItemJournal_p> entries;
397  for(int i = 0; i < mEntries.size(); ++i)
398  {
399  if(account == mEntries[i]->debit() || account == mEntries[i]->credit())
400  entries << mEntries[i];
401  }
402 
403  return entries;
404 }
405 
412 {
413  orderByDate();
414  return mEntries[0]->date();
415 }
416 
423 {
424  orderByDate();
425  return mEntries[rowCount()-1]->date();
426 }
427 
431 bool FdModelJournal::isUsed(FdItemCoa_p account) const
432 {
433  for(int i = 0; i < mEntries.size(); ++i)
434  {
435  if(mEntries[i]->debit() == account || mEntries[i]->credit() == account)
436  return true;
437  }
438 
439  return false;
440 }
441 
442 QDomElement FdModelJournal::toXml(QDomDocument &document) const
443 {
444  QDomElement journal = document.createElement("Journal");
445  for(int i = 0; i < mEntries.size(); ++i)
446  {
447  if(!mEntries[i]->isEmpty())
448  {
449  QDomElement entry = mEntries[i]->toXml(document);
450  journal.appendChild(entry);
451  }
452  }
453 
455  mStack->setClean();
456  return journal;
457 }
458 
464 QModelIndex FdModelJournal::addEntry(const QModelIndex &selected)
465 {
466  int row;
467  if(selected.isValid())
468  row = selected.row()+1;
469  else
470  row = rowCount();
471 
472  QDate date;
473  if(row == 0)
474  date = mSubwindow->initialDate();
475  else
476  date = mEntries[row-1]->date();
477 
478  FdItemJournal_p item = FdItemJournal_p(new FdItemJournal(date));
479  insertEntry(item, row);
480  return index(row, JournalDate);
481 }
482 
486 void FdModelJournal::removeSelected(const QList<int> &rows)
487 {
488  QList<FdItemJournal_p> entries;
489  QList<FdItemJournal_p> toDelete;
490  for(int i = 0; i < rows.size(); ++i)
491  {
492  if(mEntries[rows[i]]->isEmpty())
493  toDelete << mEntries[rows[i]];
494  else
495  entries << mEntries[rows[i]];
496  }
497 
498  removeEntries(toDelete);
499 
500  if(entries.size())
501  mStack->push(new FdJournalRemove(this, entries));
502 }
503 
508 {
509  for(int i = 0; i < rowCount(); ++i)
510  {
511  if(mEntries[i]->isEmpty())
512  {
513  removeEntries(QList<FdItemJournal_p>() << mEntries[i]);
514  --i;
515  }
516  }
517 
518  for(int i = 0; i < rowCount()-1; ++i)
519  {
520  if(mEntries[i+1]->date() < mEntries[i]->date())
521  {
522  int j = i-1;
523  while(j >= 0 && mEntries[i+1]->date() < mEntries[j]->date())
524  --j;
525 
526  mEntries.move(i+1,j+1);
527  }
528  }
529  emit(dataChanged(index(0,0), index(rowCount()-1, columnCount()-1)));
530 }
531 
535 void FdModelJournal::copyFromPreviousLine(const QModelIndex& selected)
536 {
537  int row = selected.row();
538  if(selected.row() != 0)
539  setData(selected, data(mEntries[row-1], JournalColumn(selected.column())));
540 }
541 
546 {
547  for(int i = 0; i < mEntries.size(); ++i)
548  {
549  if(!mEntries[i]->breakDown())
550  {
551  emit(error(index(i,0)));
552  return false;
553  }
554  }
555 
556  return true;
557 }