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