diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 9f363e8..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-financer-server.log*
-.attach*
\ No newline at end of file
diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java
index 6c36465..91738b7 100644
--- a/src/main/java/de/financer/controller/RecurringTransactionController.java
+++ b/src/main/java/de/financer/controller/RecurringTransactionController.java
@@ -53,7 +53,7 @@ public class RecurringTransactionController {
public ResponseEntity createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
- String lastOccurrence
+ String lastOccurrence, Boolean remind
) {
final String decodedFrom = ControllerUtil.urlDecode(fromAccountKey);
final String decodedTo = ControllerUtil.urlDecode(toAccountKey);
@@ -62,13 +62,13 @@ public class RecurringTransactionController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
.format("/recurringTransactions/createRecurringTransaction got parameters: %s, %s, %s, %s, %s, " +
- "%s, %s, %s", decodedFrom, decodedTo, amount, decodedDesc, holidayWeekendType,
- intervalType, firstOccurrence, lastOccurrence));
+ "%s, %s, %s, %s", decodedFrom, decodedTo, amount, decodedDesc, holidayWeekendType,
+ intervalType, firstOccurrence, lastOccurrence, remind));
}
final ResponseReason responseReason = this.recurringTransactionService
.createRecurringTransaction(decodedFrom, decodedTo, amount, decodedDesc, holidayWeekendType,
- intervalType, firstOccurrence, lastOccurrence);
+ intervalType, firstOccurrence, lastOccurrence, remind);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String
diff --git a/src/main/java/de/financer/model/RecurringTransaction.java b/src/main/java/de/financer/model/RecurringTransaction.java
index 3df99c8..135fabe 100644
--- a/src/main/java/de/financer/model/RecurringTransaction.java
+++ b/src/main/java/de/financer/model/RecurringTransaction.java
@@ -21,6 +21,7 @@ public class RecurringTransaction {
@Enumerated(EnumType.STRING)
private HolidayWeekendType holidayWeekendType;
private boolean deleted;
+ private boolean remind;
public Long getId() {
return id;
@@ -97,4 +98,12 @@ public class RecurringTransaction {
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
+
+ public boolean isRemind() {
+ return remind;
+ }
+
+ public void setRemind(boolean remind) {
+ this.remind = remind;
+ }
}
diff --git a/src/main/java/de/financer/service/RecurringTransactionService.java b/src/main/java/de/financer/service/RecurringTransactionService.java
index b921365..38fd3ad 100644
--- a/src/main/java/de/financer/service/RecurringTransactionService.java
+++ b/src/main/java/de/financer/service/RecurringTransactionService.java
@@ -8,6 +8,7 @@ import de.financer.model.HolidayWeekendType;
import de.financer.model.IntervalType;
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.math.NumberUtils;
import org.slf4j.Logger;
@@ -261,16 +262,18 @@ public class RecurringTransactionService {
return due;
}
+
+
@Transactional(propagation = Propagation.REQUIRED)
public ResponseReason createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
- String lastOccurrence
+ String lastOccurrence, Boolean remind
) {
final Account fromAccount = this.accountService.getAccountByKey(fromAccountKey);
final Account toAccount = this.accountService.getAccountByKey(toAccountKey);
ResponseReason response = validateParameters(fromAccount, toAccount, amount, holidayWeekendType, intervalType,
- firstOccurrence, lastOccurrence);
+ firstOccurrence, lastOccurrence); // no validation of 'remind' as it's completely optional
// If we detected an issue with the given parameters return the first error found to the caller
if (response != null) {
@@ -279,7 +282,7 @@ public class RecurringTransactionService {
try {
final RecurringTransaction transaction = buildRecurringTransaction(fromAccount, toAccount, amount,
- description, holidayWeekendType, intervalType, firstOccurrence, lastOccurrence);
+ description, holidayWeekendType, intervalType, firstOccurrence, lastOccurrence, remind);
this.recurringTransactionRepository.save(transaction);
@@ -303,13 +306,14 @@ public class RecurringTransactionService {
* @param intervalType the interval type
* @param firstOccurrence the first occurrence
* @param lastOccurrence the last occurrence, may be null
+ * @param remind the remind flag
*
* @return the build {@link RecurringTransaction} instance
*/
private RecurringTransaction buildRecurringTransaction(Account fromAccount, Account toAccount, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
- String lastOccurrence
+ String lastOccurrence, Boolean remind
) {
final RecurringTransaction recurringTransaction = new RecurringTransaction();
@@ -321,6 +325,9 @@ public class RecurringTransactionService {
recurringTransaction.setIntervalType(IntervalType.valueOf(intervalType));
recurringTransaction.setFirstOccurrence(LocalDate
.parse(firstOccurrence, DateTimeFormatter.ofPattern(this.financerConfig.getDateFormat())));
+ // See 'resources/database/postgres/readme_V1_0_0__init.txt'
+ recurringTransaction.setDeleted(false);
+ recurringTransaction.setRemind(BooleanUtils.toBooleanDefaultIfNull(remind, true));
// lastOccurrence is optional
if (StringUtils.isNotEmpty(lastOccurrence)) {
diff --git a/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java b/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java
index f534e7c..48cf729 100644
--- a/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java
+++ b/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java
@@ -13,6 +13,8 @@ import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
+import java.util.stream.Collectors;
+
@Component
public class SendRecurringTransactionReminderTask {
@@ -33,7 +35,7 @@ public class SendRecurringTransactionReminderTask {
LOGGER.debug("Enter recurring transaction reminder task");
}
- final Iterable recurringTransactions = this.recurringTransactionService.getAllDueToday();
+ Iterable recurringTransactions = this.recurringTransactionService.getAllDueToday();
// If no recurring transaction is due today we don't need to send a reminder
if (IterableUtils.isEmpty(recurringTransactions)) {
@@ -42,6 +44,12 @@ public class SendRecurringTransactionReminderTask {
return; // early return
}
+ // TODO Filtering currently happens in memory but should be done via SQL
+ recurringTransactions = IterableUtils.toList(recurringTransactions)
+ .stream()
+ .filter((rt) -> rt.isRemind())
+ .collect(Collectors.toList());
+
LOGGER.info(String
.format("%s recurring transaction are due today and are about to be included in the reminder email",
IterableUtils.size(recurringTransactions)));
diff --git a/src/main/resources/database/hsqldb/V6_0_0__remindFlagRecurringTransaction.sql b/src/main/resources/database/hsqldb/V6_0_0__remindFlagRecurringTransaction.sql
new file mode 100644
index 0000000..a9410ad
--- /dev/null
+++ b/src/main/resources/database/hsqldb/V6_0_0__remindFlagRecurringTransaction.sql
@@ -0,0 +1,4 @@
+-- Add a new column to the recurring transaction table that controls whether
+-- a reminder about the maturity should be send
+ALTER TABLE recurring_transaction
+ ADD COLUMN remind BOOLEAN DEFAULT TRUE NOT NULL;
\ No newline at end of file
diff --git a/src/main/resources/database/postgres/V6_0_0__remindFlagRecurringTransaction.sql b/src/main/resources/database/postgres/V6_0_0__remindFlagRecurringTransaction.sql
new file mode 100644
index 0000000..aa29189
--- /dev/null
+++ b/src/main/resources/database/postgres/V6_0_0__remindFlagRecurringTransaction.sql
@@ -0,0 +1,4 @@
+-- Add a new column to the recurring transaction table that controls whether
+-- a reminder about the maturity should be send
+ALTER TABLE recurring_transaction
+ ADD COLUMN remind BOOLEAN DEFAULT 'TRUE' NOT NULL
\ No newline at end of file
diff --git a/src/main/resources/database/postgres/readme_V1_0_0__init.txt b/src/main/resources/database/postgres/readme_V1_0_0__init.txt
new file mode 100644
index 0000000..8d853b0
--- /dev/null
+++ b/src/main/resources/database/postgres/readme_V1_0_0__init.txt
@@ -0,0 +1,25 @@
+The recurring transaction table is defined like this (at least for postgres):
+ -- Recurring transaction table
+ CREATE TABLE recurring_transaction (
+ id BIGINT NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
+ from_account_id BIGINT NOT NULL,
+ to_account_id BIGINT NOT NULL,
+ description VARCHAR(1000),
+ amount BIGINT NOT NULL,
+ interval_type VARCHAR(255) NOT NULL,
+ first_occurrence DATE NOT NULL,
+ last_occurrence DATE,
+ holiday_weekend_type VARCHAR(255) NOT NULL,
+ deleted BOOLEAN DEFAULT 'TRUE' NOT NULL,
+
+ CONSTRAINT fk_recurring_transaction_from_account FOREIGN KEY (from_account_id) REFERENCES account (id),
+ CONSTRAINT fk_recurring_transaction_to_account FOREIGN KEY (to_account_id) REFERENCES account (id)
+ );
+
+Note the
+ deleted BOOLEAN DEFAULT 'TRUE' NOT NULL,
+column definition. Not sure why the default is TRUE here is it doesn't make sense.
+It was probably a mistake, however fixing it here _WILL_ break existing installations
+as Flyway uses a checksum for scripts. So there is no easy fix, except for effectively
+overwriting this default in Java code when creating a new recurring transaction.
+See RecurringTransactionService.createRecurringTransaction()
\ No newline at end of file
diff --git a/src/test/java/de/financer/controller/integration/RecurringTransactionService_createRecurringTransactionIntegrationTest.java b/src/test/java/de/financer/controller/integration/RecurringTransactionService_createRecurringTransactionIntegrationTest.java
index c64e6ae..1b7cf20 100644
--- a/src/test/java/de/financer/controller/integration/RecurringTransactionService_createRecurringTransactionIntegrationTest.java
+++ b/src/test/java/de/financer/controller/integration/RecurringTransactionService_createRecurringTransactionIntegrationTest.java
@@ -42,7 +42,8 @@ public class RecurringTransactionService_createRecurringTransactionIntegrationTe
.param("description", "Monthly rent")
.param("holidayWeekendType", "SAME_DAY")
.param("intervalType", "MONTHLY")
- .param("firstOccurrence", "07.03.2019"))
+ .param("firstOccurrence", "07.03.2019")
+ .param("remind", "true"))
.andExpect(status().isOk())
.andReturn();
diff --git a/src/test/java/de/financer/service/RecurringTransactionService_createRecurringTransactionTest.java b/src/test/java/de/financer/service/RecurringTransactionService_createRecurringTransactionTest.java
index c551797..c34cf0d 100644
--- a/src/test/java/de/financer/service/RecurringTransactionService_createRecurringTransactionTest.java
+++ b/src/test/java/de/financer/service/RecurringTransactionService_createRecurringTransactionTest.java
@@ -51,7 +51,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.FROM_AND_TO_ACCOUNT_NOT_FOUND, response);
@@ -70,7 +71,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.TO_ACCOUNT_NOT_FOUND, response);
@@ -89,7 +91,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.FROM_ACCOUNT_NOT_FOUND, response);
@@ -109,7 +112,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.INVALID_BOOKING_ACCOUNTS, response);
@@ -129,7 +133,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.MISSING_AMOUNT, response);
@@ -149,7 +154,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.AMOUNT_ZERO, response);
@@ -169,7 +175,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
null,
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.MISSING_HOLIDAY_WEEKEND_TYPE, response);
@@ -189,7 +196,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
"HOLIDAY_WEEKEND_TYPE",
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.INVALID_HOLIDAY_WEEKEND_TYPE, response);
@@ -209,7 +217,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
null,
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.MISSING_INTERVAL_TYPE, response);
@@ -229,7 +238,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
"INTERVAL_TYPE",
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.INVALID_INTERVAL_TYPE, response);
@@ -249,7 +259,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
IntervalType.DAILY.name(),
null,
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.MISSING_FIRST_OCCURRENCE, response);
@@ -269,7 +280,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
IntervalType.DAILY.name(),
"FIRST_OCCURRENCE",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.INVALID_FIRST_OCCURRENCE_FORMAT, response);
@@ -289,7 +301,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
IntervalType.DAILY.name(),
"07.03.2019",
- "LAST_OCCURRENCE");
+ "LAST_OCCURRENCE",
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.INVALID_LAST_OCCURRENCE_FORMAT, response);
@@ -310,7 +323,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
IntervalType.DAILY.name(),
"07.03.2019",
- null);
+ null,
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.UNKNOWN_ERROR, response);
@@ -330,7 +344,8 @@ public class RecurringTransactionService_createRecurringTransactionTest {
HolidayWeekendType.SAME_DAY.name(),
IntervalType.DAILY.name(),
"07.03.2019",
- null);
+ null,
+ Boolean.TRUE);
// Assert
Assert.assertEquals(ResponseReason.OK, response);
diff --git a/src/test/java/de/financer/task/SendRecurringTransactionReminderTaskTest.java b/src/test/java/de/financer/task/SendRecurringTransactionReminderTaskTest.java
index 844178d..215f2fc 100644
--- a/src/test/java/de/financer/task/SendRecurringTransactionReminderTaskTest.java
+++ b/src/test/java/de/financer/task/SendRecurringTransactionReminderTaskTest.java
@@ -36,9 +36,10 @@ public class SendRecurringTransactionReminderTaskTest {
public void test_sendReminder() {
// Arrange
final Collection recurringTransactions = Arrays.asList(
- createRecurringTransaction("Test booking 1", "Income", "accounts.bank", Long.valueOf(250000)),
- createRecurringTransaction("Test booking 2", "Bank", "accounts.rent", Long.valueOf(41500)),
- createRecurringTransaction("Test booking 3", "Bank", "accounts.cash", Long.valueOf(5000))
+ createRecurringTransaction("Test booking 1", "Income", "accounts.bank", Long.valueOf(250000), true),
+ createRecurringTransaction("Test booking 2", "Bank", "accounts.rent", Long.valueOf(41500), true),
+ createRecurringTransaction("Test booking 3", "Bank", "accounts.cash", Long.valueOf(5000), true),
+ createRecurringTransaction("Test booking 4", "Car", "accounts.car", Long.valueOf(1234), false)
);
Mockito.when(this.recurringTransactionService.getAllDueToday()).thenReturn(recurringTransactions);
@@ -51,13 +52,14 @@ public class SendRecurringTransactionReminderTaskTest {
Mockito.verify(this.mailSender, Mockito.times(1)).send(Mockito.any(SimpleMailMessage.class));
}
- private RecurringTransaction createRecurringTransaction(String description, String fromAccountKey, String toAccountKey, Long amount) {
+ private RecurringTransaction createRecurringTransaction(String description, String fromAccountKey, String toAccountKey, Long amount, boolean remind) {
final RecurringTransaction recurringTransaction = new RecurringTransaction();
recurringTransaction.setDescription(description);
recurringTransaction.setFromAccount(createAccount(fromAccountKey));
recurringTransaction.setToAccount(createAccount(toAccountKey));
recurringTransaction.setAmount(amount);
+ recurringTransaction.setRemind(remind);
return recurringTransaction;
}