diff --git a/kmymoney/widgets/stdtransaction.cpp b/kmymoney/widgets/stdtransaction.cpp index 40367f4ba..e2dd088bc 100644 --- a/kmymoney/widgets/stdtransaction.cpp +++ b/kmymoney/widgets/stdtransaction.cpp @@ -1,703 +1,706 @@ /* * Copyright 2006-2018 Thomas Baumgart * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "stdtransaction.h" #include "stdtransaction_p.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "kmymoneypayeecombo.h" #include "kmymoneycombo.h" #include "kmymoneytagcombo.h" #include "tabbar.h" #include "ktagcontainer.h" #include "mymoneytransaction.h" #include "mymoneysplit.h" #include "mymoneyexception.h" #include "mymoneyfile.h" #include "register.h" #include "transactionform.h" #include "kmymoneylineedit.h" #include "kmymoneyutils.h" #ifndef KMM_DESIGNER #include "stdtransactioneditor.h" #endif #include "kmymoneysettings.h" #include "widgetenums.h" #include "mymoneyenums.h" using namespace eWidgets; using namespace KMyMoneyRegister; using namespace KMyMoneyTransactionForm; StdTransaction::StdTransaction(Register *parent, const MyMoneyTransaction& transaction, const MyMoneySplit& split, int uniqueId) : Transaction(*new StdTransactionPrivate, parent, transaction, split, uniqueId) { Q_D(StdTransaction); d->m_showAccountRow = false; try { d->m_categoryHeader = i18n("Category"); switch (transaction.splitCount()) { default: d->m_category = i18nc("Split transaction (category replacement)", "Split transaction"); break; case 0: // the empty transaction case 1: break; case 2: setupFormHeader(d->m_transaction.splitByAccount(d->m_split.accountId(), false).accountId()); break; } } catch (const MyMoneyException &e) { qDebug() << "Problem determining the category for transaction '" << d->m_transaction.id() << "'. Reason: " << e.what() << "\n"; } d->m_rowsForm = 6; if (KMyMoneyUtils::transactionType(d->m_transaction) == KMyMoneyUtils::InvestmentTransaction) { MyMoneySplit stockSplit = KMyMoneyUtils::stockSplit(d->m_transaction); d->m_payee = MyMoneyFile::instance()->account(stockSplit.accountId()).name(); QString addon; if (stockSplit.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::BuyShares)) { if (stockSplit.value().isNegative()) { addon = i18n("Sell"); } else { addon = i18n("Buy"); } } else if (stockSplit.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::Dividend)) { addon = i18n("Dividend"); } else if (stockSplit.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::Yield)) { addon = i18n("Yield"); } else if (stockSplit.action() == MyMoneySplit::actionName(eMyMoney::Split::Action::InterestIncome)) { addon = i18n("Interest Income"); } if (!addon.isEmpty()) { d->m_payee += QString(" (%1)").arg(addon); } d->m_payeeHeader = i18n("Activity"); d->m_category = i18n("Investment transaction"); } // setup initial size setNumRowsRegister(numRowsRegister(KMyMoneySettings::showRegisterDetailed())); emit parent->itemAdded(this); } StdTransaction::~StdTransaction() { } const char* StdTransaction::className() { return "StdTransaction"; } void StdTransaction::setupFormHeader(const QString& id) { Q_D(StdTransaction); d->m_category = MyMoneyFile::instance()->accountToCategory(id); switch (MyMoneyFile::instance()->account(id).accountGroup()) { case eMyMoney::Account::Type::Asset: case eMyMoney::Account::Type::Liability: d->m_categoryHeader = d->m_split.shares().isNegative() ? i18n("Transfer to") : i18n("Transfer from"); break; default: d->m_categoryHeader = i18n("Category"); break; } } eRegister::Action StdTransaction::actionType() const { Q_D(const StdTransaction); eRegister::Action action = eRegister::Action::None; // if at least one split is referencing an income or // expense account, we will not call it a transfer auto found = false; foreach (const auto split, d->m_transaction.splits()) { if (split.accountId() == d->m_split.accountId()) continue; auto acc = MyMoneyFile::instance()->account(split.accountId()); if (acc.accountGroup() == eMyMoney::Account::Type::Income || acc.accountGroup() == eMyMoney::Account::Type::Expense) { // otherwise, we have to determine between deposit and withdrawal action = d->m_split.shares().isNegative() ? eRegister::Action::Withdrawal : eRegister::Action::Deposit; found = true; break; } } // otherwise, it's a transfer if (!found) action = eRegister::Action::Transfer; return action; } void StdTransaction::loadTab(TransactionForm* form) { Q_D(StdTransaction); KMyMoneyTransactionForm::TabBar* bar = form->getTabBar(); bar->setSignalEmission(eTabBar::SignalEmission::Never); for (auto i = 0; i < bar->count(); ++i) { bar->setTabEnabled(i, true); } if (d->m_transaction.splitCount() > 0) { bar->setCurrentIndex((int)actionType()); } bar->setSignalEmission(eTabBar::SignalEmission::Always); } int StdTransaction::numColsForm() const { return 4; } void StdTransaction::setupForm(TransactionForm* form) { Transaction::setupForm(form); form->setSpan(4, (int)eTransactionForm::Column::Value1, 3, 1); } bool StdTransaction::showRowInForm(int row) const { Q_D(const StdTransaction); return row == 0 ? d->m_showAccountRow : true; } void StdTransaction::setShowRowInForm(int row, bool show) { Q_D(StdTransaction); if (row == 0) d->m_showAccountRow = show; } bool StdTransaction::formCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* /* painter */) { Q_D(const StdTransaction); // if(m_transaction != MyMoneyTransaction()) { switch (row) { case 0: switch (col) { case (int)eTransactionForm::Column::Label1: align |= Qt::AlignLeft; txt = i18n("Account"); break; } break; case 1: switch (col) { case (int)eTransactionForm::Column::Label1: align |= Qt::AlignLeft; txt = d->m_payeeHeader; break; case (int)eTransactionForm::Column::Value1: align |= Qt::AlignLeft; txt = d->m_payee; break; case (int)eTransactionForm::Column::Label2: align |= Qt::AlignLeft; if (haveNumberField()) txt = i18n("Number"); break; case (int)eTransactionForm::Column::Value2: align |= Qt::AlignRight; if (haveNumberField()) txt = d->m_split.number(); break; } break; case 2: switch (col) { case (int)eTransactionForm::Column::Label1: align |= Qt::AlignLeft; txt = d->m_categoryHeader; break; case (int)eTransactionForm::Column::Value1: align |= Qt::AlignLeft; txt = d->m_category; if (d->m_transaction != MyMoneyTransaction()) { if (txt.isEmpty() && !d->m_split.value().isZero()) txt = i18n("*** UNASSIGNED ***"); } break; case (int)eTransactionForm::Column::Label2: align |= Qt::AlignLeft; txt = i18n("Date"); break; case (int)eTransactionForm::Column::Value2: align |= Qt::AlignRight; if (d->m_transaction != MyMoneyTransaction()) txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); break; } break; case 3: switch (col) { case (int)eTransactionForm::Column::Label1: align |= Qt::AlignLeft; txt = i18n("Tags"); break; case (int)eTransactionForm::Column::Value1: align |= Qt::AlignLeft; if (!d->m_tagList.isEmpty()) { for (auto i = 0; i < d->m_tagList.size() - 1; ++i) txt += d->m_tagList[i] + ", "; txt += d->m_tagList.last(); } //if (m_transaction != MyMoneyTransaction()) // txt = m_split.tagId(); break; case (int)eTransactionForm::Column::Label2: align |= Qt::AlignLeft; txt = i18n("Amount"); break; case (int)eTransactionForm::Column::Value2: align |= Qt::AlignRight; if (d->m_transaction != MyMoneyTransaction()) { txt = (d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).abs()).formatMoney(d->m_account.fraction()); } break; } break; case 4: switch (col) { case (int)eTransactionForm::Column::Label1: align |= Qt::AlignLeft; txt = i18n("Memo"); break; case (int)eTransactionForm::Column::Value1: align &= ~Qt::AlignVCenter; align |= Qt::AlignTop; align |= Qt::AlignLeft; if (d->m_transaction != MyMoneyTransaction()) txt = d->m_split.memo().section('\n', 0, 2); break; } break; case 5: switch (col) { case (int)eTransactionForm::Column::Label2: align |= Qt::AlignLeft; txt = i18n("Status"); break; case (int)eTransactionForm::Column::Value2: align |= Qt::AlignRight; txt = reconcileState(); break; } } // } if (col == (int)eTransactionForm::Column::Value2 && row == 1) { return haveNumberField(); } return (col == (int)eTransactionForm::Column::Value1 && row < 5) || (col == (int)eTransactionForm::Column::Value2 && row > 0 && row != 4); } void StdTransaction::registerCellText(QString& txt, Qt::Alignment& align, int row, int col, QPainter* painter) { Q_D(const StdTransaction); switch (row) { case 0: switch (col) { case (int)eTransaction::Column::Number: align |= Qt::AlignLeft; if (haveNumberField()) txt = d->m_split.number(); break; case (int)eTransaction::Column::Date: align |= Qt::AlignLeft; txt = QLocale().toString(d->m_transaction.postDate(), QLocale::ShortFormat); break; case (int)eTransaction::Column::Detail: switch (d->m_parent->getDetailsColumnType()) { case eRegister::DetailColumn::PayeeFirst: txt = d->m_payee; break; case eRegister::DetailColumn::AccountFirst: txt = d->m_category; if (!d->m_tagList.isEmpty()) { txt += " ( "; for (auto i = 0; i < d->m_tagList.size() - 1; ++i) { txt += " " + d->m_tagList[i] + ", "; } txt += " " + d->m_tagList.last() + " )"; } break; } align |= Qt::AlignLeft; if (txt.isEmpty() && d->m_rowsRegister < 3) { singleLineMemo(txt, d->m_split); } if (txt.isEmpty() && d->m_rowsRegister < 2) { if (d->m_account.accountType() != eMyMoney::Account::Type::Income && d->m_account.accountType() != eMyMoney::Account::Type::Expense) { txt = d->m_category; if (txt.isEmpty() && !d->m_split.value().isZero()) { txt = i18n("*** UNASSIGNED ***"); if (painter) painter->setPen(KMyMoneySettings::schemeColor(SchemeColor::TransactionErroneous)); } } } break; case (int)eTransaction::Column::ReconcileFlag: align |= Qt::AlignHCenter; txt = reconcileState(false); break; case (int)eTransaction::Column::Payment: align |= Qt::AlignRight; if (d->m_split.value().isNegative()) { txt = (-d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId)).formatMoney(d->m_account.fraction()); } break; case (int)eTransaction::Column::Deposit: align |= Qt::AlignRight; if (!d->m_split.value().isNegative()) { txt = d->m_split.value(d->m_transaction.commodity(), d->m_splitCurrencyId).formatMoney(d->m_account.fraction()); } break; case (int)eTransaction::Column::Balance: align |= Qt::AlignRight; if (d->m_showBalance) txt = d->m_balance.formatMoney(d->m_account.fraction()); else txt = "----"; break; case (int)eTransaction::Column::Account: // txt = m_objects->account(m_transaction.splits()[0].accountId()).name(); txt = MyMoneyFile::instance()->account(d->m_split.accountId()).name(); break; default: break; } break; case 1: switch (col) { case (int)eTransaction::Column::Detail: switch (d->m_parent->getDetailsColumnType()) { case eRegister::DetailColumn::PayeeFirst: txt = d->m_category; + if (txt.isEmpty() && !d->m_split.value().isZero()) { + txt = i18n("*** UNASSIGNED ***"); + if (painter) + painter->setPen(KMyMoneySettings::schemeColor(SchemeColor::TransactionErroneous)); + } if (!d->m_tagList.isEmpty()) { txt += " ( "; for (auto i = 0; i < d->m_tagList.size() - 1; ++i) { txt += " " + d->m_tagList[i] + ", "; } txt += " " + d->m_tagList.last() + " )"; } break; case eRegister::DetailColumn::AccountFirst: txt = d->m_payee; + if (txt.isEmpty()) { + singleLineMemo(txt, d->m_split); + } break; } align |= Qt::AlignLeft; - if (txt.isEmpty() && !d->m_split.value().isZero()) { - txt = i18n("*** UNASSIGNED ***"); - if (painter) - painter->setPen(KMyMoneySettings::schemeColor(SchemeColor::TransactionErroneous)); - } break; default: break; } break; case 2: switch (col) { case (int)eTransaction::Column::Detail: align |= Qt::AlignLeft; singleLineMemo(txt, d->m_split); break; default: break; } break; } } int StdTransaction::registerColWidth(int col, const QFontMetrics& cellFontMetrics) { QString txt; int firstRow = 0, lastRow = numRowsRegister(); int nw = 0; for (int i = firstRow; i <= lastRow; ++i) { Qt::Alignment align; registerCellText(txt, align, i, col, 0); int w = cellFontMetrics.width(txt + " "); if (w > nw) nw = w; } return nw; } void StdTransaction::arrangeWidgetsInForm(QMap& editWidgets) { Q_D(StdTransaction); if (!d->m_form || !d->m_parent) return; setupFormPalette(editWidgets); arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Label1, editWidgets["account-label"]); arrangeWidget(d->m_form, 0, (int)eTransactionForm::Column::Value1, editWidgets["account"]); arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label1, editWidgets["cashflow"]); arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value1, editWidgets["payee"]); arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label1, editWidgets["category-label"]); arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value1, editWidgets["category"]->parentWidget()); arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label1, editWidgets["tag-label"]); arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value1, editWidgets["tag"]); arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Label1, editWidgets["memo-label"]); arrangeWidget(d->m_form, 4, (int)eTransactionForm::Column::Value1, editWidgets["memo"]); if (haveNumberField()) { arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Label2, editWidgets["number-label"]); arrangeWidget(d->m_form, 1, (int)eTransactionForm::Column::Value2, editWidgets["number"]); } arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Label2, editWidgets["date-label"]); arrangeWidget(d->m_form, 2, (int)eTransactionForm::Column::Value2, editWidgets["postdate"]); arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Label2, editWidgets["amount-label"]); arrangeWidget(d->m_form, 3, (int)eTransactionForm::Column::Value2, editWidgets["amount"]); arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Label2, editWidgets["status-label"]); arrangeWidget(d->m_form, 5, (int)eTransactionForm::Column::Value2, editWidgets["status"]); // get rid of the hints. we don't need them for the form QMap::iterator it; for (it = editWidgets.begin(); it != editWidgets.end(); ++it) { KMyMoneyCombo* combo = dynamic_cast(*it); KMyMoneyLineEdit* edit = dynamic_cast(*it); KMyMoneyPayeeCombo* payee = dynamic_cast(*it); KTagContainer* tag = dynamic_cast(*it); if (combo) combo->setPlaceholderText(QString()); if (edit) edit->setPlaceholderText(QString()); if (payee) payee->setPlaceholderText(QString()); if (tag) tag->tagCombo()->setPlaceholderText(QString()); } auto form = dynamic_cast(d->m_form); auto w = dynamic_cast(editWidgets["tabbar"]); if (w && form) { // insert the tabbar in the boxlayout so it will take the place of the original tabbar which was hidden if (auto boxLayout = dynamic_cast(form->getTabBar()->parentWidget()->layout())) boxLayout->insertWidget(0, w); } } void StdTransaction::tabOrderInForm(QWidgetList& tabOrderWidgets) const { Q_D(const StdTransaction); QStringList taborder = KMyMoneySettings::stdTransactionFormTabOrder().split(',', QString::SkipEmptyParts); QStringList::const_iterator it_s = taborder.constBegin(); QWidget* w; while (it_s != taborder.constEnd()) { if (*it_s == "account") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(0, (int)eTransactionForm::Column::Value1))); } else if (*it_s == "cashflow") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Label1))); } else if (*it_s == "payee") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value1))); } else if (*it_s == "category") { // make sure to have the category field and the split button as separate tab order widgets // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but // it's one of our own widgets, so we actually don't care. Just make sure, that we don't // go haywire when someone changes the KMyMoneyCategory object ... w = d->m_form->cellWidget(2, (int)eTransactionForm::Column::Value1); tabOrderWidgets.append(focusWidget(w)); w = w->findChild("splitButton"); if (w) tabOrderWidgets.append(w); } else if (*it_s == "tag") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, (int)eTransactionForm::Column::Value1))); } else if (*it_s == "memo") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(4, (int)eTransactionForm::Column::Value1))); } else if (*it_s == "number") { if (haveNumberField()) { if ((w = focusWidget(d->m_form->cellWidget(1, (int)eTransactionForm::Column::Value2)))) tabOrderWidgets.append(w); } } else if (*it_s == "date") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(2, (int)eTransactionForm::Column::Value2))); } else if (*it_s == "amount") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(3, (int)eTransactionForm::Column::Value2))); } else if (*it_s == "state") { tabOrderWidgets.append(focusWidget(d->m_form->cellWidget(5, (int)eTransactionForm::Column::Value2))); } ++it_s; } } void StdTransaction::arrangeWidgetsInRegister(QMap& editWidgets) { Q_D(StdTransaction); if (!d->m_parent) return; setupRegisterPalette(editWidgets); if (haveNumberField()) arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Number, editWidgets["number"]); arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Date, editWidgets["postdate"]); arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Date, editWidgets["status"]); arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Detail, editWidgets["payee"]); arrangeWidget(d->m_parent, d->m_startRow + 1, (int)eTransaction::Column::Detail, editWidgets["category"]->parentWidget()); arrangeWidget(d->m_parent, d->m_startRow + 2, (int)eTransaction::Column::Detail, editWidgets["tag"]); arrangeWidget(d->m_parent, d->m_startRow + 3, (int)eTransaction::Column::Detail, editWidgets["memo"]); arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Payment, editWidgets["payment"]); arrangeWidget(d->m_parent, d->m_startRow + 0, (int)eTransaction::Column::Deposit, editWidgets["deposit"]); // increase the height of the row containing the memo widget d->m_parent->setRowHeight(d->m_startRow + 3, d->m_parent->rowHeightHint() * 3); } void StdTransaction::tabOrderInRegister(QWidgetList& tabOrderWidgets) const { Q_D(const StdTransaction); QStringList taborder = KMyMoneySettings::stdTransactionRegisterTabOrder().split(',', QString::SkipEmptyParts); QStringList::const_iterator it_s = taborder.constBegin(); QWidget* w; while (it_s != taborder.constEnd()) { if (*it_s == "number") { if (haveNumberField()) { if ((w = focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Number)))) tabOrderWidgets.append(w); } } else if (*it_s == "date") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Date))); } else if (*it_s == "payee") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Detail))); } else if (*it_s == "category") { // make sure to have the category field and the split button as separate tab order widgets // ok, we have to have some internal knowledge about the KMyMoneyCategory object, but // it's one of our own widgets, so we actually don't care. Just make sure, that we don't // go haywire when someone changes the KMyMoneyCategory object ... w = d->m_parent->cellWidget(d->m_startRow + 1, (int)eTransaction::Column::Detail); tabOrderWidgets.append(focusWidget(w)); w = w->findChild("splitButton"); if (w) tabOrderWidgets.append(w); } else if (*it_s == "tag") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 2, (int)eTransaction::Column::Detail))); } else if (*it_s == "memo") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 3, (int)eTransaction::Column::Detail))); } else if (*it_s == "payment") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Payment))); } else if (*it_s == "deposit") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 0, (int)eTransaction::Column::Deposit))); } else if (*it_s == "state") { tabOrderWidgets.append(focusWidget(d->m_parent->cellWidget(d->m_startRow + 1, (int)eTransaction::Column::Date))); } ++it_s; } } int StdTransaction::numRowsRegister(bool expanded) const { Q_D(const StdTransaction); int numRows = 1; if (expanded) { numRows = 4; if (!d->m_inEdit) { //When not in edit Tags haven't a separate row; numRows--; if (d->m_payee.isEmpty()) { numRows--; } if (d->m_split.memo().isEmpty()) { numRows--; } // For income and expense accounts that only have // two splits we only show one line, because the // account name is already contained in the account column. if (d->m_account.accountType() == eMyMoney::Account::Type::Income || d->m_account.accountType() == eMyMoney::Account::Type::Expense) { if (numRows > 2 && d->m_transaction.splitCount() == 2) numRows = 1; } } } return numRows; } int StdTransaction::numRowsRegister() const { return RegisterItem::numRowsRegister(); } TransactionEditor* StdTransaction::createEditor(TransactionEditorContainer* regForm, const KMyMoneyRegister::SelectedTransactions& list, const QDate& lastPostDate) { #ifndef KMM_DESIGNER Q_D(StdTransaction); d->m_inRegisterEdit = regForm == d->m_parent; return new StdTransactionEditor(regForm, this, list, lastPostDate); #else return NULL; #endif }