File indexing completed on 2024-05-12 16:42:40
0001 /* 0002 SPDX-FileCopyrightText: 2000-2004 Michael Edwardes <mte@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2002-2018 Thomas Baumgart <tbaumgart@kde.org> 0004 SPDX-FileCopyrightText: 2005 Ace Jones <acejones@users.sourceforge.net> 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "mymoneyschedule.h" 0009 #include "mymoneyschedule_p.h" 0010 0011 // ---------------------------------------------------------------------------- 0012 // QT Includes 0013 0014 #include <QList> 0015 #include <QMap> 0016 0017 // ---------------------------------------------------------------------------- 0018 // KDE Includes 0019 0020 #include <KLocalizedString> 0021 0022 // ---------------------------------------------------------------------------- 0023 // Project Includes 0024 0025 #include "mymoneyutils.h" 0026 #include "mymoneyexception.h" 0027 #include "mymoneyfile.h" 0028 #include "mymoneyaccount.h" 0029 #include "mymoneysplit.h" 0030 #include "imymoneyprocessingcalendar.h" 0031 0032 using namespace eMyMoney; 0033 0034 static IMyMoneyProcessingCalendar* processingCalendarPtr = 0; 0035 0036 MyMoneySchedule::MyMoneySchedule() : 0037 MyMoneyObject(*new MyMoneySchedulePrivate) 0038 { 0039 } 0040 0041 MyMoneySchedule::MyMoneySchedule(const QString &id) : 0042 MyMoneyObject(*new MyMoneySchedulePrivate, id) 0043 { 0044 } 0045 0046 MyMoneySchedule::MyMoneySchedule(const QString& name, 0047 Schedule::Type type, 0048 Schedule::Occurrence occurrence, 0049 int occurrenceMultiplier, 0050 Schedule::PaymentType paymentType, 0051 const QDate& /* startDate */, 0052 const QDate& endDate, 0053 bool fixed, 0054 bool autoEnter) : 0055 MyMoneyObject(*new MyMoneySchedulePrivate) 0056 { 0057 Q_D(MyMoneySchedule); 0058 // Set up the values possibly differing from defaults 0059 d->m_name = name; 0060 d->m_occurrence = occurrence; 0061 d->m_occurrenceMultiplier = occurrenceMultiplier; 0062 simpleToCompoundOccurrence(d->m_occurrenceMultiplier, d->m_occurrence); 0063 d->m_type = type; 0064 d->m_paymentType = paymentType; 0065 d->m_fixed = fixed; 0066 d->m_autoEnter = autoEnter; 0067 d->m_endDate = endDate; 0068 } 0069 0070 MyMoneySchedule::MyMoneySchedule(const MyMoneySchedule& other) : 0071 MyMoneyObject(*new MyMoneySchedulePrivate(*other.d_func()), other.id()) 0072 { 0073 } 0074 0075 MyMoneySchedule::MyMoneySchedule(const QString& id, const MyMoneySchedule& other) : 0076 MyMoneyObject(*new MyMoneySchedulePrivate(*other.d_func()), id) 0077 { 0078 } 0079 0080 MyMoneySchedule::~MyMoneySchedule() 0081 { 0082 } 0083 0084 Schedule::Occurrence MyMoneySchedule::baseOccurrence() const 0085 { 0086 Q_D(const MyMoneySchedule); 0087 Schedule::Occurrence occ = d->m_occurrence; 0088 int mult = d->m_occurrenceMultiplier; 0089 compoundToSimpleOccurrence(mult, occ); 0090 return occ; 0091 } 0092 0093 int MyMoneySchedule::occurrenceMultiplier() const 0094 { 0095 Q_D(const MyMoneySchedule); 0096 return d->m_occurrenceMultiplier; 0097 } 0098 0099 eMyMoney::Schedule::Type MyMoneySchedule::type() const 0100 { 0101 Q_D(const MyMoneySchedule); 0102 return d->m_type; 0103 } 0104 0105 eMyMoney::Schedule::Occurrence MyMoneySchedule::occurrence() const 0106 { 0107 Q_D(const MyMoneySchedule); 0108 return d->m_occurrence; 0109 } 0110 0111 void MyMoneySchedule::setStartDate(const QDate& date) 0112 { 0113 Q_D(MyMoneySchedule); 0114 d->m_startDate = date; 0115 } 0116 0117 void MyMoneySchedule::setPaymentType(Schedule::PaymentType type) 0118 { 0119 Q_D(MyMoneySchedule); 0120 d->m_paymentType = type; 0121 } 0122 0123 void MyMoneySchedule::setFixed(bool fixed) 0124 { 0125 Q_D(MyMoneySchedule); 0126 d->m_fixed = fixed; 0127 } 0128 0129 void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction) 0130 { 0131 setTransaction(transaction, false); 0132 } 0133 0134 void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction, bool noDateCheck) 0135 { 0136 auto t = transaction; 0137 Q_D(MyMoneySchedule); 0138 if (!noDateCheck) { 0139 // don't allow a transaction that has no due date 0140 // if we get something like that, then we use the 0141 // the current next due date. If that is also invalid 0142 // we can't help it. 0143 if (!t.postDate().isValid()) { 0144 t.setPostDate(d->m_transaction.postDate()); 0145 } 0146 0147 if (!t.postDate().isValid()) 0148 return; 0149 } 0150 0151 // make sure to clear out some unused information in scheduled transactions 0152 // we need to do this for the case that the transaction passed as argument 0153 // is a matched or imported transaction. 0154 auto firstSplit = true; 0155 foreach (const auto split, t.splits()) { 0156 MyMoneySplit s = split; 0157 // clear out the bankID 0158 if (!split.bankID().isEmpty()) { 0159 s.setBankID(QString()); 0160 t.modifySplit(s); 0161 } 0162 0163 // only clear payees from second split onwards 0164 if (firstSplit) { 0165 firstSplit = false; 0166 continue; 0167 } 0168 0169 if (!split.payeeId().isEmpty()) { 0170 // but only if the split references an income/expense category 0171 auto file = MyMoneyFile::instance(); 0172 // some unit tests don't have a storage attached, so we 0173 // simply skip the test 0174 // Don't check for accounts with an id of 'Phony-ID' which is used 0175 // internally for non-existing accounts (during creation of accounts) 0176 if (file->storageAttached() && s.accountId() != QString("Phony-ID")) { 0177 auto acc = file->account(s.accountId()); 0178 if (acc.isIncomeExpense()) { 0179 s.setPayeeId(QString()); 0180 t.modifySplit(s); 0181 } 0182 } 0183 } 0184 } 0185 0186 d->m_transaction = t; 0187 // make sure that the transaction does not have an id so that we can enter 0188 // it into the engine 0189 d->m_transaction.clearId(); 0190 } 0191 0192 void MyMoneySchedule::setEndDate(const QDate& date) 0193 { 0194 Q_D(MyMoneySchedule); 0195 d->m_endDate = date; 0196 } 0197 0198 void MyMoneySchedule::setLastDayInMonth(bool state) 0199 { 0200 Q_D(MyMoneySchedule); 0201 d->m_lastDayInMonth = state; 0202 } 0203 0204 void MyMoneySchedule::setAutoEnter(bool autoenter) 0205 { 0206 Q_D(MyMoneySchedule); 0207 d->m_autoEnter = autoenter; 0208 } 0209 0210 QDate MyMoneySchedule::startDate() const 0211 { 0212 Q_D(const MyMoneySchedule); 0213 if (d->m_startDate.isValid()) 0214 return d->m_startDate; 0215 return nextDueDate(); 0216 } 0217 0218 eMyMoney::Schedule::PaymentType MyMoneySchedule::paymentType() const 0219 { 0220 Q_D(const MyMoneySchedule); 0221 return d->m_paymentType; 0222 } 0223 0224 /** 0225 * Simple get method that returns true if the schedule is fixed. 0226 * 0227 * @return bool To indicate whether the instance is fixed. 0228 */ 0229 bool MyMoneySchedule::isFixed() const 0230 { 0231 Q_D(const MyMoneySchedule); 0232 return d->m_fixed; 0233 } 0234 0235 /** 0236 * Simple get method that returns true if the schedule will end 0237 * at some time. 0238 * 0239 * @return bool Indicates whether the instance will end. 0240 */ 0241 bool MyMoneySchedule::willEnd() const 0242 { 0243 Q_D(const MyMoneySchedule); 0244 return d->m_endDate.isValid(); 0245 } 0246 0247 0248 QDate MyMoneySchedule::nextDueDate() const 0249 { 0250 Q_D(const MyMoneySchedule); 0251 0252 if (lastDayInMonth()) { 0253 const auto date = d->m_transaction.postDate(); 0254 return adjustedDate(QDate(date.year(), date.month(), date.daysInMonth()), weekendOption()); 0255 } 0256 0257 return d->m_transaction.postDate(); 0258 } 0259 0260 QDate MyMoneySchedule::adjustedNextDueDate() const 0261 { 0262 if (isFinished()) 0263 return QDate(); 0264 0265 return adjustedDate(nextDueDate(), weekendOption()); 0266 } 0267 0268 QDate MyMoneySchedule::adjustedDate(QDate date, Schedule::WeekendOption option) const 0269 { 0270 if (!date.isValid() || option == Schedule::WeekendOption::MoveNothing || isProcessingDate(date)) 0271 return date; 0272 0273 int step = 1; 0274 if (option == Schedule::WeekendOption::MoveBefore) 0275 step = -1; 0276 0277 while (!isProcessingDate(date)) 0278 date = date.addDays(step); 0279 0280 return date; 0281 } 0282 0283 void MyMoneySchedule::setNextDueDate(const QDate& date) 0284 { 0285 Q_D(MyMoneySchedule); 0286 if (date.isValid()) { 0287 d->m_transaction.setPostDate(date); 0288 // m_startDate = date; 0289 } 0290 } 0291 0292 void MyMoneySchedule::setLastPayment(const QDate& date) 0293 { 0294 Q_D(MyMoneySchedule); 0295 // Delete all payments older than date 0296 QList<QDate>::Iterator it; 0297 QList<QDate> delList; 0298 0299 for (it = d->m_recordedPayments.begin(); it != d->m_recordedPayments.end(); ++it) { 0300 if (*it < date || !date.isValid()) 0301 delList.append(*it); 0302 } 0303 0304 for (it = delList.begin(); it != delList.end(); ++it) { 0305 d->m_recordedPayments.removeAll(*it); 0306 } 0307 0308 d->m_lastPayment = date; 0309 if (!d->m_startDate.isValid()) 0310 d->m_startDate = date; 0311 } 0312 0313 QString MyMoneySchedule::name() const 0314 { 0315 Q_D(const MyMoneySchedule); 0316 return d->m_name; 0317 } 0318 0319 void MyMoneySchedule::setName(const QString& nm) 0320 { 0321 Q_D(MyMoneySchedule); 0322 d->m_name = nm; 0323 } 0324 0325 eMyMoney::Schedule::WeekendOption MyMoneySchedule::weekendOption() const 0326 { 0327 Q_D(const MyMoneySchedule); 0328 return d->m_weekendOption; 0329 } 0330 0331 void MyMoneySchedule::setOccurrence(Schedule::Occurrence occ) 0332 { 0333 auto occ2 = occ; 0334 auto mult = 1; 0335 simpleToCompoundOccurrence(mult, occ2); 0336 setOccurrencePeriod(occ2); 0337 setOccurrenceMultiplier(mult); 0338 } 0339 0340 void MyMoneySchedule::setOccurrencePeriod(Schedule::Occurrence occ) 0341 { 0342 Q_D(MyMoneySchedule); 0343 d->m_occurrence = occ; 0344 } 0345 0346 void MyMoneySchedule::setOccurrenceMultiplier(int occmultiplier) 0347 { 0348 Q_D(MyMoneySchedule); 0349 d->m_occurrenceMultiplier = occmultiplier < 1 ? 1 : occmultiplier; 0350 } 0351 0352 void MyMoneySchedule::setType(Schedule::Type type) 0353 { 0354 Q_D(MyMoneySchedule); 0355 d->m_type = type; 0356 } 0357 0358 void MyMoneySchedule::validate(bool id_check) const 0359 { 0360 /* Check the supplied instance is valid... 0361 * 0362 * To be valid it must not have the id set and have the following fields set: 0363 * 0364 * m_occurrence 0365 * m_type 0366 * m_startDate 0367 * m_paymentType 0368 * m_transaction 0369 * the transaction must contain at least one split (two is better ;-) ) 0370 */ 0371 Q_D(const MyMoneySchedule); 0372 if (id_check && !d->m_id.isEmpty()) 0373 throw MYMONEYEXCEPTION_CSTRING("ID for schedule not empty when required"); 0374 0375 if (d->m_occurrence == Schedule::Occurrence::Any) 0376 throw MYMONEYEXCEPTION_CSTRING("Invalid occurrence type for schedule"); 0377 0378 if (d->m_type == Schedule::Type::Any) 0379 throw MYMONEYEXCEPTION_CSTRING("Invalid type for schedule"); 0380 0381 if (!nextDueDate().isValid()) 0382 throw MYMONEYEXCEPTION_CSTRING("Invalid next due date for schedule"); 0383 0384 if (d->m_paymentType == Schedule::PaymentType::Any) 0385 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for schedule"); 0386 0387 if (d->m_transaction.splitCount() == 0) 0388 throw MYMONEYEXCEPTION_CSTRING("Scheduled transaction does not contain splits"); 0389 0390 // Check the payment types 0391 switch (d->m_type) { 0392 case Schedule::Type::Bill: 0393 if (d->m_paymentType == Schedule::PaymentType::DirectDeposit || d->m_paymentType == Schedule::PaymentType::ManualDeposit) 0394 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for bills"); 0395 break; 0396 0397 case Schedule::Type::Deposit: 0398 if (d->m_paymentType == Schedule::PaymentType::DirectDebit || d->m_paymentType == Schedule::PaymentType::WriteChecque) 0399 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for deposits"); 0400 break; 0401 0402 case Schedule::Type::Any: 0403 throw MYMONEYEXCEPTION_CSTRING("Invalid type ANY"); 0404 break; 0405 0406 case Schedule::Type::Transfer: 0407 // if (m_paymentType == DirectDeposit || m_paymentType == ManualDeposit) 0408 // return false; 0409 break; 0410 0411 case Schedule::Type::LoanPayment: 0412 break; 0413 } 0414 } 0415 0416 QDate MyMoneySchedule::adjustedNextPayment(const QDate& refDate) const 0417 { 0418 return nextPaymentDate(true, refDate); 0419 } 0420 0421 QDate MyMoneySchedule::adjustedNextPayment() const 0422 { 0423 return adjustedNextPayment(QDate::currentDate()); 0424 } 0425 0426 QDate MyMoneySchedule::nextPayment(const QDate& refDate) const 0427 { 0428 return nextPaymentDate(false, refDate); 0429 } 0430 0431 QDate MyMoneySchedule::nextPayment() const 0432 { 0433 return nextPayment(QDate::currentDate()); 0434 } 0435 0436 QDate MyMoneySchedule::nextPaymentDate(const bool& adjust, const QDate& refDate) const 0437 { 0438 Schedule::WeekendOption option(adjust ? weekendOption() : 0439 Schedule::WeekendOption::MoveNothing); 0440 0441 Q_D(const MyMoneySchedule); 0442 QDate adjEndDate(adjustedDate(d->m_endDate, option)); 0443 0444 // if the enddate is valid and it is before the reference date, 0445 // then there will be no more payments. 0446 if (adjEndDate.isValid() && adjEndDate < refDate) { 0447 return QDate(); 0448 } 0449 0450 QDate dueDate(nextDueDate()); 0451 QDate paymentDate(adjustedDate(dueDate, option)); 0452 0453 if (paymentDate.isValid() && 0454 (paymentDate <= refDate || d->m_recordedPayments.contains(dueDate))) { 0455 switch (d->m_occurrence) { 0456 case Schedule::Occurrence::Once: 0457 // If the lastPayment is already set or the payment should have been 0458 // prior to the reference date then invalidate the payment date. 0459 if (d->m_lastPayment.isValid() || paymentDate <= refDate) 0460 paymentDate = QDate(); 0461 break; 0462 0463 case Schedule::Occurrence::Daily: { 0464 int step = d->m_occurrenceMultiplier; 0465 do { 0466 dueDate = dueDate.addDays(step); 0467 paymentDate = adjustedDate(dueDate, option); 0468 } while (paymentDate.isValid() && 0469 (paymentDate <= refDate || 0470 d->m_recordedPayments.contains(dueDate))); 0471 } 0472 break; 0473 0474 case Schedule::Occurrence::Weekly: { 0475 int step = 7 * d->m_occurrenceMultiplier; 0476 do { 0477 dueDate = dueDate.addDays(step); 0478 paymentDate = adjustedDate(dueDate, option); 0479 } while (paymentDate.isValid() && 0480 (paymentDate <= refDate || 0481 d->m_recordedPayments.contains(dueDate))); 0482 } 0483 break; 0484 0485 case Schedule::Occurrence::EveryHalfMonth: 0486 do { 0487 dueDate = addHalfMonths(dueDate, d->m_occurrenceMultiplier); 0488 paymentDate = adjustedDate(dueDate, option); 0489 } while (paymentDate.isValid() && 0490 (paymentDate <= refDate || 0491 d->m_recordedPayments.contains(dueDate))); 0492 break; 0493 0494 case Schedule::Occurrence::Monthly: 0495 do { 0496 dueDate = dueDate.addMonths(d->m_occurrenceMultiplier); 0497 fixDate(dueDate); 0498 paymentDate = adjustedDate(dueDate, option); 0499 } while (paymentDate.isValid() && 0500 (paymentDate <= refDate || 0501 d->m_recordedPayments.contains(dueDate))); 0502 break; 0503 0504 case Schedule::Occurrence::Yearly: 0505 do { 0506 dueDate = dueDate.addYears(d->m_occurrenceMultiplier); 0507 fixDate(dueDate); 0508 paymentDate = adjustedDate(dueDate, option); 0509 } while (paymentDate.isValid() && 0510 (paymentDate <= refDate || 0511 d->m_recordedPayments.contains(dueDate))); 0512 break; 0513 0514 case Schedule::Occurrence::Any: 0515 default: 0516 paymentDate = QDate(); 0517 break; 0518 } 0519 } 0520 if (paymentDate.isValid() && adjEndDate.isValid() && paymentDate > adjEndDate) 0521 paymentDate = QDate(); 0522 0523 return paymentDate; 0524 } 0525 0526 QDate MyMoneySchedule::nextPaymentDate(const bool& adjust) const 0527 { 0528 return nextPaymentDate(adjust, QDate::currentDate()); 0529 } 0530 0531 QList<QDate> MyMoneySchedule::paymentDates(const QDate& _startDate, const QDate& _endDate) const 0532 { 0533 QDate paymentDate(nextDueDate()); 0534 QList<QDate> theDates; 0535 0536 Schedule::WeekendOption option(weekendOption()); 0537 0538 Q_D(const MyMoneySchedule); 0539 QDate endDate(_endDate); 0540 if (willEnd() && d->m_endDate < endDate) { 0541 // consider the adjusted end date instead of the plain end date 0542 endDate = adjustedDate(d->m_endDate, option); 0543 } 0544 0545 QDate start_date(adjustedDate(startDate(), option)); 0546 // if the period specified by the parameters and the adjusted period 0547 // defined for this schedule don't overlap, then the list remains empty 0548 if ((willEnd() && adjustedDate(d->m_endDate, option) < _startDate) 0549 || start_date > endDate) 0550 return theDates; 0551 0552 QDate date(adjustedDate(paymentDate, option)); 0553 0554 switch (d->m_occurrence) { 0555 case Schedule::Occurrence::Once: 0556 if (start_date >= _startDate && start_date <= endDate) 0557 theDates.append(start_date); 0558 break; 0559 0560 case Schedule::Occurrence::Daily: 0561 while (date.isValid() && (date <= endDate)) { 0562 if (date >= _startDate) 0563 theDates.append(date); 0564 paymentDate = paymentDate.addDays(d->m_occurrenceMultiplier); 0565 date = adjustedDate(paymentDate, option); 0566 } 0567 break; 0568 0569 case Schedule::Occurrence::Weekly: { 0570 int step = 7 * d->m_occurrenceMultiplier; 0571 while (date.isValid() && (date <= endDate)) { 0572 if (date >= _startDate) 0573 theDates.append(date); 0574 paymentDate = paymentDate.addDays(step); 0575 date = adjustedDate(paymentDate, option); 0576 } 0577 } 0578 break; 0579 0580 case Schedule::Occurrence::EveryHalfMonth: 0581 while (date.isValid() && (date <= endDate)) { 0582 if (date >= _startDate) 0583 theDates.append(date); 0584 paymentDate = addHalfMonths(paymentDate, d->m_occurrenceMultiplier); 0585 date = adjustedDate(paymentDate, option); 0586 } 0587 break; 0588 0589 case Schedule::Occurrence::Monthly: 0590 while (date.isValid() && (date <= endDate)) { 0591 if (date >= _startDate) 0592 theDates.append(date); 0593 paymentDate = paymentDate.addMonths(d->m_occurrenceMultiplier); 0594 fixDate(paymentDate); 0595 date = adjustedDate(paymentDate, option); 0596 } 0597 break; 0598 0599 case Schedule::Occurrence::Yearly: 0600 while (date.isValid() && (date <= endDate)) { 0601 if (date >= _startDate) 0602 theDates.append(date); 0603 paymentDate = paymentDate.addYears(d->m_occurrenceMultiplier); 0604 fixDate(paymentDate); 0605 date = adjustedDate(paymentDate, option); 0606 } 0607 break; 0608 0609 case Schedule::Occurrence::Any: 0610 default: 0611 break; 0612 } 0613 0614 return theDates; 0615 } 0616 0617 bool MyMoneySchedule::operator <(const MyMoneySchedule& right) const 0618 { 0619 return adjustedNextDueDate() < right.adjustedNextDueDate(); 0620 } 0621 0622 bool MyMoneySchedule::operator ==(const MyMoneySchedule& right) const 0623 { 0624 Q_D(const MyMoneySchedule); 0625 auto d2 = static_cast<const MyMoneySchedulePrivate *>(right.d_func()); 0626 // clang-format off 0627 if (MyMoneyObject::operator==(right) 0628 && d->m_occurrence == d2->m_occurrence 0629 && d->m_occurrenceMultiplier == d2->m_occurrenceMultiplier 0630 && d->m_type == d2->m_type 0631 && d->m_startDate == d2->m_startDate 0632 && d->m_paymentType == d2->m_paymentType 0633 && d->m_fixed == d2->m_fixed 0634 && d->m_transaction == d2->m_transaction 0635 && d->m_endDate == d2->m_endDate 0636 && d->m_lastDayInMonth == d2->m_lastDayInMonth 0637 && d->m_autoEnter == d2->m_autoEnter 0638 && d->m_lastPayment == d2->m_lastPayment 0639 && ((d->m_name.length() == 0 && d2->m_name.length() == 0) || (d->m_name == d2->m_name))) 0640 return true; 0641 // clang-format on 0642 return false; 0643 } 0644 0645 bool MyMoneySchedule::operator !=(const MyMoneySchedule& right) const 0646 { 0647 return ! operator==(right); 0648 } 0649 0650 int MyMoneySchedule::transactionsRemaining() const 0651 { 0652 Q_D(const MyMoneySchedule); 0653 return transactionsRemainingUntil(adjustedDate(d->m_endDate, weekendOption())); 0654 } 0655 0656 int MyMoneySchedule::transactionsRemainingUntil(const QDate& endDate) const 0657 { 0658 auto counter = 0; 0659 Q_D(const MyMoneySchedule); 0660 0661 const auto beginDate = d->m_lastPayment.isValid() ? d->m_lastPayment : startDate(); 0662 if (beginDate.isValid() && endDate.isValid()) { 0663 QList<QDate> dates = paymentDates(beginDate, endDate); 0664 counter = dates.count(); 0665 } 0666 return counter; 0667 } 0668 0669 QDate MyMoneySchedule::endDate() const 0670 { 0671 Q_D(const MyMoneySchedule); 0672 return d->m_endDate; 0673 } 0674 0675 bool MyMoneySchedule::autoEnter() const 0676 { 0677 Q_D(const MyMoneySchedule); 0678 return d->m_autoEnter; 0679 } 0680 0681 bool MyMoneySchedule::lastDayInMonth() const 0682 { 0683 Q_D(const MyMoneySchedule); 0684 return d->m_lastDayInMonth; 0685 } 0686 0687 MyMoneyTransaction MyMoneySchedule::transaction() const 0688 { 0689 Q_D(const MyMoneySchedule); 0690 return d->m_transaction; 0691 } 0692 0693 QDate MyMoneySchedule::lastPayment() const 0694 { 0695 Q_D(const MyMoneySchedule); 0696 return d->m_lastPayment; 0697 } 0698 0699 MyMoneyAccount MyMoneySchedule::account(int cnt) const 0700 { 0701 Q_D(const MyMoneySchedule); 0702 QList<MyMoneySplit> splits = d->m_transaction.splits(); 0703 QList<MyMoneySplit>::ConstIterator it; 0704 auto file = MyMoneyFile::instance(); 0705 MyMoneyAccount acc; 0706 0707 // search the first asset or liability account 0708 for (it = splits.constBegin(); it != splits.constEnd() && (acc.id().isEmpty() || cnt); ++it) { 0709 try { 0710 acc = file->account((*it).accountId()); 0711 if (acc.isAssetLiability()) 0712 --cnt; 0713 0714 if (!cnt) 0715 return acc; 0716 } catch (const MyMoneyException &) { 0717 qWarning("Schedule '%s' references unknown account '%s'", qPrintable(id()), qPrintable((*it).accountId())); 0718 return MyMoneyAccount(); 0719 } 0720 } 0721 0722 return MyMoneyAccount(); 0723 } 0724 0725 MyMoneyAccount MyMoneySchedule::transferAccount() const { 0726 return account(2); 0727 } 0728 0729 QDate MyMoneySchedule::dateAfter(int transactions) const 0730 { 0731 auto counter = 1; 0732 QDate paymentDate(startDate()); 0733 0734 if (transactions <= 0) 0735 return paymentDate; 0736 0737 Q_D(const MyMoneySchedule); 0738 switch (d->m_occurrence) { 0739 case Schedule::Occurrence::Once: 0740 break; 0741 0742 case Schedule::Occurrence::Daily: 0743 while (counter++ < transactions) 0744 paymentDate = paymentDate.addDays(d->m_occurrenceMultiplier); 0745 break; 0746 0747 case Schedule::Occurrence::Weekly: { 0748 int step = 7 * d->m_occurrenceMultiplier; 0749 while (counter++ < transactions) 0750 paymentDate = paymentDate.addDays(step); 0751 } 0752 break; 0753 0754 case Schedule::Occurrence::EveryHalfMonth: 0755 paymentDate = addHalfMonths(paymentDate, d->m_occurrenceMultiplier * (transactions - 1)); 0756 break; 0757 0758 case Schedule::Occurrence::Monthly: 0759 while (counter++ < transactions) 0760 paymentDate = paymentDate.addMonths(d->m_occurrenceMultiplier); 0761 break; 0762 0763 case Schedule::Occurrence::Yearly: 0764 while (counter++ < transactions) 0765 paymentDate = paymentDate.addYears(d->m_occurrenceMultiplier); 0766 break; 0767 0768 case Schedule::Occurrence::Any: 0769 default: 0770 break; 0771 } 0772 0773 return paymentDate; 0774 } 0775 0776 bool MyMoneySchedule::isOverdue() const 0777 { 0778 if (isFinished()) 0779 return false; 0780 0781 if (adjustedNextDueDate() >= QDate::currentDate()) 0782 return false; 0783 0784 return true; 0785 } 0786 0787 bool MyMoneySchedule::isFinished() const 0788 { 0789 Q_D(const MyMoneySchedule); 0790 if (!d->m_lastPayment.isValid()) 0791 return false; 0792 0793 if (d->m_endDate.isValid()) { 0794 if (d->m_lastPayment >= d->m_endDate 0795 || !nextDueDate().isValid() 0796 || nextDueDate() > d->m_endDate) 0797 return true; 0798 } 0799 0800 // Check to see if its a once off payment 0801 if (d->m_occurrence == Schedule::Occurrence::Once) 0802 return true; 0803 0804 return false; 0805 } 0806 0807 bool MyMoneySchedule::hasRecordedPayment(const QDate& date) const 0808 { 0809 Q_D(const MyMoneySchedule); 0810 // m_lastPayment should always be > recordedPayments() 0811 if (d->m_lastPayment.isValid() && d->m_lastPayment >= date) 0812 return true; 0813 0814 if (d->m_recordedPayments.contains(date)) 0815 return true; 0816 0817 return false; 0818 } 0819 0820 void MyMoneySchedule::recordPayment(const QDate& date) 0821 { 0822 Q_D(MyMoneySchedule); 0823 d->m_recordedPayments.append(date); 0824 } 0825 0826 QList<QDate> MyMoneySchedule::recordedPayments() const 0827 { 0828 Q_D(const MyMoneySchedule); 0829 return d->m_recordedPayments; 0830 } 0831 0832 void MyMoneySchedule::setWeekendOption(const Schedule::WeekendOption option) 0833 { 0834 Q_D(MyMoneySchedule); 0835 // make sure only valid values are used. Invalid defaults to MoveNothing. 0836 switch (option) { 0837 case Schedule::WeekendOption::MoveBefore: 0838 case Schedule::WeekendOption::MoveAfter: 0839 d->m_weekendOption = option; 0840 break; 0841 0842 default: 0843 d->m_weekendOption = Schedule::WeekendOption::MoveNothing; 0844 break; 0845 } 0846 } 0847 0848 void MyMoneySchedule::fixDate(QDate& date) const 0849 { 0850 Q_D(const MyMoneySchedule); 0851 QDate fixDate(d->m_startDate); 0852 0853 if (d->m_lastDayInMonth) { 0854 fixDate = QDate(fixDate.year(), fixDate.month(), fixDate.daysInMonth()); 0855 } 0856 0857 if (fixDate.isValid() 0858 && date.day() != fixDate.day() 0859 && QDate::isValid(date.year(), date.month(), fixDate.day())) { 0860 date = QDate(date.year(), date.month(), fixDate.day()); 0861 } 0862 } 0863 0864 bool MyMoneySchedule::hasReferenceTo(const QString& id) const 0865 { 0866 Q_D(const MyMoneySchedule); 0867 return d->m_transaction.hasReferenceTo(id); 0868 } 0869 0870 QString MyMoneySchedule::occurrenceToString() const 0871 { 0872 return occurrenceToString(occurrenceMultiplier(), occurrence()); 0873 } 0874 0875 QString MyMoneySchedule::occurrenceToString(Schedule::Occurrence occurrence) 0876 { 0877 QString occurrenceString = I18N_NOOP2("Frequency of schedule", "Any"); 0878 0879 if (occurrence == Schedule::Occurrence::Once) 0880 occurrenceString = I18N_NOOP2("Frequency of schedule", "Once"); 0881 else if (occurrence == Schedule::Occurrence::Daily) 0882 occurrenceString = I18N_NOOP2("Frequency of schedule", "Daily"); 0883 else if (occurrence == Schedule::Occurrence::Weekly) 0884 occurrenceString = I18N_NOOP2("Frequency of schedule", "Weekly"); 0885 else if (occurrence == Schedule::Occurrence::Fortnightly) 0886 occurrenceString = I18N_NOOP2("Frequency of schedule", "Fortnightly"); 0887 else if (occurrence == Schedule::Occurrence::EveryOtherWeek) 0888 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other week"); 0889 else if (occurrence == Schedule::Occurrence::EveryHalfMonth) 0890 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every half month"); 0891 else if (occurrence == Schedule::Occurrence::EveryThreeWeeks) 0892 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three weeks"); 0893 else if (occurrence == Schedule::Occurrence::EveryFourWeeks) 0894 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four weeks"); 0895 else if (occurrence == Schedule::Occurrence::EveryThirtyDays) 0896 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every thirty days"); 0897 else if (occurrence == Schedule::Occurrence::Monthly) 0898 occurrenceString = I18N_NOOP2("Frequency of schedule", "Monthly"); 0899 else if (occurrence == Schedule::Occurrence::EveryEightWeeks) 0900 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every eight weeks"); 0901 else if (occurrence == Schedule::Occurrence::EveryOtherMonth) 0902 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every two months"); 0903 else if (occurrence == Schedule::Occurrence::EveryThreeMonths) 0904 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three months"); 0905 else if (occurrence == Schedule::Occurrence::Quarterly) 0906 occurrenceString = I18N_NOOP2("Frequency of schedule", "Quarterly"); 0907 else if (occurrence == Schedule::Occurrence::EveryFourMonths) 0908 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four months"); 0909 else if (occurrence == Schedule::Occurrence::TwiceYearly) 0910 occurrenceString = I18N_NOOP2("Frequency of schedule", "Twice yearly"); 0911 else if (occurrence == Schedule::Occurrence::Yearly) 0912 occurrenceString = I18N_NOOP2("Frequency of schedule", "Yearly"); 0913 else if (occurrence == Schedule::Occurrence::EveryOtherYear) 0914 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other year"); 0915 return occurrenceString; 0916 } 0917 0918 QString MyMoneySchedule::occurrenceToString(int mult, Schedule::Occurrence type) 0919 { 0920 QString occurrenceString = I18N_NOOP2("Frequency of schedule", "Any"); 0921 0922 if (type == Schedule::Occurrence::Once) 0923 switch (mult) { 0924 case 1: 0925 occurrenceString = I18N_NOOP2("Frequency of schedule", "Once"); 0926 break; 0927 default: 0928 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("%1 times").arg(mult)); 0929 } 0930 else if (type == Schedule::Occurrence::Daily) 0931 switch (mult) { 0932 case 1: 0933 occurrenceString = I18N_NOOP2("Frequency of schedule", "Daily"); 0934 break; 0935 case 30: 0936 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every thirty days"); 0937 break; 0938 default: 0939 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 days").arg(mult)); 0940 } 0941 else if (type == Schedule::Occurrence::Weekly) 0942 switch (mult) { 0943 case 1: 0944 occurrenceString = I18N_NOOP2("Frequency of schedule", "Weekly"); 0945 break; 0946 case 2: 0947 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other week"); 0948 break; 0949 case 3: 0950 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three weeks"); 0951 break; 0952 case 4: 0953 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four weeks"); 0954 break; 0955 case 8: 0956 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every eight weeks"); 0957 break; 0958 default: 0959 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 weeks").arg(mult)); 0960 } 0961 else if (type == Schedule::Occurrence::EveryHalfMonth) 0962 switch (mult) { 0963 case 1: 0964 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every half month"); 0965 break; 0966 default: 0967 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 half months").arg(mult)); 0968 } 0969 else if (type == Schedule::Occurrence::Monthly) 0970 switch (mult) { 0971 case 1: 0972 occurrenceString = I18N_NOOP2("Frequency of schedule", "Monthly"); 0973 break; 0974 case 2: 0975 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every two months"); 0976 break; 0977 case 3: 0978 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three months"); 0979 break; 0980 case 4: 0981 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four months"); 0982 break; 0983 case 6: 0984 occurrenceString = I18N_NOOP2("Frequency of schedule", "Twice yearly"); 0985 break; 0986 default: 0987 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 months").arg(mult)); 0988 } 0989 else if (type == Schedule::Occurrence::Yearly) 0990 switch (mult) { 0991 case 1: 0992 occurrenceString = I18N_NOOP2("Frequency of schedule", "Yearly"); 0993 break; 0994 case 2: 0995 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other year"); 0996 break; 0997 default: 0998 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 years").arg(mult)); 0999 } 1000 return occurrenceString; 1001 } 1002 1003 QString MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence type) 1004 { 1005 QString occurrenceString = I18N_NOOP2("Schedule occurrence period", "Any"); 1006 1007 if (type == Schedule::Occurrence::Once) 1008 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Once"); 1009 else if (type == Schedule::Occurrence::Daily) 1010 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Day"); 1011 else if (type == Schedule::Occurrence::Weekly) 1012 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Week"); 1013 else if (type == Schedule::Occurrence::EveryHalfMonth) 1014 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Half-month"); 1015 else if (type == Schedule::Occurrence::Monthly) 1016 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Month"); 1017 else if (type == Schedule::Occurrence::Yearly) 1018 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Year"); 1019 return occurrenceString; 1020 } 1021 1022 QString MyMoneySchedule::scheduleTypeToString(Schedule::Type type) 1023 { 1024 QString text; 1025 1026 switch (type) { 1027 case Schedule::Type::Bill: 1028 text = I18N_NOOP2("Scheduled transaction type", "Bill"); 1029 break; 1030 case Schedule::Type::Deposit: 1031 text = I18N_NOOP2("Scheduled transaction type", "Deposit"); 1032 break; 1033 case Schedule::Type::Transfer: 1034 text = I18N_NOOP2("Scheduled transaction type", "Transfer"); 1035 break; 1036 case Schedule::Type::LoanPayment: 1037 text = I18N_NOOP2("Scheduled transaction type", "Loan payment"); 1038 break; 1039 case Schedule::Type::Any: 1040 default: 1041 text = I18N_NOOP2("Scheduled transaction type", "Unknown"); 1042 } 1043 return text; 1044 } 1045 1046 1047 QString MyMoneySchedule::paymentMethodToString(Schedule::PaymentType paymentType) 1048 { 1049 QString text; 1050 1051 switch (paymentType) { 1052 case Schedule::PaymentType::DirectDebit: 1053 text = I18N_NOOP2("Scheduled Transaction payment type", "Direct debit"); 1054 break; 1055 case Schedule::PaymentType::DirectDeposit: 1056 text = I18N_NOOP2("Scheduled Transaction payment type", "Direct deposit"); 1057 break; 1058 case Schedule::PaymentType::ManualDeposit: 1059 text = I18N_NOOP2("Scheduled Transaction payment type", "Manual deposit"); 1060 break; 1061 case Schedule::PaymentType::Other: 1062 text = I18N_NOOP2("Scheduled Transaction payment type", "Other"); 1063 break; 1064 case Schedule::PaymentType::WriteChecque: 1065 text = I18N_NOOP2("Scheduled Transaction payment type", "Write check"); 1066 break; 1067 case Schedule::PaymentType::StandingOrder: 1068 text = I18N_NOOP2("Scheduled Transaction payment type", "Standing order"); 1069 break; 1070 case Schedule::PaymentType::BankTransfer: 1071 text = I18N_NOOP2("Scheduled Transaction payment type", "Bank transfer"); 1072 break; 1073 case Schedule::PaymentType::Any: 1074 text = I18N_NOOP2("Scheduled Transaction payment type", "Any (Error)"); 1075 break; 1076 } 1077 return text; 1078 } 1079 1080 QString MyMoneySchedule::weekendOptionToString(Schedule::WeekendOption weekendOption) 1081 { 1082 QString text; 1083 1084 switch (weekendOption) { 1085 case Schedule::WeekendOption::MoveBefore: 1086 text = I18N_NOOP("Change the date to the previous processing day"); 1087 break; 1088 case Schedule::WeekendOption::MoveAfter: 1089 text = I18N_NOOP("Change the date to the next processing day"); 1090 break; 1091 case Schedule::WeekendOption::MoveNothing: 1092 text = I18N_NOOP("Do not change the date"); 1093 break; 1094 } 1095 return text; 1096 } 1097 1098 // until we don't have the means to store the value 1099 // of the variation, we default to 10% in case this 1100 // scheduled transaction is marked 'not fixed'. 1101 // 1102 // ipwizard 2009-04-18 1103 1104 int MyMoneySchedule::variation() const 1105 { 1106 int rc = 0; 1107 if (!isFixed()) { 1108 rc = 10; 1109 #if 0 1110 QString var = value("kmm-variation"); 1111 if (!var.isEmpty()) 1112 rc = var.toInt(); 1113 #endif 1114 } 1115 return rc; 1116 } 1117 1118 void MyMoneySchedule::setVariation(int var) 1119 { 1120 Q_UNUSED(var) 1121 #if 0 1122 deletePair("kmm-variation"); 1123 if (var != 0) 1124 setValue("kmm-variation", QString("%1").arg(var)); 1125 #endif 1126 } 1127 1128 int MyMoneySchedule::eventsPerYear(Schedule::Occurrence occurrence) 1129 { 1130 int rc = 0; 1131 1132 switch (occurrence) { 1133 case Schedule::Occurrence::Daily: 1134 rc = 365; 1135 break; 1136 case Schedule::Occurrence::Weekly: 1137 rc = 52; 1138 break; 1139 case Schedule::Occurrence::Fortnightly: 1140 rc = 26; 1141 break; 1142 case Schedule::Occurrence::EveryOtherWeek: 1143 rc = 26; 1144 break; 1145 case Schedule::Occurrence::EveryHalfMonth: 1146 rc = 24; 1147 break; 1148 case Schedule::Occurrence::EveryThreeWeeks: 1149 rc = 17; 1150 break; 1151 case Schedule::Occurrence::EveryFourWeeks: 1152 rc = 13; 1153 break; 1154 case Schedule::Occurrence::Monthly: 1155 case Schedule::Occurrence::EveryThirtyDays: 1156 rc = 12; 1157 break; 1158 case Schedule::Occurrence::EveryEightWeeks: 1159 rc = 6; 1160 break; 1161 case Schedule::Occurrence::EveryOtherMonth: 1162 rc = 6; 1163 break; 1164 case Schedule::Occurrence::EveryThreeMonths: 1165 case Schedule::Occurrence::Quarterly: 1166 rc = 4; 1167 break; 1168 case Schedule::Occurrence::EveryFourMonths: 1169 rc = 3; 1170 break; 1171 case Schedule::Occurrence::TwiceYearly: 1172 rc = 2; 1173 break; 1174 case Schedule::Occurrence::Yearly: 1175 rc = 1; 1176 break; 1177 default: 1178 qWarning("Occurrence not supported by financial calculator"); 1179 } 1180 1181 return rc; 1182 } 1183 1184 int MyMoneySchedule::daysBetweenEvents(Schedule::Occurrence occurrence) 1185 { 1186 int rc = 0; 1187 1188 switch (occurrence) { 1189 case Schedule::Occurrence::Daily: 1190 rc = 1; 1191 break; 1192 case Schedule::Occurrence::Weekly: 1193 rc = 7; 1194 break; 1195 case Schedule::Occurrence::Fortnightly: 1196 rc = 14; 1197 break; 1198 case Schedule::Occurrence::EveryOtherWeek: 1199 rc = 14; 1200 break; 1201 case Schedule::Occurrence::EveryHalfMonth: 1202 rc = 15; 1203 break; 1204 case Schedule::Occurrence::EveryThreeWeeks: 1205 rc = 21; 1206 break; 1207 case Schedule::Occurrence::EveryFourWeeks: 1208 rc = 28; 1209 break; 1210 case Schedule::Occurrence::EveryThirtyDays: 1211 rc = 30; 1212 break; 1213 case Schedule::Occurrence::Monthly: 1214 rc = 30; 1215 break; 1216 case Schedule::Occurrence::EveryEightWeeks: 1217 rc = 56; 1218 break; 1219 case Schedule::Occurrence::EveryOtherMonth: 1220 rc = 60; 1221 break; 1222 case Schedule::Occurrence::EveryThreeMonths: 1223 case Schedule::Occurrence::Quarterly: 1224 rc = 90; 1225 break; 1226 case Schedule::Occurrence::EveryFourMonths: 1227 rc = 120; 1228 break; 1229 case Schedule::Occurrence::TwiceYearly: 1230 rc = 180; 1231 break; 1232 case Schedule::Occurrence::Yearly: 1233 rc = 360; 1234 break; 1235 default: 1236 qWarning("Occurrence not supported by financial calculator"); 1237 } 1238 1239 return rc; 1240 } 1241 1242 QDate MyMoneySchedule::addHalfMonths(QDate date, int mult) const 1243 { 1244 QDate newdate = date; 1245 int d, dm; 1246 if (mult > 0) { 1247 d = newdate.day(); 1248 if (d <= 12) { 1249 if (mult % 2 == 0) 1250 newdate = newdate.addMonths(mult >> 1); 1251 else 1252 newdate = newdate.addMonths(mult >> 1).addDays(15); 1253 } else 1254 for (int i = 0; i < mult; i++) { 1255 if (d <= 13) 1256 newdate = newdate.addDays(15); 1257 else { 1258 dm = newdate.daysInMonth(); 1259 if (d == 14) 1260 newdate = newdate.addDays((dm < 30) ? dm - d : 15); 1261 else if (d == 15) 1262 newdate = newdate.addDays(dm - d); 1263 else if (d == dm) 1264 newdate = newdate.addDays(15 - d).addMonths(1); 1265 else 1266 newdate = newdate.addDays(-15).addMonths(1); 1267 } 1268 d = newdate.day(); 1269 } 1270 } else if (mult < 0) // Go backwards 1271 for (int i = 0; i > mult; i--) { 1272 d = newdate.day(); 1273 dm = newdate.daysInMonth(); 1274 if (d > 15) { 1275 dm = newdate.daysInMonth(); 1276 newdate = newdate.addDays((d == dm) ? 15 - dm : -15); 1277 } else if (d <= 13) 1278 newdate = newdate.addMonths(-1).addDays(15); 1279 else if (d == 15) 1280 newdate = newdate.addDays(-15); 1281 else { // 14 1282 newdate = newdate.addMonths(-1); 1283 dm = newdate.daysInMonth(); 1284 newdate = newdate.addDays((dm < 30) ? dm - d : 15); 1285 } 1286 } 1287 return newdate; 1288 } 1289 1290 /** 1291 * Helper method to convert simple occurrence to compound occurrence + multiplier 1292 * 1293 * @param multiplier Returned by reference. Adjusted multiplier 1294 * @param occurrence Returned by reference. Occurrence type 1295 */ 1296 void MyMoneySchedule::simpleToCompoundOccurrence(int& multiplier, Schedule::Occurrence& occurrence) 1297 { 1298 Schedule::Occurrence newOcc = occurrence; 1299 int newMulti = 1; 1300 if (occurrence == Schedule::Occurrence::Once // 1301 || occurrence == Schedule::Occurrence::Daily // 1302 || occurrence == Schedule::Occurrence::Weekly // 1303 || occurrence == Schedule::Occurrence::EveryHalfMonth // 1304 || occurrence == Schedule::Occurrence::Monthly // 1305 || occurrence == Schedule::Occurrence::Yearly) { // Already a base occurrence and multiplier 1306 } else if (occurrence == Schedule::Occurrence::Fortnightly || 1307 occurrence == Schedule::Occurrence::EveryOtherWeek) { 1308 newOcc = Schedule::Occurrence::Weekly; 1309 newMulti = 2; 1310 } else if (occurrence == Schedule::Occurrence::EveryThreeWeeks) { 1311 newOcc = Schedule::Occurrence::Weekly; 1312 newMulti = 3; 1313 } else if (occurrence == Schedule::Occurrence::EveryFourWeeks) { 1314 newOcc = Schedule::Occurrence::Weekly; 1315 newMulti = 4; 1316 } else if (occurrence == Schedule::Occurrence::EveryThirtyDays) { 1317 newOcc = Schedule::Occurrence::Daily; 1318 newMulti = 30; 1319 } else if (occurrence == Schedule::Occurrence::EveryEightWeeks) { 1320 newOcc = Schedule::Occurrence::Weekly; 1321 newMulti = 8; 1322 } else if (occurrence == Schedule::Occurrence::EveryOtherMonth) { 1323 newOcc = Schedule::Occurrence::Monthly; 1324 newMulti = 2; 1325 } else if (occurrence == Schedule::Occurrence::EveryThreeMonths // 1326 || occurrence == Schedule::Occurrence::Quarterly) { 1327 newOcc = Schedule::Occurrence::Monthly; 1328 newMulti = 3; 1329 } else if (occurrence == Schedule::Occurrence::EveryFourMonths) { 1330 newOcc = Schedule::Occurrence::Monthly; 1331 newMulti = 4; 1332 } else if (occurrence == Schedule::Occurrence::TwiceYearly) { 1333 newOcc = Schedule::Occurrence::Monthly; 1334 newMulti = 6; 1335 } else if (occurrence == Schedule::Occurrence::EveryOtherYear) { 1336 newOcc = Schedule::Occurrence::Yearly; 1337 newMulti = 2; 1338 } else { // Unknown 1339 newOcc = Schedule::Occurrence::Any; 1340 newMulti = 1; 1341 } 1342 if (newOcc != occurrence) { 1343 occurrence = newOcc; 1344 multiplier = newMulti == 1 ? multiplier : newMulti * multiplier; 1345 } 1346 } 1347 1348 /** 1349 * Helper method to convert compound occurrence + multiplier to simple occurrence 1350 * 1351 * @param multiplier Returned by reference. Adjusted multiplier 1352 * @param occurrence Returned by reference. Occurrence type 1353 */ 1354 void MyMoneySchedule::compoundToSimpleOccurrence(int& multiplier, Schedule::Occurrence& occurrence) 1355 { 1356 Schedule::Occurrence newOcc = occurrence; 1357 if (occurrence == Schedule::Occurrence::Once) { // Nothing to do 1358 } else if (occurrence == Schedule::Occurrence::Daily) { 1359 switch (multiplier) { 1360 case 1: 1361 break; 1362 case 30: 1363 newOcc = Schedule::Occurrence::EveryThirtyDays; 1364 break; 1365 } 1366 } else if (newOcc == Schedule::Occurrence::Weekly) { 1367 switch (multiplier) { 1368 case 1: 1369 break; 1370 case 2: 1371 newOcc = Schedule::Occurrence::EveryOtherWeek; 1372 break; 1373 case 3: 1374 newOcc = Schedule::Occurrence::EveryThreeWeeks; 1375 break; 1376 case 4: 1377 newOcc = Schedule::Occurrence::EveryFourWeeks; 1378 break; 1379 case 8: 1380 newOcc = Schedule::Occurrence::EveryEightWeeks; 1381 break; 1382 } 1383 } else if (occurrence == Schedule::Occurrence::Monthly) 1384 switch (multiplier) { 1385 case 1: 1386 break; 1387 case 2: 1388 newOcc = Schedule::Occurrence::EveryOtherMonth; 1389 break; 1390 case 3: 1391 newOcc = Schedule::Occurrence::EveryThreeMonths; 1392 break; 1393 case 4: 1394 newOcc = Schedule::Occurrence::EveryFourMonths; 1395 break; 1396 case 6: 1397 newOcc = Schedule::Occurrence::TwiceYearly; 1398 break; 1399 } 1400 else if (occurrence == Schedule::Occurrence::EveryHalfMonth) 1401 switch (multiplier) { 1402 case 1: 1403 break; 1404 } 1405 else if (occurrence == Schedule::Occurrence::Yearly) { 1406 switch (multiplier) { 1407 case 1: 1408 break; 1409 case 2: 1410 newOcc = Schedule::Occurrence::EveryOtherYear; 1411 break; 1412 } 1413 } 1414 if (occurrence != newOcc) { // Changed to derived type 1415 occurrence = newOcc; 1416 multiplier = 1; 1417 } 1418 } 1419 1420 void MyMoneySchedule::setProcessingCalendar(IMyMoneyProcessingCalendar* pc) 1421 { 1422 processingCalendarPtr = pc; 1423 } 1424 1425 bool MyMoneySchedule::isProcessingDate(const QDate& date) const 1426 { 1427 if (processingCalendarPtr) 1428 return processingCalendarPtr->isProcessingDate(date); 1429 1430 /// @todo test against m_processingDays instead? (currently only for tests) 1431 return date.dayOfWeek() < Qt::Saturday; 1432 } 1433 1434 IMyMoneyProcessingCalendar* MyMoneySchedule::processingCalendar() const 1435 { 1436 return processingCalendarPtr; 1437 } 1438 1439 bool MyMoneySchedule::replaceId(const QString& newId, const QString& oldId) 1440 { 1441 Q_D(MyMoneySchedule); 1442 return d->m_transaction.replaceId(newId, oldId); 1443 }