1 /*
2 * Copyright 2000-2003 Michael Edwardes <mte@users.sourceforge.net>
3 * Copyright 2005-2018 Thomas Baumgart <tbaumgart@kde.org>
4 * Copyright 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "knewaccountdlg.h"
21
22 // ----------------------------------------------------------------------------
23 // QT Includes
24
25 #include <QPushButton>
26 #include <QLabel>
27 #include <QButtonGroup>
28 #include <QCheckBox>
29 #include <QTabWidget>
30 #include <QRadioButton>
31 #include <QList>
32
33 // ----------------------------------------------------------------------------
34 // KDE Headers
35
36 #include <KMessageBox>
37 #include <KComboBox>
38 #include <kguiutils.h>
39 #include <KLocalizedString>
40
41 // ----------------------------------------------------------------------------
42 // Project Includes
43
44 #include "ui_knewaccountdlg.h"
45
46 #include "kmymoneydateinput.h"
47 #include <mymoneyexception.h>
48 #include "mymoneyfile.h"
49 #include "mymoneyinstitution.h"
50 #include "mymoneyaccount.h"
51 #include "kmymoneysettings.h"
52 #include "kmymoneycurrencyselector.h"
53 #include "knewbankdlg.h"
54 #include "models.h"
55 #include "accountsmodel.h"
56 #include "hierarchyfilterproxymodel.h"
57 #include "mymoneyenums.h"
58 #include "modelenums.h"
59
60 using namespace eMyMoney;
61
62 class KNewAccountDlgPrivate
63 {
64 Q_DISABLE_COPY(KNewAccountDlgPrivate)
65 Q_DECLARE_PUBLIC(KNewAccountDlg)
66
67 public:
KNewAccountDlgPrivate(KNewAccountDlg * qq)68 explicit KNewAccountDlgPrivate(KNewAccountDlg *qq) :
69 q_ptr(qq),
70 ui(new Ui::KNewAccountDlg),
71 m_filterProxyModel(nullptr),
72 m_categoryEditor(false),
73 m_isEditing(false)
74 {
75 }
76
~KNewAccountDlgPrivate()77 ~KNewAccountDlgPrivate()
78 {
79 delete ui;
80 }
81
init()82 void init()
83 {
84 Q_Q(KNewAccountDlg);
85 ui->setupUi(q);
86
87 auto file = MyMoneyFile::instance();
88
89 // initialize the m_parentAccount member
90 if (!m_account.parentAccountId().isEmpty()) {
91 try {
92 m_parentAccount = file->account(m_account.parentAccountId());
93 } catch (MyMoneyException&) {
94 m_account.setParentAccountId(QString());
95 }
96 }
97
98 // assign a standard account if the selected parent is not set/found
99 QVector<Account::Type> filterAccountGroup {m_account.accountGroup()};
100 if (m_account.parentAccountId().isEmpty()) {
101 switch (m_account.accountGroup()) {
102 case Account::Type::Asset:
103 m_parentAccount = file->asset();
104 break;
105 case Account::Type::Liability:
106 m_parentAccount = file->liability();
107 break;
108 case Account::Type::Income:
109 m_parentAccount = file->income();
110 break;
111 case Account::Type::Expense:
112 m_parentAccount = file->expense();
113 break;
114 case Account::Type::Equity:
115 m_parentAccount = file->equity();
116 break;
117 default:
118 qDebug("Seems we have an account that hasn't been mapped to the top five");
119 if (m_categoryEditor) {
120 m_parentAccount = file->income();
121 filterAccountGroup[0] = Account::Type::Income;
122 } else {
123 m_parentAccount = file->asset();
124 filterAccountGroup[0] = Account::Type::Asset;
125 }
126 }
127 }
128
129 ui->m_amountGroup->setId(ui->m_grossAmount, 0);
130 ui->m_amountGroup->setId(ui->m_netAmount, 1);
131
132 // the proxy filter model
133 m_filterProxyModel = new HierarchyFilterProxyModel(q);
134 m_filterProxyModel->setHideClosedAccounts(true);
135 m_filterProxyModel->setHideEquityAccounts(!KMyMoneySettings::expertMode());
136 m_filterProxyModel->addAccountGroup(filterAccountGroup);
137 m_filterProxyModel->setCurrentAccountId(m_account.id());
138 auto const model = Models::instance()->accountsModel();
139 m_filterProxyModel->setSourceModel(model);
140 m_filterProxyModel->setSourceColumns(model->getColumns());
141 m_filterProxyModel->setDynamicSortFilter(true);
142
143 ui->m_parentAccounts->setModel(m_filterProxyModel);
144 ui->m_parentAccounts->sortByColumn((int)eAccountsModel::Column::Account, Qt::AscendingOrder);
145
146 ui->m_subAccountLabel->setText(i18n("Is a sub account"));
147
148 ui->accountNameEdit->setText(m_account.name());
149 ui->descriptionEdit->setText(m_account.description());
150
151 ui->typeCombo->setEnabled(true);
152
153 // load the price mode combo
154 ui->m_priceMode->insertItem(i18nc("default price mode", "(default)"), 0);
155 ui->m_priceMode->insertItem(i18n("Price per share"), 1);
156 ui->m_priceMode->insertItem(i18n("Total for all shares"), 2);
157
158 int priceMode = 0;
159 if (m_account.accountType() == Account::Type::Investment) {
160 ui->m_priceMode->setEnabled(true);
161 if (!m_account.value("priceMode").isEmpty())
162 priceMode = m_account.value("priceMode").toInt();
163 }
164 ui->m_priceMode->setCurrentItem(priceMode);
165
166 bool haveMinBalance = false;
167 bool haveMaxCredit = false;
168 if (!m_account.openingDate().isValid()) {
169 m_account.setOpeningDate(KMyMoneySettings::firstFiscalDate());
170 }
171 ui->m_openingDateEdit->setDate(m_account.openingDate());
172
173 handleOpeningBalanceCheckbox(m_account.currencyId());
174
175 if (m_categoryEditor) {
176 // get rid of the tabs that are not used for categories
177 int tab = ui->m_tab->indexOf(ui->m_institutionTab);
178 if (tab != -1)
179 ui->m_tab->removeTab(tab);
180 tab = ui->m_tab->indexOf(ui->m_limitsTab);
181 if (tab != -1)
182 ui->m_tab->removeTab(tab);
183
184 //m_qlistviewParentAccounts->setEnabled(true);
185 ui->accountNoEdit->setEnabled(false);
186
187 ui->m_institutionBox->hide();
188 ui->m_qcheckboxNoVat->hide();
189
190 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Income), (int)Account::Type::Income);
191 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Expense), (int)Account::Type::Expense);
192
193 // Hardcoded but acceptable - if above we set the default to income do the same here
194 switch (m_account.accountType()) {
195 case Account::Type::Expense:
196 ui->typeCombo->setCurrentItem(MyMoneyAccount::accountTypeToString(Account::Type::Expense), false);
197 break;
198
199 case Account::Type::Income:
200 default:
201 ui->typeCombo->setCurrentItem(MyMoneyAccount::accountTypeToString(Account::Type::Income), false);
202 break;
203 }
204 ui->m_currency->setEnabled(true);
205 if (m_isEditing) {
206 ui->typeCombo->setEnabled(false);
207 ui->m_currency->setDisabled(MyMoneyFile::instance()->isReferenced(m_account));
208 }
209 ui->m_qcheckboxPreferred->hide();
210
211 ui->m_qcheckboxTax->setChecked(m_account.value("Tax").toLower() == "yes");
212 ui->m_costCenterRequiredCheckBox->setChecked(m_account.isCostCenterRequired());
213
214 loadVatAccounts();
215 } else {
216 // get rid of the tabs that are not used for accounts
217 int taxtab = ui->m_tab->indexOf(ui->m_taxTab);
218 if (taxtab != -1) {
219 ui->m_vatCategory->setText(i18n("VAT account"));
220 ui->m_qcheckboxTax->setChecked(m_account.value("Tax") == "Yes");
221 loadVatAccounts();
222 } else {
223 ui->m_tab->removeTab(taxtab);
224 }
225
226 ui->m_costCenterRequiredCheckBox->hide();
227
228 switch (m_account.accountType()) {
229 case Account::Type::Savings:
230 case Account::Type::Cash:
231 haveMinBalance = true;
232 break;
233
234 case Account::Type::Checkings:
235 haveMinBalance = true;
236 haveMaxCredit = true;
237 break;
238
239 case Account::Type::CreditCard:
240 haveMaxCredit = true;
241 break;
242
243 default:
244 // no limit available, so we might get rid of the tab
245 int tab = ui->m_tab->indexOf(ui->m_limitsTab);
246 if (tab != -1)
247 ui->m_tab->removeTab(tab);
248 // don't try to hide the widgets we just wiped
249 // in the next step
250 haveMaxCredit = haveMinBalance = true;
251 break;
252 }
253
254 if (!haveMaxCredit) {
255 ui->m_maxCreditLabel->setEnabled(false);
256 ui->m_maxCreditLabel->hide();
257 ui->m_maxCreditEarlyEdit->hide();
258 ui->m_maxCreditAbsoluteEdit->hide();
259 }
260 if (!haveMinBalance) {
261 ui->m_minBalanceLabel->setEnabled(false);
262 ui->m_minBalanceLabel->hide();
263 ui->m_minBalanceEarlyEdit->hide();
264 ui->m_minBalanceAbsoluteEdit->hide();
265 }
266
267 QString typeString = MyMoneyAccount::accountTypeToString(m_account.accountType());
268
269 if (m_isEditing) {
270 if (m_account.isLiquidAsset()) {
271 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Checkings), (int)Account::Type::Checkings);
272 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Savings), (int)Account::Type::Savings);
273 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Cash), (int)Account::Type::Cash);
274 } else {
275 ui->typeCombo->addItem(typeString, (int)m_account.accountType());
276 // Once created, accounts of other account types are not
277 // allowed to be changed.
278 ui->typeCombo->setEnabled(false);
279 }
280 // Once created, a currency cannot be changed if it is referenced.
281 ui->m_currency->setDisabled(MyMoneyFile::instance()->isReferenced(m_account));
282 } else {
283 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Checkings), (int)Account::Type::Checkings);
284 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Savings), (int)Account::Type::Savings);
285 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Cash), (int)Account::Type::Cash);
286 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::CreditCard), (int)Account::Type::CreditCard);
287 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Loan), (int)Account::Type::Loan);
288 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Investment), (int)Account::Type::Investment);
289 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Asset), (int)Account::Type::Asset);
290 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Liability), (int)Account::Type::Liability);
291 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Stock), (int)Account::Type::Stock);
292 /*
293 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::CertificateDep), (int)Account::Type::CertificateDep);
294 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::MoneyMarket), (int)Account::Type::MoneyMarket);
295 ui->typeCombo->addItem(MyMoneyAccount::accountTypeToString(Account::Type::Currency), (int)Account::Type::Currency);
296 */
297 // Do not create account types that are not supported
298 // by the current engine.
299 if (m_account.accountType() == Account::Type::Unknown ||
300 m_account.accountType() == Account::Type::CertificateDep ||
301 m_account.accountType() == Account::Type::MoneyMarket ||
302 m_account.accountType() == Account::Type::Currency)
303 typeString = MyMoneyAccount::accountTypeToString(Account::Type::Checkings);
304 }
305
306 ui->typeCombo->setCurrentItem(typeString, false);
307
308 if (m_account.isInvest())
309 ui->m_institutionBox->hide();
310
311 ui->accountNoEdit->setText(m_account.number());
312 ui->m_qcheckboxPreferred->setChecked(m_account.value("PreferredAccount") == "Yes");
313 ui->m_qcheckboxNoVat->setChecked(m_account.value("NoVat") == "Yes");
314 loadKVP("iban", ui->ibanEdit);
315 loadKVP("minBalanceAbsolute", ui->m_minBalanceAbsoluteEdit);
316 loadKVP("minBalanceEarly", ui->m_minBalanceEarlyEdit);
317 loadKVP("maxCreditAbsolute", ui->m_maxCreditAbsoluteEdit);
318 loadKVP("maxCreditEarly", ui->m_maxCreditEarlyEdit);
319 // reverse the sign for display purposes
320 if (!ui->m_maxCreditAbsoluteEdit->text().isEmpty())
321 ui->m_maxCreditAbsoluteEdit->setValue(ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE);
322 if (!ui->m_maxCreditEarlyEdit->text().isEmpty())
323 ui->m_maxCreditEarlyEdit->setValue(ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE);
324 loadKVP("lastNumberUsed", ui->m_lastCheckNumberUsed);
325
326 if (m_account.isInvest()) {
327 ui->typeCombo->setEnabled(false);
328 ui->m_qcheckboxPreferred->hide();
329 ui->m_currencyText->hide();
330 ui->m_currency->hide();
331 } else {
332 // use the old field and override a possible new value
333 if (!MyMoneyMoney(m_account.value("minimumBalance")).isZero()) {
334 ui->m_minBalanceAbsoluteEdit->setValue(MyMoneyMoney(m_account.value("minimumBalance")));
335 }
336 }
337
338 // ui->m_qcheckboxTax->hide(); TODO should only be visible for VAT category/account
339 }
340
341 ui->m_currency->setSecurity(file->currency(m_account.currencyId()));
342
343 // Load the institutions
344 // then the accounts
345 QString institutionName;
346
347 try {
348 if (m_isEditing && !m_account.institutionId().isEmpty())
349 institutionName = file->institution(m_account.institutionId()).name();
350 else
351 institutionName.clear();
352 } catch (const MyMoneyException &e) {
353 qDebug("exception in init for account dialog: %s", e.what());
354 }
355
356 if (m_account.isInvest())
357 ui->m_parentAccounts->setEnabled(false);
358
359 if (!m_categoryEditor)
360 q->slotLoadInstitutions(institutionName);
361
362 ui->accountNameEdit->setFocus();
363
364 q->connect(ui->buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
365 q->connect(ui->buttonBox, &QDialogButtonBox::accepted, q, &KNewAccountDlg::okClicked);
366 q->connect(ui->m_parentAccounts->selectionModel(), &QItemSelectionModel::selectionChanged,
367 q, &KNewAccountDlg::slotSelectionChanged);
368 q->connect(ui->m_qbuttonNew, &QAbstractButton::clicked, q, &KNewAccountDlg::slotNewClicked);
369 q->connect(ui->typeCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), q, &KNewAccountDlg::slotAccountTypeChanged);
370
371 q->connect(ui->accountNameEdit, &QLineEdit::textChanged, q, &KNewAccountDlg::slotCheckFinished);
372
373 q->connect(ui->m_vatCategory, &QAbstractButton::toggled, q, &KNewAccountDlg::slotVatChanged);
374 q->connect(ui->m_vatAssignment, &QAbstractButton::toggled, q, &KNewAccountDlg::slotVatAssignmentChanged);
375 q->connect(ui->m_vatCategory, &QAbstractButton::toggled, q, &KNewAccountDlg::slotCheckFinished);
376 q->connect(ui->m_vatAssignment, &QAbstractButton::toggled, q, &KNewAccountDlg::slotCheckFinished);
377 q->connect(ui->m_vatRate, &AmountEdit::textChanged, q, &KNewAccountDlg::slotCheckFinished);
378 q->connect(ui->m_vatAccount, &KMyMoneySelector::stateChanged, q, &KNewAccountDlg::slotCheckFinished);
379 q->connect(ui->m_currency, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), q, &KNewAccountDlg::slotCheckCurrency);
380
381 q->connect(ui->m_minBalanceEarlyEdit, &AmountEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceAbsoluteEdit);
382 q->connect(ui->m_minBalanceAbsoluteEdit, &AmountEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMinBalanceEarlyEdit);
383 q->connect(ui->m_maxCreditEarlyEdit, &AmountEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditAbsoluteEdit);
384 q->connect(ui->m_maxCreditAbsoluteEdit, &AmountEdit::valueChanged, q, &KNewAccountDlg::slotAdjustMaxCreditEarlyEdit);
385
386 q->connect(ui->m_qcomboboxInstitutions, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::activated), q, &KNewAccountDlg::slotLoadInstitutions);
387
388 QModelIndex parentIndex;
389 if (!m_parentAccount.id().isEmpty()) {
390 const auto baseIdx = model->accountById(m_parentAccount.id());
391 parentIndex = m_filterProxyModel->mapFromSource(baseIdx);
392 }
393 selectParentAccount(parentIndex);
394
395 ui->m_vatCategory->setChecked(false);
396 ui->m_vatAssignment->setChecked(false);
397
398 // make sure our account does not have an id and no parent assigned
399 // and certainly no children in case we create a new account
400 if (!m_isEditing) {
401 m_account.clearId();
402 m_account.setParentAccountId(QString());
403 m_account.removeAccountIds();
404 } else {
405 if (!m_account.value("VatRate").isEmpty()) {
406 ui->m_vatCategory->setChecked(true);
407 ui->m_vatRate->setValue(MyMoneyMoney(m_account.value("VatRate"))*MyMoneyMoney(100, 1));
408 } else {
409 if (!m_account.value("VatAccount").isEmpty()) {
410 QString accId = m_account.value("VatAccount").toLatin1();
411 try {
412 // make sure account exists
413 MyMoneyFile::instance()->account(accId);
414 ui->m_vatAssignment->setChecked(true);
415 ui->m_vatAccount->setSelected(accId);
416 ui->m_grossAmount->setChecked(true);
417 if (m_account.value("VatAmount") == "Net")
418 ui->m_netAmount->setChecked(true);
419 } catch (const MyMoneyException &) {
420 }
421 }
422 }
423 }
424 q->slotVatChanged(ui->m_vatCategory->isChecked());
425 q->slotVatAssignmentChanged(ui->m_vatAssignment->isChecked());
426 q->slotCheckFinished();
427
428 auto requiredFields = new KMandatoryFieldGroup(q);
429 requiredFields->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present
430 requiredFields->add(ui->accountNameEdit);
431 }
432
loadKVP(const QString & key,AmountEdit * widget)433 void loadKVP(const QString& key, AmountEdit* widget)
434 {
435 if (!widget)
436 return;
437
438 if (m_account.value(key).isEmpty()) {
439 widget->setText(QString());
440 } else {
441 widget->setValue(MyMoneyMoney(m_account.value(key)));
442 }
443 }
444
loadKVP(const QString & key,KLineEdit * widget)445 void loadKVP(const QString& key, KLineEdit* widget)
446 {
447 if (!widget)
448 return;
449
450 widget->setText(m_account.value(key));
451 }
452
storeKVP(const QString & key,const QString & text,const QString & value)453 void storeKVP(const QString& key, const QString& text, const QString& value)
454 {
455 if (text.isEmpty())
456 m_account.deletePair(key);
457 else
458 m_account.setValue(key, value);
459 }
460
storeKVP(const QString & key,QCheckBox * widget)461 void storeKVP(const QString& key, QCheckBox* widget)
462 {
463 if (widget) {
464 if(widget->isChecked()) {
465 m_account.setValue(key, "Yes");;
466 } else {
467 m_account.deletePair(key);
468 }
469 }
470 }
471
storeKVP(const QString & key,AmountEdit * widget)472 void storeKVP(const QString& key, AmountEdit* widget)
473 {
474 storeKVP(key, widget->text(), widget->text());
475 }
476
storeKVP(const QString & key,KLineEdit * widget)477 void storeKVP(const QString& key, KLineEdit* widget)
478 {
479 storeKVP(key, widget->text(), widget->text());
480 }
481
loadVatAccounts()482 void loadVatAccounts()
483 {
484 QList<MyMoneyAccount> list;
485 MyMoneyFile::instance()->accountList(list);
486 QList<MyMoneyAccount>::Iterator it;
487 QStringList loadListExpense;
488 QStringList loadListIncome;
489 QStringList loadListAsset;
490 QStringList loadListLiability;
491 for (it = list.begin(); it != list.end(); ++it) {
492 if (!(*it).value("VatRate").isEmpty()) {
493 if ((*it).accountType() == Account::Type::Expense)
494 loadListExpense += (*it).id();
495 else if ((*it).accountType() == Account::Type::Income)
496 loadListIncome += (*it).id();
497 else if ((*it).accountType() == Account::Type::Asset)
498 loadListAsset += (*it).id();
499 else if ((*it).accountType() == Account::Type::Liability)
500 loadListLiability += (*it).id();
501 }
502 }
503 AccountSet vatSet;
504 if (!loadListAsset.isEmpty())
505 vatSet.load(ui->m_vatAccount, i18n("Asset"), loadListAsset, true);
506 if (!loadListLiability.isEmpty())
507 vatSet.load(ui->m_vatAccount, i18n("Liability"), loadListLiability, false);
508 if (!loadListIncome.isEmpty())
509 vatSet.load(ui->m_vatAccount, i18n("Income"), loadListIncome, false);
510 if (!loadListExpense.isEmpty())
511 vatSet.load(ui->m_vatAccount, i18n("Expense"), loadListExpense, false);
512 }
513
adjustEditWidgets(AmountEdit * dst,AmountEdit * src,char mode,int corr)514 void adjustEditWidgets(AmountEdit* dst, AmountEdit* src, char mode, int corr)
515 {
516 MyMoneyMoney factor(corr, 1);
517 if (m_account.accountGroup() == Account::Type::Asset)
518 factor = -factor;
519
520 switch (mode) {
521 case '<':
522 if (src->value()*factor < dst->value()*factor)
523 dst->setValue(src->value());
524 break;
525
526 case '>':
527 if (src->value()*factor > dst->value()*factor)
528 dst->setValue(src->value());
529 break;
530 }
531 }
532
handleOpeningBalanceCheckbox(const QString & currencyId)533 void handleOpeningBalanceCheckbox(const QString ¤cyId)
534 {
535 if (m_account.accountType() == Account::Type::Equity) {
536 // check if there is another opening balance account with the same currency
537 bool isOtherOpenBalancingAccount = false;
538 QList<MyMoneyAccount> list;
539 MyMoneyFile::instance()->accountList(list);
540 QList<MyMoneyAccount>::Iterator it;
541 for (it = list.begin(); it != list.end(); ++it) {
542 if (it->id() == m_account.id() || currencyId != it->currencyId()
543 || it->accountType() != Account::Type::Equity)
544 continue;
545 if (it->value("OpeningBalanceAccount") == "Yes") {
546 isOtherOpenBalancingAccount = true;
547 break;
548 }
549 }
550 if (!isOtherOpenBalancingAccount) {
551 bool isOpenBalancingAccount = m_account.value("OpeningBalanceAccount") == "Yes";
552 ui->m_qcheckboxOpeningBalance->setChecked(isOpenBalancingAccount);
553 if (isOpenBalancingAccount) {
554 // let only allow state change if no transactions are assigned to this account
555 bool hasTransactions = MyMoneyFile::instance()->transactionCount(m_account.id()) != 0;
556 ui->m_qcheckboxOpeningBalance->setEnabled(!hasTransactions);
557 if (hasTransactions)
558 ui->m_qcheckboxOpeningBalance->setToolTip(i18n("Option has been disabled because there are transactions assigned to this account"));
559 }
560 } else {
561 ui->m_qcheckboxOpeningBalance->setChecked(false);
562 ui->m_qcheckboxOpeningBalance->setEnabled(false);
563 ui->m_qcheckboxOpeningBalance->setToolTip(i18n("Option has been disabled because there is another account flagged to be an opening balance account for this currency"));
564 }
565 } else {
566 ui->m_qcheckboxOpeningBalance->setVisible(false);
567 }
568 }
569
selectParentAccount(const QModelIndex & parentIndex)570 void selectParentAccount(const QModelIndex& parentIndex)
571 {
572 ui->m_parentAccounts->expand(parentIndex);
573 ui->m_parentAccounts->selectionModel()->select(parentIndex, QItemSelectionModel::SelectCurrent);
574 ui->m_parentAccounts->setCurrentIndex(parentIndex);
575 ui->m_parentAccounts->scrollTo(parentIndex, QAbstractItemView::PositionAtCenter);
576 }
577
578 KNewAccountDlg *q_ptr;
579 Ui::KNewAccountDlg *ui;
580 MyMoneyAccount m_account;
581 MyMoneyAccount m_parentAccount;
582 HierarchyFilterProxyModel *m_filterProxyModel;
583
584 bool m_categoryEditor;
585 bool m_isEditing;
586 };
587
KNewAccountDlg(const MyMoneyAccount & account,bool isEditing,bool categoryEditor,QWidget * parent,const QString & title)588 KNewAccountDlg::KNewAccountDlg(const MyMoneyAccount& account, bool isEditing, bool categoryEditor, QWidget *parent, const QString& title) :
589 QDialog(parent),
590 d_ptr(new KNewAccountDlgPrivate(this))
591 {
592 Q_D(KNewAccountDlg);
593 d->m_account = account;
594 d->m_categoryEditor = categoryEditor;
595 d->m_isEditing = isEditing;
596 d->init();
597 if (!title.isEmpty())
598 setWindowTitle(title);
599 }
600
openingBalance() const601 MyMoneyMoney KNewAccountDlg::openingBalance() const
602 {
603 Q_D(const KNewAccountDlg);
604 return d->ui->m_openingBalanceEdit->value();
605 }
606
setOpeningBalance(const MyMoneyMoney & balance)607 void KNewAccountDlg::setOpeningBalance(const MyMoneyMoney& balance)
608 {
609 Q_D(KNewAccountDlg);
610 d->ui->m_openingBalanceEdit->setValue(balance);
611 }
612
setOpeningBalanceShown(bool shown)613 void KNewAccountDlg::setOpeningBalanceShown(bool shown)
614 {
615 Q_D(KNewAccountDlg);
616 d->ui->m_openingBalanceLabel->setVisible(shown);
617 d->ui->m_openingBalanceEdit->setVisible(shown);
618 }
619
setOpeningDateShown(bool shown)620 void KNewAccountDlg::setOpeningDateShown(bool shown)
621 {
622 Q_D(KNewAccountDlg);
623 d->ui->m_openingDateLabel->setVisible(shown);
624 d->ui->m_openingDateEdit->setVisible(shown);
625 }
626
okClicked()627 void KNewAccountDlg::okClicked()
628 {
629 Q_D(KNewAccountDlg);
630 auto file = MyMoneyFile::instance();
631
632 QString accountNameText = d->ui->accountNameEdit->text();
633 if (accountNameText.isEmpty()) {
634 KMessageBox::error(this, i18n("You have not specified a name.\nPlease fill in this field."));
635 d->ui->accountNameEdit->setFocus();
636 return;
637 }
638
639 MyMoneyAccount parent = parentAccount();
640 if (parent.name().length() == 0) {
641 KMessageBox::error(this, i18n("Please select a parent account."));
642 return;
643 }
644
645 if (!d->m_categoryEditor) {
646 QString institutionNameText = d->ui->m_qcomboboxInstitutions->currentText();
647 if (institutionNameText != i18n("(No Institution)")) {
648 try {
649 QList<MyMoneyInstitution> list = file->institutionList();
650 QList<MyMoneyInstitution>::ConstIterator institutionIterator;
651 for (institutionIterator = list.constBegin(); institutionIterator != list.constEnd(); ++institutionIterator) {
652 if ((*institutionIterator).name() == institutionNameText)
653 d->m_account.setInstitutionId((*institutionIterator).id());
654 }
655 } catch (const MyMoneyException &e) {
656 qDebug("Exception in account institution set: %s", e.what());
657 }
658 } else {
659 d->m_account.setInstitutionId(QString());
660 }
661 }
662
663 d->m_account.setName(accountNameText);
664 d->m_account.setNumber(d->ui->accountNoEdit->text());
665 d->storeKVP("iban", d->ui->ibanEdit);
666 d->storeKVP("minBalanceAbsolute", d->ui->m_minBalanceAbsoluteEdit);
667 d->storeKVP("minBalanceEarly", d->ui->m_minBalanceEarlyEdit);
668
669 // the figures for credit line with reversed sign
670 if (!d->ui->m_maxCreditAbsoluteEdit->text().isEmpty())
671 d->ui->m_maxCreditAbsoluteEdit->setValue(d->ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE);
672 if (!d->ui->m_maxCreditEarlyEdit->text().isEmpty())
673 d->ui->m_maxCreditEarlyEdit->setValue(d->ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE);
674 d->storeKVP("maxCreditAbsolute", d->ui->m_maxCreditAbsoluteEdit);
675 d->storeKVP("maxCreditEarly", d->ui->m_maxCreditEarlyEdit);
676 if (!d->ui->m_maxCreditAbsoluteEdit->text().isEmpty())
677 d->ui->m_maxCreditAbsoluteEdit->setValue(d->ui->m_maxCreditAbsoluteEdit->value()*MyMoneyMoney::MINUS_ONE);
678 if (!d->ui->m_maxCreditEarlyEdit->text().isEmpty())
679 d->ui->m_maxCreditEarlyEdit->setValue(d->ui->m_maxCreditEarlyEdit->value()*MyMoneyMoney::MINUS_ONE);
680
681 d->storeKVP("lastNumberUsed", d->ui->m_lastCheckNumberUsed);
682 // delete a previous version of the minimumbalance information
683 d->storeKVP("minimumBalance", QString(), QString());
684
685 Account::Type acctype;
686 if (!d->m_categoryEditor) {
687 acctype = static_cast<Account::Type>(d->ui->typeCombo->currentData().toInt());
688 // If it's a loan, check if the parent is asset or liability. In
689 // case of asset, we change the account type to be AssetLoan
690 if (acctype == Account::Type::Loan
691 && parent.accountGroup() == Account::Type::Asset)
692 acctype = Account::Type::AssetLoan;
693 } else {
694 acctype = parent.accountGroup();
695 QString newName;
696 if (!MyMoneyFile::instance()->isStandardAccount(parent.id())) {
697 newName = MyMoneyFile::instance()->accountToCategory(parent.id()) + MyMoneyFile::AccountSeparator;
698 }
699 newName += accountNameText;
700 if (!file->categoryToAccount(newName, acctype).isEmpty()
701 && (file->categoryToAccount(newName, acctype) != d->m_account.id())) {
702 KMessageBox::error(this, QString("<qt>") + i18n("A category named <b>%1</b> already exists. You cannot create a second category with the same name.", newName) + QString("</qt>"));
703 return;
704 }
705 }
706 d->m_account.setAccountType(acctype);
707
708 d->m_account.setDescription(d->ui->descriptionEdit->toPlainText());
709
710 d->m_account.setOpeningDate(d->ui->m_openingDateEdit->date());
711
712 if (!d->m_categoryEditor) {
713 d->m_account.setCurrencyId(d->ui->m_currency->security().id());
714
715 d->storeKVP("PreferredAccount", d->ui->m_qcheckboxPreferred);
716 d->storeKVP("NoVat", d->ui->m_qcheckboxNoVat);
717
718 if (d->ui->m_minBalanceAbsoluteEdit->isVisible()) {
719 d->m_account.setValue("minimumBalance", d->ui->m_minBalanceAbsoluteEdit->value().toString());
720 }
721 } else {
722 if (KMyMoneySettings::hideUnusedCategory() && !d->m_isEditing) {
723 KMessageBox::information(this, i18n("You have selected to suppress the display of unused categories in the KMyMoney configuration dialog. The category you just created will therefore only be shown if it is used. Otherwise, it will be hidden in the accounts/categories view."), i18n("Hidden categories"), "NewHiddenCategory");
724 }
725 d->m_account.setCostCenterRequired(d->ui->m_costCenterRequiredCheckBox->isChecked());
726 }
727
728 d->storeKVP("Tax", d->ui->m_qcheckboxTax);
729
730 if (d->ui->m_qcheckboxOpeningBalance->isChecked())
731 d->m_account.setValue("OpeningBalanceAccount", "Yes");
732 else
733 d->m_account.deletePair("OpeningBalanceAccount");
734
735 d->m_account.deletePair("VatAccount");
736 d->m_account.deletePair("VatAmount");
737 d->m_account.deletePair("VatRate");
738
739 if (d->ui->m_vatCategory->isChecked()) {
740 d->m_account.setValue("VatRate", (d->ui->m_vatRate->value().abs() / MyMoneyMoney(100, 1)).toString());
741 } else {
742 if (d->ui->m_vatAssignment->isChecked() && !d->ui->m_vatAccount->selectedItems().isEmpty()) {
743 d->m_account.setValue("VatAccount", d->ui->m_vatAccount->selectedItems().first());
744 if (d->ui->m_netAmount->isChecked())
745 d->m_account.setValue("VatAmount", "Net");
746 }
747 }
748
749 accept();
750 }
751
752
account()753 MyMoneyAccount KNewAccountDlg::account()
754 {
755 Q_D(KNewAccountDlg);
756 // assign the right currency to the account
757 d->m_account.setCurrencyId(d->ui->m_currency->security().id());
758
759 // and the price mode
760 switch (d->ui->m_priceMode->currentItem()) {
761 case 0:
762 d->m_account.deletePair("priceMode");
763 break;
764 case 1:
765 case 2:
766 d->m_account.setValue("priceMode", QString("%1").arg(d->ui->m_priceMode->currentItem()));
767 break;
768 }
769
770 return d->m_account;
771 }
772
parentAccount() const773 MyMoneyAccount KNewAccountDlg::parentAccount() const
774 {
775 Q_D(const KNewAccountDlg);
776 return d->m_parentAccount;
777 }
778
slotSelectionChanged(const QItemSelection & current,const QItemSelection & previous)779 void KNewAccountDlg::slotSelectionChanged(const QItemSelection ¤t, const QItemSelection &previous)
780 {
781 Q_UNUSED(previous)
782 Q_D(KNewAccountDlg);
783 if (!current.indexes().empty()) {
784 QVariant account = d->ui->m_parentAccounts->model()->data(current.indexes().front(), (int)eAccountsModel::Role::Account);
785 if (account.isValid()) {
786 d->m_parentAccount = account.value<MyMoneyAccount>();
787 d->ui->m_subAccountLabel->setText(i18n("Is a sub account of %1", d->m_parentAccount.name()));
788 }
789 }
790 }
791
slotLoadInstitutions(const QString & name)792 void KNewAccountDlg::slotLoadInstitutions(const QString& name)
793 {
794 Q_D(KNewAccountDlg);
795 d->ui->m_qcomboboxInstitutions->clear();
796 // Are we forcing the user to use institutions?
797 d->ui->m_qcomboboxInstitutions->addItem(i18n("(No Institution)"));
798 d->ui->m_bicValue->setText(" ");
799 d->ui->ibanEdit->setEnabled(false);
800 d->ui->accountNoEdit->setEnabled(false);
801 try {
802 auto file = MyMoneyFile::instance();
803
804 QList<MyMoneyInstitution> list = file->institutionList();
805 QList<MyMoneyInstitution>::ConstIterator institutionIterator;
806 for (institutionIterator = list.constBegin(); institutionIterator != list.constEnd(); ++institutionIterator) {
807 if ((*institutionIterator).name() == name) {
808 d->ui->ibanEdit->setEnabled(true);
809 d->ui->accountNoEdit->setEnabled(true);
810 d->ui->m_bicValue->setText((*institutionIterator).value("bic"));
811 }
812 d->ui->m_qcomboboxInstitutions->addItem((*institutionIterator).name());
813 }
814
815 d->ui->m_qcomboboxInstitutions->setCurrentItem(name, false);
816 } catch (const MyMoneyException &e) {
817 qDebug("Exception in institution load: %s", e.what());
818 }
819 }
820
slotNewClicked()821 void KNewAccountDlg::slotNewClicked()
822 {
823 MyMoneyInstitution institution;
824
825 QPointer<KNewBankDlg> dlg = new KNewBankDlg(institution, this);
826 if (dlg->exec()) {
827 MyMoneyFileTransaction ft;
828 try {
829 auto file = MyMoneyFile::instance();
830
831 institution = dlg->institution();
832 file->addInstitution(institution);
833 ft.commit();
834 slotLoadInstitutions(institution.name());
835 } catch (const MyMoneyException &) {
836 KMessageBox::information(this, i18n("Cannot add institution"));
837 }
838 }
839 delete dlg;
840 }
841
slotAccountTypeChanged(int index)842 void KNewAccountDlg::slotAccountTypeChanged(int index)
843 {
844 Q_D(KNewAccountDlg);
845 Account::Type oldType;
846
847 auto type = d->ui->typeCombo->itemData(index).value<Account::Type>();
848 try {
849 oldType = d->m_account.accountType();
850 if (oldType != type) {
851 d->m_account.setAccountType(type);
852 // update the account group displayed in the accounts hierarchy
853 d->m_filterProxyModel->clear();
854 d->m_filterProxyModel->addAccountGroup(QVector<Account::Type> {d->m_account.accountGroup()});
855 d->selectParentAccount(d->m_filterProxyModel->index(0, 0));
856
857 }
858 } catch (const MyMoneyException &) {
859 qWarning("Unexpected exception in KNewAccountDlg::slotAccountTypeChanged()");
860 }
861 }
862
slotCheckFinished()863 void KNewAccountDlg::slotCheckFinished()
864 {
865 Q_D(KNewAccountDlg);
866 auto showButton = true;
867
868 if (d->ui->accountNameEdit->text().length() == 0) {
869 showButton = false;
870 }
871
872 if (d->ui->m_vatCategory->isChecked() && d->ui->m_vatRate->value() <= MyMoneyMoney()) {
873 showButton = false;
874 } else {
875 if (d->ui->m_vatAssignment->isChecked() && d->ui->m_vatAccount->selectedItems().isEmpty())
876 showButton = false;
877 }
878 d->ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(showButton);
879 }
880
slotVatChanged(bool state)881 void KNewAccountDlg::slotVatChanged(bool state)
882 {
883 Q_D(KNewAccountDlg);
884 if (state) {
885 d->ui->m_vatCategoryFrame->show();
886 d->ui->m_vatAssignmentFrame->hide();
887 } else {
888 d->ui->m_vatCategoryFrame->hide();
889 if (!d->m_account.isAssetLiability()) {
890 d->ui->m_vatAssignmentFrame->show();
891 }
892 }
893 }
894
slotVatAssignmentChanged(bool state)895 void KNewAccountDlg::slotVatAssignmentChanged(bool state)
896 {
897 Q_D(KNewAccountDlg);
898 d->ui->m_vatAccount->setEnabled(state);
899 d->ui->m_amountGroupBox->setEnabled(state);
900 }
901
slotAdjustMinBalanceAbsoluteEdit(const QString &)902 void KNewAccountDlg::slotAdjustMinBalanceAbsoluteEdit(const QString&)
903 {
904 Q_D(KNewAccountDlg);
905 d->adjustEditWidgets(d->ui->m_minBalanceAbsoluteEdit, d->ui->m_minBalanceEarlyEdit, '<', -1);
906 }
907
slotAdjustMinBalanceEarlyEdit(const QString &)908 void KNewAccountDlg::slotAdjustMinBalanceEarlyEdit(const QString&)
909 {
910 Q_D(KNewAccountDlg);
911 d->adjustEditWidgets(d->ui->m_minBalanceEarlyEdit, d->ui->m_minBalanceAbsoluteEdit, '>', -1);
912 }
913
slotAdjustMaxCreditAbsoluteEdit(const QString &)914 void KNewAccountDlg::slotAdjustMaxCreditAbsoluteEdit(const QString&)
915 {
916 Q_D(KNewAccountDlg);
917 d->adjustEditWidgets(d->ui->m_maxCreditAbsoluteEdit, d->ui->m_maxCreditEarlyEdit, '>', 1);
918 }
919
slotAdjustMaxCreditEarlyEdit(const QString &)920 void KNewAccountDlg::slotAdjustMaxCreditEarlyEdit(const QString&)
921 {
922 Q_D(KNewAccountDlg);
923 d->adjustEditWidgets(d->ui->m_maxCreditEarlyEdit, d->ui->m_maxCreditAbsoluteEdit, '<', 1);
924 }
925
slotCheckCurrency(int index)926 void KNewAccountDlg::slotCheckCurrency(int index)
927 {
928 Q_D(KNewAccountDlg);
929 Q_UNUSED(index)
930 d->handleOpeningBalanceCheckbox(d->ui->m_currency->security().id());
931 }
932
addTab(QWidget * w,const QString & name)933 void KNewAccountDlg::addTab(QWidget* w, const QString& name)
934 {
935 Q_D(KNewAccountDlg);
936 if (w) {
937 w->setParent(d->ui->m_tab);
938 d->ui->m_tab->addTab(w, name);
939 }
940 }
941
newCategory(MyMoneyAccount & account,const MyMoneyAccount & parent)942 void KNewAccountDlg::newCategory(MyMoneyAccount& account, const MyMoneyAccount& parent)
943 {
944 if (KMessageBox::questionYesNo(nullptr,
945 QString::fromLatin1("<qt>%1</qt>").arg(i18n("<p>The category <b>%1</b> currently does not exist. Do you want to create it?</p><p><i>The parent account will default to <b>%2</b> but can be changed in the following dialog</i>.</p>", account.name(), parent.name())), i18n("Create category"),
946 KStandardGuiItem::yes(), KStandardGuiItem::no(), "CreateNewCategories") == KMessageBox::Yes) {
947 KNewAccountDlg::createCategory(account, parent);
948 } else {
949 // we should not keep the 'no' setting because that can confuse people like
950 // I have seen in some usability tests. So we just delete it right away.
951 KSharedConfigPtr kconfig = KSharedConfig::openConfig();
952 if (kconfig) {
953 kconfig->group(QLatin1String("Notification Messages")).deleteEntry(QLatin1String("CreateNewCategories"));
954 }
955 }
956 }
957
createCategory(MyMoneyAccount & account,const MyMoneyAccount & parent)958 void KNewAccountDlg::createCategory(MyMoneyAccount& account, const MyMoneyAccount& parent)
959 {
960 if (!parent.id().isEmpty()) {
961 try {
962 // make sure parent account exists
963 MyMoneyFile::instance()->account(parent.id());
964 account.setParentAccountId(parent.id());
965 account.setAccountType(parent.accountType());
966 } catch (const MyMoneyException &) {
967 }
968 }
969
970 QPointer<KNewAccountDlg> dialog =
971 new KNewAccountDlg(account, false, true, 0, i18n("Create a new Category"));
972
973 dialog->setOpeningBalanceShown(false);
974 dialog->setOpeningDateShown(false);
975
976 if (dialog->exec() == QDialog::Accepted && dialog != 0) {
977 MyMoneyAccount parentAccount, brokerageAccount;
978 account = dialog->account();
979 parentAccount = dialog->parentAccount();
980
981 MyMoneyFile::instance()->createAccount(account, parentAccount, brokerageAccount, MyMoneyMoney());
982 }
983 delete dialog;
984 }
985