1 /***************************************************************************
2 newtransactioneditor.cpp
3 -------------------
4 begin : Sat Aug 8 2015
5 copyright : (C) 2015 by Thomas Baumgart
6 email : Thomas Baumgart <tbaumgart@kde.org>
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "newtransactioneditor.h"
19
20 // ----------------------------------------------------------------------------
21 // QT Includes
22
23 #include <QCompleter>
24 #include <QSortFilterProxyModel>
25 #include <QStringList>
26 #include <QDebug>
27 #include <QGlobalStatic>
28 #include <QStandardItemModel>
29 #include <QAbstractItemView>
30
31 // ----------------------------------------------------------------------------
32 // KDE Includes
33
34 #include <KLocalizedString>
35
36 // ----------------------------------------------------------------------------
37 // Project Includes
38
39 #include "creditdebithelper.h"
40 #include "mymoneyfile.h"
41 #include "mymoneyaccount.h"
42 #include "mymoneyexception.h"
43 #include "kmymoneyutils.h"
44 #include "kmymoneyaccountcombo.h"
45 #include "models.h"
46 #include "accountsmodel.h"
47 #include "costcentermodel.h"
48 #include "ledgermodel.h"
49 #include "splitmodel.h"
50 #include "payeesmodel.h"
51 #include "mymoneysplit.h"
52 #include "mymoneytransaction.h"
53 #include "ui_newtransactioneditor.h"
54 #include "splitdialog.h"
55 #include "widgethintframe.h"
56 #include "icons/icons.h"
57 #include "modelenums.h"
58 #include "mymoneyenums.h"
59
60 using namespace Icons;
61
62 Q_GLOBAL_STATIC(QDate, lastUsedPostDate)
63
64 class NewTransactionEditor::Private
65 {
66 public:
Private(NewTransactionEditor * parent)67 Private(NewTransactionEditor* parent)
68 : ui(new Ui_NewTransactionEditor)
69 , accountsModel(new AccountNamesFilterProxyModel(parent))
70 , costCenterModel(new QSortFilterProxyModel(parent))
71 , payeesModel(new QSortFilterProxyModel(parent))
72 , accepted(false)
73 , costCenterRequired(false)
74 , amountHelper(nullptr)
75 {
76 accountsModel->setObjectName("NewTransactionEditor::accountsModel");
77 costCenterModel->setObjectName("SortedCostCenterModel");
78 payeesModel->setObjectName("SortedPayeesModel");
79 statusModel.setObjectName("StatusModel");
80 splitModel.setObjectName("SplitModel");
81
82 costCenterModel->setSortLocaleAware(true);
83 costCenterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
84
85 payeesModel->setSortLocaleAware(true);
86 payeesModel->setSortCaseSensitivity(Qt::CaseInsensitive);
87
88 createStatusEntry(eMyMoney::Split::State::NotReconciled);
89 createStatusEntry(eMyMoney::Split::State::Cleared);
90 createStatusEntry(eMyMoney::Split::State::Reconciled);
91 // createStatusEntry(eMyMoney::Split::State::Frozen);
92 }
93
~Private()94 ~Private()
95 {
96 delete ui;
97 }
98
99 void createStatusEntry(eMyMoney::Split::State status);
100 void updateWidgetState();
101 bool checkForValidTransaction(bool doUserInteraction = true);
102 bool isDatePostOpeningDate(const QDate& date, const QString& accountId);
103
104 bool postdateChanged(const QDate& date);
105 bool costCenterChanged(int costCenterIndex);
106 bool categoryChanged(const QString& accountId);
107 bool numberChanged(const QString& newNumber);
108 bool valueChanged(CreditDebitHelper* valueHelper);
109
110 Ui_NewTransactionEditor* ui;
111 AccountNamesFilterProxyModel* accountsModel;
112 QSortFilterProxyModel* costCenterModel;
113 QSortFilterProxyModel* payeesModel;
114 bool accepted;
115 bool costCenterRequired;
116 SplitModel splitModel;
117 QStandardItemModel statusModel;
118 QString transactionSplitId;
119 MyMoneyAccount m_account;
120 MyMoneyTransaction transaction;
121 MyMoneySplit split;
122 CreditDebitHelper* amountHelper;
123 };
124
createStatusEntry(eMyMoney::Split::State status)125 void NewTransactionEditor::Private::createStatusEntry(eMyMoney::Split::State status)
126 {
127 QStandardItem* p = new QStandardItem(KMyMoneyUtils::reconcileStateToString(status, true));
128 p->setData((int)status);
129 statusModel.appendRow(p);
130 }
131
updateWidgetState()132 void NewTransactionEditor::Private::updateWidgetState()
133 {
134 // just in case it is disabled we turn it on
135 ui->costCenterCombo->setEnabled(true);
136
137 // setup the category/account combo box. If we have a split transaction, we disable the
138 // combo box altogether. Changes can only be made via the split dialog editor
139 bool blocked = false;
140 QModelIndex index;
141
142 // update the category combo box
143 ui->accountCombo->setEnabled(true);
144 switch(splitModel.rowCount()) {
145 case 0:
146 ui->accountCombo->setSelected(QString());
147 break;
148 case 1:
149 index = splitModel.index(0, 0);
150 ui->accountCombo->setSelected(splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString());
151 break;
152 default:
153 index = splitModel.index(0, 0);
154 blocked = ui->accountCombo->lineEdit()->blockSignals(true);
155 ui->accountCombo->lineEdit()->setText(i18n("Split transaction"));
156 ui->accountCombo->setDisabled(true);
157 ui->accountCombo->lineEdit()->blockSignals(blocked);
158 ui->costCenterCombo->setDisabled(true);
159 ui->costCenterLabel->setDisabled(true);
160 break;
161 }
162 ui->accountCombo->hidePopup();
163
164 // update the costcenter combo box
165 if(ui->costCenterCombo->isEnabled()) {
166 // extract the cost center
167 index = splitModel.index(0, 0);
168 QModelIndexList ccList = costCenterModel->match(costCenterModel->index(0, 0), CostCenterModel::CostCenterIdRole,
169 splitModel.data(index, (int)eLedgerModel::Role::CostCenterId),
170 1,
171 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
172 if (ccList.count() > 0) {
173 index = ccList.front();
174 ui->costCenterCombo->setCurrentIndex(index.row());
175 }
176 }
177 }
178
checkForValidTransaction(bool doUserInteraction)179 bool NewTransactionEditor::Private::checkForValidTransaction(bool doUserInteraction)
180 {
181 QStringList infos;
182 bool rc = true;
183 if(!postdateChanged(ui->dateEdit->date())) {
184 infos << ui->dateEdit->toolTip();
185 rc = false;
186 }
187
188 if(!costCenterChanged(ui->costCenterCombo->currentIndex())) {
189 infos << ui->costCenterCombo->toolTip();
190 rc = false;
191 }
192
193 if(doUserInteraction) {
194 /// @todo add dialog here that shows the @a infos about the problem
195 }
196 return rc;
197 }
198
isDatePostOpeningDate(const QDate & date,const QString & accountId)199 bool NewTransactionEditor::Private::isDatePostOpeningDate(const QDate& date, const QString& accountId)
200 {
201 bool rc = true;
202
203 try {
204 MyMoneyAccount account = MyMoneyFile::instance()->account(accountId);
205 const bool isIncomeExpense = account.isIncomeExpense();
206
207 // we don't check for categories
208 if(!isIncomeExpense) {
209 if(date < account.openingDate())
210 rc = false;
211 }
212 } catch (MyMoneyException &e) {
213 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO;
214 }
215 return rc;
216 }
217
postdateChanged(const QDate & date)218 bool NewTransactionEditor::Private::postdateChanged(const QDate& date)
219 {
220 bool rc = true;
221 WidgetHintFrame::hide(ui->dateEdit, i18n("The posting date of the transaction."));
222
223 // collect all account ids
224 QStringList accountIds;
225 accountIds << m_account.id();
226 for(int row = 0; row < splitModel.rowCount(); ++row) {
227 QModelIndex index = splitModel.index(row, 0);
228 accountIds << splitModel.data(index, (int)eLedgerModel::Role::AccountId).toString();;
229 }
230
231 Q_FOREACH(QString accountId, accountIds) {
232 if(!isDatePostOpeningDate(date, accountId)) {
233 MyMoneyAccount account = MyMoneyFile::instance()->account(accountId);
234 WidgetHintFrame::show(ui->dateEdit, i18n("The posting date is prior to the opening date of account <b>%1</b>.", account.name()));
235 rc = false;
236 break;
237 }
238 }
239 return rc;
240 }
241
242
costCenterChanged(int costCenterIndex)243 bool NewTransactionEditor::Private::costCenterChanged(int costCenterIndex)
244 {
245 bool rc = true;
246 WidgetHintFrame::hide(ui->costCenterCombo, i18n("The cost center this transaction should be assigned to."));
247 if(costCenterIndex != -1) {
248 if(costCenterRequired && ui->costCenterCombo->currentText().isEmpty()) {
249 WidgetHintFrame::show(ui->costCenterCombo, i18n("A cost center assignment is required for a transaction in the selected category."));
250 rc = false;
251 }
252 if(rc == true && splitModel.rowCount() == 1) {
253 QModelIndex index = costCenterModel->index(costCenterIndex, 0);
254 QString costCenterId = costCenterModel->data(index, CostCenterModel::CostCenterIdRole).toString();
255 index = splitModel.index(0, 0);
256 splitModel.setData(index, costCenterId, (int)eLedgerModel::Role::CostCenterId);
257 }
258 }
259
260 return rc;
261 }
262
categoryChanged(const QString & accountId)263 bool NewTransactionEditor::Private::categoryChanged(const QString& accountId)
264 {
265 bool rc = true;
266 if(!accountId.isEmpty() && splitModel.rowCount() <= 1) {
267 try {
268 MyMoneyAccount category = MyMoneyFile::instance()->account(accountId);
269 const bool isIncomeExpense = category.isIncomeExpense();
270 ui->costCenterCombo->setEnabled(isIncomeExpense);
271 ui->costCenterLabel->setEnabled(isIncomeExpense);
272 costCenterRequired = category.isCostCenterRequired();
273 rc &= costCenterChanged(ui->costCenterCombo->currentIndex());
274 rc &= postdateChanged(ui->dateEdit->date());
275
276 // make sure we have a split in the model
277 bool newSplit = false;
278 if(splitModel.rowCount() == 0) {
279 splitModel.addEmptySplitEntry();
280 newSplit = true;
281 }
282
283 const QModelIndex index = splitModel.index(0, 0);
284 splitModel.setData(index, accountId, (int)eLedgerModel::Role::AccountId);
285 if(newSplit) {
286 costCenterChanged(ui->costCenterCombo->currentIndex());
287
288 if(amountHelper->haveValue()) {
289 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue);
290
291 /// @todo make sure to convert initial value to shares according to price information
292
293 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares);
294 }
295 }
296
297 /// @todo we need to make sure to support multiple currencies here
298
299
300 } catch (MyMoneyException &e) {
301 qDebug() << "Ooops: invalid account id" << accountId << "in" << Q_FUNC_INFO;
302 }
303 }
304 return rc;
305 }
306
numberChanged(const QString & newNumber)307 bool NewTransactionEditor::Private::numberChanged(const QString& newNumber)
308 {
309 bool rc = true;
310 WidgetHintFrame::hide(ui->numberEdit, i18n("The check number used for this transaction."));
311 if(!newNumber.isEmpty()) {
312 const LedgerModel* model = Models::instance()->ledgerModel();
313 QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::Number,
314 QVariant(newNumber),
315 -1, // all splits
316 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
317
318 foreach(QModelIndex index, list) {
319 if(model->data(index, (int)eLedgerModel::Role::AccountId) == m_account.id()
320 && model->data(index, (int)eLedgerModel::Role::TransactionSplitId) != transactionSplitId) {
321 WidgetHintFrame::show(ui->numberEdit, i18n("The check number <b>%1</b> has already been used in this account.", newNumber));
322 rc = false;
323 break;
324 }
325 }
326 }
327 return rc;
328 }
329
valueChanged(CreditDebitHelper * valueHelper)330 bool NewTransactionEditor::Private::valueChanged(CreditDebitHelper* valueHelper)
331 {
332 bool rc = true;
333 if(valueHelper->haveValue() && splitModel.rowCount() <= 1) {
334 rc = false;
335 try {
336 MyMoneyMoney shares;
337 if(splitModel.rowCount() == 1) {
338 const QModelIndex index = splitModel.index(0, 0);
339 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitValue);
340
341 /// @todo make sure to support multiple currencies
342
343 splitModel.setData(index, QVariant::fromValue<MyMoneyMoney>(-amountHelper->value()), (int)eLedgerModel::Role::SplitShares);
344 } else {
345 /// @todo ask what to do: if the rest of the splits is the same amount we could simply reverse the sign
346 /// of all splits, otherwise we could ask if the user wants to start the split editor or anything else.
347 }
348 rc = true;
349
350 } catch (MyMoneyException &e) {
351 qDebug() << "Ooops: something went wrong in" << Q_FUNC_INFO;
352 }
353 }
354 return rc;
355 }
356
357
NewTransactionEditor(QWidget * parent,const QString & accountId)358 NewTransactionEditor::NewTransactionEditor(QWidget* parent, const QString& accountId)
359 : QFrame(parent, Qt::FramelessWindowHint /* | Qt::X11BypassWindowManagerHint */)
360 , d(new Private(this))
361 {
362 auto const model = Models::instance()->accountsModel();
363 // extract account information from model
364 const auto index = model->accountById(accountId);
365 d->m_account = model->data(index, (int)eAccountsModel::Role::Account).value<MyMoneyAccount>();
366
367 d->ui->setupUi(this);
368
369 d->accountsModel->addAccountGroup(QVector<eMyMoney::Account::Type> {eMyMoney::Account::Type::Asset, eMyMoney::Account::Type::Liability, eMyMoney::Account::Type::Income, eMyMoney::Account::Type::Expense, eMyMoney::Account::Type::Equity});
370 d->accountsModel->setHideEquityAccounts(false);
371 d->accountsModel->setSourceModel(model);
372 d->accountsModel->setSourceColumns(model->getColumns());
373 d->accountsModel->sort((int)eAccountsModel::Column::Account);
374 d->ui->accountCombo->setModel(d->accountsModel);
375
376 d->costCenterModel->setSortRole(Qt::DisplayRole);
377 d->costCenterModel->setSourceModel(Models::instance()->costCenterModel());
378 d->costCenterModel->sort(0);
379
380 d->ui->costCenterCombo->setEditable(true);
381 d->ui->costCenterCombo->setModel(d->costCenterModel);
382 d->ui->costCenterCombo->setModelColumn(0);
383 d->ui->costCenterCombo->completer()->setFilterMode(Qt::MatchContains);
384
385 d->payeesModel->setSortRole(Qt::DisplayRole);
386 d->payeesModel->setSourceModel(Models::instance()->payeesModel());
387 d->payeesModel->sort(0);
388
389 d->ui->payeeEdit->setEditable(true);
390 d->ui->payeeEdit->setModel(d->payeesModel);
391 d->ui->payeeEdit->setModelColumn(0);
392 d->ui->payeeEdit->completer()->setFilterMode(Qt::MatchContains);
393
394 d->ui->enterButton->setIcon(Icons::get(Icon::DialogOK));
395 d->ui->cancelButton->setIcon(Icons::get(Icon::DialogCancel));
396
397 d->ui->statusCombo->setModel(&d->statusModel);
398
399 d->ui->dateEdit->setDisplayFormat(QLocale().dateFormat(QLocale::ShortFormat));
400
401 d->ui->amountEditCredit->setAllowEmpty(true);
402 d->ui->amountEditDebit->setAllowEmpty(true);
403 d->amountHelper = new CreditDebitHelper(this, d->ui->amountEditCredit, d->ui->amountEditDebit);
404
405 WidgetHintFrameCollection* frameCollection = new WidgetHintFrameCollection(this);
406 frameCollection->addFrame(new WidgetHintFrame(d->ui->dateEdit));
407 frameCollection->addFrame(new WidgetHintFrame(d->ui->costCenterCombo));
408 frameCollection->addFrame(new WidgetHintFrame(d->ui->numberEdit, WidgetHintFrame::Warning));
409 frameCollection->addWidget(d->ui->enterButton);
410
411 connect(d->ui->numberEdit, SIGNAL(textChanged(QString)), this, SLOT(numberChanged(QString)));
412 connect(d->ui->costCenterCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(costCenterChanged(int)));
413 connect(d->ui->accountCombo, SIGNAL(accountSelected(QString)), this, SLOT(categoryChanged(QString)));
414 connect(d->ui->dateEdit, SIGNAL(dateChanged(QDate)), this, SLOT(postdateChanged(QDate)));
415 connect(d->amountHelper, SIGNAL(valueChanged()), this, SLOT(valueChanged()));
416
417 connect(d->ui->cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject()));
418 connect(d->ui->enterButton, SIGNAL(clicked(bool)), this, SLOT(acceptEdit()));
419 connect(d->ui->splitEditorButton, SIGNAL(clicked(bool)), this, SLOT(editSplits()));
420
421 // handle some events in certain conditions different from default
422 d->ui->payeeEdit->installEventFilter(this);
423 d->ui->costCenterCombo->installEventFilter(this);
424 d->ui->tagComboBox->installEventFilter(this);
425 d->ui->statusCombo->installEventFilter(this);
426
427 // setup tooltip
428
429 // setWindowFlags(Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
430 }
431
~NewTransactionEditor()432 NewTransactionEditor::~NewTransactionEditor()
433 {
434 }
435
accepted() const436 bool NewTransactionEditor::accepted() const
437 {
438 return d->accepted;
439 }
440
acceptEdit()441 void NewTransactionEditor::acceptEdit()
442 {
443 if(d->checkForValidTransaction()) {
444 d->accepted = true;
445 emit done();
446 }
447 }
448
reject()449 void NewTransactionEditor::reject()
450 {
451 emit done();
452 }
453
keyPressEvent(QKeyEvent * e)454 void NewTransactionEditor::keyPressEvent(QKeyEvent* e)
455 {
456 if (!e->modifiers() || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter)) {
457 switch (e->key()) {
458 case Qt::Key_Enter:
459 case Qt::Key_Return:
460 {
461 if(focusWidget() == d->ui->cancelButton) {
462 reject();
463 } else {
464 if(d->ui->enterButton->isEnabled()) {
465 d->ui->enterButton->click();
466 }
467 return;
468 }
469 }
470 break;
471
472 case Qt::Key_Escape:
473 reject();
474 break;
475
476 default:
477 e->ignore();
478 return;
479 }
480 } else {
481 e->ignore();
482 }
483 }
484
loadTransaction(const QString & id)485 void NewTransactionEditor::loadTransaction(const QString& id)
486 {
487 const LedgerModel* model = Models::instance()->ledgerModel();
488 const QString transactionId = model->transactionIdFromTransactionSplitId(id);
489
490 if(id.isEmpty()) {
491 d->transactionSplitId.clear();
492 d->transaction = MyMoneyTransaction();
493 if(lastUsedPostDate()->isValid()) {
494 d->ui->dateEdit->setDate(*lastUsedPostDate());
495 } else {
496 d->ui->dateEdit->setDate(QDate::currentDate());
497 }
498 bool blocked = d->ui->accountCombo->lineEdit()->blockSignals(true);
499 d->ui->accountCombo->lineEdit()->clear();
500 d->ui->accountCombo->lineEdit()->blockSignals(blocked);
501
502 } else {
503 // find which item has this id and set is as the current item
504 QModelIndexList list = model->match(model->index(0, 0), (int)eLedgerModel::Role::TransactionId,
505 QVariant(transactionId),
506 -1, // all splits
507 Qt::MatchFlags(Qt::MatchExactly | Qt::MatchCaseSensitive | Qt::MatchRecursive));
508
509 Q_FOREACH(QModelIndex index, list) {
510 // the selected split?
511 const QString transactionSplitId = model->data(index, (int)eLedgerModel::Role::TransactionSplitId).toString();
512 if(transactionSplitId == id) {
513 d->transactionSplitId = id;
514 d->transaction = model->data(index, (int)eLedgerModel::Role::Transaction).value<MyMoneyTransaction>();
515 d->split = model->data(index, (int)eLedgerModel::Role::Split).value<MyMoneySplit>();
516 d->ui->dateEdit->setDate(model->data(index, (int)eLedgerModel::Role::PostDate).toDate());
517 d->ui->payeeEdit->lineEdit()->setText(model->data(index, (int)eLedgerModel::Role::PayeeName).toString());
518 d->ui->memoEdit->clear();
519 d->ui->memoEdit->insertPlainText(model->data(index, (int)eLedgerModel::Role::Memo).toString());
520 d->ui->memoEdit->moveCursor(QTextCursor::Start);
521 d->ui->memoEdit->ensureCursorVisible();
522
523 // The calculator for the amount field can simply be added as an icon to the line edit widget.
524 // See https://stackoverflow.com/questions/11381865/how-to-make-an-extra-icon-in-qlineedit-like-this howto do it
525 d->ui->amountEditCredit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Payment)).toString());
526 d->ui->amountEditDebit->setText(model->data(model->index(index.row(), (int)eLedgerModel::Column::Deposit)).toString());
527
528 d->ui->numberEdit->setText(model->data(index, (int)eLedgerModel::Role::Number).toString());
529 d->ui->statusCombo->setCurrentIndex(model->data(index, (int)eLedgerModel::Role::Number).toInt());
530
531 QModelIndexList stList = d->statusModel.match(d->statusModel.index(0, 0), Qt::UserRole+1, model->data(index, (int)eLedgerModel::Role::Reconciliation).toInt());
532 if(stList.count()) {
533 QModelIndex stIndex = stList.front();
534 d->ui->statusCombo->setCurrentIndex(stIndex.row());
535 }
536 } else {
537 d->splitModel.addSplit(transactionSplitId);
538 }
539 }
540 d->updateWidgetState();
541 }
542
543 // set focus to date edit once we return to event loop
544 QMetaObject::invokeMethod(d->ui->dateEdit, "setFocus", Qt::QueuedConnection);
545 }
546
numberChanged(const QString & newNumber)547 void NewTransactionEditor::numberChanged(const QString& newNumber)
548 {
549 d->numberChanged(newNumber);
550 }
551
categoryChanged(const QString & accountId)552 void NewTransactionEditor::categoryChanged(const QString& accountId)
553 {
554 d->categoryChanged(accountId);
555 }
556
costCenterChanged(int costCenterIndex)557 void NewTransactionEditor::costCenterChanged(int costCenterIndex)
558 {
559 d->costCenterChanged(costCenterIndex);
560 }
561
postdateChanged(const QDate & date)562 void NewTransactionEditor::postdateChanged(const QDate& date)
563 {
564 d->postdateChanged(date);
565 }
566
valueChanged()567 void NewTransactionEditor::valueChanged()
568 {
569 d->valueChanged(d->amountHelper);
570 }
571
editSplits()572 void NewTransactionEditor::editSplits()
573 {
574 SplitModel splitModel;
575
576 splitModel.deepCopy(d->splitModel, true);
577
578 // create an empty split at the end
579 splitModel.addEmptySplitEntry();
580
581 QPointer<SplitDialog> splitDialog = new SplitDialog(d->m_account, transactionAmount(), this);
582 splitDialog->setModel(&splitModel);
583
584 int rc = splitDialog->exec();
585
586 if(splitDialog && (rc == QDialog::Accepted)) {
587 // remove that empty split again before we update the splits
588 splitModel.removeEmptySplitEntry();
589
590 // copy the splits model contents
591 d->splitModel.deepCopy(splitModel, true);
592
593 // update the transaction amount
594 d->amountHelper->setValue(splitDialog->transactionAmount());
595
596 d->updateWidgetState();
597 QWidget *next = d->ui->tagComboBox;
598 if(d->ui->costCenterCombo->isEnabled()) {
599 next = d->ui->costCenterCombo;
600 }
601 next->setFocus();
602 }
603
604 if(splitDialog) {
605 splitDialog->deleteLater();
606 }
607 }
608
transactionAmount() const609 MyMoneyMoney NewTransactionEditor::transactionAmount() const
610 {
611 return d->amountHelper->value();
612 }
613
saveTransaction()614 void NewTransactionEditor::saveTransaction()
615 {
616 MyMoneyTransaction t;
617
618 if(!d->transactionSplitId.isEmpty()) {
619 t = d->transaction;
620 } else {
621 // we keep the date when adding a new transaction
622 // for the next new one
623 *lastUsedPostDate() = d->ui->dateEdit->date();
624 }
625
626 // first remove the splits that are gone
627 foreach (const auto split, t.splits()) {
628 if(split.id() == d->split.id()) {
629 continue;
630 }
631 int row;
632 for(row = 0; row < d->splitModel.rowCount(); ++row) {
633 QModelIndex index = d->splitModel.index(row, 0);
634 if(d->splitModel.data(index, (int)eLedgerModel::Role::SplitId).toString() == split.id()) {
635 break;
636 }
637 }
638
639 // if the split is not in the model, we get rid of it
640 if(d->splitModel.rowCount() == row) {
641 t.removeSplit(split);
642 }
643 }
644
645 MyMoneyFileTransaction ft;
646 try {
647 // new we update the split we are opened for
648 MyMoneySplit sp(d->split);
649 sp.setNumber(d->ui->numberEdit->text());
650 sp.setMemo(d->ui->memoEdit->toPlainText());
651 sp.setShares(d->amountHelper->value());
652 if(t.commodity().isEmpty()) {
653 t.setCommodity(d->m_account.currencyId());
654 sp.setValue(d->amountHelper->value());
655 } else {
656 /// @todo check that the transactions commodity is the same
657 /// as the one of the account this split references. If
658 /// that is not the case, the next statement would create
659 /// a problem
660 sp.setValue(d->amountHelper->value());
661 }
662
663 if(sp.reconcileFlag() != eMyMoney::Split::State::Reconciled
664 && !sp.reconcileDate().isValid()
665 && d->ui->statusCombo->currentIndex() == (int)eMyMoney::Split::State::Reconciled) {
666 sp.setReconcileDate(QDate::currentDate());
667 }
668 sp.setReconcileFlag(static_cast<eMyMoney::Split::State>(d->ui->statusCombo->currentIndex()));
669 // sp.setPayeeId(d->ui->payeeEdit->cu)
670 if(sp.id().isEmpty()) {
671 t.addSplit(sp);
672 } else {
673 t.modifySplit(sp);
674 }
675 t.setPostDate(d->ui->dateEdit->date());
676
677 // now update and add what we have in the model
678 const SplitModel * model = &d->splitModel;
679 for(int row = 0; row < model->rowCount(); ++row) {
680 QModelIndex index = model->index(row, 0);
681 MyMoneySplit s;
682 const QString splitId = model->data(index, (int)eLedgerModel::Role::SplitId).toString();
683 if(!SplitModel::isNewSplitId(splitId)) {
684 s = t.splitById(splitId);
685 }
686 s.setNumber(model->data(index, (int)eLedgerModel::Role::Number).toString());
687 s.setMemo(model->data(index, (int)eLedgerModel::Role::Memo).toString());
688 s.setAccountId(model->data(index, (int)eLedgerModel::Role::AccountId).toString());
689 s.setShares(model->data(index, (int)eLedgerModel::Role::SplitShares).value<MyMoneyMoney>());
690 s.setValue(model->data(index, (int)eLedgerModel::Role::SplitValue).value<MyMoneyMoney>());
691 s.setCostCenterId(model->data(index, (int)eLedgerModel::Role::CostCenterId).toString());
692 s.setPayeeId(model->data(index, (int)eLedgerModel::Role::PayeeId).toString());
693
694 // reconcile flag and date
695 if(s.id().isEmpty()) {
696 t.addSplit(s);
697 } else {
698 t.modifySplit(s);
699 }
700 }
701
702 if(t.id().isEmpty()) {
703 MyMoneyFile::instance()->addTransaction(t);
704 } else {
705 MyMoneyFile::instance()->modifyTransaction(t);
706 }
707 ft.commit();
708
709 } catch (const MyMoneyException &e) {
710 qDebug() << Q_FUNC_INFO << "something went wrong" << e.what();
711 }
712
713 }
714
eventFilter(QObject * o,QEvent * e)715 bool NewTransactionEditor::eventFilter(QObject* o, QEvent* e)
716 {
717 auto cb = qobject_cast<QComboBox*>(o);
718 if (o) {
719 // filter out wheel events for combo boxes if the popup view is not visible
720 if ((e->type() == QEvent::Wheel) && !cb->view()->isVisible()) {
721 return true;
722 }
723 }
724 return QFrame::eventFilter(o, e);
725 }
726
727 // kate: space-indent on; indent-width 2; remove-trailing-space on; remove-trailing-space-save on;
728