diff --git a/src/main/java/de/financer/service/RecurringTransactionService.java b/src/main/java/de/financer/service/RecurringTransactionService.java index d41bbc9..a9bb2b3 100644 --- a/src/main/java/de/financer/service/RecurringTransactionService.java +++ b/src/main/java/de/financer/service/RecurringTransactionService.java @@ -10,6 +10,7 @@ import de.financer.model.RecurringTransaction; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -79,8 +80,15 @@ public class RecurringTransactionService { // Visible for unit tests /* package */ Iterable getAllDueToday(LocalDate now) { + // Subtract one week/seven days from the current date so that recurring transactions that have their last + // occurrence on a weekend or a holiday in the near past and HWT NEXT_WORKDAY are also grabbed. Otherwise + // there would never be a reminder about them. On the actual due date the reminder is deferred because of the + // HWT and for later runs it's not grabbed because of the condition '...LastOccurrenceGreaterThanEqual(now)' final Iterable allRecurringTransactions = this.recurringTransactionRepository - .findByDeletedFalseAndLastOccurrenceIsNullOrLastOccurrenceGreaterThanEqual(now); + .findByDeletedFalseAndLastOccurrenceIsNullOrLastOccurrenceGreaterThanEqual(now.minusDays(7)); + + LOGGER.debug(String.format("Found %s candidate recurring transactions. Checking which are due", + IterableUtils.size(allRecurringTransactions))); //@formatter:off return IterableUtils.toList(allRecurringTransactions).stream() @@ -115,6 +123,10 @@ public class RecurringTransactionService { // now.plusDays(1) = 2019-05-15 // => IllegalArgumentException: 2019-05-15 < 2019-05-27 if (recurringTransaction.getFirstOccurrence().isAfter(now)) { + LOGGER.debug(String.format("Recurring transaction %s has its first occurrence in the future and thus " + + "cannot be due today", + ReflectionToStringBuilder.toString(recurringTransaction))); + return false; // early return } @@ -141,6 +153,9 @@ public class RecurringTransactionService { || recurringTransaction.getHolidayWeekendType() == HolidayWeekendType.PREVIOUS_WORKDAY; } + LOGGER.debug(String.format("Recurring transaction %s due today? %s (defer=%s, dueToday=%s)", + ReflectionToStringBuilder.toString(recurringTransaction), (!defer && dueToday), defer, dueToday)); + return !defer && dueToday; } @@ -162,18 +177,31 @@ public class RecurringTransactionService { private boolean checkRecurringTransactionDuePast(RecurringTransaction recurringTransaction, LocalDate now) { // Recurring transactions with holiday weekend type SAME_DAY or PREVIOUS_WORKDAY can't be due in the past if (!HolidayWeekendType.NEXT_WORKDAY.equals(recurringTransaction.getHolidayWeekendType())) { + LOGGER.debug(String.format("Recurring transaction %s has HWT %s and thus cannot be due in the past", + ReflectionToStringBuilder.toString(recurringTransaction), + recurringTransaction.getHolidayWeekendType())); + return false; // early return } // If a recurring transactions first occurrence is in the future it can never be relevant for this // method, as this method handles recurring transactions due in the past. if (recurringTransaction.getFirstOccurrence().isAfter(now)) { + LOGGER.debug(String.format("Recurring transaction %s has its first occurrence in the future and thus " + + "cannot be due in the past", + ReflectionToStringBuilder.toString(recurringTransaction))); + return false; // early return } // If today is a weekend day or holiday the recurring transaction cannot be due today, because the // holiday weekend type says NEXT_WORKDAY. if (this.ruleService.isHoliday(now) || this.ruleService.isWeekend(now)) { + LOGGER.debug(String.format("Recurring transaction %s has HWT %s and today is either a holiday or weekend," + + " thus it cannot be due in the past", + ReflectionToStringBuilder.toString(recurringTransaction), + recurringTransaction.getHolidayWeekendType())); + return false; // early return } @@ -207,6 +235,9 @@ public class RecurringTransactionService { } while (holiday || weekend); + LOGGER.debug(String.format("Recurring transaction %s is due in the past? %s", + ReflectionToStringBuilder.toString(recurringTransaction), due)); + return due; } @@ -227,6 +258,10 @@ public class RecurringTransactionService { private boolean checkRecurringTransactionDueFuture(RecurringTransaction recurringTransaction, LocalDate now) { // Recurring transactions with holiday weekend type SAME_DAY or PREVIOUS_WORKDAY can't be due in the future if (!HolidayWeekendType.PREVIOUS_WORKDAY.equals(recurringTransaction.getHolidayWeekendType())) { + LOGGER.debug(String.format("Recurring transaction %s has HWT %s and thus cannot be due in the future", + ReflectionToStringBuilder.toString(recurringTransaction), + recurringTransaction.getHolidayWeekendType())); + return false; // early return } @@ -260,6 +295,9 @@ public class RecurringTransactionService { } while (holiday || weekend); + LOGGER.debug(String.format("Recurring transaction %s is due in the future? %s", + ReflectionToStringBuilder.toString(recurringTransaction), due)); + return due; } diff --git a/src/test/java/de/financer/service/RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest.java b/src/test/java/de/financer/service/RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest.java index 85d0d4b..83733ba 100644 --- a/src/test/java/de/financer/service/RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest.java +++ b/src/test/java/de/financer/service/RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest.java @@ -181,6 +181,32 @@ public class RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest Assert.assertEquals(0, IterableUtils.size(recurringDueToday)); } + @Test + public void test_() { + // Arrange + final LocalDate now = LocalDate.of(2019, 6, 17); // A monday + final RecurringTransaction recurringTransaction = new RecurringTransaction(); + + recurringTransaction.setLastOccurrence(LocalDate.of(2019, 6, 15)); // a saturday + recurringTransaction.setFirstOccurrence(LocalDate.of(2019, 5, 15)); // a wednesday + recurringTransaction.setHolidayWeekendType(HolidayWeekendType.NEXT_WORKDAY); + recurringTransaction.setIntervalType(IntervalType.MONTHLY); + + Mockito.when(this.recurringTransactionRepository + .findByDeletedFalseAndLastOccurrenceIsNullOrLastOccurrenceGreaterThanEqual(Mockito.any())) + .thenReturn(Collections.singletonList(recurringTransaction)); + Mockito.when(this.ruleService.isWeekend(Mockito.any())) + .thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, Boolean.FALSE); + Mockito.when(this.ruleService.isHoliday(Mockito.any())) + .thenReturn(Boolean.FALSE); + + // Act + final Iterable recurringDueToday = this.classUnderTest.getAllDueToday(now); + + // Assert + Assert.assertEquals(1, IterableUtils.size(recurringDueToday)); + } + private RecurringTransaction createRecurringTransaction(int days) { final RecurringTransaction recurringTransaction = new RecurringTransaction();