Fix a bug in the rec. trx. service if it has HWT 'NW' and LO on a weekend
Adjust it so that recurring transactions that have their last occurrence (LO) on a weekend or a holiday in the near past and HWT NEXT_WORKDAY (NW) 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)'. Also add a unit test case for this. Further add better logging.
This commit is contained in:
@@ -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<RecurringTransaction> 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<RecurringTransaction> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<RecurringTransaction> recurringDueToday = this.classUnderTest.getAllDueToday(now);
|
||||
|
||||
// Assert
|
||||
Assert.assertEquals(1, IterableUtils.size(recurringDueToday));
|
||||
}
|
||||
|
||||
private RecurringTransaction createRecurringTransaction(int days) {
|
||||
final RecurringTransaction recurringTransaction = new RecurringTransaction();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user