/*************************************************************************** mymoneyscheduled.cpp ------------------- copyright : (C) 2000-2002 by Michael Edwardes (C) 2007 by Thomas Baumgart email : mte@users.sourceforge.net Thomas Baumgart ***************************************************************************/ /*************************************************************************** * * * 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 // ---------------------------------------------------------------------------- // 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 = TQDate(); m_endDate = TQDate(); m_lastPayment = TQDate(); m_weekendOption = MoveNothing; } MyMoneySchedule::MyMoneySchedule(const TQString& name, typeE type, occurenceE occurence, int occurenceMultiplier, paymentTypeE paymentType, const TQDate& /* startDate */, const TQDate& 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 = TQDate(); m_endDate = endDate; m_lastPayment = TQDate(); m_weekendOption = MoveNothing; } MyMoneySchedule::MyMoneySchedule(const TQDomElement& 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(node.attribute("type").toInt()); m_paymentType = static_cast(node.attribute("paymentType").toInt()); m_occurence = static_cast(node.attribute("occurence").toInt()); m_occurenceMultiplier = node.attribute("occurenceMultiplier", "1").toInt(); // Convert to compound occurence simpleToCompoundOccurence(m_occurenceMultiplier,m_occurence); m_autoEnter = static_cast(node.attribute("autoEnter").toInt()); m_fixed = static_cast(node.attribute("fixed").toInt()); m_weekendOption = static_cast(node.attribute("weekendOption").toInt()); // read in the associated transaction TQDomNodeList 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(TQDate()); m_transaction.setEntryDate(TQDate()); } // 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 = TQDate(); } // 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(TQDate()); } if(!nextDueDate().isValid()) { m_transaction.setPostDate(m_startDate); m_transaction.setPostDate(nextPayment(m_lastPayment.addDays(1))); } } MyMoneySchedule::MyMoneySchedule(const TQString& 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 TQDate& 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. TQValueList splits = t.splits(); if(splits.count() > 0) { TQValueList::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(TQString()); 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() != TQString("Phony-ID")) { MyMoneyAccount acc = file->account(s.accountId()); if(acc.isIncomeExpense()) { s.setPayeeId(TQString()); 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 TQDate& date) { m_endDate = date; } void MyMoneySchedule::setAutoEnter(bool autoenter) { m_autoEnter = autoenter; } const TQDate& MyMoneySchedule::startDate(void) const { if(m_startDate.isValid()) return m_startDate; return nextDueDate(); } const TQDate& MyMoneySchedule::nextDueDate(void) const { return m_transaction.postDate(); } TQDate MyMoneySchedule::adjustedNextDueDate(void) const { if(isFinished()) return TQDate(); return adjustedDate(nextDueDate(), weekendOption()); } TQDate MyMoneySchedule::adjustedDate(TQDate 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 TQDate& date) { if(date.isValid()) { m_transaction.setPostDate(date); m_startDate = date; } } void MyMoneySchedule::setLastPayment(const TQDate& date) { // Delete all payments older than date TQValueList::Iterator it; TQValueList 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 TQString& 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_WRITECHETQUE) 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; } } TQDate MyMoneySchedule::adjustedNextPayment(const TQDate& refDate) const { TQDate date(nextPayment(refDate)); return date.isValid() ? adjustedDate(date, weekendOption()) : date; } TQDate MyMoneySchedule::nextPayment(const TQDate& 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 TQDate(); } TQDate 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 TQDate(); // if the only payment should have been prior to the reference date, // then don't show it if(paymentDate < refDate) return TQDate(); 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 = TQDate(); break; } } if(paymentDate.isValid()) { if(m_endDate.isValid() && paymentDate > m_endDate) paymentDate = TQDate(); } if (paymentDate.isValid() && m_recordedPayments.tqcontains(paymentDate)) paymentDate = nextPayment(paymentDate); return paymentDate; } TQValueList MyMoneySchedule::paymentDates(const TQDate& _startDate, const TQDate& _endDate) const { TQDate paymentDate(nextDueDate()); TQValueList theDates; TQDate endDate(_endDate); if ( willEnd() && m_endDate < endDate ) endDate = m_endDate; weekendOptionE option(weekendOption()); TQDate 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; TQDate 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()) { TQValueList dates = paymentDates(m_lastPayment, m_endDate); // Dont include the last payment so -1 counter = dates.count(); } return counter; } MyMoneyAccount MyMoneySchedule::account(int cnt) const { TQValueList splits = m_transaction.splits(); TQValueList::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(); } TQDate MyMoneySchedule::dateAfter(int transactions) const { int counter=1; TQDate 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() >= TQDate::tqcurrentDate()) 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 TQDate& date) const { // m_lastPayment should always be > recordedPayments() if (m_lastPayment.isValid() && m_lastPayment >= date) return true; if (m_recordedPayments.tqcontains(date)) return true; return false; } void MyMoneySchedule::recordPayment(const TQDate& 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(TQDate& date) const { TQDate fixDate(m_startDate); if(fixDate.isValid() && date.day() != fixDate.day() && TQDate::isValid(date.year(), date.month(), fixDate.day())) { date.setYMD(date.year(), date.month(), fixDate.day()); } } void MyMoneySchedule::writeXML(TQDomDocument& document, TQDomElement& tqparent) const { TQDomElement 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. TQValueList payments = recordedPayments(); TQValueList::ConstIterator it; TQDomElement paymentsElement = document.createElement("PAYMENTS"); for (it=payments.begin(); it!=payments.end(); ++it) { TQDomElement 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); tqparent.appendChild(el); } bool MyMoneySchedule::hasReferenceTo(const TQString& id) const { return m_transaction.hasReferenceTo(id); } TQString MyMoneySchedule::occurenceToString() const { return occurenceToString( occurenceMultiplier(), occurencePeriod() ); } TQString MyMoneySchedule::occurenceToString(occurenceE occurence) { TQString 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_TQUARTERLY) 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; } TQString MyMoneySchedule::occurenceToString(int mult, occurenceE type) { TQString occurenceString = I18N_NOOP("Any"); if (type == MyMoneySchedule::OCCUR_ONCE) switch (mult) { case 1: occurenceString = I18N_NOOP("Once"); break; default: occurenceString = I18N_NOOP(static_cast("%1 times").tqarg(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("Every %1 days").tqarg(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("Every %1 weeks").tqarg(mult)); } else if(type == MyMoneySchedule::OCCUR_EVERYHALFMONTH) switch (mult) { case 1: occurenceString = I18N_NOOP("Every half month"); break; default: occurenceString = I18N_NOOP(static_cast("Every %1 half months").tqarg(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("Every %1 months").tqarg(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("Every %1 years").tqarg(mult)); } return occurenceString; } TQString MyMoneySchedule::occurencePeriodToString(MyMoneySchedule::occurenceE type) { TQString 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; } TQString MyMoneySchedule::scheduleTypeToString(MyMoneySchedule::typeE type) { TQString 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; } TQString MyMoneySchedule::paymentMethodToString(MyMoneySchedule::paymentTypeE paymentType) { TQString 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_WRITECHETQUE: 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; } TQString MyMoneySchedule::weekendOptionToString(MyMoneySchedule::weekendOptionE weekendOption) { TQString 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 TQString 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", TQString("%1").tqarg(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_TQUARTERLY: 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_TQUARTERLY: 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; } TQDate MyMoneySchedule::addHalfMonths( TQDate date, int mult ) const { TQDate 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 TQString& text) { MyMoneySchedule::occurenceE occurence = MyMoneySchedule::OCCUR_ANY; TQString 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_TQUARTERLY; 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_TQUARTERLY ) { 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; } }