/*************************************************************************** knewloanwizard.cpp - description ------------------- begin : Wed Oct 8 2003 copyright : (C) 2000-2003 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales <javi_c@users.sourceforge.net> Felix Rodriguez <frodriguez@users.sourceforge.net> John C <thetacoturtle@users.sourceforge.net> Thomas Baumgart <ipwizard@users.sourceforge.net> Kevin Tambascio <ktambascio@users.sourceforge.net> ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #include <math.h> // ---------------------------------------------------------------------------- // QT Includes #include <tqradiobutton.h> #include <tqcheckbox.h> #include <tqlabel.h> // ---------------------------------------------------------------------------- // KDE Includes #include <klocale.h> #include <kpushbutton.h> #include <knuminput.h> #include <kmessagebox.h> #include <kiconloader.h> #include <kguiitem.h> // ---------------------------------------------------------------------------- // Project Includes #include "knewloanwizard.h" #include "../kmymoneyutils.h" #include <kmymoney/kmymoneylineedit.h> #include <kmymoney/kmymoneydateinput.h> #include <kmymoney/kmymoneyedit.h> #include <kmymoney/kmymoneyaccountselector.h> #include "../widgets/kmymoneycombo.h" #include "../dialogs/knewaccountdlg.h" #include "../dialogs/ksplittransactiondlg.h" #include "../mymoney/mymoneyfinancialcalculator.h" #include "../mymoney/mymoneyfile.h" #include "../kmymoney2.h" KNewLoanWizard::KNewLoanWizard(TQWidget *parent, const char *name ) : KNewLoanWizardDecl(parent, name, true) { connect(m_borrowButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotLiabilityLoan())); connect(m_lendButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotAssetLoan())); connect(m_nameEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotCheckPageFinished())); // connect(m_payeeEdit, TQT_SIGNAL(newPayee(const TQString&)), this, TQT_SLOT(slotNewPayee(const TQString&))); connect(m_payeeEdit, TQT_SIGNAL(createItem(const TQString&, TQString&)), this, TQT_SIGNAL(createPayee(const TQString&, TQString&))); connect(m_previousPaymentButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotPaymentsMade())); connect(m_noPreviousPaymentButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotNoPaymentsMade())); connect(m_allPaymentsButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotRecordAllPayments())); connect(m_thisYearPaymentButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotRecordThisYearsPayments())); connect(m_firstDueDateEdit, TQT_SIGNAL(dateChanged(const TQDate&)), this, TQT_SLOT(slotCheckPageFinished())); connect(m_interestOnPaymentButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotInterestOnPayment())); connect(m_interestOnReceptionButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotInterestOnReception())); connect(m_loanAmountEdit, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(slotCheckPageFinished())); connect(m_interestAccountEdit, TQT_SIGNAL(stateChanged()), this, TQT_SLOT(slotCheckPageFinished())); connect(m_nextDueDateEdit, TQT_SIGNAL(dateChanged(const TQDate&)), this, TQT_SLOT(slotCheckPageFinished())); connect(m_paymentAccountEdit, TQT_SIGNAL(stateChanged()), this, TQT_SLOT(slotCheckPageFinished())); connect(m_assetAccountEdit, TQT_SIGNAL(stateChanged()), this, TQT_SLOT(slotCheckPageFinished())); connect(m_dontCreatePayoutCheckBox, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCheckPageFinished())); connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotReloadEditWidgets())); loadComboBoxes(); resetCalculator(); slotReloadEditWidgets(); // As default we assume a liability loan, with fixed interest rate, // with a first payment due on the 30th of this month. All payments // should be recorded and none have been made so far. m_dontCreatePayoutCheckBox->setChecked(false); m_borrowButton->animateClick(); m_fixedInterestButton->animateClick(); m_noPreviousPaymentButton->animateClick(); m_allPaymentsButton->animateClick(); m_interestOnReceptionButton->animateClick(); m_interestFrequencyAmountEdit->setValue(1); m_interestFrequencyUnitEdit->setCurrentItem(static_cast<int>(MyMoneyAccountLoan::changeYearly)); m_paymentFrequencyUnitEdit->setCurrentItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_MONTHLY))); m_firstDueDateEdit->loadDate(TQDate(TQDate::currentDate().year(),TQDate::currentDate().month(),30)); m_paymentAccountEdit->removeButtons(); m_assetAccountEdit->removeButtons(); m_interestAccountEdit->removeButtons(); // load button icons KIconLoader* il = KGlobal::iconLoader(); KGuiItem createCategoryButtenItem( i18n( "&Create..." ), TQIconSet(il->loadIcon("filenew", KIcon::Small, KIcon::SizeSmall)), i18n("Create a new category"), i18n("Use this to open the new account editor")); m_createCategoryButton->setGuiItem(createCategoryButtenItem); connect(m_createCategoryButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotCreateCategory())); KGuiItem additionalFeeButtenItem( i18n( "&Additional fees..." ), 0, //TQIconSet(il->loadIcon("filenew", KIcon::Small, KIcon::SizeSmall)), i18n("Enter additional fees"), i18n("Use this to add any additional fees other than principal and interest contained in your periodical payments.")); m_additionalFeeButton->setGuiItem(additionalFeeButtenItem); connect(m_additionalFeeButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotAdditionalFees())); KGuiItem createAssetButtenItem( i18n( "&Create..." ), TQIconSet(il->loadIcon("filenew", KIcon::Small, KIcon::SizeSmall)), i18n("Create a new asset account"), i18n("Use this to create a new account to which the initial payment should be made")); m_createNewAssetButton->setGuiItem(createAssetButtenItem); connect(m_createNewAssetButton, TQT_SIGNAL(clicked()), kmymoney2, TQT_SLOT(slotAccountNew())); // enable the finish button on the last page setFinishEnabled(m_summaryPage, true); // FIXME: we currently only support interest calculation on reception setAppropriate(m_interestCalculationPage, false); // turn off all pages that are contained here for derived classes setAppropriate(m_editIntroPage, false); setAppropriate(m_editSelectionPage, false); setAppropriate(m_effectiveDatePage, false); setAppropriate(m_paymentEditPage, false); setAppropriate(m_interestEditPage, false); setAppropriate(m_summaryEditPage, false); // for now, we don't have online help :-( helpButton()->hide(); // setup a phony transaction for additional fee processing m_account = MyMoneyAccount("Phony-ID", MyMoneyAccount()); m_split.setAccountId(m_account.id()); m_split.setValue(0); m_transaction.addSplit(m_split); } KNewLoanWizard::~KNewLoanWizard() { } void KNewLoanWizard::resetCalculator(void) { m_loanAmount1->setText(TQString()); m_interestRate1->setText(TQString()); m_duration1->setText(TQString()); m_payment1->setText(TQString()); m_balloon1->setText(TQString()); m_loanAmount2->setText(TQString()); m_interestRate2->setText(TQString()); m_duration2->setText(TQString()); m_payment2->setText(TQString()); m_balloon2->setText(TQString()); m_loanAmount3->setText(TQString()); m_interestRate3->setText(TQString()); m_duration3->setText(TQString()); m_payment3->setText(TQString()); m_balloon3->setText(TQString()); m_loanAmount4->setText(TQString()); m_interestRate4->setText(TQString()); m_duration4->setText(TQString()); m_payment4->setText(TQString()); m_balloon4->setText(TQString()); m_loanAmount5->setText(TQString()); m_interestRate5->setText(TQString()); m_duration5->setText(TQString()); m_payment5->setText(TQString()); m_balloon5->setText(TQString()); m_additionalCost->setText(MyMoneyMoney(0).formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId())))); } void KNewLoanWizard::slotLiabilityLoan(void) { m_generalReceiverText->setText(i18n("To whom do you make payments?")); m_receiverLabel->setText(i18n("Payments to")); } void KNewLoanWizard::slotAssetLoan(void) { m_generalReceiverText->setText(i18n("From whom do you expect payments?")); m_receiverLabel->setText(i18n("Payments from")); } void KNewLoanWizard::slotPaymentsMade(void) { setAppropriate(m_recordPaymentPage, true); } void KNewLoanWizard::slotNoPaymentsMade(void) { m_allPaymentsButton->animateClick(); setAppropriate(m_recordPaymentPage, false); } void KNewLoanWizard::slotRecordAllPayments(void) { m_firstPaymentLabel->setText( TQString("\n") + i18n("Please enter the date, the first payment for this loan was/is due.")); m_firstPaymentNote->setText( i18n("Note: Consult the loan contract for details of the first due date. " "Keep in mind, that the first due date usually differs from the date " "the contract was signed")); m_balanceLabel->setText( TQString("\n") + i18n("Please enter the original loan amount in the field below or leave it " "empty to be calculated.")); } void KNewLoanWizard::slotRecordThisYearsPayments(void) { m_firstPaymentLabel->setText( TQString("\n") + i18n("Please enter the date, the first payment for this loan was/is due this year.")); m_firstPaymentNote->setText( i18n("Note: You can easily figure out the date of the first payment " "if you consult the last statement of last year.")); m_balanceLabel->setText( TQString("\n") + i18n("Please enter the remaining loan amount of last years final " "statement in the field below. You should not leave this field empty.")); } void KNewLoanWizard::slotCheckPageFinished(void) { nextButton()->setEnabled(false); if(currentPage() == m_namePage) { if(!m_nameEdit->text().isEmpty()) { nextButton()->setEnabled(true); } } else if(currentPage() == m_loanAmountPage) { nextButton()->setEnabled(true); if(m_thisYearPaymentButton->isChecked() && !m_loanAmountEdit->isValid()) { nextButton()->setEnabled(false); } } else if(currentPage() == m_interestCategoryPage) { if(m_interestAccountEdit->selectedItems().count() > 0) { nextButton()->setEnabled(true); } } else if(currentPage() == m_firstPaymentPage) { if(m_firstDueDateEdit->date().isValid()) nextButton()->setEnabled(true); } else if(currentPage() == m_schedulePage) { if(m_nextDueDateEdit->date().isValid() && m_nextDueDateEdit->date() >= m_firstDueDateEdit->date() && m_paymentAccountEdit->selectedItems().count() > 0) nextButton()->setEnabled(true); } else if(currentPage() == m_assetAccountPage) { if(m_dontCreatePayoutCheckBox->isChecked()) { m_assetAccountEdit->setEnabled(false); m_paymentDate->setEnabled(false); m_createNewAssetButton->setEnabled(false); nextButton()->setEnabled(true); } else { m_assetAccountEdit->setEnabled(true); m_paymentDate->setEnabled(true); m_createNewAssetButton->setEnabled(true); if(!m_assetAccountEdit->selectedItems().isEmpty() && m_paymentDate->date().isValid()) nextButton()->setEnabled(true); } } else nextButton()->setEnabled(true); } void KNewLoanWizard::updateLoanAmount(void) { TQString txt; if(m_loanAmountEdit->lineedit()->text().isEmpty()) { txt = TQString("<") + i18n("calculate") + TQString(">"); } else { txt = m_loanAmountEdit->value().formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId()))); } m_loanAmount1->setText(txt); m_loanAmount2->setText(txt); m_loanAmount3->setText(txt); m_loanAmount4->setText(txt); m_loanAmount5->setText(txt); } void KNewLoanWizard::updateInterestRate(void) { TQString txt; if(m_interestRateEdit->lineedit()->text().isEmpty()) { txt = TQString("<") + i18n("calculate") + TQString(">"); } else { txt = m_interestRateEdit->value().formatMoney("", 3) + TQString("%"); } m_interestRate1->setText(txt); m_interestRate2->setText(txt); m_interestRate3->setText(txt); m_interestRate4->setText(txt); m_interestRate5->setText(txt); } void KNewLoanWizard::updateDuration(void) { TQString txt; if(m_durationValueEdit->value() == 0) { txt = TQString("<") + i18n("calculate") + TQString(">"); } else { txt = TQString().sprintf("%d ", m_durationValueEdit->value()) + m_durationUnitEdit->currentText(); } m_duration1->setText(txt); m_duration2->setText(txt); m_duration3->setText(txt); m_duration4->setText(txt); m_duration5->setText(txt); } void KNewLoanWizard::updatePayment(void) { TQString txt; if(m_paymentEdit->lineedit()->text().isEmpty()) { txt = TQString("<") + i18n("calculate") + TQString(">"); } else { txt = m_paymentEdit->value().formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId()))); } m_payment1->setText(txt); m_payment2->setText(txt); m_payment3->setText(txt); m_payment4->setText(txt); m_payment5->setText(txt); m_basePayment->setText(txt); } void KNewLoanWizard::updateFinalPayment(void) { TQString txt; if(m_finalPaymentEdit->lineedit()->text().isEmpty()) { txt = TQString("<") + i18n("calculate") + TQString(">"); } else { txt = m_finalPaymentEdit->value().formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId()))); } m_balloon1->setText(txt); m_balloon2->setText(txt); m_balloon3->setText(txt); m_balloon4->setText(txt); m_balloon5->setText(txt); } void KNewLoanWizard::updateLoanInfo(void) { updateLoanAmount(); updateInterestRate(); updateDuration(); updatePayment(); updateFinalPayment(); updatePeriodicPayment(); TQString txt; int fraction = m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId())); m_loanAmount6->setText(m_loanAmountEdit->value().formatMoney(fraction)); m_interestRate6->setText(m_interestRateEdit->value().formatMoney("", 3) + TQString("%")); txt = TQString().sprintf("%d ", m_durationValueEdit->value()) + m_durationUnitEdit->currentText(); m_duration6->setText(txt); m_payment6->setText(m_paymentEdit->value().formatMoney(fraction)); m_balloon6->setText(m_finalPaymentEdit->value().formatMoney(fraction)); } void KNewLoanWizard::updatePeriodicPayment(void) { MyMoneyMoney base(m_basePayment->text()); MyMoneyMoney add(m_additionalCost->text()); m_periodicPayment->setText((base + add).formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId())))); } void KNewLoanWizard::updateSummary(void) { // General if(m_borrowButton->isChecked()) m_summaryLoanType->setText(i18n("borrowed")); else m_summaryLoanType->setText(i18n("lend")); m_summaryFirstPayment->setText(KGlobal::locale()->formatDate(m_firstDueDateEdit->date(), true)); if(m_payeeEdit->selectedItem().isEmpty()) { m_summaryPayee->setText(i18n("not assigned")); } else { m_summaryPayee->setText(m_payeeEdit->currentText()); } // Calculation if(m_interestOnReceptionButton->isChecked()) m_summaryInterestDue->setText(i18n("on reception")); else m_summaryInterestDue->setText(i18n("on due date")); m_summaryPaymentFrequency->setText(m_paymentFrequencyUnitEdit->currentText()); m_summaryAmount->setText(m_loanAmount6->text()); m_summaryInterestRate->setText(m_interestRate6->text()); m_summaryTerm->setText(m_duration6->text()); m_summaryPeriodicPayment->setText(m_payment6->text()); m_summaryBalloonPayment->setText(m_balloon6->text()); // Payment try { TQStringList sel = m_interestAccountEdit->selectedItems(); if(sel.count() != 1) throw new MYMONEYEXCEPTION("Need a single selected interest category"); MyMoneyAccount acc = MyMoneyFile::instance()->account(sel.first()); m_summaryInterestCategory->setText(acc.name()); } catch(MyMoneyException *e) { qWarning("Unable to determine interest category for loan account creation"); delete e; } m_summaryAdditionalFees->setText(m_additionalCost->text()); m_summaryTotalPeriodicPayment->setText(m_periodicPayment->text()); m_summaryNextPayment->setText(KGlobal::locale()->formatDate(m_nextDueDateEdit->date(), true)); try { TQStringList sel = m_paymentAccountEdit->selectedItems(); if(sel.count() != 1) throw new MYMONEYEXCEPTION("Need a single selected payment account"); MyMoneyAccount acc = MyMoneyFile::instance()->account(sel.first()); m_summaryPaymentAccount->setText(acc.name()); } catch(MyMoneyException *e) { qWarning("Unable to determine payment account for loan account creation"); delete e; } } void KNewLoanWizard::next() { bool dontLeavePage = false; TQString errMsg = i18n( "The loan wizard is unable to calculate two different values for your loan " "at the same time. " "Please enter a value for the %1 on this page or backup to the page where the " " current value to be calculated is defined and fill in a value."); if(currentPage() == m_lendBorrowPage) { // load the appropriate categories into the list loadAccountList(); m_nameEdit->setFocus(); } else if(currentPage() == m_interestTypePage) { if(m_fixedInterestButton->isChecked()) { setAppropriate(m_previousPaymentsPage, true); if(m_previousPaymentButton->isChecked()) setAppropriate(m_recordPaymentPage, true); else setAppropriate(m_recordPaymentPage, false); setAppropriate(m_variableInterestDatePage, false); } else { setAppropriate(m_previousPaymentsPage, false); setAppropriate(m_recordPaymentPage, false); setAppropriate(m_variableInterestDatePage, true); } } else if(currentPage() == m_loanAmountPage) { m_interestRateEdit->setFocus(); if(m_thisYearPaymentButton->isChecked() && m_loanAmountEdit->lineedit()->text().isEmpty()) { errMsg = i18n("You selected, that payments have already been made towards this loan. " "This requires you to enter the loan amount exactly as found on your " "last statement."); dontLeavePage = true; KMessageBox::error(0, errMsg, i18n("Calculation error")); } else updateLoanAmount(); } else if(currentPage() == m_interestPage) { if(m_loanAmountEdit->lineedit()->text().isEmpty() && m_interestRateEdit->lineedit()->text().isEmpty()) { dontLeavePage = true; KMessageBox::error(0, errMsg.arg(i18n("interest rate")), i18n("Calculation error")); } else updateInterestRate(); } else if(currentPage() == m_durationPage) { if((m_loanAmountEdit->lineedit()->text().isEmpty() || m_interestRateEdit->lineedit()->text().isEmpty()) && m_durationValueEdit->value() == 0) { dontLeavePage = true; KMessageBox::error(0, errMsg.arg(i18n("term")), i18n("Calculation error")); } else updateDuration(); } else if(currentPage() == m_paymentPage) { if((m_loanAmountEdit->lineedit()->text().isEmpty() || m_interestRateEdit->lineedit()->text().isEmpty() || m_durationValueEdit->value() == 0) && m_paymentEdit->lineedit()->text().isEmpty()) { dontLeavePage = true; KMessageBox::error(0, errMsg.arg(i18n("principal and interest")), i18n("Calculation error")); } else updatePayment(); } else if(currentPage() == m_finalPaymentPage) { if((m_loanAmountEdit->lineedit()->text().isEmpty() || m_interestRateEdit->lineedit()->text().isEmpty() || m_durationValueEdit->value() == 0 || m_paymentEdit->lineedit()->text().isEmpty()) && m_finalPaymentEdit->lineedit()->text().isEmpty()) { // if two fields are empty and one of them is the final payment // we assume the final payment to be 0 instead of presenting a m_finalPaymentEdit->setValue(MyMoneyMoney(0, 1)); } updateFinalPayment(); if(!calculateLoan()) { dontLeavePage = true; } else updateLoanInfo(); } else if(currentPage() == m_additionalFeesPage) { m_nextDueDateEdit->setEnabled(true); if(m_allPaymentsButton->isChecked() || m_noPreviousPaymentButton->isChecked()) { m_nextDueDateEdit->setDate(m_firstDueDateEdit->date()); m_nextDueDateEdit->setEnabled(false); if(m_assetAccountPage) setAppropriate(m_assetAccountPage, true); } else { TQDate nextPayment(TQDate::currentDate().year(), 1, m_firstDueDateEdit->date().day()); m_nextDueDateEdit->setDate(nextPayment); if(m_assetAccountPage) setAppropriate(m_assetAccountPage, false); m_assetAccountEdit->slotDeselectAllAccounts(); } if(m_nextDueDateEdit->date() < m_firstDueDateEdit->date()) { m_nextDueDateEdit->setDate(m_firstDueDateEdit->date()); } } else if(currentPage() == m_schedulePage) { updateSummary(); } /* switch(m_accountType) { case MyMoneyAccount::Cash: case MyMoneyAccount::Asset: if(indexOf(accountPaymentPage) != -1) { removePage(accountPaymentPage); } setAppropriate(accountNumberPage, false); setFinishEnabled(accountDetailsPage, true); break; case MyMoneyAccount::CreditCard: if(indexOf(accountPaymentPage) == -1) { loadPaymentMethods(); addPage(accountPaymentPage, m_accountPaymentPageTitle); } setAppropriate(accountPaymentPage, true); setFinishEnabled(accountPaymentPage, true); setFinishEnabled(accountDetailsPage, false); break; default: setAppropriate(accountNumberPage, institutionComboBox->currentText() != ""); if(indexOf(accountPaymentPage) != -1) { removePage(accountPaymentPage); } setFinishEnabled(accountDetailsPage, true); break; } */ if(!dontLeavePage) KNewLoanWizardDecl::next(); // setup the availability of widgets on the selected page slotCheckPageFinished(); } void KNewLoanWizard::loadComboBoxes(void) { m_interestFrequencyUnitEdit->insertItem(i18n("Days"), static_cast<int>(MyMoneyAccountLoan::changeDaily)); m_interestFrequencyUnitEdit->insertItem(i18n("Weeks"), static_cast<int>(MyMoneyAccountLoan::changeWeekly)); m_interestFrequencyUnitEdit->insertItem(i18n("Months"), static_cast<int>(MyMoneyAccountLoan::changeMonthly)); m_interestFrequencyUnitEdit->insertItem(i18n("Years"), static_cast<int>(MyMoneyAccountLoan::changeYearly)); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_DAILY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_WEEKLY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_FORTNIGHTLY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYOTHERWEEK))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYHALFMONTH))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYTHREEWEEKS))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYFOURWEEKS))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_MONTHLY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYOTHERMONTH))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_TQUARTERLY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_EVERYFOURMONTHS))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_TWICEYEARLY))); m_paymentFrequencyUnitEdit->insertItem(i18n(MyMoneySchedule::occurenceToString(MyMoneySchedule::OCCUR_YEARLY))); m_durationUnitEdit->insertItem(i18n("Months"), static_cast<int>(MyMoneySchedule::OCCUR_MONTHLY)); m_durationUnitEdit->insertItem(i18n("Years"), static_cast<int>(MyMoneySchedule::OCCUR_YEARLY)); m_durationUnitEdit->insertItem(i18n("Payments"), static_cast<int>(MyMoneySchedule::OCCUR_ONCE)); } void KNewLoanWizard::slotInterestOnPayment(void) { m_interestOnPaymentButton->setChecked(true); m_interestOnReceptionButton->setChecked(false); } void KNewLoanWizard::slotInterestOnReception(void) { m_interestOnPaymentButton->setChecked(false); m_interestOnReceptionButton->setChecked(true); } int KNewLoanWizard::calculateLoan(void) { MyMoneyFinancialCalculator calc; long double val; int PF; TQString result; // FIXME: for now, we only support interest calculation at the end of the period calc.setBep(); // FIXME: for now, we only support periodic compounding calc.setDisc(); PF = MyMoneySchedule::eventsPerYear(MyMoneySchedule::stringToOccurence( m_paymentFrequencyUnitEdit->currentText())); if(PF == 0) return 0; calc.setPF(PF); // FIXME: for now we only support compounding frequency == payment frequency calc.setCF(PF); if(!m_loanAmountEdit->lineedit()->text().isEmpty()) { val = static_cast<long double> (m_loanAmountEdit->value().abs().toDouble()); if(m_borrowButton->isChecked()) val = -val; calc.setPv(val); } if(!m_interestRateEdit->lineedit()->text().isEmpty()) { val = static_cast<long double> (m_interestRateEdit->value().abs().toDouble()); calc.setIr(val); } if(!m_paymentEdit->lineedit()->text().isEmpty()) { val = static_cast<long double> (m_paymentEdit->value().abs().toDouble()); if(m_lendButton->isChecked()) val = -val; calc.setPmt(val); } if(!m_finalPaymentEdit->lineedit()->text().isEmpty()) { val = static_cast<long double> (m_finalPaymentEdit->value().abs().toDouble()); if(m_lendButton->isChecked()) val = -val; calc.setFv(val); } if(m_durationValueEdit->value() != 0) { calc.setNpp(static_cast<long double>(term())); } int fraction = m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId())); // setup of parameters is done, now do the calculation try { if(m_loanAmountEdit->lineedit()->text().isEmpty()) { // calculate the amount of the loan out of the other information val = calc.presentValue(); m_loanAmountEdit->loadText(MyMoneyMoney(static_cast<double>(val)).abs().formatMoney(fraction)); result = i18n("KMyMoney has calculated the amount of the loan as %1.") .arg(m_loanAmountEdit->lineedit()->text()); } else if(m_interestRateEdit->lineedit()->text().isEmpty()) { // calculate the interest rate out of the other information val = calc.interestRate(); m_interestRateEdit->loadText(MyMoneyMoney(static_cast<double>(val)).abs().formatMoney("", 3)); result = i18n("KMyMoney has calculated the interest rate to %1%.") .arg(m_interestRateEdit->lineedit()->text()); } else if(m_paymentEdit->lineedit()->text().isEmpty()) { // calculate the periodical amount of the payment out of the other information val = calc.payment(); m_paymentEdit->setValue(MyMoneyMoney(static_cast<double>(val)).abs()); // reset payment as it might have changed due to rounding val = static_cast<long double> (m_paymentEdit->value().abs().toDouble()); if(m_lendButton->isChecked()) val = -val; calc.setPmt(val); result = i18n("KMyMoney has calculated a periodic payment of %1 to cover principal and interest.") .arg(m_paymentEdit->lineedit()->text()); val = calc.futureValue(); if((m_borrowButton->isChecked() && val < 0 && fabsl(val) >= fabsl(calc.payment())) || (m_lendButton->isChecked() && val > 0 && fabs(val) >= fabs(calc.payment()))) { calc.setNpp(calc.npp()-1); updateTermWidgets(calc.npp()); val = calc.futureValue(); MyMoneyMoney refVal(static_cast<double>(val)); m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction)); result += TQString(" "); result += i18n("The number of payments has been decremented and the final payment has been modified to %1.") .arg(m_finalPaymentEdit->lineedit()->text()); } else if((m_borrowButton->isChecked() && val < 0 && fabsl(val) < fabsl(calc.payment())) || (m_lendButton->isChecked() && val > 0 && fabs(val) < fabs(calc.payment()))) { m_finalPaymentEdit->loadText(MyMoneyMoney(0,1).formatMoney(fraction)); } else { MyMoneyMoney refVal(static_cast<double>(val)); m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction)); result += i18n("The final payment has been modified to %1.") .arg(m_finalPaymentEdit->lineedit()->text()); } } else if(m_durationValueEdit->value() == 0) { // calculate the number of payments out of the other information val = calc.numPayments(); if(val == 0) throw new MYMONEYEXCEPTION("incorrect fincancial calculation"); // if the number of payments has a fractional part, then we // round it to the smallest integer and calculate the balloon payment result = i18n("KMyMoney has calculated the term of your loan as %1. ") .arg(updateTermWidgets(floorl(val))); if(val != floorl(val)) { calc.setNpp(floorl(val)); val = calc.futureValue(); MyMoneyMoney refVal(static_cast<double>(val)); m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction)); result += i18n("The final payment has been modified to %1.") .arg(m_finalPaymentEdit->lineedit()->text()); } } else { // calculate the future value of the loan out of the other information val = calc.futureValue(); // we differentiate between the following cases: // a) the future value is greater than a payment // b) the future value is less than a payment or the loan is overpaid // c) all other cases // // a) means, we have paid more than we owed. This can't be // b) means, we paid more than we owed but the last payment is // less in value than regular payments. That means, that the // future value is to be treated as (fully payed back) // c) the loan is not payed back yet if((m_borrowButton->isChecked() && val < 0 && fabsl(val) > fabsl(calc.payment())) || (m_lendButton->isChecked() && val > 0 && fabs(val) > fabs(calc.payment()))) { // case a) qDebug("Future Value is %Lf", val); throw new MYMONEYEXCEPTION("incorrect fincancial calculation"); } else if((m_borrowButton->isChecked() && val < 0 && fabsl(val) <= fabsl(calc.payment())) || (m_lendButton->isChecked() && val > 0 && fabs(val) <= fabs(calc.payment()))) { // case b) val = 0; } MyMoneyMoney refVal(static_cast<double>(val)); result = i18n("KMyMoney has calculated a final payment of %1 for this loan.") .arg(refVal.abs().formatMoney(fraction)); if(!m_finalPaymentEdit->lineedit()->text().isEmpty()) { if((m_finalPaymentEdit->value().abs() - refVal.abs()).abs().toDouble() > 1) { throw new MYMONEYEXCEPTION("incorrect fincancial calculation"); } result = i18n("KMyMoney has successfully verified your loan information."); } m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction)); } } catch (MyMoneyException *e) { delete e; KMessageBox::error(0, i18n("You have entered mis-matching information. Please backup to the " "appropriate page and update your figures or leave one value empty " "to let KMyMoney calculate it for you"), i18n("Calculation error")); return 0; } result += i18n("\n\nAccept this or modify the loan information and recalculate."); KMessageBox::information(0, result, i18n("Calculation successful")); return 1; } TQString KNewLoanWizard::updateTermWidgets(const long double val) { long long vl = static_cast<long long>(floorl(val)); TQString valString; MyMoneySchedule::occurenceE unit; unit = MyMoneySchedule::stringToOccurence(m_paymentFrequencyUnitEdit->currentText()); if((unit == MyMoneySchedule::OCCUR_MONTHLY) && ((vl % 12) == 0)) { vl /= 12; unit = MyMoneySchedule::OCCUR_YEARLY; } switch(unit) { case MyMoneySchedule::OCCUR_MONTHLY: valString = i18n("one month", "%n months", vl); m_durationUnitEdit->setCurrentItem(static_cast<int>(MyMoneySchedule::OCCUR_MONTHLY)); break; case MyMoneySchedule::OCCUR_YEARLY: valString = i18n("one year", "%n years", vl); m_durationUnitEdit->setCurrentItem(static_cast<int>(MyMoneySchedule::OCCUR_YEARLY)); break; default: valString = i18n("one payment", "%n payments", vl); m_durationUnitEdit->setCurrentItem(static_cast<int>(MyMoneySchedule::OCCUR_ONCE)); break; } m_durationValueEdit->setValue(vl); return valString; } void KNewLoanWizard::slotCreateCategory(void) { MyMoneyAccount acc, base; MyMoneyFile* file = MyMoneyFile::instance(); if(m_borrowButton->isChecked()) { base = file->expense(); acc.setAccountType(MyMoneyAccount::Expense); } else { base = file->income(); acc.setAccountType(MyMoneyAccount::Income); } acc.setParentAccountId(base.id()); KNewAccountDlg* dlg = new KNewAccountDlg(acc, true, true); if(dlg->exec() == TQDialog::Accepted) { acc = dlg->account(); MyMoneyFileTransaction ft; try { TQString id; id = file->createCategory(base, acc.name()); if(id.isEmpty()) throw new MYMONEYEXCEPTION("failure while creating the account hierarchy"); ft.commit(); m_interestAccountEdit->setSelected(id); } catch (MyMoneyException *e) { KMessageBox::information(this, i18n("Unable to add account: %1").arg(e->what())); delete e; } } delete dlg; } void KNewLoanWizard::loadAccountList(void) { AccountSet interestSet, assetSet; if(m_borrowButton->isChecked()) { interestSet.addAccountType(MyMoneyAccount::Expense); } else { interestSet.addAccountType(MyMoneyAccount::Income); } interestSet.load(m_interestAccountEdit); assetSet.addAccountType(MyMoneyAccount::Checkings); assetSet.addAccountType(MyMoneyAccount::Savings); assetSet.addAccountType(MyMoneyAccount::Cash); assetSet.addAccountType(MyMoneyAccount::Asset); assetSet.addAccountType(MyMoneyAccount::Currency); assetSet.load(m_assetAccountEdit); assetSet.addAccountType(MyMoneyAccount::CreditCard); assetSet.addAccountType(MyMoneyAccount::Liability); assetSet.load(m_paymentAccountEdit); } void KNewLoanWizard::slotAdditionalFees(void) { // KMessageBox::information(0, TQString("Not yet implemented ... if you want to help, contact kmymoney2-developer@lists.sourceforge.net"), TQString("Development notice")); MyMoneyAccount account("Phony-ID", MyMoneyAccount()); TQMap<TQString, MyMoneyMoney> priceInfo; KSplitTransactionDlg* dlg = new KSplitTransactionDlg(m_transaction, m_split, account, false, !m_borrowButton->isChecked(), MyMoneyMoney(0), priceInfo); connect(dlg, TQT_SIGNAL(newCategory(MyMoneyAccount&)), this, TQT_SIGNAL(newCategory(MyMoneyAccount&))); if(dlg->exec() == TQDialog::Accepted) { m_transaction = dlg->transaction(); // sum up the additional fees TQValueList<MyMoneySplit>::ConstIterator it; MyMoneyMoney fees; for(it = m_transaction.splits().begin(); it != m_transaction.splits().end(); ++it) { if((*it).accountId() != account.id()) { fees += (*it).value(); } } m_additionalCost->setText(fees.formatMoney(m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId())))); } delete dlg; updatePeriodicPayment(); } MyMoneyTransaction KNewLoanWizard::transaction() const { MyMoneyTransaction t; MyMoneySplit sPayment, sInterest, sAmortization; // setup accounts. at this point, we cannot fill in the id of the // account that the amortization will be performed on, because we // create the account. So the id is yet unknown. sPayment.setAccountId(m_paymentAccountEdit->selectedItems().first()); sInterest.setAccountId(m_interestAccountEdit->selectedItems().first()); // values if(m_borrowButton->isChecked()) { sPayment.setValue(-m_paymentEdit->value()); } else { sPayment.setValue(m_paymentEdit->value()); } sInterest.setValue(MyMoneyMoney::autoCalc); sAmortization.setValue(MyMoneyMoney::autoCalc); // don't forget the shares sPayment.setShares(sPayment.value()); sInterest.setShares(sInterest.value()); sAmortization.setShares(sAmortization.value()); // setup the commodity MyMoneyAccount acc = MyMoneyFile::instance()->account(sPayment.accountId()); t.setCommodity(acc.currencyId()); // actions sPayment.setAction(MyMoneySplit::ActionAmortization); sAmortization.setAction(MyMoneySplit::ActionAmortization); sInterest.setAction(MyMoneySplit::ActionInterest); // payee TQString payeeId = m_payeeEdit->selectedItem(); sPayment.setPayeeId(payeeId); sAmortization.setPayeeId(payeeId); MyMoneyAccount account("Phony-ID", MyMoneyAccount()); sAmortization.setAccountId(account.id()); // IMPORTANT: Payment split must be the first one, because // the schedule view expects it this way during display t.addSplit(sPayment); t.addSplit(sAmortization); t.addSplit(sInterest); // copy the splits from the other costs and update the payment split TQValueList<MyMoneySplit>::ConstIterator it; for(it = m_transaction.splits().begin(); it != m_transaction.splits().end(); ++it) { if((*it).accountId() != account.id()) { MyMoneySplit sp = (*it); sp.clearId(); t.addSplit(sp); sPayment.setValue(sPayment.value()-sp.value()); sPayment.setShares(sPayment.value()); t.modifySplit(sPayment); } } return t; } MyMoneySchedule KNewLoanWizard::schedule(void) const { MyMoneySchedule sched(m_nameEdit->text(), MyMoneySchedule::TYPE_LOANPAYMENT, MyMoneySchedule::stringToOccurence(m_paymentFrequencyUnitEdit->currentText()), 1, MyMoneySchedule::STYPE_OTHER, TQDate(), TQDate(), false, false); MyMoneyTransaction t = transaction(); t.setPostDate(m_nextDueDateEdit->date()); sched.setTransaction(t); return sched; } void KNewLoanWizard::slotReloadEditWidgets(void) { // load the various account widgets loadAccountList(); // reload payee widget TQString payeeId = m_payeeEdit->selectedItem(); m_payeeEdit->loadPayees(MyMoneyFile::instance()->payeeList()); if(!payeeId.isEmpty()) { m_payeeEdit->setSelectedItem(payeeId); } } int KNewLoanWizard::term(void) const { int factor = 0; if(m_durationValueEdit->value() != 0) { factor = 1; switch(m_durationUnitEdit->currentItem()) { case MyMoneySchedule::OCCUR_YEARLY: // years factor = 12; // tricky fall through here case MyMoneySchedule::OCCUR_MONTHLY: // months factor *= 30; factor *= m_durationValueEdit->value(); // factor now is the duration in days. we divide this by the // payment frequency and get the number of payments factor /= MyMoneySchedule::daysBetweenEvents(MyMoneySchedule::stringToOccurence( m_paymentFrequencyUnitEdit->currentText())); break; case MyMoneySchedule::OCCUR_ONCE: // payments factor = m_durationValueEdit->value(); break; } } return factor; } TQString KNewLoanWizard::initialPaymentAccount(void) const { if(m_dontCreatePayoutCheckBox->isChecked()) { return TQString(); } return m_assetAccountEdit->selectedItems().first(); } TQDate KNewLoanWizard::initialPaymentDate(void) const { if(m_dontCreatePayoutCheckBox->isChecked()) { return TQDate(); } return m_paymentDate->date(); } #include "knewloanwizard.moc"