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.collections4.IterableUtils;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -79,8 +80,15 @@ public class RecurringTransactionService {
|
|||||||
|
|
||||||
// Visible for unit tests
|
// Visible for unit tests
|
||||||
/* package */ Iterable<RecurringTransaction> getAllDueToday(LocalDate now) {
|
/* 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
|
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
|
//@formatter:off
|
||||||
return IterableUtils.toList(allRecurringTransactions).stream()
|
return IterableUtils.toList(allRecurringTransactions).stream()
|
||||||
@@ -115,6 +123,10 @@ public class RecurringTransactionService {
|
|||||||
// now.plusDays(1) = 2019-05-15
|
// now.plusDays(1) = 2019-05-15
|
||||||
// => IllegalArgumentException: 2019-05-15 < 2019-05-27
|
// => IllegalArgumentException: 2019-05-15 < 2019-05-27
|
||||||
if (recurringTransaction.getFirstOccurrence().isAfter(now)) {
|
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
|
return false; // early return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,6 +153,9 @@ public class RecurringTransactionService {
|
|||||||
|| recurringTransaction.getHolidayWeekendType() == HolidayWeekendType.PREVIOUS_WORKDAY;
|
|| 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;
|
return !defer && dueToday;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,18 +177,31 @@ public class RecurringTransactionService {
|
|||||||
private boolean checkRecurringTransactionDuePast(RecurringTransaction recurringTransaction, LocalDate now) {
|
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
|
// 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())) {
|
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
|
return false; // early return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a recurring transactions first occurrence is in the future it can never be relevant for this
|
// 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.
|
// method, as this method handles recurring transactions due in the past.
|
||||||
if (recurringTransaction.getFirstOccurrence().isAfter(now)) {
|
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
|
return false; // early return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If today is a weekend day or holiday the recurring transaction cannot be due today, because the
|
// If today is a weekend day or holiday the recurring transaction cannot be due today, because the
|
||||||
// holiday weekend type says NEXT_WORKDAY.
|
// holiday weekend type says NEXT_WORKDAY.
|
||||||
if (this.ruleService.isHoliday(now) || this.ruleService.isWeekend(now)) {
|
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
|
return false; // early return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +235,9 @@ public class RecurringTransactionService {
|
|||||||
}
|
}
|
||||||
while (holiday || weekend);
|
while (holiday || weekend);
|
||||||
|
|
||||||
|
LOGGER.debug(String.format("Recurring transaction %s is due in the past? %s",
|
||||||
|
ReflectionToStringBuilder.toString(recurringTransaction), due));
|
||||||
|
|
||||||
return due;
|
return due;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +258,10 @@ public class RecurringTransactionService {
|
|||||||
private boolean checkRecurringTransactionDueFuture(RecurringTransaction recurringTransaction, LocalDate now) {
|
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
|
// 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())) {
|
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
|
return false; // early return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +295,9 @@ public class RecurringTransactionService {
|
|||||||
}
|
}
|
||||||
while (holiday || weekend);
|
while (holiday || weekend);
|
||||||
|
|
||||||
|
LOGGER.debug(String.format("Recurring transaction %s is due in the future? %s",
|
||||||
|
ReflectionToStringBuilder.toString(recurringTransaction), due));
|
||||||
|
|
||||||
return due;
|
return due;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,6 +181,32 @@ public class RecurringTransactionService_getAllDueToday_MONTHLY_NEXT_WORKDAYTest
|
|||||||
Assert.assertEquals(0, IterableUtils.size(recurringDueToday));
|
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) {
|
private RecurringTransaction createRecurringTransaction(int days) {
|
||||||
final RecurringTransaction recurringTransaction = new RecurringTransaction();
|
final RecurringTransaction recurringTransaction = new RecurringTransaction();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user