Add basic mail reminder for recurring transactions
Also add some basic accounts to the DB init. Further add dependency to JAXB as it was missing. Add an optional parameter amount to the recurringTransactions/createTransaction method that can be used to overwrite the amount of the recurring transaction.
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -58,6 +58,11 @@
|
||||
<artifactId>jollyday</artifactId>
|
||||
<version>0.5.7</version>
|
||||
</dependency>
|
||||
<!-- TODO maybe set scope to provided when running in tomcat -->
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Runtime dependencies -->
|
||||
<dependency>
|
||||
|
||||
@@ -2,8 +2,10 @@ package de.financer;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class FinancerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FinancerApplication.class);
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@Configuration
|
||||
@@ -14,6 +15,8 @@ public class FinancerConfig {
|
||||
private String countryCode;
|
||||
private String state;
|
||||
private String dateFormat;
|
||||
private Collection<String> mailRecipients;
|
||||
private String fromAddress;
|
||||
|
||||
/**
|
||||
* @return the raw country code, mostly an uppercase ISO 3166 2-letter code
|
||||
@@ -77,4 +80,26 @@ public class FinancerConfig {
|
||||
|
||||
this.dateFormat = dateFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection of email addresses that should receive mails from financer
|
||||
*/
|
||||
public Collection<String> getMailRecipients() {
|
||||
return mailRecipients;
|
||||
}
|
||||
|
||||
public void setMailRecipients(Collection<String> mailRecipients) {
|
||||
this.mailRecipients = mailRecipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the from address used in emails send by financer
|
||||
*/
|
||||
public String getFromAddress() {
|
||||
return fromAddress;
|
||||
}
|
||||
|
||||
public void setFromAddress(String fromAddress) {
|
||||
this.fromAddress = fromAddress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("recurringTransactions")
|
||||
public class RecurringTransactionController {
|
||||
@@ -42,7 +44,7 @@ public class RecurringTransactionController {
|
||||
}
|
||||
|
||||
@RequestMapping("createTransaction")
|
||||
public ResponseEntity createTransaction(String recurringTransactionId) {
|
||||
return this.recurringTransactionService.createTransaction(recurringTransactionId).toResponseEntity();
|
||||
public ResponseEntity createTransaction(String recurringTransactionId, Long amount) {
|
||||
return this.recurringTransactionService.createTransaction(recurringTransactionId, Optional.of(amount)).toResponseEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ public class RecurringTransactionService {
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public ResponseReason createTransaction(String recurringTransactionId) {
|
||||
public ResponseReason createTransaction(String recurringTransactionId, Optional<Long> amount) {
|
||||
if (recurringTransactionId == null) {
|
||||
return ResponseReason.MISSING_RECURRING_TRANSACTION_ID;
|
||||
} else if (!NumberUtils.isCreatable(recurringTransactionId)) {
|
||||
@@ -378,7 +378,7 @@ public class RecurringTransactionService {
|
||||
|
||||
return this.transactionService.createTransaction(recurringTransaction.getFromAccount().getKey(),
|
||||
recurringTransaction.getToAccount().getKey(),
|
||||
recurringTransaction.getAmount(),
|
||||
amount.orElseGet(() -> recurringTransaction.getAmount()),
|
||||
LocalDate.now().format(DateTimeFormatter.ofPattern(this.financerConfig.getDateFormat())),
|
||||
recurringTransaction.getDescription(),
|
||||
recurringTransaction);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package de.financer.task;
|
||||
|
||||
import de.financer.config.FinancerConfig;
|
||||
import de.financer.model.RecurringTransaction;
|
||||
import de.financer.service.RecurringTransactionService;
|
||||
import org.apache.commons.collections4.IterableUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mail.MailException;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class SendRecurringTransactionReminderTask {
|
||||
|
||||
@Autowired
|
||||
private RecurringTransactionService recurringTransactionService;
|
||||
|
||||
@Autowired
|
||||
private FinancerConfig financerConfig;
|
||||
|
||||
@Autowired
|
||||
private JavaMailSender mailSender;
|
||||
|
||||
@Scheduled(cron = "0 30 0 * * *")
|
||||
public void sendReminder() {
|
||||
final Iterable<RecurringTransaction> recurringTransactions = this.recurringTransactionService.getAllDueToday();
|
||||
|
||||
// If no recurring transaction is due today we don't need to send a reminder
|
||||
if (IterableUtils.isEmpty(recurringTransactions)) {
|
||||
return; // early return
|
||||
}
|
||||
|
||||
final StringBuilder reminderBuilder = new StringBuilder();
|
||||
|
||||
reminderBuilder.append("The following recurring transactions are due today:")
|
||||
.append(System.lineSeparator())
|
||||
.append(System.lineSeparator());
|
||||
|
||||
IterableUtils.toList(recurringTransactions).stream().forEach((rt) -> {
|
||||
reminderBuilder.append(rt.getId())
|
||||
.append("|")
|
||||
.append(rt.getDescription())
|
||||
.append(System.lineSeparator())
|
||||
.append("From ")
|
||||
.append(rt.getFromAccount().getKey())
|
||||
.append(" to ")
|
||||
.append(rt.getToAccount().getKey())
|
||||
.append(": ")
|
||||
.append(rt.getAmount().toString())
|
||||
.append(System.lineSeparator())
|
||||
.append(System.lineSeparator());
|
||||
});
|
||||
|
||||
final SimpleMailMessage msg = new SimpleMailMessage();
|
||||
|
||||
msg.setTo(this.financerConfig.getMailRecipients().toArray(new String[]{}));
|
||||
msg.setFrom(this.financerConfig.getFromAddress());
|
||||
msg.setSubject("[Financer] Recurring transactions reminder");
|
||||
msg.setText(reminderBuilder.toString());
|
||||
|
||||
try {
|
||||
this.mailSender.send(msg);
|
||||
} catch (MailException e) {
|
||||
// TODO log
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,3 +25,14 @@ financer.state=sl
|
||||
|
||||
# The date format of the client-supplied date string, used to parse the string into a proper object
|
||||
financer.dateFormat=dd.MM.yyyy
|
||||
|
||||
# A collection of email addresses that should receive mails from financer
|
||||
financer.mailRecipients[0]=marius@kleberonline.de
|
||||
|
||||
# The from address used in emails send by financer
|
||||
financer.fromAddress=financer@77zzcx7.de
|
||||
|
||||
# Mail configuration
|
||||
spring.mail.host=localhost
|
||||
#spring.mail.username=
|
||||
#spring.mail.password=
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--
|
||||
-- This file contains the basic initialization of the financer schema
|
||||
-- This file contains the basic initialization of the financer schema and basic init data
|
||||
--
|
||||
|
||||
-- Account table
|
||||
@@ -43,3 +43,19 @@ CREATE TABLE "transaction" ( --escape keyword "transaction"
|
||||
CONSTRAINT fk_transaction_to_account FOREIGN KEY (to_account_id) REFERENCES account (id),
|
||||
CONSTRAINT fk_transaction_recurring_transaction FOREIGN KEY (recurring_transaction_id) REFERENCES recurring_transaction (id)
|
||||
);
|
||||
|
||||
-- Accounts
|
||||
INSERT INTO account (id, "key", type, status, current_balance)
|
||||
VALUES (1, 'accounts.checkaccount', 'BANK', 'OPEN', 0); -- insert first with ID 1 so we get predictable numbering
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.income', 'INCOME', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.cash', 'CASH', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.start', 'START', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.rent', 'EXPENSE', 'OPEN', 0);
|
||||
@@ -0,0 +1,72 @@
|
||||
package de.financer.task;
|
||||
|
||||
import de.financer.config.FinancerConfig;
|
||||
import de.financer.model.Account;
|
||||
import de.financer.model.RecurringTransaction;
|
||||
import de.financer.service.RecurringTransactionService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.mail.SimpleMailMessage;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class SendRecurringTransactionReminderTaskTest {
|
||||
@InjectMocks
|
||||
private SendRecurringTransactionReminderTask classUnderTest;
|
||||
|
||||
@Mock
|
||||
private RecurringTransactionService recurringTransactionService;
|
||||
|
||||
@Mock
|
||||
private JavaMailSender mailSender;
|
||||
|
||||
@Mock
|
||||
private FinancerConfig financerConfig;
|
||||
|
||||
@Test
|
||||
public void test_sendReminder() {
|
||||
// Arrange
|
||||
final Collection<RecurringTransaction> recurringTransactions = Arrays.asList(
|
||||
createRecurringTransaction("Test booking 1", "accounts.income", "accounts.bank", Long.valueOf(250000)),
|
||||
createRecurringTransaction("Test booking 2", "accounts.bank", "accounts.rent", Long.valueOf(41500)),
|
||||
createRecurringTransaction("Test booking 3", "accounts.bank", "accounts.cash", Long.valueOf(5000))
|
||||
);
|
||||
|
||||
Mockito.when(this.recurringTransactionService.getAllDueToday()).thenReturn(recurringTransactions);
|
||||
Mockito.when(this.financerConfig.getMailRecipients()).thenReturn(Collections.singletonList("test@test.com"));
|
||||
|
||||
// Act
|
||||
this.classUnderTest.sendReminder();
|
||||
|
||||
// Assert
|
||||
Mockito.verify(this.mailSender, Mockito.times(1)).send(Mockito.any(SimpleMailMessage.class));
|
||||
}
|
||||
|
||||
private RecurringTransaction createRecurringTransaction(String description, String fromAccountKey, String toAccountKey, Long amount) {
|
||||
final RecurringTransaction recurringTransaction = new RecurringTransaction();
|
||||
|
||||
recurringTransaction.setDescription(description);
|
||||
recurringTransaction.setFromAccount(createAccount(fromAccountKey));
|
||||
recurringTransaction.setToAccount(createAccount(toAccountKey));
|
||||
recurringTransaction.setAmount(amount);
|
||||
|
||||
return recurringTransaction;
|
||||
}
|
||||
|
||||
private Account createAccount(String key) {
|
||||
final Account account = new Account();
|
||||
|
||||
account.setKey(key);
|
||||
|
||||
return account;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,7 @@
|
||||
-- Accounts
|
||||
INSERT INTO account (id, "key", type, status, current_balance)
|
||||
VALUES (1, 'accounts.checkaccount', 'BANK', 'OPEN', 0); -- insert first with ID 1 so we get predictable numbering
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.income', 'INCOME', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.cash', 'CASH', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.start', 'START', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.convenience', 'EXPENSE', 'OPEN', 0);
|
||||
|
||||
INSERT INTO account ("key", type, status, current_balance)
|
||||
VALUES ('accounts.rent', 'EXPENSE', 'OPEN', 0);
|
||||
|
||||
--Recurring transactions
|
||||
INSERT INTO recurring_transaction (from_account_id, to_account_id, description, amount, interval_type, first_occurrence, holiday_weekend_type)
|
||||
VALUES (2, 1, 'Pay', 250000, 'MONTHLY', '2019-01-15', 'NEXT_WORKDAY');
|
||||
|
||||
Reference in New Issue
Block a user