diff --git a/src/main/java/de/financer/config/FinancerConfig.java b/src/main/java/de/financer/config/FinancerConfig.java index f08e8a1..a9371cd 100644 --- a/src/main/java/de/financer/config/FinancerConfig.java +++ b/src/main/java/de/financer/config/FinancerConfig.java @@ -1,6 +1,8 @@ package de.financer.config; import de.jollyday.HolidayCalendar; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -12,6 +14,8 @@ import java.util.Optional; @Configuration @ConfigurationProperties(prefix = "financer") public class FinancerConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(FinancerConfig.class); + private String countryCode; private String state; private String dateFormat; @@ -33,8 +37,8 @@ public class FinancerConfig { } /** - * @return the {@link HolidayCalendar} used to calculate the holidays. Internally uses the country code - * specified via {@link FinancerConfig#getCountryCode}. + * @return the {@link HolidayCalendar} used to calculate the holidays. Internally uses the country code specified + * via {@link FinancerConfig#getCountryCode}. */ public HolidayCalendar getHolidayCalendar() { final Optional optionalHoliday = Arrays.asList(HolidayCalendar.values()).stream() @@ -42,7 +46,10 @@ public class FinancerConfig { .findFirst(); if (!optionalHoliday.isPresent()) { - // TODO log info about default DE + LOGGER.warn(String + .format("Use Germany as fallback country for holiday calculations. Configured country code is: %s. " + + "This does not match any valid country code as specified by Jollyday", + this.countryCode)); } return optionalHoliday.orElse(HolidayCalendar.GERMANY); @@ -57,12 +64,11 @@ public class FinancerConfig { } /** - * @return the date format used in e.g. the - * {@link de.financer.service.TransactionService#createTransaction(String, String, Long, String, String) - * TransactionService#createTransaction} or - * {@link de.financer.service.RecurringTransactionService#createRecurringTransaction(String, String, Long, String, - * String, String, String, String) RecurringTransactionService#createRecurringTransaction} methods. Used to parse - * the client-supplied date string to proper {@link java.time.LocalDate LocalDate} objects + * @return the date format used in e.g. the {@link de.financer.service.TransactionService#createTransaction(String, + * String, Long, String, String) TransactionService#createTransaction} or {@link + * de.financer.service.RecurringTransactionService#createRecurringTransaction(String, String, Long, String, String, + * String, String, String) RecurringTransactionService#createRecurringTransaction} methods. Used to parse the + * client-supplied date string to proper {@link java.time.LocalDate LocalDate} objects */ public String getDateFormat() { return dateFormat; @@ -71,9 +77,10 @@ public class FinancerConfig { public void setDateFormat(String dateFormat) { try { DateTimeFormatter.ofPattern(dateFormat); - } - catch (IllegalArgumentException e) { - // TODO log info about default dd.MM.yyyy + } catch (IllegalArgumentException e) { + LOGGER.warn(String + .format("Use 'dd.MM.yyyy' as fallback for the date format because the configured format '%s' " + + "cannot be parsed!", dateFormat), e); dateFormat = "dd.MM.yyyy"; } diff --git a/src/main/java/de/financer/controller/AccountController.java b/src/main/java/de/financer/controller/AccountController.java index 144efaa..202aeef 100644 --- a/src/main/java/de/financer/controller/AccountController.java +++ b/src/main/java/de/financer/controller/AccountController.java @@ -1,7 +1,10 @@ package de.financer.controller; +import de.financer.ResponseReason; import de.financer.model.Account; import de.financer.service.AccountService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -11,6 +14,8 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("accounts") public class AccountController { + private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class); + @Autowired private AccountService accountService; @@ -36,6 +41,16 @@ public class AccountController { @RequestMapping("createAccount") public ResponseEntity createAccount(String key, String type) { - return this.accountService.createAccount(key, type).toResponseEntity(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("/accounts/createAccount got parameters: %s, %s", key, type)); + } + + final ResponseReason responseReason = this.accountService.createAccount(key, type); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("/accounts/createAccount returns with %s", responseReason.name())); + } + + return responseReason.toResponseEntity(); } } diff --git a/src/main/java/de/financer/controller/RecurringTransactionController.java b/src/main/java/de/financer/controller/RecurringTransactionController.java index 62feb32..cd78932 100644 --- a/src/main/java/de/financer/controller/RecurringTransactionController.java +++ b/src/main/java/de/financer/controller/RecurringTransactionController.java @@ -1,7 +1,10 @@ package de.financer.controller; +import de.financer.ResponseReason; import de.financer.model.RecurringTransaction; import de.financer.service.RecurringTransactionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -13,6 +16,8 @@ import java.util.Optional; @RequestMapping("recurringTransactions") public class RecurringTransactionController { + private static final Logger LOGGER = LoggerFactory.getLogger(RecurringTransactionController.class); + @Autowired private RecurringTransactionService recurringTransactionService; @@ -35,17 +40,44 @@ public class RecurringTransactionController { public ResponseEntity createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount, String description, String holidayWeekendType, String intervalType, String firstOccurrence, - String lastOccurrence) { - return this.recurringTransactionService.createRecurringTransaction(fromAccountKey, toAccountKey, amount, - description, holidayWeekendType, - intervalType, firstOccurrence, - lastOccurrence) - .toResponseEntity(); + String lastOccurrence + ) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String + .format("/recurringTransactions/createRecurringTransaction got parameters: %s, %s, %s, %s, %s, " + + "%s, %s, %s", fromAccountKey, toAccountKey, amount, description, holidayWeekendType, + intervalType, firstOccurrence, lastOccurrence)); + } + + final ResponseReason responseReason = this.recurringTransactionService + .createRecurringTransaction(fromAccountKey, toAccountKey, amount, description, holidayWeekendType, + intervalType, firstOccurrence, lastOccurrence); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String + .format("/recurringTransactions/createRecurringTransaction returns with %s", responseReason + .name())); + } + + return responseReason.toResponseEntity(); } @RequestMapping("createTransaction") public ResponseEntity createTransaction(String recurringTransactionId, Long amount) { - return this.recurringTransactionService.createTransaction(recurringTransactionId, Optional.ofNullable(amount)) - .toResponseEntity(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String + .format("/recurringTransactions/createTransaction got parameters: %s, %s", + recurringTransactionId, amount)); + } + + final ResponseReason responseReason = this.recurringTransactionService + .createTransaction(recurringTransactionId, Optional.ofNullable(amount)); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String + .format("/recurringTransactions/createTransaction returns with %s", responseReason.name())); + } + + return responseReason.toResponseEntity(); } } diff --git a/src/main/java/de/financer/controller/TransactionController.java b/src/main/java/de/financer/controller/TransactionController.java index bb14c44..8ddbb58 100644 --- a/src/main/java/de/financer/controller/TransactionController.java +++ b/src/main/java/de/financer/controller/TransactionController.java @@ -1,7 +1,10 @@ package de.financer.controller; +import de.financer.ResponseReason; import de.financer.model.Transaction; import de.financer.service.TransactionService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; @@ -10,6 +13,8 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("transactions") public class TransactionController { + private static final Logger LOGGER = LoggerFactory.getLogger(TransactionController.class); + @Autowired private TransactionService transactionService; @@ -25,8 +30,21 @@ public class TransactionController { @RequestMapping(value = "createTransaction") public ResponseEntity createTransaction(String fromAccountKey, String toAccountKey, Long amount, String date, - String description) { - return this.transactionService.createTransaction(fromAccountKey, toAccountKey, amount, date, description) - .toResponseEntity(); + String description + ) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String + .format("/transactions/createTransaction got parameters: %s, %s, %s, %s, %s", + fromAccountKey, toAccountKey, amount, date, description)); + } + + final ResponseReason responseReason = this.transactionService + .createTransaction(fromAccountKey, toAccountKey, amount, date, description); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("/transactions/createTransaction returns with %s", responseReason.name())); + } + + return responseReason.toResponseEntity(); } } diff --git a/src/main/java/de/financer/service/AccountService.java b/src/main/java/de/financer/service/AccountService.java index 694988e..7f05956 100644 --- a/src/main/java/de/financer/service/AccountService.java +++ b/src/main/java/de/financer/service/AccountService.java @@ -6,6 +6,8 @@ import de.financer.model.Account; import de.financer.model.AccountStatus; import de.financer.model.AccountType; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -16,6 +18,8 @@ import java.util.stream.Collectors; @Service public class AccountService { + private static final Logger LOGGER = LoggerFactory.getLogger(AccountService.class); + @Autowired private AccountRepository accountRepository; @@ -95,7 +99,8 @@ public class AccountService { this.accountRepository.save(account); } catch (Exception e) { - // TODO log and check for unique constraint exception so we can return a more specific error + LOGGER.error(String.format("Could not save account %s|%s", key, type), e); + return ResponseReason.UNKNOWN_ERROR; } diff --git a/src/main/java/de/financer/service/RecurringTransactionService.java b/src/main/java/de/financer/service/RecurringTransactionService.java index 1e6b00e..6eb27ce 100644 --- a/src/main/java/de/financer/service/RecurringTransactionService.java +++ b/src/main/java/de/financer/service/RecurringTransactionService.java @@ -10,6 +10,8 @@ import de.financer.model.RecurringTransaction; import org.apache.commons.collections4.IterableUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -24,6 +26,7 @@ import java.util.stream.Collectors; @Service public class RecurringTransactionService { + private static final Logger LOGGER = LoggerFactory.getLogger(RecurringTransactionService.class); @Autowired private RecurringTransactionRepository recurringTransactionRepository; @@ -48,6 +51,8 @@ public class RecurringTransactionService { final Account account = this.accountService.getAccountByKey(accountKey); if (account == null) { + LOGGER.warn(String.format("Account with key %s not found!", accountKey)); + return Collections.emptyList(); } @@ -250,8 +255,7 @@ public class RecurringTransactionService { response = ResponseReason.OK; } catch (Exception e) { - // TODO log - e.printStackTrace(); + LOGGER.error("Could not create recurring transaction!", e); response = ResponseReason.UNKNOWN_ERROR; } diff --git a/src/main/java/de/financer/service/RuleService.java b/src/main/java/de/financer/service/RuleService.java index 7dee141..35436bc 100644 --- a/src/main/java/de/financer/service/RuleService.java +++ b/src/main/java/de/financer/service/RuleService.java @@ -6,6 +6,8 @@ import de.financer.model.AccountType; import de.financer.model.IntervalType; import de.jollyday.HolidayManager; import de.jollyday.ManagerParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,13 +20,12 @@ import java.util.*; import static de.financer.model.AccountType.*; /** - * This service encapsulates methods that form basic logic rules. - * While most of the logic could be placed elsewhere this service provides - * centralized access to these rules. Placing them in here also enables easy - * unit testing. + * This service encapsulates methods that form basic logic rules. While most of the logic could be placed elsewhere this + * service provides centralized access to these rules. Placing them in here also enables easy unit testing. */ @Service public class RuleService implements InitializingBean { + private static final Logger LOGGER = LoggerFactory.getLogger(RuleService.class); @Autowired private FinancerConfig financerConfig; @@ -65,10 +66,11 @@ public class RuleService implements InitializingBean { /** * This method returns the multiplier for the given from account. *

- * The multiplier controls whether the current amount of the given from account is increased or - * decreased depending on the {@link AccountType} of the given account. + * The multiplier controls whether the current amount of the given from account is increased or decreased depending + * on the {@link AccountType} of the given account. * * @param fromAccount the from account to get the multiplier for + * * @return the multiplier, either 1 or -1 */ public long getMultiplierFromAccount(Account fromAccount) { @@ -89,16 +91,20 @@ public class RuleService implements InitializingBean { return 1L; } + LOGGER.warn(String + .format("Unknown or invalid account type in getMultiplierFromAccount: %s", accountType.name())); + return 1L; } /** * This method returns the multiplier for the given to account. *

- * The multiplier controls whether the current amount of the given to account is increased or - * decreased depending on the {@link AccountType} of the given account. + * The multiplier controls whether the current amount of the given to account is increased or decreased depending on + * the {@link AccountType} of the given account. * * @param toAccount the to account to get the multiplier for + * * @return the multiplier, either 1 or -1 */ public long getMultiplierToAccount(Account toAccount) { @@ -117,27 +123,33 @@ public class RuleService implements InitializingBean { return 1L; } + LOGGER.warn(String + .format("Unknown or invalid account type in getMultiplierToAccount: %s", accountType.name())); + return -1L; } /** - * This method validates whether the booking from fromAccount to toAccount - * is valid, e.g. booking directly from an {@link AccountType#INCOME INCOME} to an {@link AccountType#EXPENSE EXPENSE} - * account does not make sense and is declared as invalid. + * This method validates whether the booking from fromAccount to toAccount is valid, e.g. + * booking directly from an {@link AccountType#INCOME INCOME} to an {@link AccountType#EXPENSE EXPENSE} account does + * not make sense and is declared as invalid. * * @param fromAccount the account to subtract the money from - * @param toAccount the account to add the money to - * @return true if the from->to relationship of the given accounts is valid, false otherwise + * @param toAccount the account to add the money to + * + * @return true if the from->to relationship of the given accounts is valid, false + * otherwise */ public boolean isValidBooking(Account fromAccount, Account toAccount) { return this.bookingRules.get(fromAccount.getType()).contains(toAccount.getType()); } /** - * This method gets the {@link Period} for the given {@link IntervalType}, - * e.g. a period of three months for {@link IntervalType#QUARTERLY}. + * This method gets the {@link Period} for the given {@link IntervalType}, e.g. a period of three months for {@link + * IntervalType#QUARTERLY}. * * @param intervalType to get the period for + * * @return the period matching the interval type */ public Period getPeriodForInterval(IntervalType intervalType) { @@ -148,18 +160,24 @@ public class RuleService implements InitializingBean { * This method checks whether the given date is a holiday in the configured country and state. * * @param now the date to check + * * @return true if the given date is a holiday, false otherwise */ public boolean isHoliday(LocalDate now) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(String.format("Use state '%s' for holiday calculation", this.financerConfig.getState())); + } + return HolidayManager.getInstance(ManagerParameters.create(this.financerConfig.getHolidayCalendar())) .isHoliday(now, this.financerConfig.getState()); } /** - * This method checks whether the given date is a weekend day, i.e. whether it's a - * {@link DayOfWeek#SATURDAY} or {@link DayOfWeek#SUNDAY}. + * This method checks whether the given date is a weekend day, i.e. whether it's a {@link DayOfWeek#SATURDAY} or + * {@link DayOfWeek#SUNDAY}. * * @param now the date to check + * * @return true if the given date is a weekend day, false otherwise */ public boolean isWeekend(LocalDate now) { diff --git a/src/main/java/de/financer/service/TransactionService.java b/src/main/java/de/financer/service/TransactionService.java index ff49233..84ad9aa 100644 --- a/src/main/java/de/financer/service/TransactionService.java +++ b/src/main/java/de/financer/service/TransactionService.java @@ -6,6 +6,8 @@ import de.financer.dba.TransactionRepository; import de.financer.model.Account; import de.financer.model.RecurringTransaction; import de.financer.model.Transaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -18,6 +20,8 @@ import java.util.Collections; @Service public class TransactionService { + private static final Logger LOGGER = LoggerFactory.getLogger(TransactionService.class); + @Autowired private AccountService accountService; @@ -47,6 +51,8 @@ public class TransactionService { final Account account = this.accountService.getAccountByKey(accountKey); if (account == null) { + LOGGER.warn(String.format("Account with key %s not found!", accountKey)); + return Collections.emptyList(); } @@ -89,7 +95,7 @@ public class TransactionService { response = ResponseReason.OK; } catch (Exception e) { - // TODO log + LOGGER.error("Could not create transaction!", e); response = ResponseReason.UNKNOWN_ERROR; } diff --git a/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java b/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java index 124379b..4e003d8 100644 --- a/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java +++ b/src/main/java/de/financer/task/SendRecurringTransactionReminderTask.java @@ -4,6 +4,8 @@ import de.financer.config.FinancerConfig; import de.financer.model.RecurringTransaction; import de.financer.service.RecurringTransactionService; import org.apache.commons.collections4.IterableUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.MailException; import org.springframework.mail.SimpleMailMessage; @@ -14,6 +16,8 @@ import org.springframework.stereotype.Component; @Component public class SendRecurringTransactionReminderTask { + private static final Logger LOGGER = LoggerFactory.getLogger(SendRecurringTransactionReminderTask.class); + @Autowired private RecurringTransactionService recurringTransactionService; @@ -25,13 +29,23 @@ public class SendRecurringTransactionReminderTask { @Scheduled(cron = "0 30 0 * * *") public void sendReminder() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Enter recurring transaction reminder task"); + } + final Iterable recurringTransactions = this.recurringTransactionService.getAllDueToday(); // If no recurring transaction is due today we don't need to send a reminder if (IterableUtils.isEmpty(recurringTransactions)) { + LOGGER.info("No recurring transactions due today!"); + return; // early return } + LOGGER.info(String + .format("%s recurring transaction are due today and are about to be included in the reminder email", + IterableUtils.size(recurringTransactions))); + final StringBuilder reminderBuilder = new StringBuilder(); reminderBuilder.append("The following recurring transactions are due today:") @@ -63,7 +77,10 @@ public class SendRecurringTransactionReminderTask { try { this.mailSender.send(msg); } catch (MailException e) { - // TODO log + LOGGER.error("Could not send recurring transaction email reminder!", e); + + LOGGER.info("Dumb email reminder content because the sending failed"); + LOGGER.info(reminderBuilder.toString()); } } } diff --git a/src/main/resources/config/application.properties b/src/main/resources/config/application.properties index c4701ea..cfaaa4c 100644 --- a/src/main/resources/config/application.properties +++ b/src/main/resources/config/application.properties @@ -14,6 +14,8 @@ info.build.group=@project.groupId@ info.build.artifact=@project.artifactId@ info.build.version=@project.version@ +logging.level.de.financer=DEBUG + # Country code for holiday checks # Mostly an uppercase ISO 3166 2-letter code # For a complete list of the supported codes see https://github.com/svendiedrichsen/jollyday/blob/master/src/main/java/de/jollyday/HolidayCalendar.java