MyMoneyInstitution::MyMoneyInstitution(const QDomElement& node) :
    MyMoneyObject(node),
    MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement())
{
  if ("INSTITUTION" != node.tagName())
    throw MYMONEYEXCEPTION("Node was not INSTITUTION");

  m_sortcode = node.attribute("sortcode");
  m_name = node.attribute("name");
  m_manager = node.attribute("manager");

  QDomNodeList nodeList = node.elementsByTagName("ADDRESS");
  if (nodeList.count() == 0) {
    QString msg = QString("No ADDRESS in institution %1").arg(m_name);
    throw MYMONEYEXCEPTION(msg);
  }

  QDomElement addrNode = nodeList.item(0).toElement();
  m_street = addrNode.attribute("street");
  m_town = addrNode.attribute("city");
  m_postcode = addrNode.attribute("zip");
  m_telephone = addrNode.attribute("telephone");

  m_accountList.clear();

  nodeList = node.elementsByTagName("ACCOUNTIDS");
  if (nodeList.count() > 0) {
    nodeList = nodeList.item(0).toElement().elementsByTagName("ACCOUNTID");
    for (int i = 0; i < nodeList.count(); ++i) {
      m_accountList << nodeList.item(i).toElement().attribute("id");
    }
  }
}
void SummaryWizardPage::initializePage()
{
  // General
  if (field("borrowButton").toBool())
    m_summaryLoanType->setText(i18n("borrowed"));
  else
    m_summaryLoanType->setText(i18n("lend"));

  m_summaryFirstPayment->setText(KGlobal::locale()->formatDate(field("firstDueDateEdit").toDate()));

  const QString &payeeId = field("payeeEdit").toString();
  if (!payeeId.isEmpty()) {
    try {
      const MyMoneyPayee &payee = MyMoneyFile::instance()->payee(payeeId);
      m_summaryPayee->setText(payee.name());
    } catch (const MyMoneyException &) {
      qWarning("Unable to load the payee name from the id");
    }
  } else {
    m_summaryPayee->setText(i18n("not assigned"));
  }

  // Calculation
  if (field("interestOnReceptionButton").toBool())
    m_summaryInterestDue->setText(i18n("on reception"));
  else
    m_summaryInterestDue->setText(i18n("on due date"));
  m_summaryPaymentFrequency->setText(MyMoneySchedule::occurrenceToString(MyMoneySchedule::occurrenceE(field("paymentFrequencyUnitEdit").toInt())));
  m_summaryAmount->setText(field("loanAmount6").toString());
  m_summaryInterestRate->setText(field("interestRate6").toString());
  m_summaryTerm->setText(field("duration6").toString());
  m_summaryPeriodicPayment->setText(field("payment6").toString());
  m_summaryBalloonPayment->setText(field("balloon6").toString());

  // Payment
  try {
    QStringList sel = field("interestAccountEdit").toStringList();
    if (sel.count() != 1)
      throw MYMONEYEXCEPTION("Need a single selected interest category");
    MyMoneyAccount acc = MyMoneyFile::instance()->account(sel.first());
    m_summaryInterestCategory->setText(acc.name());
  } catch (const MyMoneyException &) {
    qWarning("Unable to determine interest category for loan account creation");
  }
  m_summaryAdditionalFees->setText(field("additionalCost").toString());
  m_summaryTotalPeriodicPayment->setText(field("periodicPayment").toString());
  m_summaryNextPayment->setText(KGlobal::locale()->formatDate(field("nextDueDateEdit").toDate()));

  try {
    QStringList sel = field("paymentAccountEdit").toStringList();
    if (sel.count() != 1)
      throw MYMONEYEXCEPTION("Need a single selected payment account");
    MyMoneyAccount acc = MyMoneyFile::instance()->account(sel.first());
    m_summaryPaymentAccount->setText(acc.name());
  } catch (const MyMoneyException &) {
    qWarning("Unable to determine payment account for loan account creation");
  }

}
Exemple #3
0
MyMoneySplit::MyMoneySplit(const QDomElement& node) :
    MyMoneyObject(node, false),
    MyMoneyKeyValueContainer(node.elementsByTagName("KEYVALUEPAIRS").item(0).toElement())
{
  if ("SPLIT" != node.tagName())
    throw MYMONEYEXCEPTION("Node was not SPLIT");

  clearId();

  m_payee = QStringEmpty(node.attribute("payee"));

  QDomNodeList nodeList = node.elementsByTagName("TAG");
  for (int i = 0; i < nodeList.count(); i++)
    m_tagList << QStringEmpty(nodeList.item(i).toElement().attribute("id"));

  m_reconcileDate = stringToDate(QStringEmpty(node.attribute("reconciledate")));
  m_action = QStringEmpty(node.attribute("action"));
  m_reconcileFlag = static_cast<MyMoneySplit::reconcileFlagE>(node.attribute("reconcileflag").toInt());
  m_memo = QStringEmpty(node.attribute("memo"));
  m_value = MyMoneyMoney(QStringEmpty(node.attribute("value")));
  m_shares = MyMoneyMoney(QStringEmpty(node.attribute("shares")));
  m_price = MyMoneyMoney(QStringEmpty(node.attribute("price")));
  m_account = QStringEmpty(node.attribute("account"));
  m_number = QStringEmpty(node.attribute("number"));
  m_bankID = QStringEmpty(node.attribute("bankid"));
}
MyMoneyKeyValueContainer::MyMoneyKeyValueContainer(const QDomElement& node)
{
  if (!node.isNull()) {
    if ("KEYVALUEPAIRS" != node.tagName())
      throw MYMONEYEXCEPTION("Node was not KEYVALUEPAIRS");

    m_kvp.clear();

    QDomNodeList nodeList = node.elementsByTagName("PAIR");
    for (int i = 0; i < nodeList.count(); ++i) {
      const QDomElement& el(nodeList.item(i).toElement());
      m_kvp[el.attribute("key")] = el.attribute("value");
    }
  }
}
Exemple #5
0
MyMoneyTag::MyMoneyTag(const QDomElement& node) :
    MyMoneyObject(node)
{
  if ("TAG" != node.tagName()) {
    throw MYMONEYEXCEPTION("Node was not TAG");
  }
  m_name = node.attribute("name");
  if (node.hasAttribute("tagcolor")) {
    m_tag_color.setNamedColor(node.attribute("tagcolor"));
  }
  if (node.hasAttribute("notes")) {
    m_notes = node.attribute("notes");
  }
  m_closed = node.attribute("closed", "0").toUInt();
}
Exemple #6
0
bool MyMoneyTemplate::saveTemplate(const KUrl& url)
{
  QString filename;

  if (!url.isValid()) {
    qDebug("Invalid template URL '%s'", qPrintable(url.url()));
    return false;
  }

  if (url.isLocalFile()) {
    filename = url.toLocalFile();
    KSaveFile qfile(filename/*, 0600*/);
    if (qfile.open()) {
      saveToLocalFile(&qfile);
      if (!qfile.finalize()) {
        throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename));
      }
    } else {
      throw MYMONEYEXCEPTION(i18n("Unable to write changes to '%1'", filename));
    }
  } else {
    KTemporaryFile tmpfile;
    KSaveFile qfile(tmpfile.fileName());
    if (qfile.open()) {
      saveToLocalFile(&qfile);
      if (!qfile.finalize()) {
        throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url()));
      }
    } else {
      throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url()));
    }
    if (!KIO::NetAccess::upload(tmpfile.fileName(), url, 0))
      throw MYMONEYEXCEPTION(i18n("Unable to upload to '%1'", url.url()));
  }
  return true;
}
// Function to read in the file, send to XML parser.
void MyMoneyStorageXML::readFile(QIODevice* pDevice, IMyMoneySerialize* storage)
{
  Q_CHECK_PTR(storage);
  Q_CHECK_PTR(pDevice);
  if (!storage)
    return;

  m_storage = storage;

  m_doc = new QDomDocument;
  Q_CHECK_PTR(m_doc);

  qDebug("reading file");
  // creating the QXmlInputSource object based on a QIODevice object
  // reads all data of the underlying object into memory. I have not
  // found an object that reads on the fly. I tried to derive one myself,
  // but there could be a severe problem with decoding when reading
  // blocks of data and not a stream. So I left it the way it is. (ipwizard)
  QXmlInputSource xml(pDevice);

  qDebug("start parsing file");
  MyMoneyXmlContentHandler mmxml(this);
  QXmlSimpleReader reader;
  reader.setContentHandler(&mmxml);

  if (!reader.parse(&xml, false)) {
    delete m_doc;
    m_doc = 0;
    signalProgress(-1, -1);
    throw MYMONEYEXCEPTION("File was not parsable!");
  }

  // check if we need to build up the account balances
  if (fileVersionRead < 2)
    m_storage->rebuildAccountBalances();

  delete m_doc;
  m_doc = 0;

  // this seems to be nonsense, but it clears the dirty flag
  // as a side-effect.
  m_storage->setLastModificationDate(m_storage->lastModificationDate());
  m_storage = 0;

  //hides the progress bar.
  signalProgress(-1, -1);
}
void TransactionMatcher::match(MyMoneyTransaction tm, MyMoneySplit sm, MyMoneyTransaction ti, MyMoneySplit si, bool allowImportedTransactions)
{
  const MyMoneySecurity& sec = MyMoneyFile::instance()->security(m_account.currencyId());

  // Now match the transactions.
  //
  // 'Matching' the transactions entails DELETING the end transaction,
  // and MODIFYING the start transaction as needed.
  //
  // There are a variety of ways that a transaction can conflict.
  // Post date, splits, amount are the ones that seem to matter.
  // TODO: Handle these conflicts intelligently, at least warning
  // the user, or better yet letting the user choose which to use.
  //
  // For now, we will just use the transaction details from the start
  // transaction.  The only thing we'll take from the end transaction
  // are the bank ID's.
  //
  // What we have to do here is iterate over the splits in the end
  // transaction, and find the corresponding split in the start
  // transaction.  If there is a bankID in the end split but not the
  // start split, add it to the start split.  If there is a bankID
  // in BOTH, then this transaction cannot be merged (both transactions
  // were imported!!)  If the corresponding start split cannot  be
  // found and the end split has a bankID, we should probably just fail.
  // Although we could ADD it to the transaction.

  // ipwizard: Don't know if iterating over the transactions is a good idea.
  // In case of a split transaction recorded with KMyMoney and the transaction
  // data being imported consisting only of a single category assignment, this
  // does not make much sense. The same applies for investment transactions
  // stored in KMyMoney against imported transactions. I think a better solution
  // is to just base the match on the splits referencing the same (currently
  // selected) account.

  // verify, that tm is a manual (non-matched) transaction
  // allow matching two manual transactions

  if ((!allowImportedTransactions && tm.isImported()) || sm.isMatched())
    throw MYMONEYEXCEPTION(i18n("First transaction does not match requirement for matching"));

  // verify that the amounts are the same, otherwise we should not be matching!
  if (sm.shares() != si.shares()) {
    throw MYMONEYEXCEPTION(i18n("Splits for %1 have conflicting values (%2,%3)", m_account.name(), MyMoneyUtils::formatMoney(sm.shares(), m_account, sec), MyMoneyUtils::formatMoney(si.shares(), m_account, sec)));
  }

  // check that dates are within user's setting
  const int gap = abs(tm.postDate().toJulianDay() - ti.postDate().toJulianDay());
  if (gap > KMyMoneyGlobalSettings::matchInterval()) {
    int rc = KMessageBox::questionYesNo(0, i18np("The transaction dates are one day apart. Do you want to match them anyway?",
                                        "The transaction dates are %1 days apart. Do you want to match them anyway?", gap));
    if (rc == KMessageBox::No) {
      return;
    }
  }

  // ipwizard: I took over the code to keep the bank id found in the endMatchTransaction
  // This might not work for QIF imports as they don't setup this information. It sure
  // makes sense for OFX and HBCI.
  const QString& bankID = si.bankID();
  if (!bankID.isEmpty()) {
    try {
      if (sm.bankID().isEmpty()) {
        sm.setBankID(bankID);
        tm.modifySplit(sm);
      }
    } catch (const MyMoneyException &e) {
      QString estr = e.what();
      throw MYMONEYEXCEPTION(i18n("Unable to match all splits (%1)", estr));
    }
  }
  //
  //  we now allow matching of two non-imported transactions
  //

  // mark the split as cleared if it does not have a reconciliation information yet
  if (sm.reconcileFlag() == MyMoneySplit::NotReconciled) {
    sm.setReconcileFlag(MyMoneySplit::Cleared);
  }

  // if we don't have a payee assigned to the manually entered transaction
  // we use the one we found in the imported transaction
  if (sm.payeeId().isEmpty() && !si.payeeId().isEmpty()) {
    sm.setValue("kmm-orig-payee", sm.payeeId());
    sm.setPayeeId(si.payeeId());
  }

  // We use the imported postdate and keep the previous one for unmatch
  if (tm.postDate() != ti.postDate()) {
    sm.setValue("kmm-orig-postdate", tm.postDate().toString(Qt::ISODate));
    tm.setPostDate(ti.postDate());
  }

  // combine the two memos into one
  QString memo = sm.memo();
  if (!si.memo().isEmpty() && si.memo() != memo) {
    sm.setValue("kmm-orig-memo", memo);
    if (!memo.isEmpty())
      memo += '\n';
    memo += si.memo();
  }
  sm.setMemo(memo);

  // remember the split we matched
  sm.setValue("kmm-match-split", si.id());

  sm.addMatch(ti);
  tm.modifySplit(sm);

  ti.modifySplit(si);///
  MyMoneyFile::instance()->modifyTransaction(tm);
  // Delete the end transaction if it was stored in the engine
  if (!ti.id().isEmpty())
    MyMoneyFile::instance()->removeTransaction(ti);
}
Exemple #9
0
MyMoneyReport::MyMoneyReport(ERowType _rt, unsigned _ct, dateOptionE _dl, EDetailLevel _ss, const QString& _name, const QString& _comment) :
    m_name(_name),
    m_comment(_comment),
    m_detailLevel(_ss),
    m_convertCurrency(true),
    m_favorite(false),
    m_tax(false),
    m_investments(false),
    m_loans(false),
    m_reportType(kTypeArray[_rt]),
    m_rowType(_rt),
    m_columnsAreDays(false),
    m_queryColumns(eQCnone),
    m_dateLock(_dl),
    m_accountGroupFilter(false),
    m_chartType(eChartLine),
    m_chartDataLabels(true),
    m_chartGridLines(true),
    m_chartByDefault(false),
    m_includeSchedules(false),
    m_includeTransfers(false),
    m_includeBudgetActuals(false),
    m_includeUnusedAccounts(false),
    m_showRowTotals(false),
    m_includeForecast(false),
    m_includeMovingAverage(false),
    m_movingAverageDays(0),
    m_includePrice(false),
    m_includeAveragePrice(false),
    m_mixedTime(false),
    m_currentDateColumn(0),
    m_skipZero(false)
{
  //set initial values
  m_chartLineWidth = m_lineWidth;

  //set report type
  if (m_reportType == ePivotTable)
    m_columnType = static_cast<EColumnType>(_ct);
  if (m_reportType == eQueryTable)
    m_queryColumns = static_cast<EQueryColumns>(_ct);
  setDateFilter(_dl);

  //throw exception if the type is inconsistent
  if ((_rt > static_cast<ERowType>(sizeof(kTypeArray) / sizeof(kTypeArray[0])))
      || (m_reportType == eNoReport))
    throw MYMONEYEXCEPTION("Invalid report type");

  //add the corresponding account groups
  if (_rt == MyMoneyReport::eAssetLiability) {
    addAccountGroup(MyMoneyAccount::Asset);
    addAccountGroup(MyMoneyAccount::Liability);
    m_showRowTotals = true;
  }
  if (_rt == MyMoneyReport::eExpenseIncome) {
    addAccountGroup(MyMoneyAccount::Expense);
    addAccountGroup(MyMoneyAccount::Income);
    m_showRowTotals = true;
  }
  //FIXME take this out once we have sorted out all issues regarding budget of assets and liabilities -- [email protected]
  if (_rt == MyMoneyReport::eBudget || _rt == MyMoneyReport::eBudgetActual) {
    addAccountGroup(MyMoneyAccount::Expense);
    addAccountGroup(MyMoneyAccount::Income);
  }
  if (_rt == MyMoneyReport::eAccountInfo) {
    addAccountGroup(MyMoneyAccount::Asset);
    addAccountGroup(MyMoneyAccount::Liability);
  }
  //cash flow reports show splits for all account groups
  if (_rt == MyMoneyReport::eCashFlow) {
    addAccountGroup(MyMoneyAccount::Expense);
    addAccountGroup(MyMoneyAccount::Income);
    addAccountGroup(MyMoneyAccount::Asset);
    addAccountGroup(MyMoneyAccount::Liability);
  }
}
Exemple #10
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;
}