diff options
Diffstat (limited to 'kmymoney2/mymoney/mymoneyscheduled.cpp')
-rw-r--r-- | kmymoney2/mymoney/mymoneyscheduled.cpp | 1372 |
1 files changed, 1372 insertions, 0 deletions
diff --git a/kmymoney2/mymoney/mymoneyscheduled.cpp b/kmymoney2/mymoney/mymoneyscheduled.cpp new file mode 100644 index 0000000..88b4c7b --- /dev/null +++ b/kmymoney2/mymoney/mymoneyscheduled.cpp @@ -0,0 +1,1372 @@ +/*************************************************************************** + mymoneyscheduled.cpp + ------------------- + copyright : (C) 2000-2002 by Michael Edwardes + (C) 2007 by Thomas Baumgart + email : mte@users.sourceforge.net + Thomas Baumgart <ipwizard@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. * + * * + ***************************************************************************/ +// ---------------------------------------------------------------------------- +// QT Includes + +// ---------------------------------------------------------------------------- +// KDE Includes + +#include <klocale.h> + +// ---------------------------------------------------------------------------- +// Project Includes + +#include "mymoneyscheduled.h" +#include "mymoneyexception.h" +#include "mymoneyfile.h" + +MyMoneySchedule::MyMoneySchedule() : + MyMoneyObject() +{ + // Set up the default values + m_occurence = OCCUR_ANY; + m_occurenceMultiplier = 1; + m_type = TYPE_ANY; + m_paymentType = STYPE_ANY; + m_fixed = false; + m_autoEnter = false; + m_startDate = QDate(); + m_endDate = QDate(); + m_lastPayment = QDate(); + m_weekendOption = MoveNothing; +} + +MyMoneySchedule::MyMoneySchedule(const QString& name, typeE type, + occurenceE occurence, int occurenceMultiplier, + paymentTypeE paymentType, + const QDate& /* startDate */, + const QDate& endDate, + bool fixed, bool autoEnter) : + MyMoneyObject() +{ + // Set up the default values + m_name = name; + m_occurence = occurence; + m_occurenceMultiplier = occurenceMultiplier; + simpleToCompoundOccurence(m_occurenceMultiplier,m_occurence); + m_type = type; + m_paymentType = paymentType; + m_fixed = fixed; + m_autoEnter = autoEnter; + m_startDate = QDate(); + m_endDate = endDate; + m_lastPayment = QDate(); + m_weekendOption = MoveNothing; +} + +MyMoneySchedule::MyMoneySchedule(const QDomElement& node) : + MyMoneyObject(node) +{ + if("SCHEDULED_TX" != node.tagName()) + throw new MYMONEYEXCEPTION("Node was not SCHEDULED_TX"); + + m_name = node.attribute("name"); + m_startDate = stringToDate(node.attribute("startDate")); + m_endDate = stringToDate(node.attribute("endDate")); + m_lastPayment = stringToDate(node.attribute("lastPayment")); + + m_type = static_cast<MyMoneySchedule::typeE>(node.attribute("type").toInt()); + m_paymentType = static_cast<MyMoneySchedule::paymentTypeE>(node.attribute("paymentType").toInt()); + m_occurence = static_cast<MyMoneySchedule::occurenceE>(node.attribute("occurence").toInt()); + m_occurenceMultiplier = node.attribute("occurenceMultiplier", "1").toInt(); + // Convert to compound occurence + simpleToCompoundOccurence(m_occurenceMultiplier,m_occurence); + m_autoEnter = static_cast<bool>(node.attribute("autoEnter").toInt()); + m_fixed = static_cast<bool>(node.attribute("fixed").toInt()); + m_weekendOption = static_cast<MyMoneySchedule::weekendOptionE>(node.attribute("weekendOption").toInt()); + + // read in the associated transaction + QDomNodeList nodeList = node.elementsByTagName("TRANSACTION"); + if(nodeList.count() == 0) + throw new MYMONEYEXCEPTION("SCHEDULED_TX has no TRANSACTION node"); + + setTransaction(MyMoneyTransaction(nodeList.item(0).toElement(), false), true); + + // some old versions did not remove the entry date and post date fields + // in the schedule. So if this is the case, we deal with a very old transaction + // and can't use the post date field as next due date. Hence, we wipe it out here + if(m_transaction.entryDate().isValid()) { + m_transaction.setPostDate(QDate()); + m_transaction.setEntryDate(QDate()); + } + + // readin the recorded payments + nodeList = node.elementsByTagName("PAYMENTS"); + if(nodeList.count() > 0) { + nodeList = nodeList.item(0).toElement().elementsByTagName("PAYMENT"); + for(unsigned int i = 0; i < nodeList.count(); ++i) { + m_recordedPayments << stringToDate(nodeList.item(i).toElement().attribute("date")); + } + } + + // if the next due date is not set (comes from old version) + // then set it up the old way + if(!nextDueDate().isValid() && !m_lastPayment.isValid()) { + m_transaction.setPostDate(m_startDate); + // clear it, because the schedule has never been used + m_startDate = QDate(); + } + + // There are reports that lastPayment and nextDueDate are identical or + // that nextDueDate is older than lastPayment. This could + // be caused by older versions of the application. In this case, we just + // clear out the nextDueDate and let it calculate from the lastPayment. + if(nextDueDate().isValid() && nextDueDate() <= m_lastPayment) { + m_transaction.setPostDate(QDate()); + } + + if(!nextDueDate().isValid()) { + m_transaction.setPostDate(m_startDate); + m_transaction.setPostDate(nextPayment(m_lastPayment.addDays(1))); + } +} + +MyMoneySchedule::MyMoneySchedule(const QString& id, const MyMoneySchedule& right) : + MyMoneyObject(id) +{ + *this = right; + setId(id); +} + +MyMoneySchedule::occurenceE MyMoneySchedule::occurence(void) const +{ + MyMoneySchedule::occurenceE occ = m_occurence; + int mult = m_occurenceMultiplier; + compoundToSimpleOccurence(mult, occ); + return occ; +} + +void MyMoneySchedule::setStartDate(const QDate& date) +{ + m_startDate = date; +} + +void MyMoneySchedule::setPaymentType(paymentTypeE type) +{ + m_paymentType = type; +} + +void MyMoneySchedule::setFixed(bool fixed) +{ + m_fixed = fixed; +} + +void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction) +{ + setTransaction(transaction, false); +} + +void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction, bool noDateCheck) +{ + MyMoneyTransaction t = transaction; + if(!noDateCheck) { + // don't allow a transaction that has no due date + // if we get something like that, then we use the + // the current next due date. If that is also invalid + // we can't help it. + if(!t.postDate().isValid()) { + t.setPostDate(m_transaction.postDate()); + } + + if(!t.postDate().isValid()) + return; + } + + // make sure to clear out some unused information in scheduled transactions + // we need to do this for the case that the transaction passed as argument + // is a matched or imported transaction. + QValueList<MyMoneySplit> splits = t.splits(); + if(splits.count() > 0) { + QValueList<MyMoneySplit>::const_iterator it_s; + for(it_s = splits.begin(); it_s != splits.end(); ++it_s) { + MyMoneySplit s = *it_s; + // clear out the bankID + if(!(*it_s).bankID().isEmpty()) { + s.setBankID(QString()); + t.modifySplit(s); + } + + // only clear payees from second split onwards + if(it_s == splits.begin()) + continue; + + if(!(*it_s).payeeId().isEmpty()) { + // but only if the split references an income/expense category + MyMoneyFile* file = MyMoneyFile::instance(); + // some unit tests don't have a storage attached, so we + // simply skip the test + // Don't check for accounts with an id of 'Phony-ID' which is used + // internally for non-existing accounts (during creation of accounts) + if(file->storageAttached() && s.accountId() != QString("Phony-ID")) { + MyMoneyAccount acc = file->account(s.accountId()); + if(acc.isIncomeExpense()) { + s.setPayeeId(QString()); + t.modifySplit(s); + } + } + } + } + } + + m_transaction = t; + // make sure that the transaction does not have an id so that we can enter + // it into the engine + m_transaction.clearId(); +} + +void MyMoneySchedule::setEndDate(const QDate& date) +{ + m_endDate = date; +} + +void MyMoneySchedule::setAutoEnter(bool autoenter) +{ + m_autoEnter = autoenter; +} + +const QDate& MyMoneySchedule::startDate(void) const +{ + if(m_startDate.isValid()) + return m_startDate; + return nextDueDate(); +} + +const QDate& MyMoneySchedule::nextDueDate(void) const +{ + return m_transaction.postDate(); +} + +QDate MyMoneySchedule::adjustedNextDueDate(void) const +{ + if(isFinished()) + return QDate(); + + return adjustedDate(nextDueDate(), weekendOption()); +} + +QDate MyMoneySchedule::adjustedDate(QDate date, weekendOptionE option) const +{ + if (option == MyMoneySchedule::MoveNothing) + return date; + + int step = 1; + if (option == MyMoneySchedule::MoveFriday) + step = -1; + + while (date.dayOfWeek() > 5) + date = date.addDays(step); + + return date; +} + +void MyMoneySchedule::setNextDueDate(const QDate& date) +{ + if(date.isValid()) { + m_transaction.setPostDate(date); + m_startDate = date; + } +} + +void MyMoneySchedule::setLastPayment(const QDate& date) +{ + // Delete all payments older than date + QValueList<QDate>::Iterator it; + QValueList<QDate> delList; + + for (it=m_recordedPayments.begin(); it!=m_recordedPayments.end(); ++it) + { + if (*it < date || !date.isValid()) + delList.append(*it); + } + + for (it=delList.begin(); it!=delList.end(); ++it) + { + m_recordedPayments.remove(*it); + } + + m_lastPayment = date; + if(!m_startDate.isValid()) + m_startDate = date; +} + +void MyMoneySchedule::setName(const QString& nm) +{ + m_name = nm; +} + +void MyMoneySchedule::setOccurence(occurenceE occ) +{ + MyMoneySchedule::occurenceE occ2 = occ; + int mult = 1; + simpleToCompoundOccurence(mult, occ2); + setOccurencePeriod( occ2 ); + setOccurenceMultiplier( mult ); +} + +void MyMoneySchedule::setOccurencePeriod(occurenceE occ) +{ + m_occurence = occ; +} + +void MyMoneySchedule::setOccurenceMultiplier(int occmultiplier) +{ + m_occurenceMultiplier = occmultiplier < 1 ? 1 : occmultiplier; +} + +void MyMoneySchedule::setType(typeE type) +{ + m_type = type; +} + +void MyMoneySchedule::validate(bool id_check) const +{ + /* Check the supplied instance is valid... + * + * To be valid it must not have the id set and have the following fields set: + * + * m_occurence + * m_type + * m_startDate + * m_paymentType + * m_transaction + * the transaction must contain at least one split (two is better ;-) ) + */ + if (id_check && !m_id.isEmpty()) + throw new MYMONEYEXCEPTION("ID for schedule not empty when required"); + + if(m_occurence == OCCUR_ANY) + throw new MYMONEYEXCEPTION("Invalid occurence type for schedule"); + + if(m_type == TYPE_ANY) + throw new MYMONEYEXCEPTION("Invalid type for schedule"); + + if(!nextDueDate().isValid()) + throw new MYMONEYEXCEPTION("Invalid next due date for schedule"); + + if(m_paymentType == STYPE_ANY) + throw new MYMONEYEXCEPTION("Invalid payment type for schedule"); + + if(m_transaction.splitCount() == 0) + throw new MYMONEYEXCEPTION("Scheduled transaction does not contain splits"); + + // Check the payment types + switch (m_type) + { + case TYPE_BILL: + if (m_paymentType == STYPE_DIRECTDEPOSIT || m_paymentType == STYPE_MANUALDEPOSIT) + throw new MYMONEYEXCEPTION("Invalid payment type for bills"); + break; + + case TYPE_DEPOSIT: + if (m_paymentType == STYPE_DIRECTDEBIT || m_paymentType == STYPE_WRITECHEQUE) + throw new MYMONEYEXCEPTION("Invalid payment type for deposits"); + break; + + case TYPE_ANY: + throw new MYMONEYEXCEPTION("Invalid type ANY"); + break; + + case TYPE_TRANSFER: +// if (m_paymentType == STYPE_DIRECTDEPOSIT || m_paymentType == STYPE_MANUALDEPOSIT) +// return false; + break; + + case TYPE_LOANPAYMENT: + break; + } +} + +QDate MyMoneySchedule::adjustedNextPayment(const QDate& refDate) const +{ + QDate date(nextPayment(refDate)); + return date.isValid() ? adjustedDate(date, weekendOption()) : date; +} + +QDate MyMoneySchedule::nextPayment(const QDate& refDate) const +{ + // if the enddate is valid and it is before the reference date, + // then there will be no more payments. + if(m_endDate.isValid() && m_endDate < refDate) { + return QDate(); + } + + QDate paymentDate(nextDueDate()); + + if(refDate >= paymentDate) { + switch (m_occurence) + { + case OCCUR_ONCE: + // if the lastPayment is already set, then there will be no more payments + // otherwise, the start date is the payment date + if(m_lastPayment.isValid()) + return QDate(); + // if the only payment should have been prior to the reference date, + // then don't show it + if(paymentDate < refDate) + return QDate(); + break; + + case OCCUR_DAILY: + paymentDate = refDate.addDays(m_occurenceMultiplier); + break; + + case OCCUR_WEEKLY: + { + int step = 7*m_occurenceMultiplier; + do { + paymentDate = paymentDate.addDays(step); + } + while (paymentDate <= refDate); + } + break; + + case OCCUR_EVERYHALFMONTH: + do + { + paymentDate = addHalfMonths(paymentDate,m_occurenceMultiplier); + } + while (paymentDate <= refDate); + break; + + case OCCUR_MONTHLY: + do { + paymentDate = paymentDate.addMonths(m_occurenceMultiplier); + fixDate(paymentDate); + } + while (paymentDate <= refDate); + break; + + case OCCUR_YEARLY: + do { + paymentDate = paymentDate.addYears(m_occurenceMultiplier); + fixDate(paymentDate); + } + while (paymentDate <= refDate); + break; + + case OCCUR_ANY: + default: + paymentDate = QDate(); + break; + } + } + if(paymentDate.isValid()) { + if(m_endDate.isValid() && paymentDate > m_endDate) + paymentDate = QDate(); + } + + if (paymentDate.isValid() && m_recordedPayments.contains(paymentDate)) + paymentDate = nextPayment(paymentDate); + + return paymentDate; +} + +QValueList<QDate> MyMoneySchedule::paymentDates(const QDate& _startDate, const QDate& _endDate) const +{ + QDate paymentDate(nextDueDate()); + QValueList<QDate> theDates; + + QDate endDate(_endDate); + if ( willEnd() && m_endDate < endDate ) + endDate = m_endDate; + + weekendOptionE option(weekendOption()); + QDate start_date(adjustedDate(startDate(), option)); + // if the period specified by the parameters and the period + // defined for this schedule don't overlap, then the list remains empty + if ((willEnd() && m_endDate < _startDate) + || start_date > endDate) + return theDates; + + QDate date(adjustedDate(paymentDate, option)); + + switch (m_occurence) + { + case OCCUR_ONCE: + if (start_date >= _startDate && start_date <= endDate) + theDates.append(start_date); + break; + + case OCCUR_DAILY: + while (date.isValid() && (date <= endDate)) + { + if (date >= _startDate) + theDates.append(date); + paymentDate = paymentDate.addDays(m_occurenceMultiplier); + date = adjustedDate(paymentDate, option); + } + break; + + case OCCUR_WEEKLY: + { + int step = 7*m_occurenceMultiplier; + while (date.isValid() && (date <= endDate)) + { + if (date >= _startDate) + theDates.append(date); + paymentDate = paymentDate.addDays(step); + date = adjustedDate(paymentDate, option); + } + } + break; + + case OCCUR_EVERYHALFMONTH: + while (date.isValid() && (date <= endDate)) + { + if (date >= _startDate) + theDates.append(date); + paymentDate = addHalfMonths(paymentDate,m_occurenceMultiplier); + date = adjustedDate(paymentDate, option); + } + break; + + case OCCUR_MONTHLY: + while (date.isValid() && (date <= endDate)) + { + if (date >= _startDate) + theDates.append(date); + paymentDate = paymentDate.addMonths(m_occurenceMultiplier); + fixDate(paymentDate); + date = adjustedDate(paymentDate, option); + } + break; + + case OCCUR_YEARLY: + while (date.isValid() && (date <= endDate)) + { + if (date >= _startDate) + theDates.append(date); + paymentDate = paymentDate.addYears(m_occurenceMultiplier); + fixDate(paymentDate); + date = adjustedDate(paymentDate, option); + } + break; + + case OCCUR_ANY: + default: + break; + } + + return theDates; +} + +bool MyMoneySchedule::operator <(const MyMoneySchedule& right) const +{ + return adjustedNextDueDate() < right.adjustedNextDueDate(); +} + +bool MyMoneySchedule::operator ==(const MyMoneySchedule& right) const +{ + if ( MyMoneyObject::operator==(right) && + m_occurence == right.m_occurence && + m_occurenceMultiplier == right.m_occurenceMultiplier && + m_type == right.m_type && + m_startDate == right.m_startDate && + m_paymentType == right.m_paymentType && + m_fixed == right.m_fixed && + m_transaction == right.m_transaction && + m_endDate == right.m_endDate && + m_autoEnter == right.m_autoEnter && + m_lastPayment == right.m_lastPayment && + ((m_name.length() == 0 && right.m_name.length() == 0) || (m_name == right.m_name))) + return true; + return false; +} + +int MyMoneySchedule::transactionsRemaining(void) const +{ + int counter=0; + + if (m_endDate.isValid()) + { + QValueList<QDate> dates = paymentDates(m_lastPayment, m_endDate); + // Dont include the last payment so -1 + counter = dates.count(); + } + return counter; +} + +MyMoneyAccount MyMoneySchedule::account(int cnt) const +{ + QValueList<MyMoneySplit> splits = m_transaction.splits(); + QValueList<MyMoneySplit>::ConstIterator it; + MyMoneyFile* file = MyMoneyFile::instance(); + MyMoneyAccount acc; + + // search the first asset or liability account + for(it = splits.begin(); it != splits.end() && (acc.id().isEmpty() || cnt); ++it) { + try { + acc = file->account((*it).accountId()); + if(acc.isAssetLiability()) + --cnt; + + if(!cnt) + return acc; + } catch(MyMoneyException *e) { + qWarning("Schedule '%s' references unknown account '%s'", id().data(), (*it).accountId().data()); + delete e; + return MyMoneyAccount(); + } + } + + return MyMoneyAccount(); +} + +QDate MyMoneySchedule::dateAfter(int transactions) const +{ + int counter=1; + QDate paymentDate(startDate()); + + if (transactions<=0) + return paymentDate; + + switch (m_occurence) + { + case OCCUR_ONCE: + break; + + case OCCUR_DAILY: + while (counter++ < transactions) + paymentDate = paymentDate.addDays(m_occurenceMultiplier); + break; + + case OCCUR_WEEKLY: + { + int step = 7 * m_occurenceMultiplier; + while (counter++ < transactions) + paymentDate = paymentDate.addDays(step); + } + break; + + case OCCUR_EVERYHALFMONTH: + paymentDate = addHalfMonths(paymentDate,m_occurenceMultiplier*(transactions-1)); + break; + + case OCCUR_MONTHLY: + while (counter++ < transactions) + paymentDate = paymentDate.addMonths(m_occurenceMultiplier); + break; + + case OCCUR_YEARLY: + while (counter++ < transactions) + paymentDate = paymentDate.addYears(m_occurenceMultiplier); + break; + + case OCCUR_ANY: + default: + break; + } + + return paymentDate; +} + +bool MyMoneySchedule::isOverdue() const +{ + if (isFinished()) + return false; + + if(adjustedNextDueDate() >= QDate::currentDate()) + return false; + + return true; +} + +bool MyMoneySchedule::isFinished() const +{ + if(!m_lastPayment.isValid()) + return false; + + if (m_endDate.isValid()) { + if(m_lastPayment >= m_endDate + || !nextDueDate().isValid() + || nextDueDate() > m_endDate) + return true; + } + + // Check to see if its a once off payment + if (m_occurence == MyMoneySchedule::OCCUR_ONCE) + return true; + + return false; +} + +bool MyMoneySchedule::hasRecordedPayment(const QDate& date) const +{ + // m_lastPayment should always be > recordedPayments() + if (m_lastPayment.isValid() && m_lastPayment >= date) + return true; + + if (m_recordedPayments.contains(date)) + return true; + + return false; +} + +void MyMoneySchedule::recordPayment(const QDate& date) +{ + m_recordedPayments.append(date); +} + +void MyMoneySchedule::setWeekendOption(const weekendOptionE option) +{ + // make sure only valid values are used. Invalid defaults to MoveNothing. + switch(option) { + case MoveFriday: + case MoveMonday: + m_weekendOption = option; + break; + + default: + m_weekendOption = MoveNothing; + break; + } +} + +void MyMoneySchedule::fixDate(QDate& date) const +{ + QDate fixDate(m_startDate); + if(fixDate.isValid() + && date.day() != fixDate.day() + && QDate::isValid(date.year(), date.month(), fixDate.day())) { + date.setYMD(date.year(), date.month(), fixDate.day()); + } +} + +void MyMoneySchedule::writeXML(QDomDocument& document, QDomElement& parent) const +{ + QDomElement el = document.createElement("SCHEDULED_TX"); + + writeBaseXML(document, el); + + el.setAttribute("name", m_name); + el.setAttribute("type", m_type); + el.setAttribute("occurence", m_occurence); + el.setAttribute("occurenceMultiplier", m_occurenceMultiplier); + el.setAttribute("paymentType", m_paymentType); + el.setAttribute("startDate", dateToString(m_startDate)); + el.setAttribute("endDate", dateToString(m_endDate)); + el.setAttribute("fixed", m_fixed); + el.setAttribute("autoEnter", m_autoEnter); + el.setAttribute("lastPayment", dateToString(m_lastPayment)); + el.setAttribute("weekendOption", m_weekendOption); + + //store the payment history for this scheduled task. + QValueList<QDate> payments = recordedPayments(); + QValueList<QDate>::ConstIterator it; + QDomElement paymentsElement = document.createElement("PAYMENTS"); + for (it=payments.begin(); it!=payments.end(); ++it) { + QDomElement paymentEntry = document.createElement("PAYMENT"); + paymentEntry.setAttribute("date", dateToString(*it)); + paymentsElement.appendChild(paymentEntry); + } + el.appendChild(paymentsElement); + + //store the transaction data for this task. + m_transaction.writeXML(document, el); + + parent.appendChild(el); +} + +bool MyMoneySchedule::hasReferenceTo(const QString& id) const +{ + return m_transaction.hasReferenceTo(id); +} + +QString MyMoneySchedule::occurenceToString() const +{ + return occurenceToString( occurenceMultiplier(), occurencePeriod() ); +} + +QString MyMoneySchedule::occurenceToString(occurenceE occurence) +{ + QString occurenceString = I18N_NOOP("Any"); + + if(occurence == MyMoneySchedule::OCCUR_ONCE) + occurenceString = I18N_NOOP("Once"); + else if(occurence == MyMoneySchedule::OCCUR_DAILY) + occurenceString = I18N_NOOP("Daily"); + else if(occurence == MyMoneySchedule::OCCUR_WEEKLY) + occurenceString = I18N_NOOP("Weekly"); + else if(occurence == MyMoneySchedule::OCCUR_FORTNIGHTLY) + occurenceString = I18N_NOOP("Fortnightly"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYOTHERWEEK) + occurenceString = I18N_NOOP("Every other week"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYHALFMONTH) + occurenceString = I18N_NOOP("Every half month"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHREEWEEKS) + occurenceString = I18N_NOOP("Every three weeks"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYFOURWEEKS) + occurenceString = I18N_NOOP("Every four weeks"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS) + occurenceString = I18N_NOOP("Every thirty days"); + else if(occurence == MyMoneySchedule::OCCUR_MONTHLY) + occurenceString = I18N_NOOP("Monthly"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS) + occurenceString = I18N_NOOP("Every eight weeks"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYOTHERMONTH) + occurenceString = I18N_NOOP("Every two months"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHREEMONTHS) + occurenceString = I18N_NOOP("Every three months"); + else if(occurence == MyMoneySchedule::OCCUR_QUARTERLY) + occurenceString = I18N_NOOP("Quarterly"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYFOURMONTHS) + occurenceString = I18N_NOOP("Every four months"); + else if(occurence == MyMoneySchedule::OCCUR_TWICEYEARLY) + occurenceString = I18N_NOOP("Twice yearly"); + else if(occurence == MyMoneySchedule::OCCUR_YEARLY) + occurenceString = I18N_NOOP("Yearly"); + else if(occurence == MyMoneySchedule::OCCUR_EVERYOTHERYEAR) + occurenceString = I18N_NOOP("Every other year"); + return occurenceString; +} + +QString MyMoneySchedule::occurenceToString(int mult, occurenceE type) +{ + QString occurenceString = I18N_NOOP("Any"); + + if (type == MyMoneySchedule::OCCUR_ONCE) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Once"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("%1 times").arg(mult)); + } + else if(type == MyMoneySchedule::OCCUR_DAILY) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Daily"); break; + case 30: occurenceString = I18N_NOOP("Every thirty days"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("Every %1 days").arg(mult)); + } + else if(type == MyMoneySchedule::OCCUR_WEEKLY) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Weekly"); break; + case 2: occurenceString = I18N_NOOP("Every other week"); break; + case 3: occurenceString = I18N_NOOP("Every three weeks"); break; + case 4: occurenceString = I18N_NOOP("Every four weeks"); break; + case 8: occurenceString = I18N_NOOP("Every eight weeks"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("Every %1 weeks").arg(mult)); + } + else if(type == MyMoneySchedule::OCCUR_EVERYHALFMONTH) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Every half month"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("Every %1 half months").arg(mult)); + } + else if(type == MyMoneySchedule::OCCUR_MONTHLY) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Monthly"); break; + case 2: occurenceString = I18N_NOOP("Every two months"); break; + case 3: occurenceString = I18N_NOOP("Every three months"); break; + case 4: occurenceString = I18N_NOOP("Every four months"); break; + case 6: occurenceString = I18N_NOOP("Twice yearly"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("Every %1 months").arg(mult)); + } + else if(type == MyMoneySchedule::OCCUR_YEARLY) + switch (mult) + { + case 1: occurenceString = I18N_NOOP("Yearly"); break; + case 2: occurenceString = I18N_NOOP("Every other year"); break; + default: occurenceString = I18N_NOOP(static_cast<QString>("Every %1 years").arg(mult)); + } + return occurenceString; +} + +QString MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::occurenceE type) +{ + QString occurenceString = I18N_NOOP("Any"); + + if(type == MyMoneySchedule::OCCUR_ONCE) + occurenceString = I18N_NOOP("Once"); + else if(type == MyMoneySchedule::OCCUR_DAILY) + occurenceString = I18N_NOOP("Day"); + else if(type == MyMoneySchedule::OCCUR_WEEKLY) + occurenceString = I18N_NOOP("Week"); + else if(type == MyMoneySchedule::OCCUR_EVERYHALFMONTH) + occurenceString = I18N_NOOP("Half-month"); + else if(type == MyMoneySchedule::OCCUR_MONTHLY) + occurenceString = I18N_NOOP("Month"); + else if(type == MyMoneySchedule::OCCUR_YEARLY) + occurenceString = I18N_NOOP("Year"); + return occurenceString; +} + +QString MyMoneySchedule::scheduleTypeToString(MyMoneySchedule::typeE type) +{ + QString text; + + switch (type) { + case MyMoneySchedule::TYPE_BILL: + text = I18N_NOOP("Bill"); + break; + case MyMoneySchedule::TYPE_DEPOSIT: + text = I18N_NOOP("Deposit"); + break; + case MyMoneySchedule::TYPE_TRANSFER: + text = I18N_NOOP("Transfer"); + break; + case MyMoneySchedule::TYPE_LOANPAYMENT: + text = I18N_NOOP("Loan payment"); + break; + case MyMoneySchedule::TYPE_ANY: + default: + text = I18N_NOOP("Unknown"); + } + return text; +} + + +QString MyMoneySchedule::paymentMethodToString(MyMoneySchedule::paymentTypeE paymentType) +{ + QString text; + + switch (paymentType) { + case MyMoneySchedule::STYPE_DIRECTDEBIT: + text = I18N_NOOP("Direct debit"); + break; + case MyMoneySchedule::STYPE_DIRECTDEPOSIT: + text = I18N_NOOP("Direct deposit"); + break; + case MyMoneySchedule::STYPE_MANUALDEPOSIT: + text = I18N_NOOP("Manual deposit"); + break; + case MyMoneySchedule::STYPE_OTHER: + text = I18N_NOOP("Other"); + break; + case MyMoneySchedule::STYPE_WRITECHEQUE: + text = I18N_NOOP("Write check"); + break; + case MyMoneySchedule::STYPE_STANDINGORDER: + text = I18N_NOOP("Standing order"); + break; + case MyMoneySchedule::STYPE_BANKTRANSFER: + text = I18N_NOOP("Bank transfer"); + break; + case MyMoneySchedule::STYPE_ANY: + text = I18N_NOOP("Any (Error)"); + break; + } + return text; +} + +QString MyMoneySchedule::weekendOptionToString(MyMoneySchedule::weekendOptionE weekendOption) +{ + QString text; + + switch (weekendOption) { + case MyMoneySchedule::MoveFriday: + text = I18N_NOOP("Change the date to the previous Friday"); + break; + case MyMoneySchedule::MoveMonday: + text = I18N_NOOP("Change the date to the next Monday"); + break; + case MyMoneySchedule::MoveNothing: + text = I18N_NOOP("Do Nothing"); + break; + } + return text; +} + +// until we don't have the means to store the value +// of the variation, we default to 10% in case this +// scheduled transaction is marked 'not fixed'. +// +// ipwizard 2009-04-18 + +int MyMoneySchedule::variation(void) const +{ + int rc = 0; + if(!isFixed()) { + rc = 10; +#if 0 + QString var = value("kmm-variation"); + if(!var.isEmpty()) + rc = var.toInt(); +#endif + } + return rc; +} + +void MyMoneySchedule::setVariation(int var) +{ +#if 0 + deletePair("kmm-variation"); + if(var != 0) + setValue("kmm-variation", QString("%1").arg(var)); +#endif +} + +int MyMoneySchedule::eventsPerYear(MyMoneySchedule::occurenceE occurence) +{ + int rc = 0; + + switch(occurence) { + case MyMoneySchedule::OCCUR_DAILY: + rc = 365; + break; + case MyMoneySchedule::OCCUR_WEEKLY: + rc = 52; + break; + case MyMoneySchedule::OCCUR_FORTNIGHTLY: + rc = 26; + break; + case MyMoneySchedule::OCCUR_EVERYOTHERWEEK: + rc = 26; + break; + case MyMoneySchedule::OCCUR_EVERYHALFMONTH: + rc = 24; + break; + case MyMoneySchedule::OCCUR_EVERYTHREEWEEKS: + rc = 17; + break; + case MyMoneySchedule::OCCUR_EVERYFOURWEEKS: + rc = 13; + break; + case MyMoneySchedule::OCCUR_MONTHLY: + case MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS: + rc = 12; + break; + case MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS: + rc = 6; + break; + case MyMoneySchedule::OCCUR_EVERYOTHERMONTH: + rc = 6; + break; + case MyMoneySchedule::OCCUR_EVERYTHREEMONTHS: + case MyMoneySchedule::OCCUR_QUARTERLY: + rc = 4; + break; + case MyMoneySchedule::OCCUR_EVERYFOURMONTHS: + rc = 3; + break; + case MyMoneySchedule::OCCUR_TWICEYEARLY: + rc = 2; + break; + case MyMoneySchedule::OCCUR_YEARLY: + rc = 1; + break; + default: + qWarning("Occurence not supported by financial calculator"); + } + + return rc; +} + +int MyMoneySchedule::daysBetweenEvents(MyMoneySchedule::occurenceE occurence) +{ + int rc = 0; + + switch(occurence) { + case MyMoneySchedule::OCCUR_DAILY: + rc = 1; + break; + case MyMoneySchedule::OCCUR_WEEKLY: + rc = 7; + break; + case MyMoneySchedule::OCCUR_FORTNIGHTLY: + rc = 14; + break; + case MyMoneySchedule::OCCUR_EVERYOTHERWEEK: + rc = 14; + break; + case MyMoneySchedule::OCCUR_EVERYHALFMONTH: + rc = 15; + break; + case MyMoneySchedule::OCCUR_EVERYTHREEWEEKS: + rc = 21; + break; + case MyMoneySchedule::OCCUR_EVERYFOURWEEKS: + rc = 28; + break; + case MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS: + rc = 30; + break; + case MyMoneySchedule::OCCUR_MONTHLY: + rc = 30; + break; + case MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS: + rc = 56; + break; + case MyMoneySchedule::OCCUR_EVERYOTHERMONTH: + rc = 60; + break; + case MyMoneySchedule::OCCUR_EVERYTHREEMONTHS: + case MyMoneySchedule::OCCUR_QUARTERLY: + rc = 90; + break; + case MyMoneySchedule::OCCUR_EVERYFOURMONTHS: + rc = 120; + break; + case MyMoneySchedule::OCCUR_TWICEYEARLY: + rc = 180; + break; + case MyMoneySchedule::OCCUR_YEARLY: + rc = 360; + break; + default: + qWarning("Occurence not supported by financial calculator"); + } + + return rc; +} + +QDate MyMoneySchedule::addHalfMonths( QDate date, int mult ) const +{ + QDate newdate = date; + int d, dm; + if ( mult > 0 ) + { + d = newdate.day(); + if ( d <= 12 ) + { + if ( mult % 2 == 0 ) + newdate = newdate.addMonths(mult>>1); + else + newdate = newdate.addMonths(mult>>1).addDays(15); + } + else + for ( int i = 0; i < mult; i++ ) + { + if ( d <= 13 ) + newdate = newdate.addDays(15); + else + { + dm = newdate.daysInMonth(); + if ( d == 14 ) + newdate = newdate.addDays(( dm < 30 ) ? dm - d : 15); + else if ( d == 15 ) + newdate = newdate.addDays(dm - d); + else if ( d == dm ) + newdate = newdate.addDays(15 - d).addMonths(1); + else + newdate = newdate.addDays(-15).addMonths(1); + } + d = newdate.day(); + } + } + else if ( mult < 0 ) // Go backwards + for ( int i = 0; i > mult; i-- ) + { + d = newdate.day(); + dm = newdate.daysInMonth(); + if ( d > 15 ) + { + dm = newdate.daysInMonth(); + newdate = newdate.addDays( (d == dm) ? 15 - dm : -15); + } + else if ( d <= 13 ) + newdate = newdate.addMonths(-1).addDays(15); + else if ( d == 15 ) + newdate = newdate.addDays(-15); + else // 14 + { + newdate = newdate.addMonths(-1); + dm = newdate.daysInMonth(); + newdate = newdate.addDays(( dm < 30 ) ? dm - d : 15 ); + } + } + return newdate; +} + +MyMoneySchedule::occurenceE MyMoneySchedule::stringToOccurence(const QString& text) +{ + MyMoneySchedule::occurenceE occurence = MyMoneySchedule::OCCUR_ANY; + QString tmp = text.lower(); + + if(tmp == i18n("Once").lower()) + occurence = MyMoneySchedule::OCCUR_ONCE; + else if(tmp == i18n("Daily").lower()) + occurence = MyMoneySchedule::OCCUR_DAILY; + else if(tmp == i18n("Weekly").lower()) + occurence = MyMoneySchedule::OCCUR_WEEKLY; + else if(tmp == i18n("Fortnightly").lower()) + occurence = MyMoneySchedule::OCCUR_FORTNIGHTLY; + else if(tmp == i18n("Every other week").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYOTHERWEEK; + else if(tmp == i18n("Every half month").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYHALFMONTH; + else if(tmp == i18n("Every three weeks").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYTHREEWEEKS; + else if(tmp == i18n("Every four weeks").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYFOURWEEKS; + else if(tmp == i18n("Every thirty days").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS; + else if(tmp == i18n("Monthly").lower()) + occurence = MyMoneySchedule::OCCUR_MONTHLY; + else if(tmp == i18n("Every eight weeks").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS; + else if(tmp == i18n("Every two months").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYOTHERMONTH; + else if(tmp == i18n("Every three months").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYTHREEMONTHS; + else if(tmp == i18n("Quarterly").lower()) + occurence = MyMoneySchedule::OCCUR_QUARTERLY; + else if(tmp == i18n("Every four months").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYFOURMONTHS; + else if(tmp == i18n("Twice yearly").lower()) + occurence = MyMoneySchedule::OCCUR_TWICEYEARLY; + else if(tmp == i18n("Yearly").lower()) + occurence = MyMoneySchedule::OCCUR_YEARLY; + else if(tmp == i18n("Every other year").lower()) + occurence = MyMoneySchedule::OCCUR_EVERYOTHERYEAR; + + return occurence; +} + +/** + * Helper method to convert simple occurence to compound occurence + multiplier + * + * @param multiplier Returned by reference. Adjusted multiplier + * @param occurence Returned by reference. Occurence type + */ +void MyMoneySchedule::simpleToCompoundOccurence(int& multiplier,occurenceE& occurence) +{ + occurenceE newOcc = occurence; + int newMulti = 1; + if (occurence == MyMoneySchedule::OCCUR_ONCE || + occurence == MyMoneySchedule::OCCUR_DAILY || + occurence == MyMoneySchedule::OCCUR_WEEKLY || + occurence == MyMoneySchedule::OCCUR_EVERYHALFMONTH || + occurence == MyMoneySchedule::OCCUR_MONTHLY || + occurence == MyMoneySchedule::OCCUR_YEARLY ) + { // Already a base occurence and multiplier + } + else if(occurence == MyMoneySchedule::OCCUR_FORTNIGHTLY || + occurence == MyMoneySchedule::OCCUR_EVERYOTHERWEEK) + { + newOcc = MyMoneySchedule::OCCUR_WEEKLY; + newMulti = 2; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHREEWEEKS) + { + newOcc = MyMoneySchedule::OCCUR_WEEKLY; + newMulti = 3; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYFOURWEEKS) + { + newOcc = MyMoneySchedule::OCCUR_WEEKLY; + newMulti = 4; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS) + { + newOcc = MyMoneySchedule::OCCUR_DAILY; + newMulti = 30; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS) + { + newOcc = MyMoneySchedule::OCCUR_WEEKLY; + newMulti = 8; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYOTHERMONTH) + { + newOcc = MyMoneySchedule::OCCUR_MONTHLY; + newMulti = 2; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYTHREEMONTHS || + occurence == MyMoneySchedule::OCCUR_QUARTERLY ) + { + newOcc = MyMoneySchedule::OCCUR_MONTHLY; + newMulti = 3; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYFOURMONTHS) + { + newOcc = MyMoneySchedule::OCCUR_MONTHLY; + newMulti = 4; + } + else if(occurence == MyMoneySchedule::OCCUR_TWICEYEARLY) + { + newOcc = MyMoneySchedule::OCCUR_MONTHLY; + newMulti = 6; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYOTHERYEAR) + { + newOcc = MyMoneySchedule::OCCUR_YEARLY; + newMulti = 2; + } + else // Unknown + { + newOcc = MyMoneySchedule::OCCUR_ANY; + newMulti = 1; + } + if (newOcc != occurence) + { + occurence = newOcc; + multiplier = newMulti == 1 ? multiplier : newMulti * multiplier; + } +} + +/** + * Helper method to convert compound occurence + multiplier to simple occurence + * + * @param multiplier Returned by reference. Adjusted multiplier + * @param occurence Returned by reference. Occurence type + */ +void MyMoneySchedule::compoundToSimpleOccurence(int& multiplier,occurenceE& occurence) +{ + occurenceE newOcc = occurence; + if(occurence == MyMoneySchedule::OCCUR_ONCE) + { // Nothing to do + } + else if(occurence == MyMoneySchedule::OCCUR_DAILY) + { + switch (multiplier) + { + case 1: break; + case 30: newOcc = MyMoneySchedule::OCCUR_EVERYTHIRTYDAYS; break; + } + } + else if(newOcc == MyMoneySchedule::OCCUR_WEEKLY) + { + switch (multiplier) + { + case 1: break; + case 2: newOcc = MyMoneySchedule::OCCUR_EVERYOTHERWEEK; break; + case 3: newOcc = MyMoneySchedule::OCCUR_EVERYTHREEWEEKS; break; + case 4: newOcc = MyMoneySchedule::OCCUR_EVERYFOURWEEKS; break; + case 8: newOcc = MyMoneySchedule::OCCUR_EVERYEIGHTWEEKS; break; + } + } + else if(occurence == MyMoneySchedule::OCCUR_MONTHLY) + switch (multiplier) + { + case 1: break; + case 2: newOcc = MyMoneySchedule::OCCUR_EVERYOTHERMONTH; break; + case 3: newOcc = MyMoneySchedule::OCCUR_EVERYTHREEMONTHS; break; + case 4: newOcc = MyMoneySchedule::OCCUR_EVERYFOURMONTHS; break; + case 6: newOcc = MyMoneySchedule::OCCUR_TWICEYEARLY; break; + } + else if(occurence == MyMoneySchedule::OCCUR_EVERYHALFMONTH) + switch (multiplier) + { + case 1: break; + } + else if(occurence == MyMoneySchedule::OCCUR_YEARLY) + { + switch (multiplier) + { + case 1: break; + case 2: newOcc = MyMoneySchedule::OCCUR_EVERYOTHERYEAR; break; + } + } + if (occurence != newOcc ) // Changed to derived type + { + occurence = newOcc; + multiplier = 1; + } +} |