1 /*
2 * Copyright 2000-2004 Michael Edwardes <mte@users.sourceforge.net>
3 * Copyright 2002-2018 Thomas Baumgart <tbaumgart@kde.org>
4 * Copyright 2005 Ace Jones <acejones@users.sourceforge.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "mymoneyschedule.h"
21 #include "mymoneyschedule_p.h"
22
23 // ----------------------------------------------------------------------------
24 // QT Includes
25
26 #include <QList>
27 #include <QMap>
28
29 // ----------------------------------------------------------------------------
30 // KDE Includes
31
32 #include <KLocalizedString>
33
34 // ----------------------------------------------------------------------------
35 // Project Includes
36
37 #include "mymoneyutils.h"
38 #include "mymoneyexception.h"
39 #include "mymoneyfile.h"
40 #include "mymoneyaccount.h"
41 #include "mymoneysplit.h"
42 #include "imymoneyprocessingcalendar.h"
43
44 using namespace eMyMoney;
45
46 static IMyMoneyProcessingCalendar* processingCalendarPtr = 0;
47
MyMoneySchedule()48 MyMoneySchedule::MyMoneySchedule() :
49 MyMoneyObject(*new MyMoneySchedulePrivate)
50 {
51 }
52
MyMoneySchedule(const QString & id)53 MyMoneySchedule::MyMoneySchedule(const QString &id) :
54 MyMoneyObject(*new MyMoneySchedulePrivate, id)
55 {
56 }
57
MyMoneySchedule(const QString & name,Schedule::Type type,Schedule::Occurrence occurrence,int occurrenceMultiplier,Schedule::PaymentType paymentType,const QDate &,const QDate & endDate,bool fixed,bool autoEnter)58 MyMoneySchedule::MyMoneySchedule(const QString& name,
59 Schedule::Type type,
60 Schedule::Occurrence occurrence,
61 int occurrenceMultiplier,
62 Schedule::PaymentType paymentType,
63 const QDate& /* startDate */,
64 const QDate& endDate,
65 bool fixed,
66 bool autoEnter) :
67 MyMoneyObject(*new MyMoneySchedulePrivate)
68 {
69 Q_D(MyMoneySchedule);
70 // Set up the values possibly differing from defaults
71 d->m_name = name;
72 d->m_occurrence = occurrence;
73 d->m_occurrenceMultiplier = occurrenceMultiplier;
74 simpleToCompoundOccurrence(d->m_occurrenceMultiplier, d->m_occurrence);
75 d->m_type = type;
76 d->m_paymentType = paymentType;
77 d->m_fixed = fixed;
78 d->m_autoEnter = autoEnter;
79 d->m_endDate = endDate;
80 }
81
MyMoneySchedule(const MyMoneySchedule & other)82 MyMoneySchedule::MyMoneySchedule(const MyMoneySchedule& other) :
83 MyMoneyObject(*new MyMoneySchedulePrivate(*other.d_func()), other.id())
84 {
85 }
86
MyMoneySchedule(const QString & id,const MyMoneySchedule & other)87 MyMoneySchedule::MyMoneySchedule(const QString& id, const MyMoneySchedule& other) :
88 MyMoneyObject(*new MyMoneySchedulePrivate(*other.d_func()), id)
89 {
90 }
91
~MyMoneySchedule()92 MyMoneySchedule::~MyMoneySchedule()
93 {
94 }
95
baseOccurrence() const96 Schedule::Occurrence MyMoneySchedule::baseOccurrence() const
97 {
98 Q_D(const MyMoneySchedule);
99 Schedule::Occurrence occ = d->m_occurrence;
100 int mult = d->m_occurrenceMultiplier;
101 compoundToSimpleOccurrence(mult, occ);
102 return occ;
103 }
104
occurrenceMultiplier() const105 int MyMoneySchedule::occurrenceMultiplier() const
106 {
107 Q_D(const MyMoneySchedule);
108 return d->m_occurrenceMultiplier;
109 }
110
type() const111 eMyMoney::Schedule::Type MyMoneySchedule::type() const
112 {
113 Q_D(const MyMoneySchedule);
114 return d->m_type;
115 }
116
occurrence() const117 eMyMoney::Schedule::Occurrence MyMoneySchedule::occurrence() const
118 {
119 Q_D(const MyMoneySchedule);
120 return d->m_occurrence;
121 }
122
setStartDate(const QDate & date)123 void MyMoneySchedule::setStartDate(const QDate& date)
124 {
125 Q_D(MyMoneySchedule);
126 d->m_startDate = date;
127 }
128
setPaymentType(Schedule::PaymentType type)129 void MyMoneySchedule::setPaymentType(Schedule::PaymentType type)
130 {
131 Q_D(MyMoneySchedule);
132 d->m_paymentType = type;
133 }
134
setFixed(bool fixed)135 void MyMoneySchedule::setFixed(bool fixed)
136 {
137 Q_D(MyMoneySchedule);
138 d->m_fixed = fixed;
139 }
140
setTransaction(const MyMoneyTransaction & transaction)141 void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction)
142 {
143 setTransaction(transaction, false);
144 }
145
setTransaction(const MyMoneyTransaction & transaction,bool noDateCheck)146 void MyMoneySchedule::setTransaction(const MyMoneyTransaction& transaction, bool noDateCheck)
147 {
148 auto t = transaction;
149 Q_D(MyMoneySchedule);
150 if (!noDateCheck) {
151 // don't allow a transaction that has no due date
152 // if we get something like that, then we use the
153 // the current next due date. If that is also invalid
154 // we can't help it.
155 if (!t.postDate().isValid()) {
156 t.setPostDate(d->m_transaction.postDate());
157 }
158
159 if (!t.postDate().isValid())
160 return;
161 }
162
163 // make sure to clear out some unused information in scheduled transactions
164 // we need to do this for the case that the transaction passed as argument
165 // is a matched or imported transaction.
166 auto firstSplit = true;
167 foreach (const auto split, t.splits()) {
168 MyMoneySplit s = split;
169 // clear out the bankID
170 if (!split.bankID().isEmpty()) {
171 s.setBankID(QString());
172 t.modifySplit(s);
173 }
174
175 // only clear payees from second split onwards
176 if (firstSplit) {
177 firstSplit = false;
178 continue;
179 }
180
181 if (!split.payeeId().isEmpty()) {
182 // but only if the split references an income/expense category
183 auto file = MyMoneyFile::instance();
184 // some unit tests don't have a storage attached, so we
185 // simply skip the test
186 // Don't check for accounts with an id of 'Phony-ID' which is used
187 // internally for non-existing accounts (during creation of accounts)
188 if (file->storageAttached() && s.accountId() != QString("Phony-ID")) {
189 auto acc = file->account(s.accountId());
190 if (acc.isIncomeExpense()) {
191 s.setPayeeId(QString());
192 t.modifySplit(s);
193 }
194 }
195 }
196 }
197
198 d->m_transaction = t;
199 // make sure that the transaction does not have an id so that we can enter
200 // it into the engine
201 d->m_transaction.clearId();
202 }
203
setEndDate(const QDate & date)204 void MyMoneySchedule::setEndDate(const QDate& date)
205 {
206 Q_D(MyMoneySchedule);
207 d->m_endDate = date;
208 }
209
setLastDayInMonth(bool state)210 void MyMoneySchedule::setLastDayInMonth(bool state)
211 {
212 Q_D(MyMoneySchedule);
213 d->m_lastDayInMonth = state;
214 }
215
setAutoEnter(bool autoenter)216 void MyMoneySchedule::setAutoEnter(bool autoenter)
217 {
218 Q_D(MyMoneySchedule);
219 d->m_autoEnter = autoenter;
220 }
221
startDate() const222 QDate MyMoneySchedule::startDate() const
223 {
224 Q_D(const MyMoneySchedule);
225 if (d->m_startDate.isValid())
226 return d->m_startDate;
227 return nextDueDate();
228 }
229
paymentType() const230 eMyMoney::Schedule::PaymentType MyMoneySchedule::paymentType() const
231 {
232 Q_D(const MyMoneySchedule);
233 return d->m_paymentType;
234 }
235
236 /**
237 * Simple get method that returns true if the schedule is fixed.
238 *
239 * @return bool To indicate whether the instance is fixed.
240 */
isFixed() const241 bool MyMoneySchedule::isFixed() const
242 {
243 Q_D(const MyMoneySchedule);
244 return d->m_fixed;
245 }
246
247 /**
248 * Simple get method that returns true if the schedule will end
249 * at some time.
250 *
251 * @return bool Indicates whether the instance will end.
252 */
willEnd() const253 bool MyMoneySchedule::willEnd() const
254 {
255 Q_D(const MyMoneySchedule);
256 return d->m_endDate.isValid();
257 }
258
259
nextDueDate() const260 QDate MyMoneySchedule::nextDueDate() const
261 {
262 Q_D(const MyMoneySchedule);
263 return d->m_transaction.postDate();
264 }
265
adjustedNextDueDate() const266 QDate MyMoneySchedule::adjustedNextDueDate() const
267 {
268 if (isFinished())
269 return QDate();
270
271 if (lastDayInMonth()) {
272 QDate date = nextDueDate();
273 return adjustedDate(QDate(date.year(), date.month(), date.daysInMonth()), weekendOption());
274 }
275
276 return adjustedDate(nextDueDate(), weekendOption());
277 }
278
adjustedDate(QDate date,Schedule::WeekendOption option) const279 QDate MyMoneySchedule::adjustedDate(QDate date, Schedule::WeekendOption option) const
280 {
281 if (!date.isValid() || option == Schedule::WeekendOption::MoveNothing || isProcessingDate(date))
282 return date;
283
284 int step = 1;
285 if (option == Schedule::WeekendOption::MoveBefore)
286 step = -1;
287
288 while (!isProcessingDate(date))
289 date = date.addDays(step);
290
291 return date;
292 }
293
setNextDueDate(const QDate & date)294 void MyMoneySchedule::setNextDueDate(const QDate& date)
295 {
296 Q_D(MyMoneySchedule);
297 if (date.isValid()) {
298 d->m_transaction.setPostDate(date);
299 // m_startDate = date;
300 }
301 }
302
setLastPayment(const QDate & date)303 void MyMoneySchedule::setLastPayment(const QDate& date)
304 {
305 Q_D(MyMoneySchedule);
306 // Delete all payments older than date
307 QList<QDate>::Iterator it;
308 QList<QDate> delList;
309
310 for (it = d->m_recordedPayments.begin(); it != d->m_recordedPayments.end(); ++it) {
311 if (*it < date || !date.isValid())
312 delList.append(*it);
313 }
314
315 for (it = delList.begin(); it != delList.end(); ++it) {
316 d->m_recordedPayments.removeAll(*it);
317 }
318
319 d->m_lastPayment = date;
320 if (!d->m_startDate.isValid())
321 d->m_startDate = date;
322 }
323
name() const324 QString MyMoneySchedule::name() const
325 {
326 Q_D(const MyMoneySchedule);
327 return d->m_name;
328 }
329
setName(const QString & nm)330 void MyMoneySchedule::setName(const QString& nm)
331 {
332 Q_D(MyMoneySchedule);
333 d->m_name = nm;
334 }
335
weekendOption() const336 eMyMoney::Schedule::WeekendOption MyMoneySchedule::weekendOption() const
337 {
338 Q_D(const MyMoneySchedule);
339 return d->m_weekendOption;
340 }
341
setOccurrence(Schedule::Occurrence occ)342 void MyMoneySchedule::setOccurrence(Schedule::Occurrence occ)
343 {
344 auto occ2 = occ;
345 auto mult = 1;
346 simpleToCompoundOccurrence(mult, occ2);
347 setOccurrencePeriod(occ2);
348 setOccurrenceMultiplier(mult);
349 }
350
setOccurrencePeriod(Schedule::Occurrence occ)351 void MyMoneySchedule::setOccurrencePeriod(Schedule::Occurrence occ)
352 {
353 Q_D(MyMoneySchedule);
354 d->m_occurrence = occ;
355 }
356
setOccurrenceMultiplier(int occmultiplier)357 void MyMoneySchedule::setOccurrenceMultiplier(int occmultiplier)
358 {
359 Q_D(MyMoneySchedule);
360 d->m_occurrenceMultiplier = occmultiplier < 1 ? 1 : occmultiplier;
361 }
362
setType(Schedule::Type type)363 void MyMoneySchedule::setType(Schedule::Type type)
364 {
365 Q_D(MyMoneySchedule);
366 d->m_type = type;
367 }
368
validate(bool id_check) const369 void MyMoneySchedule::validate(bool id_check) const
370 {
371 /* Check the supplied instance is valid...
372 *
373 * To be valid it must not have the id set and have the following fields set:
374 *
375 * m_occurrence
376 * m_type
377 * m_startDate
378 * m_paymentType
379 * m_transaction
380 * the transaction must contain at least one split (two is better ;-) )
381 */
382 Q_D(const MyMoneySchedule);
383 if (id_check && !d->m_id.isEmpty())
384 throw MYMONEYEXCEPTION_CSTRING("ID for schedule not empty when required");
385
386 if (d->m_occurrence == Schedule::Occurrence::Any)
387 throw MYMONEYEXCEPTION_CSTRING("Invalid occurrence type for schedule");
388
389 if (d->m_type == Schedule::Type::Any)
390 throw MYMONEYEXCEPTION_CSTRING("Invalid type for schedule");
391
392 if (!nextDueDate().isValid())
393 throw MYMONEYEXCEPTION_CSTRING("Invalid next due date for schedule");
394
395 if (d->m_paymentType == Schedule::PaymentType::Any)
396 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for schedule");
397
398 if (d->m_transaction.splitCount() == 0)
399 throw MYMONEYEXCEPTION_CSTRING("Scheduled transaction does not contain splits");
400
401 // Check the payment types
402 switch (d->m_type) {
403 case Schedule::Type::Bill:
404 if (d->m_paymentType == Schedule::PaymentType::DirectDeposit || d->m_paymentType == Schedule::PaymentType::ManualDeposit)
405 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for bills");
406 break;
407
408 case Schedule::Type::Deposit:
409 if (d->m_paymentType == Schedule::PaymentType::DirectDebit || d->m_paymentType == Schedule::PaymentType::WriteChecque)
410 throw MYMONEYEXCEPTION_CSTRING("Invalid payment type for deposits");
411 break;
412
413 case Schedule::Type::Any:
414 throw MYMONEYEXCEPTION_CSTRING("Invalid type ANY");
415 break;
416
417 case Schedule::Type::Transfer:
418 // if (m_paymentType == DirectDeposit || m_paymentType == ManualDeposit)
419 // return false;
420 break;
421
422 case Schedule::Type::LoanPayment:
423 break;
424 }
425 }
426
adjustedNextPayment(const QDate & refDate) const427 QDate MyMoneySchedule::adjustedNextPayment(const QDate& refDate) const
428 {
429 return nextPaymentDate(true, refDate);
430 }
431
adjustedNextPayment() const432 QDate MyMoneySchedule::adjustedNextPayment() const
433 {
434 return adjustedNextPayment(QDate::currentDate());
435 }
436
nextPayment(const QDate & refDate) const437 QDate MyMoneySchedule::nextPayment(const QDate& refDate) const
438 {
439 return nextPaymentDate(false, refDate);
440 }
441
nextPayment() const442 QDate MyMoneySchedule::nextPayment() const
443 {
444 return nextPayment(QDate::currentDate());
445 }
446
nextPaymentDate(const bool & adjust,const QDate & refDate) const447 QDate MyMoneySchedule::nextPaymentDate(const bool& adjust, const QDate& refDate) const
448 {
449 Schedule::WeekendOption option(adjust ? weekendOption() :
450 Schedule::WeekendOption::MoveNothing);
451
452 Q_D(const MyMoneySchedule);
453 QDate adjEndDate(adjustedDate(d->m_endDate, option));
454
455 // if the enddate is valid and it is before the reference date,
456 // then there will be no more payments.
457 if (adjEndDate.isValid() && adjEndDate < refDate) {
458 return QDate();
459 }
460
461 QDate dueDate(nextDueDate());
462 QDate paymentDate(adjustedDate(dueDate, option));
463
464 if (paymentDate.isValid() &&
465 (paymentDate <= refDate || d->m_recordedPayments.contains(dueDate))) {
466 switch (d->m_occurrence) {
467 case Schedule::Occurrence::Once:
468 // If the lastPayment is already set or the payment should have been
469 // prior to the reference date then invalidate the payment date.
470 if (d->m_lastPayment.isValid() || paymentDate <= refDate)
471 paymentDate = QDate();
472 break;
473
474 case Schedule::Occurrence::Daily: {
475 int step = d->m_occurrenceMultiplier;
476 do {
477 dueDate = dueDate.addDays(step);
478 paymentDate = adjustedDate(dueDate, option);
479 } while (paymentDate.isValid() &&
480 (paymentDate <= refDate ||
481 d->m_recordedPayments.contains(dueDate)));
482 }
483 break;
484
485 case Schedule::Occurrence::Weekly: {
486 int step = 7 * d->m_occurrenceMultiplier;
487 do {
488 dueDate = dueDate.addDays(step);
489 paymentDate = adjustedDate(dueDate, option);
490 } while (paymentDate.isValid() &&
491 (paymentDate <= refDate ||
492 d->m_recordedPayments.contains(dueDate)));
493 }
494 break;
495
496 case Schedule::Occurrence::EveryHalfMonth:
497 do {
498 dueDate = addHalfMonths(dueDate, d->m_occurrenceMultiplier);
499 paymentDate = adjustedDate(dueDate, option);
500 } while (paymentDate.isValid() &&
501 (paymentDate <= refDate ||
502 d->m_recordedPayments.contains(dueDate)));
503 break;
504
505 case Schedule::Occurrence::Monthly:
506 do {
507 dueDate = dueDate.addMonths(d->m_occurrenceMultiplier);
508 fixDate(dueDate);
509 paymentDate = adjustedDate(dueDate, option);
510 } while (paymentDate.isValid() &&
511 (paymentDate <= refDate ||
512 d->m_recordedPayments.contains(dueDate)));
513 break;
514
515 case Schedule::Occurrence::Yearly:
516 do {
517 dueDate = dueDate.addYears(d->m_occurrenceMultiplier);
518 fixDate(dueDate);
519 paymentDate = adjustedDate(dueDate, option);
520 } while (paymentDate.isValid() &&
521 (paymentDate <= refDate ||
522 d->m_recordedPayments.contains(dueDate)));
523 break;
524
525 case Schedule::Occurrence::Any:
526 default:
527 paymentDate = QDate();
528 break;
529 }
530 }
531 if (paymentDate.isValid() && adjEndDate.isValid() && paymentDate > adjEndDate)
532 paymentDate = QDate();
533
534 return paymentDate;
535 }
536
nextPaymentDate(const bool & adjust) const537 QDate MyMoneySchedule::nextPaymentDate(const bool& adjust) const
538 {
539 return nextPaymentDate(adjust, QDate::currentDate());
540 }
541
paymentDates(const QDate & _startDate,const QDate & _endDate) const542 QList<QDate> MyMoneySchedule::paymentDates(const QDate& _startDate, const QDate& _endDate) const
543 {
544 QDate paymentDate(nextDueDate());
545 QList<QDate> theDates;
546
547 Schedule::WeekendOption option(weekendOption());
548
549 Q_D(const MyMoneySchedule);
550 QDate endDate(_endDate);
551 if (willEnd() && d->m_endDate < endDate) {
552 // consider the adjusted end date instead of the plain end date
553 endDate = adjustedDate(d->m_endDate, option);
554 }
555
556 QDate start_date(adjustedDate(startDate(), option));
557 // if the period specified by the parameters and the adjusted period
558 // defined for this schedule don't overlap, then the list remains empty
559 if ((willEnd() && adjustedDate(d->m_endDate, option) < _startDate)
560 || start_date > endDate)
561 return theDates;
562
563 QDate date(adjustedDate(paymentDate, option));
564
565 switch (d->m_occurrence) {
566 case Schedule::Occurrence::Once:
567 if (start_date >= _startDate && start_date <= endDate)
568 theDates.append(start_date);
569 break;
570
571 case Schedule::Occurrence::Daily:
572 while (date.isValid() && (date <= endDate)) {
573 if (date >= _startDate)
574 theDates.append(date);
575 paymentDate = paymentDate.addDays(d->m_occurrenceMultiplier);
576 date = adjustedDate(paymentDate, option);
577 }
578 break;
579
580 case Schedule::Occurrence::Weekly: {
581 int step = 7 * d->m_occurrenceMultiplier;
582 while (date.isValid() && (date <= endDate)) {
583 if (date >= _startDate)
584 theDates.append(date);
585 paymentDate = paymentDate.addDays(step);
586 date = adjustedDate(paymentDate, option);
587 }
588 }
589 break;
590
591 case Schedule::Occurrence::EveryHalfMonth:
592 while (date.isValid() && (date <= endDate)) {
593 if (date >= _startDate)
594 theDates.append(date);
595 paymentDate = addHalfMonths(paymentDate, d->m_occurrenceMultiplier);
596 date = adjustedDate(paymentDate, option);
597 }
598 break;
599
600 case Schedule::Occurrence::Monthly:
601 while (date.isValid() && (date <= endDate)) {
602 if (date >= _startDate)
603 theDates.append(date);
604 paymentDate = paymentDate.addMonths(d->m_occurrenceMultiplier);
605 fixDate(paymentDate);
606 date = adjustedDate(paymentDate, option);
607 }
608 break;
609
610 case Schedule::Occurrence::Yearly:
611 while (date.isValid() && (date <= endDate)) {
612 if (date >= _startDate)
613 theDates.append(date);
614 paymentDate = paymentDate.addYears(d->m_occurrenceMultiplier);
615 fixDate(paymentDate);
616 date = adjustedDate(paymentDate, option);
617 }
618 break;
619
620 case Schedule::Occurrence::Any:
621 default:
622 break;
623 }
624
625 return theDates;
626 }
627
operator <(const MyMoneySchedule & right) const628 bool MyMoneySchedule::operator <(const MyMoneySchedule& right) const
629 {
630 return adjustedNextDueDate() < right.adjustedNextDueDate();
631 }
632
operator ==(const MyMoneySchedule & right) const633 bool MyMoneySchedule::operator ==(const MyMoneySchedule& right) const
634 {
635 Q_D(const MyMoneySchedule);
636 auto d2 = static_cast<const MyMoneySchedulePrivate *>(right.d_func());
637 if (MyMoneyObject::operator==(right) &&
638 d->m_occurrence == d2->m_occurrence &&
639 d->m_occurrenceMultiplier == d2->m_occurrenceMultiplier &&
640 d->m_type == d2->m_type &&
641 d->m_startDate == d2->m_startDate &&
642 d->m_paymentType == d2->m_paymentType &&
643 d->m_fixed == d2->m_fixed &&
644 d->m_transaction == d2->m_transaction &&
645 d->m_endDate == d2->m_endDate &&
646 d->m_lastDayInMonth == d2->m_lastDayInMonth &&
647 d->m_autoEnter == d2->m_autoEnter &&
648 d->m_lastPayment == d2->m_lastPayment &&
649 ((d->m_name.length() == 0 && d2->m_name.length() == 0) || (d->m_name == d2->m_name)))
650 return true;
651 return false;
652 }
653
operator !=(const MyMoneySchedule & right) const654 bool MyMoneySchedule::operator !=(const MyMoneySchedule& right) const
655 {
656 return ! operator==(right);
657 }
658
transactionsRemaining() const659 int MyMoneySchedule::transactionsRemaining() const
660 {
661 Q_D(const MyMoneySchedule);
662 return transactionsRemainingUntil(adjustedDate(d->m_endDate, weekendOption()));
663 }
664
transactionsRemainingUntil(const QDate & endDate) const665 int MyMoneySchedule::transactionsRemainingUntil(const QDate& endDate) const
666 {
667 auto counter = 0;
668 Q_D(const MyMoneySchedule);
669
670 const auto beginDate = d->m_lastPayment.isValid() ? d->m_lastPayment : startDate();
671 if (beginDate.isValid() && endDate.isValid()) {
672 QList<QDate> dates = paymentDates(beginDate, endDate);
673 counter = dates.count();
674 }
675 return counter;
676 }
677
endDate() const678 QDate MyMoneySchedule::endDate() const
679 {
680 Q_D(const MyMoneySchedule);
681 return d->m_endDate;
682 }
683
autoEnter() const684 bool MyMoneySchedule::autoEnter() const
685 {
686 Q_D(const MyMoneySchedule);
687 return d->m_autoEnter;
688 }
689
lastDayInMonth() const690 bool MyMoneySchedule::lastDayInMonth() const
691 {
692 Q_D(const MyMoneySchedule);
693 return d->m_lastDayInMonth;
694 }
695
transaction() const696 MyMoneyTransaction MyMoneySchedule::transaction() const
697 {
698 Q_D(const MyMoneySchedule);
699 return d->m_transaction;
700 }
701
lastPayment() const702 QDate MyMoneySchedule::lastPayment() const
703 {
704 Q_D(const MyMoneySchedule);
705 return d->m_lastPayment;
706 }
707
account(int cnt) const708 MyMoneyAccount MyMoneySchedule::account(int cnt) const
709 {
710 Q_D(const MyMoneySchedule);
711 QList<MyMoneySplit> splits = d->m_transaction.splits();
712 QList<MyMoneySplit>::ConstIterator it;
713 auto file = MyMoneyFile::instance();
714 MyMoneyAccount acc;
715
716 // search the first asset or liability account
717 for (it = splits.constBegin(); it != splits.constEnd() && (acc.id().isEmpty() || cnt); ++it) {
718 try {
719 acc = file->account((*it).accountId());
720 if (acc.isAssetLiability())
721 --cnt;
722
723 if (!cnt)
724 return acc;
725 } catch (const MyMoneyException &) {
726 qWarning("Schedule '%s' references unknown account '%s'", qPrintable(id()), qPrintable((*it).accountId()));
727 return MyMoneyAccount();
728 }
729 }
730
731 return MyMoneyAccount();
732 }
733
transferAccount() const734 MyMoneyAccount MyMoneySchedule::transferAccount() const {
735 return account(2);
736 }
737
dateAfter(int transactions) const738 QDate MyMoneySchedule::dateAfter(int transactions) const
739 {
740 auto counter = 1;
741 QDate paymentDate(startDate());
742
743 if (transactions <= 0)
744 return paymentDate;
745
746 Q_D(const MyMoneySchedule);
747 switch (d->m_occurrence) {
748 case Schedule::Occurrence::Once:
749 break;
750
751 case Schedule::Occurrence::Daily:
752 while (counter++ < transactions)
753 paymentDate = paymentDate.addDays(d->m_occurrenceMultiplier);
754 break;
755
756 case Schedule::Occurrence::Weekly: {
757 int step = 7 * d->m_occurrenceMultiplier;
758 while (counter++ < transactions)
759 paymentDate = paymentDate.addDays(step);
760 }
761 break;
762
763 case Schedule::Occurrence::EveryHalfMonth:
764 paymentDate = addHalfMonths(paymentDate, d->m_occurrenceMultiplier * (transactions - 1));
765 break;
766
767 case Schedule::Occurrence::Monthly:
768 while (counter++ < transactions)
769 paymentDate = paymentDate.addMonths(d->m_occurrenceMultiplier);
770 break;
771
772 case Schedule::Occurrence::Yearly:
773 while (counter++ < transactions)
774 paymentDate = paymentDate.addYears(d->m_occurrenceMultiplier);
775 break;
776
777 case Schedule::Occurrence::Any:
778 default:
779 break;
780 }
781
782 return paymentDate;
783 }
784
isOverdue() const785 bool MyMoneySchedule::isOverdue() const
786 {
787 if (isFinished())
788 return false;
789
790 if (adjustedNextDueDate() >= QDate::currentDate())
791 return false;
792
793 return true;
794 }
795
isFinished() const796 bool MyMoneySchedule::isFinished() const
797 {
798 Q_D(const MyMoneySchedule);
799 if (!d->m_lastPayment.isValid())
800 return false;
801
802 if (d->m_endDate.isValid()) {
803 if (d->m_lastPayment >= d->m_endDate
804 || !nextDueDate().isValid()
805 || nextDueDate() > d->m_endDate)
806 return true;
807 }
808
809 // Check to see if its a once off payment
810 if (d->m_occurrence == Schedule::Occurrence::Once)
811 return true;
812
813 return false;
814 }
815
hasRecordedPayment(const QDate & date) const816 bool MyMoneySchedule::hasRecordedPayment(const QDate& date) const
817 {
818 Q_D(const MyMoneySchedule);
819 // m_lastPayment should always be > recordedPayments()
820 if (d->m_lastPayment.isValid() && d->m_lastPayment >= date)
821 return true;
822
823 if (d->m_recordedPayments.contains(date))
824 return true;
825
826 return false;
827 }
828
recordPayment(const QDate & date)829 void MyMoneySchedule::recordPayment(const QDate& date)
830 {
831 Q_D(MyMoneySchedule);
832 d->m_recordedPayments.append(date);
833 }
834
recordedPayments() const835 QList<QDate> MyMoneySchedule::recordedPayments() const
836 {
837 Q_D(const MyMoneySchedule);
838 return d->m_recordedPayments;
839 }
840
setWeekendOption(const Schedule::WeekendOption option)841 void MyMoneySchedule::setWeekendOption(const Schedule::WeekendOption option)
842 {
843 Q_D(MyMoneySchedule);
844 // make sure only valid values are used. Invalid defaults to MoveNothing.
845 switch (option) {
846 case Schedule::WeekendOption::MoveBefore:
847 case Schedule::WeekendOption::MoveAfter:
848 d->m_weekendOption = option;
849 break;
850
851 default:
852 d->m_weekendOption = Schedule::WeekendOption::MoveNothing;
853 break;
854 }
855 }
856
fixDate(QDate & date) const857 void MyMoneySchedule::fixDate(QDate& date) const
858 {
859 Q_D(const MyMoneySchedule);
860 QDate fixDate(d->m_startDate);
861 if (fixDate.isValid()
862 && date.day() != fixDate.day()
863 && QDate::isValid(date.year(), date.month(), fixDate.day())) {
864 date = QDate(date.year(), date.month(), fixDate.day());
865 }
866 }
867
hasReferenceTo(const QString & id) const868 bool MyMoneySchedule::hasReferenceTo(const QString& id) const
869 {
870 Q_D(const MyMoneySchedule);
871 return d->m_transaction.hasReferenceTo(id);
872 }
873
occurrenceToString() const874 QString MyMoneySchedule::occurrenceToString() const
875 {
876 return occurrenceToString(occurrenceMultiplier(), occurrence());
877 }
878
occurrenceToString(Schedule::Occurrence occurrence)879 QString MyMoneySchedule::occurrenceToString(Schedule::Occurrence occurrence)
880 {
881 QString occurrenceString = I18N_NOOP2("Frequency of schedule", "Any");
882
883 if (occurrence == Schedule::Occurrence::Once)
884 occurrenceString = I18N_NOOP2("Frequency of schedule", "Once");
885 else if (occurrence == Schedule::Occurrence::Daily)
886 occurrenceString = I18N_NOOP2("Frequency of schedule", "Daily");
887 else if (occurrence == Schedule::Occurrence::Weekly)
888 occurrenceString = I18N_NOOP2("Frequency of schedule", "Weekly");
889 else if (occurrence == Schedule::Occurrence::Fortnightly)
890 occurrenceString = I18N_NOOP2("Frequency of schedule", "Fortnightly");
891 else if (occurrence == Schedule::Occurrence::EveryOtherWeek)
892 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other week");
893 else if (occurrence == Schedule::Occurrence::EveryHalfMonth)
894 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every half month");
895 else if (occurrence == Schedule::Occurrence::EveryThreeWeeks)
896 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three weeks");
897 else if (occurrence == Schedule::Occurrence::EveryFourWeeks)
898 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four weeks");
899 else if (occurrence == Schedule::Occurrence::EveryThirtyDays)
900 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every thirty days");
901 else if (occurrence == Schedule::Occurrence::Monthly)
902 occurrenceString = I18N_NOOP2("Frequency of schedule", "Monthly");
903 else if (occurrence == Schedule::Occurrence::EveryEightWeeks)
904 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every eight weeks");
905 else if (occurrence == Schedule::Occurrence::EveryOtherMonth)
906 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every two months");
907 else if (occurrence == Schedule::Occurrence::EveryThreeMonths)
908 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three months");
909 else if (occurrence == Schedule::Occurrence::Quarterly)
910 occurrenceString = I18N_NOOP2("Frequency of schedule", "Quarterly");
911 else if (occurrence == Schedule::Occurrence::EveryFourMonths)
912 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four months");
913 else if (occurrence == Schedule::Occurrence::TwiceYearly)
914 occurrenceString = I18N_NOOP2("Frequency of schedule", "Twice yearly");
915 else if (occurrence == Schedule::Occurrence::Yearly)
916 occurrenceString = I18N_NOOP2("Frequency of schedule", "Yearly");
917 else if (occurrence == Schedule::Occurrence::EveryOtherYear)
918 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other year");
919 return occurrenceString;
920 }
921
occurrenceToString(int mult,Schedule::Occurrence type)922 QString MyMoneySchedule::occurrenceToString(int mult, Schedule::Occurrence type)
923 {
924 QString occurrenceString = I18N_NOOP2("Frequency of schedule", "Any");
925
926 if (type == Schedule::Occurrence::Once)
927 switch (mult) {
928 case 1:
929 occurrenceString = I18N_NOOP2("Frequency of schedule", "Once");
930 break;
931 default:
932 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("%1 times").arg(mult));
933 }
934 else if (type == Schedule::Occurrence::Daily)
935 switch (mult) {
936 case 1:
937 occurrenceString = I18N_NOOP2("Frequency of schedule", "Daily");
938 break;
939 case 30:
940 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every thirty days");
941 break;
942 default:
943 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 days").arg(mult));
944 }
945 else if (type == Schedule::Occurrence::Weekly)
946 switch (mult) {
947 case 1:
948 occurrenceString = I18N_NOOP2("Frequency of schedule", "Weekly");
949 break;
950 case 2:
951 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other week");
952 break;
953 case 3:
954 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three weeks");
955 break;
956 case 4:
957 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four weeks");
958 break;
959 case 8:
960 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every eight weeks");
961 break;
962 default:
963 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 weeks").arg(mult));
964 }
965 else if (type == Schedule::Occurrence::EveryHalfMonth)
966 switch (mult) {
967 case 1:
968 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every half month");
969 break;
970 default:
971 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 half months").arg(mult));
972 }
973 else if (type == Schedule::Occurrence::Monthly)
974 switch (mult) {
975 case 1:
976 occurrenceString = I18N_NOOP2("Frequency of schedule", "Monthly");
977 break;
978 case 2:
979 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every two months");
980 break;
981 case 3:
982 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every three months");
983 break;
984 case 4:
985 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every four months");
986 break;
987 case 6:
988 occurrenceString = I18N_NOOP2("Frequency of schedule", "Twice yearly");
989 break;
990 default:
991 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 months").arg(mult));
992 }
993 else if (type == Schedule::Occurrence::Yearly)
994 switch (mult) {
995 case 1:
996 occurrenceString = I18N_NOOP2("Frequency of schedule", "Yearly");
997 break;
998 case 2:
999 occurrenceString = I18N_NOOP2("Frequency of schedule", "Every other year");
1000 break;
1001 default:
1002 occurrenceString = I18N_NOOP2("Frequency of schedule", QString("Every %1 years").arg(mult));
1003 }
1004 return occurrenceString;
1005 }
1006
occurrencePeriodToString(Schedule::Occurrence type)1007 QString MyMoneySchedule::occurrencePeriodToString(Schedule::Occurrence type)
1008 {
1009 QString occurrenceString = I18N_NOOP2("Schedule occurrence period", "Any");
1010
1011 if (type == Schedule::Occurrence::Once)
1012 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Once");
1013 else if (type == Schedule::Occurrence::Daily)
1014 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Day");
1015 else if (type == Schedule::Occurrence::Weekly)
1016 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Week");
1017 else if (type == Schedule::Occurrence::EveryHalfMonth)
1018 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Half-month");
1019 else if (type == Schedule::Occurrence::Monthly)
1020 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Month");
1021 else if (type == Schedule::Occurrence::Yearly)
1022 occurrenceString = I18N_NOOP2("Schedule occurrence period", "Year");
1023 return occurrenceString;
1024 }
1025
scheduleTypeToString(Schedule::Type type)1026 QString MyMoneySchedule::scheduleTypeToString(Schedule::Type type)
1027 {
1028 QString text;
1029
1030 switch (type) {
1031 case Schedule::Type::Bill:
1032 text = I18N_NOOP2("Scheduled transaction type", "Bill");
1033 break;
1034 case Schedule::Type::Deposit:
1035 text = I18N_NOOP2("Scheduled transaction type", "Deposit");
1036 break;
1037 case Schedule::Type::Transfer:
1038 text = I18N_NOOP2("Scheduled transaction type", "Transfer");
1039 break;
1040 case Schedule::Type::LoanPayment:
1041 text = I18N_NOOP2("Scheduled transaction type", "Loan payment");
1042 break;
1043 case Schedule::Type::Any:
1044 default:
1045 text = I18N_NOOP2("Scheduled transaction type", "Unknown");
1046 }
1047 return text;
1048 }
1049
1050
paymentMethodToString(Schedule::PaymentType paymentType)1051 QString MyMoneySchedule::paymentMethodToString(Schedule::PaymentType paymentType)
1052 {
1053 QString text;
1054
1055 switch (paymentType) {
1056 case Schedule::PaymentType::DirectDebit:
1057 text = I18N_NOOP2("Scheduled Transaction payment type", "Direct debit");
1058 break;
1059 case Schedule::PaymentType::DirectDeposit:
1060 text = I18N_NOOP2("Scheduled Transaction payment type", "Direct deposit");
1061 break;
1062 case Schedule::PaymentType::ManualDeposit:
1063 text = I18N_NOOP2("Scheduled Transaction payment type", "Manual deposit");
1064 break;
1065 case Schedule::PaymentType::Other:
1066 text = I18N_NOOP2("Scheduled Transaction payment type", "Other");
1067 break;
1068 case Schedule::PaymentType::WriteChecque:
1069 text = I18N_NOOP2("Scheduled Transaction payment type", "Write check");
1070 break;
1071 case Schedule::PaymentType::StandingOrder:
1072 text = I18N_NOOP2("Scheduled Transaction payment type", "Standing order");
1073 break;
1074 case Schedule::PaymentType::BankTransfer:
1075 text = I18N_NOOP2("Scheduled Transaction payment type", "Bank transfer");
1076 break;
1077 case Schedule::PaymentType::Any:
1078 text = I18N_NOOP2("Scheduled Transaction payment type", "Any (Error)");
1079 break;
1080 }
1081 return text;
1082 }
1083
weekendOptionToString(Schedule::WeekendOption weekendOption)1084 QString MyMoneySchedule::weekendOptionToString(Schedule::WeekendOption weekendOption)
1085 {
1086 QString text;
1087
1088 switch (weekendOption) {
1089 case Schedule::WeekendOption::MoveBefore:
1090 text = I18N_NOOP("Change the date to the previous processing day");
1091 break;
1092 case Schedule::WeekendOption::MoveAfter:
1093 text = I18N_NOOP("Change the date to the next processing day");
1094 break;
1095 case Schedule::WeekendOption::MoveNothing:
1096 text = I18N_NOOP("Do not change the date");
1097 break;
1098 }
1099 return text;
1100 }
1101
1102 // until we don't have the means to store the value
1103 // of the variation, we default to 10% in case this
1104 // scheduled transaction is marked 'not fixed'.
1105 //
1106 // ipwizard 2009-04-18
1107
variation() const1108 int MyMoneySchedule::variation() const
1109 {
1110 int rc = 0;
1111 if (!isFixed()) {
1112 rc = 10;
1113 #if 0
1114 QString var = value("kmm-variation");
1115 if (!var.isEmpty())
1116 rc = var.toInt();
1117 #endif
1118 }
1119 return rc;
1120 }
1121
setVariation(int var)1122 void MyMoneySchedule::setVariation(int var)
1123 {
1124 Q_UNUSED(var)
1125 #if 0
1126 deletePair("kmm-variation");
1127 if (var != 0)
1128 setValue("kmm-variation", QString("%1").arg(var));
1129 #endif
1130 }
1131
eventsPerYear(Schedule::Occurrence occurrence)1132 int MyMoneySchedule::eventsPerYear(Schedule::Occurrence occurrence)
1133 {
1134 int rc = 0;
1135
1136 switch (occurrence) {
1137 case Schedule::Occurrence::Daily:
1138 rc = 365;
1139 break;
1140 case Schedule::Occurrence::Weekly:
1141 rc = 52;
1142 break;
1143 case Schedule::Occurrence::Fortnightly:
1144 rc = 26;
1145 break;
1146 case Schedule::Occurrence::EveryOtherWeek:
1147 rc = 26;
1148 break;
1149 case Schedule::Occurrence::EveryHalfMonth:
1150 rc = 24;
1151 break;
1152 case Schedule::Occurrence::EveryThreeWeeks:
1153 rc = 17;
1154 break;
1155 case Schedule::Occurrence::EveryFourWeeks:
1156 rc = 13;
1157 break;
1158 case Schedule::Occurrence::Monthly:
1159 case Schedule::Occurrence::EveryThirtyDays:
1160 rc = 12;
1161 break;
1162 case Schedule::Occurrence::EveryEightWeeks:
1163 rc = 6;
1164 break;
1165 case Schedule::Occurrence::EveryOtherMonth:
1166 rc = 6;
1167 break;
1168 case Schedule::Occurrence::EveryThreeMonths:
1169 case Schedule::Occurrence::Quarterly:
1170 rc = 4;
1171 break;
1172 case Schedule::Occurrence::EveryFourMonths:
1173 rc = 3;
1174 break;
1175 case Schedule::Occurrence::TwiceYearly:
1176 rc = 2;
1177 break;
1178 case Schedule::Occurrence::Yearly:
1179 rc = 1;
1180 break;
1181 default:
1182 qWarning("Occurrence not supported by financial calculator");
1183 }
1184
1185 return rc;
1186 }
1187
daysBetweenEvents(Schedule::Occurrence occurrence)1188 int MyMoneySchedule::daysBetweenEvents(Schedule::Occurrence occurrence)
1189 {
1190 int rc = 0;
1191
1192 switch (occurrence) {
1193 case Schedule::Occurrence::Daily:
1194 rc = 1;
1195 break;
1196 case Schedule::Occurrence::Weekly:
1197 rc = 7;
1198 break;
1199 case Schedule::Occurrence::Fortnightly:
1200 rc = 14;
1201 break;
1202 case Schedule::Occurrence::EveryOtherWeek:
1203 rc = 14;
1204 break;
1205 case Schedule::Occurrence::EveryHalfMonth:
1206 rc = 15;
1207 break;
1208 case Schedule::Occurrence::EveryThreeWeeks:
1209 rc = 21;
1210 break;
1211 case Schedule::Occurrence::EveryFourWeeks:
1212 rc = 28;
1213 break;
1214 case Schedule::Occurrence::EveryThirtyDays:
1215 rc = 30;
1216 break;
1217 case Schedule::Occurrence::Monthly:
1218 rc = 30;
1219 break;
1220 case Schedule::Occurrence::EveryEightWeeks:
1221 rc = 56;
1222 break;
1223 case Schedule::Occurrence::EveryOtherMonth:
1224 rc = 60;
1225 break;
1226 case Schedule::Occurrence::EveryThreeMonths:
1227 case Schedule::Occurrence::Quarterly:
1228 rc = 90;
1229 break;
1230 case Schedule::Occurrence::EveryFourMonths:
1231 rc = 120;
1232 break;
1233 case Schedule::Occurrence::TwiceYearly:
1234 rc = 180;
1235 break;
1236 case Schedule::Occurrence::Yearly:
1237 rc = 360;
1238 break;
1239 default:
1240 qWarning("Occurrence not supported by financial calculator");
1241 }
1242
1243 return rc;
1244 }
1245
addHalfMonths(QDate date,int mult) const1246 QDate MyMoneySchedule::addHalfMonths(QDate date, int mult) const
1247 {
1248 QDate newdate = date;
1249 int d, dm;
1250 if (mult > 0) {
1251 d = newdate.day();
1252 if (d <= 12) {
1253 if (mult % 2 == 0)
1254 newdate = newdate.addMonths(mult >> 1);
1255 else
1256 newdate = newdate.addMonths(mult >> 1).addDays(15);
1257 } else
1258 for (int i = 0; i < mult; i++) {
1259 if (d <= 13)
1260 newdate = newdate.addDays(15);
1261 else {
1262 dm = newdate.daysInMonth();
1263 if (d == 14)
1264 newdate = newdate.addDays((dm < 30) ? dm - d : 15);
1265 else if (d == 15)
1266 newdate = newdate.addDays(dm - d);
1267 else if (d == dm)
1268 newdate = newdate.addDays(15 - d).addMonths(1);
1269 else
1270 newdate = newdate.addDays(-15).addMonths(1);
1271 }
1272 d = newdate.day();
1273 }
1274 } else if (mult < 0) // Go backwards
1275 for (int i = 0; i > mult; i--) {
1276 d = newdate.day();
1277 dm = newdate.daysInMonth();
1278 if (d > 15) {
1279 dm = newdate.daysInMonth();
1280 newdate = newdate.addDays((d == dm) ? 15 - dm : -15);
1281 } else if (d <= 13)
1282 newdate = newdate.addMonths(-1).addDays(15);
1283 else if (d == 15)
1284 newdate = newdate.addDays(-15);
1285 else { // 14
1286 newdate = newdate.addMonths(-1);
1287 dm = newdate.daysInMonth();
1288 newdate = newdate.addDays((dm < 30) ? dm - d : 15);
1289 }
1290 }
1291 return newdate;
1292 }
1293
1294 /**
1295 * Helper method to convert simple occurrence to compound occurrence + multiplier
1296 *
1297 * @param multiplier Returned by reference. Adjusted multiplier
1298 * @param occurrence Returned by reference. Occurrence type
1299 */
simpleToCompoundOccurrence(int & multiplier,Schedule::Occurrence & occurrence)1300 void MyMoneySchedule::simpleToCompoundOccurrence(int& multiplier, Schedule::Occurrence& occurrence)
1301 {
1302 Schedule::Occurrence newOcc = occurrence;
1303 int newMulti = 1;
1304 if (occurrence == Schedule::Occurrence::Once ||
1305 occurrence == Schedule::Occurrence::Daily ||
1306 occurrence == Schedule::Occurrence::Weekly ||
1307 occurrence == Schedule::Occurrence::EveryHalfMonth ||
1308 occurrence == Schedule::Occurrence::Monthly ||
1309 occurrence == Schedule::Occurrence::Yearly) { // Already a base occurrence and multiplier
1310 } else if (occurrence == Schedule::Occurrence::Fortnightly ||
1311 occurrence == Schedule::Occurrence::EveryOtherWeek) {
1312 newOcc = Schedule::Occurrence::Weekly;
1313 newMulti = 2;
1314 } else if (occurrence == Schedule::Occurrence::EveryThreeWeeks) {
1315 newOcc = Schedule::Occurrence::Weekly;
1316 newMulti = 3;
1317 } else if (occurrence == Schedule::Occurrence::EveryFourWeeks) {
1318 newOcc = Schedule::Occurrence::Weekly;
1319 newMulti = 4;
1320 } else if (occurrence == Schedule::Occurrence::EveryThirtyDays) {
1321 newOcc = Schedule::Occurrence::Daily;
1322 newMulti = 30;
1323 } else if (occurrence == Schedule::Occurrence::EveryEightWeeks) {
1324 newOcc = Schedule::Occurrence::Weekly;
1325 newMulti = 8;
1326 } else if (occurrence == Schedule::Occurrence::EveryOtherMonth) {
1327 newOcc = Schedule::Occurrence::Monthly;
1328 newMulti = 2;
1329 } else if (occurrence == Schedule::Occurrence::EveryThreeMonths ||
1330 occurrence == Schedule::Occurrence::Quarterly) {
1331 newOcc = Schedule::Occurrence::Monthly;
1332 newMulti = 3;
1333 } else if (occurrence == Schedule::Occurrence::EveryFourMonths) {
1334 newOcc = Schedule::Occurrence::Monthly;
1335 newMulti = 4;
1336 } else if (occurrence == Schedule::Occurrence::TwiceYearly) {
1337 newOcc = Schedule::Occurrence::Monthly;
1338 newMulti = 6;
1339 } else if (occurrence == Schedule::Occurrence::EveryOtherYear) {
1340 newOcc = Schedule::Occurrence::Yearly;
1341 newMulti = 2;
1342 } else { // Unknown
1343 newOcc = Schedule::Occurrence::Any;
1344 newMulti = 1;
1345 }
1346 if (newOcc != occurrence) {
1347 occurrence = newOcc;
1348 multiplier = newMulti == 1 ? multiplier : newMulti * multiplier;
1349 }
1350 }
1351
1352 /**
1353 * Helper method to convert compound occurrence + multiplier to simple occurrence
1354 *
1355 * @param multiplier Returned by reference. Adjusted multiplier
1356 * @param occurrence Returned by reference. Occurrence type
1357 */
compoundToSimpleOccurrence(int & multiplier,Schedule::Occurrence & occurrence)1358 void MyMoneySchedule::compoundToSimpleOccurrence(int& multiplier, Schedule::Occurrence& occurrence)
1359 {
1360 Schedule::Occurrence newOcc = occurrence;
1361 if (occurrence == Schedule::Occurrence::Once) { // Nothing to do
1362 } else if (occurrence == Schedule::Occurrence::Daily) {
1363 switch (multiplier) {
1364 case 1:
1365 break;
1366 case 30:
1367 newOcc = Schedule::Occurrence::EveryThirtyDays;
1368 break;
1369 }
1370 } else if (newOcc == Schedule::Occurrence::Weekly) {
1371 switch (multiplier) {
1372 case 1:
1373 break;
1374 case 2:
1375 newOcc = Schedule::Occurrence::EveryOtherWeek;
1376 break;
1377 case 3:
1378 newOcc = Schedule::Occurrence::EveryThreeWeeks;
1379 break;
1380 case 4:
1381 newOcc = Schedule::Occurrence::EveryFourWeeks;
1382 break;
1383 case 8:
1384 newOcc = Schedule::Occurrence::EveryEightWeeks;
1385 break;
1386 }
1387 } else if (occurrence == Schedule::Occurrence::Monthly)
1388 switch (multiplier) {
1389 case 1:
1390 break;
1391 case 2:
1392 newOcc = Schedule::Occurrence::EveryOtherMonth;
1393 break;
1394 case 3:
1395 newOcc = Schedule::Occurrence::EveryThreeMonths;
1396 break;
1397 case 4:
1398 newOcc = Schedule::Occurrence::EveryFourMonths;
1399 break;
1400 case 6:
1401 newOcc = Schedule::Occurrence::TwiceYearly;
1402 break;
1403 }
1404 else if (occurrence == Schedule::Occurrence::EveryHalfMonth)
1405 switch (multiplier) {
1406 case 1:
1407 break;
1408 }
1409 else if (occurrence == Schedule::Occurrence::Yearly) {
1410 switch (multiplier) {
1411 case 1:
1412 break;
1413 case 2:
1414 newOcc = Schedule::Occurrence::EveryOtherYear;
1415 break;
1416 }
1417 }
1418 if (occurrence != newOcc) { // Changed to derived type
1419 occurrence = newOcc;
1420 multiplier = 1;
1421 }
1422 }
1423
setProcessingCalendar(IMyMoneyProcessingCalendar * pc)1424 void MyMoneySchedule::setProcessingCalendar(IMyMoneyProcessingCalendar* pc)
1425 {
1426 processingCalendarPtr = pc;
1427 }
1428
isProcessingDate(const QDate & date) const1429 bool MyMoneySchedule::isProcessingDate(const QDate& date) const
1430 {
1431 if (processingCalendarPtr)
1432 return processingCalendarPtr->isProcessingDate(date);
1433
1434 /// @todo test against m_processingDays instead? (currently only for tests)
1435 return date.dayOfWeek() < Qt::Saturday;
1436 }
1437
processingCalendar() const1438 IMyMoneyProcessingCalendar* MyMoneySchedule::processingCalendar() const
1439 {
1440 return processingCalendarPtr;
1441 }
1442
replaceId(const QString & newId,const QString & oldId)1443 bool MyMoneySchedule::replaceId(const QString& newId, const QString& oldId)
1444 {
1445 Q_D(MyMoneySchedule);
1446 return d->m_transaction.replaceId(newId, oldId);
1447 }
1448