Пример #1
0
void MyMoneyForecast::calculateAutoLoan(const MyMoneySchedule& schedule, MyMoneyTransaction& transaction, const QMap<QString, MyMoneyMoney>& balances)
{
  if (schedule.type() == MyMoneySchedule::TYPE_LOANPAYMENT) {

    //get amortization and interest autoCalc splits
    MyMoneySplit amortizationSplit = transaction.amortizationSplit();
    MyMoneySplit interestSplit = transaction.interestSplit();

    if(!amortizationSplit.id().isEmpty() && !interestSplit.id().isEmpty()) {
      MyMoneyAccountLoan acc(MyMoneyFile::instance()->account(amortizationSplit.accountId()));
      MyMoneyFinancialCalculator calc;
      QDate dueDate;

      // FIXME: setup dueDate according to when the interest should be calculated
      // current implementation: take the date of the next payment according to
      // the schedule. If the calculation is based on the payment reception, and
      // the payment is overdue then take the current date
      dueDate = schedule.nextDueDate();
      if(acc.interestCalculation() == MyMoneyAccountLoan::paymentReceived) {
        if(dueDate < QDate::currentDate())
          dueDate = QDate::currentDate();
      }

      // we need to calculate the balance at the time the payment is due

      MyMoneyMoney balance;
      if(balances.count() == 0)
        balance = MyMoneyFile::instance()->balance(acc.id(), dueDate.addDays(-1));
      else
        balance = balances[acc.id()];

      /*
         QValueList<MyMoneyTransaction> list;
         QValueList<MyMoneyTransaction>::ConstIterator it;
         MyMoneySplit split;
         MyMoneyTransactionFilter filter(acc.id());

         filter.setDateFilter(QDate(), dueDate.addDays(-1));
         list = MyMoneyFile::instance()->transactionList(filter);

         for(it = list.begin(); it != list.end(); ++it) {
         try {
         split = (*it).splitByAccount(acc.id());
         balance += split.value();

         } catch(MyMoneyException *e) {
      // account is not referenced within this transaction
      delete e;
      }
      }
      */

      // 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();

      calc.setPF(MyMoneySchedule::eventsPerYear(schedule.occurence()));
      MyMoneySchedule::occurenceE compoundingOccurence = static_cast<MyMoneySchedule::occurenceE>(acc.interestCompounding());
      if(compoundingOccurence == MyMoneySchedule::OCCUR_ANY)
        compoundingOccurence = schedule.occurence();

      calc.setCF(MyMoneySchedule::eventsPerYear(compoundingOccurence));

      calc.setPv(balance.toDouble());
      calc.setIr(static_cast<FCALC_DOUBLE> (acc.interestRate(dueDate).abs().toDouble()));
      calc.setPmt(acc.periodicPayment().toDouble());

      MyMoneyMoney interest(calc.interestDue()), amortization;
      interest = interest.abs();    // make sure it's positive for now
      amortization = acc.periodicPayment() - interest;

      if(acc.accountType() == MyMoneyAccount::AssetLoan) {
        interest = -interest;
        amortization = -amortization;
      }

      amortizationSplit.setShares(amortization);
      interestSplit.setShares(interest);

      // FIXME: for now we only assume loans to be in the currency of the transaction
      amortizationSplit.setValue(amortization);
      interestSplit.setValue(interest);

      transaction.modifySplit(amortizationSplit);
      transaction.modifySplit(interestSplit);
    }
  }
}
Пример #2
0
int KNewLoanWizard::calculateLoan()
{
  MyMoneyFinancialCalculator calc;
  double val;
  int PF;
  QString 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::occurrenceE(field("paymentFrequencyUnitEdit").toInt()));
  if (PF == 0)
    return 0;
  calc.setPF(PF);

  // FIXME: for now we only support compounding frequency == payment frequency
  calc.setCF(PF);

  if (field("loanAmountEditValid").toBool()) {
    val = field("loanAmountEdit").value<MyMoneyMoney>().abs().toDouble();
    if (field("borrowButton").toBool())
      val = -val;
    calc.setPv(val);
  }

  if (field("interestRateEditValid").toBool()) {
    val = field("interestRateEdit").value<MyMoneyMoney>().abs().toDouble();
    calc.setIr(val);
  }

  if (field("paymentEditValid").toBool()) {
    val = field("paymentEdit").value<MyMoneyMoney>().abs().toDouble();
    if (field("lendButton").toBool())
      val = -val;
    calc.setPmt(val);
  }

  if (field("finalPaymentEditValid").toBool()) {
    val = field("finalPaymentEditValid").value<MyMoneyMoney>().abs().toDouble();
    if (field("lendButton").toBool())
      val = -val;
    calc.setFv(val);
  }

  if (field("durationValueEdit").toInt() != 0) {
    calc.setNpp(m_durationPage->term());
  }

  int fraction = m_account.fraction(MyMoneyFile::instance()->security(m_account.currencyId()));
  // setup of parameters is done, now do the calculation
  try {
    //FIXME: port
    if (!field("loanAmountEditValid").toBool()) {
      // calculate the amount of the loan out of the other information
      val = calc.presentValue();
      m_loanAmountPage->m_loanAmountEdit->loadText(MyMoneyMoney(static_cast<double>(val)).abs().formatMoney(fraction));
      result = i18n("KMyMoney has calculated the amount of the loan as %1.", m_loanAmountPage->m_loanAmountEdit->lineedit()->text());

    } else if (!field("interestRateEditValid").toBool()) {
      // calculate the interest rate out of the other information
      val = calc.interestRate();

      m_interestPage->m_interestRateEdit->loadText(MyMoneyMoney(static_cast<double>(val)).abs().formatMoney("", 3));
      result = i18n("KMyMoney has calculated the interest rate to %1%.", m_interestPage->m_interestRateEdit->lineedit()->text());

    } else if (!field("paymentEditValid").toBool()) {
      // calculate the periodical amount of the payment out of the other information
      val = calc.payment();
      setField("paymentEdit", QVariant::fromValue<MyMoneyMoney>(MyMoneyMoney(val).abs()));
      // reset payment as it might have changed due to rounding
      val = field("paymentEdit").value<MyMoneyMoney>().abs().toDouble();
      if (field("lendButton").toBool())
        val = -val;
      calc.setPmt(val);

      result = i18n("KMyMoney has calculated a periodic payment of %1 to cover principal and interest.", m_paymentPage->m_paymentEdit->lineedit()->text());

      val = calc.futureValue();
      if ((field("borrowButton").toBool() && val < 0 && qAbs(val) >= qAbs(calc.payment()))
          || (field("lendButton").toBool() && val > 0 && qAbs(val) >= qAbs(calc.payment()))) {
        calc.setNpp(calc.npp() - 1);
        m_durationPage->updateTermWidgets(calc.npp());
        val = calc.futureValue();
        MyMoneyMoney refVal(static_cast<double>(val));
        m_finalPaymentPage->m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction));
        result += QString(" ");
        result += i18n("The number of payments has been decremented and the final payment has been modified to %1.", m_finalPaymentPage->m_finalPaymentEdit->lineedit()->text());
      } else if ((field("borrowButton").toBool() && val < 0 && qAbs(val) < qAbs(calc.payment()))
                 || (field("lendButton").toBool() && val > 0 && qAbs(val) < qAbs(calc.payment()))) {
        m_finalPaymentPage->m_finalPaymentEdit->loadText(MyMoneyMoney().formatMoney(fraction));
      } else {
        MyMoneyMoney refVal(static_cast<double>(val));
        m_finalPaymentPage->m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction));
        result += i18n("The final payment has been modified to %1.", m_finalPaymentPage->m_finalPaymentEdit->lineedit()->text());
      }

    } else if (field("durationValueEdit").toInt() == 0) {
      // calculate the number of payments out of the other information
      val = calc.numPayments();
      if (val == 0)
        throw 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. ", m_durationPage->updateTermWidgets(qFloor(val)));

      if (val != qFloor(val)) {
        calc.setNpp(qFloor(val));
        val = calc.futureValue();
        MyMoneyMoney refVal(static_cast<double>(val));
        m_finalPaymentPage->m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction));
        result += i18n("The final payment has been modified to %1.", m_finalPaymentPage->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 ((field("borrowButton").toBool() && val < 0 && qAbs(val) > qAbs(calc.payment()))
          || (field("lendButton").toBool() && val > 0 && qAbs(val) > qAbs(calc.payment()))) {
        // case a)
        qDebug("Future Value is %f", val);
        throw MYMONEYEXCEPTION("incorrect fincancial calculation");

      } else if ((field("borrowButton").toBool() && val < 0 && qAbs(val) <= qAbs(calc.payment()))
                 || (field("lendButton").toBool() && val > 0 && qAbs(val) <= qAbs(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.", refVal.abs().formatMoney(fraction));

      if (field("finalPaymentEditValid").toBool()) {
        if ((field("finalPaymentEdit").value<MyMoneyMoney>().abs() - refVal.abs()).abs().toDouble() > 1) {
          throw MYMONEYEXCEPTION("incorrect fincancial calculation");
        }
        result = i18n("KMyMoney has successfully verified your loan information.");
      }
      //FIXME: port
      m_finalPaymentPage->m_finalPaymentEdit->loadText(refVal.abs().formatMoney(fraction));
    }

  } catch (const MyMoneyException &) {
    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;
}