void MyMoneySplitTest::testWriteXML() { MyMoneySplit s; s.setPayeeId("P000001"); QList<QString> tagIdList; tagIdList << "G000001"; s.setTagIdList(tagIdList); s.setShares(MyMoneyMoney(96379, 100)); s.setValue(MyMoneyMoney(96379, 1000)); s.setAccountId("A000076"); s.setNumber("124"); s.setBankID("SPID"); s.setAction(MyMoneySplit::ActionDeposit); s.setReconcileFlag(MyMoneySplit::Reconciled); QDomDocument doc("TEST"); QDomElement el = doc.createElement("SPLIT-CONTAINER"); doc.appendChild(el); s.writeXML(doc, el); QCOMPARE(doc.doctype().name(), QLatin1String("TEST")); QDomElement splitContainer = doc.documentElement(); QCOMPARE(splitContainer.tagName(), QLatin1String("SPLIT-CONTAINER")); QCOMPARE(splitContainer.childNodes().size(), 1); QVERIFY(splitContainer.childNodes().at(0).isElement()); QDomElement split = splitContainer.childNodes().at(0).toElement(); QCOMPARE(split.tagName(), QLatin1String("SPLIT")); QCOMPARE(split.attribute("payee"), QLatin1String("P000001")); QCOMPARE(split.attribute("reconcileflag"), QLatin1String("2")); QCOMPARE(split.attribute("shares"), QLatin1String("96379/100")); QCOMPARE(split.attribute("reconciledate"), QString()); QCOMPARE(split.attribute("action"), QLatin1String("Deposit")); QCOMPARE(split.attribute("bankid"), QLatin1String("SPID")); QCOMPARE(split.attribute("account"), QLatin1String("A000076")); QCOMPARE(split.attribute("number"), QLatin1String("124")); QCOMPARE(split.attribute("value"), QLatin1String("96379/1000")); QCOMPARE(split.attribute("memo"), QString()); QCOMPARE(split.attribute("id"), QString()); QCOMPARE(split.childNodes().size(), 1); QVERIFY(split.childNodes().at(0).isElement()); QDomElement tag = split.childNodes().at(0).toElement(); QCOMPARE(tag.tagName(), QLatin1String("TAG")); QCOMPARE(tag.attribute("id"), QLatin1String("G000001")); QCOMPARE(tag.childNodes().size(), 0); QString ref = QString( "<!DOCTYPE TEST>\n" "<SPLIT-CONTAINER>\n" " <SPLIT payee=\"P000001\" reconcileflag=\"2\" shares=\"96379/100\" reconciledate=\"\" action=\"Deposit\" bankid=\"SPID\" account=\"A000076\" number=\"124\" value=\"96379/1000\" memo=\"\" id=\"\">\n" " <TAG id=\"G000001\"/>\n" " </SPLIT>\n" "</SPLIT-CONTAINER>\n"); }
TransactionHelper::TransactionHelper( const QDate& _date, const QString& _action, MyMoneyMoney _value, const QString& _accountid, const QString& _categoryid, const QString& _currencyid, const QString& _payee ) { // _currencyid is the currency of the transaction, and of the _value // both the account and category can have their own currency (athough the category having // a foreign currency is not yet supported by the program, the reports will still allow it, // so it must be tested.) MyMoneyFile* file = MyMoneyFile::instance(); bool haspayee = ! _payee.isEmpty(); MyMoneyPayee payeeTest = file->payeeByName(_payee); MyMoneyFileTransaction ft; setPostDate(_date); QString currencyid = _currencyid; if ( currencyid.isEmpty() ) currencyid=MyMoneyFile::instance()->baseCurrency().id(); setCommodity(currencyid); MyMoneyMoney price; MyMoneySplit splitLeft; if ( haspayee ) splitLeft.setPayeeId(payeeTest.id()); splitLeft.setAction(_action); splitLeft.setValue(-_value); price = MyMoneyFile::instance()->price(currencyid, file->account(_accountid).currencyId(),_date).rate(file->account(_accountid).currencyId()); splitLeft.setShares(-_value * price); splitLeft.setAccountId(_accountid); addSplit(splitLeft); MyMoneySplit splitRight; if ( haspayee ) splitRight.setPayeeId(payeeTest.id()); splitRight.setAction(_action); splitRight.setValue(_value); price = MyMoneyFile::instance()->price(currencyid, file->account(_categoryid).currencyId(),_date).rate(file->account(_categoryid).currencyId()); splitRight.setShares(_value * price ); splitRight.setAccountId(_categoryid); addSplit(splitRight); MyMoneyFile::instance()->addTransaction(*this); ft.commit(); }
void MyMoneyAccountTest::testAdjustBalance() { MyMoneyAccount a; MyMoneySplit s; s.setShares(MyMoneyMoney(3, 1)); a.adjustBalance(s); QVERIFY(a.balance() == MyMoneyMoney(3, 1)); s.setShares(MyMoneyMoney(5, 1)); a.adjustBalance(s, true); QVERIFY(a.balance() == MyMoneyMoney(-2, 1)); s.setShares(MyMoneyMoney(2, 1)); s.setAction(MyMoneySplit::ActionSplitShares); a.adjustBalance(s); QVERIFY(a.balance() == MyMoneyMoney(-4, 1)); s.setShares(MyMoneyMoney(4, 1)); s.setAction(QString()); a.adjustBalance(s); QVERIFY(a.balance().isZero()); }
void MyMoneyAccountTest::testConstructor() { QString id = "A000001"; QString institutionid = "B000001"; QString parent = "Parent"; MyMoneyAccount r; MyMoneySplit s; r.setAccountType(MyMoneyAccount::Asset); r.setOpeningDate(QDate::currentDate()); r.setLastModified(QDate::currentDate()); r.setDescription("Desc"); r.setNumber("465500"); r.setParentAccountId(parent); r.setValue(QString("key"), "value"); s.setShares(MyMoneyMoney::ONE); r.adjustBalance(s); QVERIFY(r.m_kvp.count() == 1); QVERIFY(r.value("key") == "value"); MyMoneyAccount a(id, r); QVERIFY(a.id() == id); QVERIFY(a.institutionId().isEmpty()); QVERIFY(a.accountType() == MyMoneyAccount::Asset); QVERIFY(a.openingDate() == QDate::currentDate()); QVERIFY(a.lastModified() == QDate::currentDate()); QVERIFY(a.number() == "465500"); QVERIFY(a.description() == "Desc"); QVERIFY(a.accountList().count() == 0); QVERIFY(a.parentAccountId() == "Parent"); QVERIFY(a.balance() == MyMoneyMoney::ONE); QMap<QString, QString> copy; copy = r.pairs(); QVERIFY(copy.count() == 1); QVERIFY(copy[QString("key")] == "value"); }
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); } } }
int KSplitTransactionDlg::exec() { // for deposits, we invert the sign of all splits. // don't forget to revert when we're done ;-) if (m_isDeposit) { for (int i = 0; i < m_transaction.splits().count(); ++i) { MyMoneySplit split = m_transaction.splits()[i]; split.setValue(-split.value()); split.setShares(-split.shares()); m_transaction.modifySplit(split); } } int rc; do { transactionsTable->setFocus(); // initialize the display transactionsTable->setTransaction(m_transaction, m_split, m_account); updateSums(); rc = KSplitTransactionDlgDecl::exec(); if (rc == Accepted) { if (!diffAmount().isZero()) { KSplitCorrectionDlgDecl* corrDlg = new KSplitCorrectionDlgDecl(this); QVBoxLayout *mainLayout = new QVBoxLayout; corrDlg->setLayout(mainLayout); mainLayout->addWidget(corrDlg->findChild<QWidget*>("verticalLayout")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), corrDlg, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), corrDlg, SLOT(reject())); mainLayout->addWidget(buttonBox); corrDlg->buttonGroup->setId(corrDlg->continueBtn, 0); corrDlg->buttonGroup->setId(corrDlg->changeBtn, 1); corrDlg->buttonGroup->setId(corrDlg->distributeBtn, 2); corrDlg->buttonGroup->setId(corrDlg->leaveBtn, 3); corrDlg->setModal(true); MyMoneySplit split = m_transaction.splits()[0]; QString total = (-split.value()).formatMoney("", m_precision); QString sums = splitsValue().formatMoney("", m_precision); QString diff = diffAmount().formatMoney("", m_precision); // now modify the text items of the dialog to contain the correct values QString q = i18n("The total amount of this transaction is %1 while " "the sum of the splits is %2. The remaining %3 are " "unassigned.", total, sums, diff); corrDlg->explanation->setText(q); q = i18n("Change &total amount of transaction to %1.", sums); corrDlg->changeBtn->setText(q); q = i18n("&Distribute difference of %1 among all splits.", diff); corrDlg->distributeBtn->setText(q); // FIXME remove the following line once distribution among // all splits is implemented corrDlg->distributeBtn->hide(); // if we have only two splits left, we don't allow leaving sth. unassigned. if (m_transaction.splitCount() < 3) { q = i18n("&Leave total amount of transaction at %1.", total); } else { q = i18n("&Leave %1 unassigned.", diff); } corrDlg->leaveBtn->setText(q); if ((rc = corrDlg->exec()) == Accepted) { switch (corrDlg->buttonGroup->checkedId()) { case 0: // continue to edit rc = Rejected; break; case 1: // modify total split.setValue(-splitsValue()); split.setShares(-splitsValue()); m_transaction.modifySplit(split); break; case 2: // distribute difference qDebug("distribution of difference not yet supported in KSplitTransactionDlg::slotFinishClicked()"); break; case 3: // leave unassigned break; } } delete corrDlg; } } else break; } while (rc != Accepted); // for deposits, we inverted the sign of all splits. // now we revert it back, so that things are left correct if (m_isDeposit) { for (int i = 0; i < m_transaction.splits().count(); ++i) { MyMoneySplit split = m_transaction.splits()[i]; split.setValue(-split.value()); split.setShares(-split.shares()); m_transaction.modifySplit(split); } } return rc; }