diff --git a/src/main/java/com/faf223/expensetrackerfaf/config/ApplicationConfig.java b/src/main/java/com/faf223/expensetrackerfaf/config/ApplicationConfig.java index 57d1823..fd41b70 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/config/ApplicationConfig.java +++ b/src/main/java/com/faf223/expensetrackerfaf/config/ApplicationConfig.java @@ -1,8 +1,7 @@ package com.faf223.expensetrackerfaf.config; -import com.faf223.expensetrackerfaf.repository.CredentialRepository; -import com.faf223.expensetrackerfaf.repository.UserRepository; import com.faf223.expensetrackerfaf.security.PersonDetails; +import com.faf223.expensetrackerfaf.service.CredentialService; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -19,12 +18,11 @@ import org.springframework.security.crypto.password.PasswordEncoder; @RequiredArgsConstructor public class ApplicationConfig { - private final UserRepository userRepository; - private final CredentialRepository credentialRepository; + private final CredentialService credentialService; @Bean public UserDetailsService userDetailsService() { - return username -> new PersonDetails(credentialRepository.findByEmail(username).orElseThrow((() -> new UsernameNotFoundException("User not found")))); + return username -> new PersonDetails(credentialService.findByEmail(username).orElseThrow((() -> new UsernameNotFoundException("User not found")))); } @Bean diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java b/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java index 079cd0f..84da712 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/ExpenseController.java @@ -22,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; @@ -96,25 +97,36 @@ public class ExpenseController { } @GetMapping("/personal-expenses") + @Transactional(readOnly = true) public ResponseEntity> getExpensesByUser(@RequestParam Optional date, - @RequestParam Optional month) { + @RequestParam Optional month, + @RequestParam Optional startYear, + @RequestParam Optional endYear, + @RequestParam Optional lastUnit) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { String email = userDetails.getUsername(); - List expenses; + List expenses = Collections.emptyList(); - expenses = date.map(localDate -> expenseService.getTransactionsByDate(localDate, email).stream().map(expenseMapper::toDto).toList()) - .orElseGet(() -> month.map(value -> expenseService.getTransactionsByMonth(value, email).stream().map(expenseMapper::toDto).toList()) - .orElseGet(() -> expenseService.getTransactionsByEmail(email).stream().map(expenseMapper::toDto).toList())); + if(date.isPresent()) + expenses = expenseService.getTransactionsByDate(date.get(), email).stream().map(expenseMapper::toDto).toList(); + else if(month.isPresent()) + expenses = expenseService.getTransactionsByMonth(Month.of(month.get()), email).stream().map(expenseMapper::toDto).toList(); + else if(startYear.isPresent() && endYear.isPresent()) + expenses = expenseService.getYearIntervalTransactions(email, startYear.get(), endYear.get()).stream().map(expenseMapper::toDto).toList(); + else if(lastUnit.isPresent()) { + + if(lastUnit.get().equalsIgnoreCase("week")) + expenses = expenseService.getLastWeekTransactions(email).stream().map(expenseMapper::toDto).toList(); + else if(lastUnit.get().equalsIgnoreCase("month")) + expenses = expenseService.getLastMonthTransactions(email).stream().map(expenseMapper::toDto).toList(); - if (!expenses.isEmpty()) { - return ResponseEntity.ok(expenses); - } else { - return ResponseEntity.ok(Collections.emptyList()); } + + return ResponseEntity.ok(expenses); } throw new TransactionsNotFoundException("The expenses have not been found"); diff --git a/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java b/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java index 24208f3..c66a544 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java +++ b/src/main/java/com/faf223/expensetrackerfaf/controller/IncomeController.java @@ -22,6 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; @@ -96,25 +97,36 @@ public class IncomeController { } @GetMapping("/personal-incomes") - public ResponseEntity> getExpensesByUser(@RequestParam Optional date, - @RequestParam Optional month) { + @Transactional(readOnly = true) + public ResponseEntity> getIncomesByUser(@RequestParam Optional date, + @RequestParam Optional month, + @RequestParam Optional startYear, + @RequestParam Optional endYear, + @RequestParam Optional lastUnit) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) { String email = userDetails.getUsername(); - List incomes; + List incomes = Collections.emptyList(); - incomes = date.map(localDate -> incomeService.getTransactionsByDate(localDate, email).stream().map(incomeMapper::toDto).toList()) - .orElseGet(() -> month.map(value -> incomeService.getTransactionsByMonth(value, email).stream().map(incomeMapper::toDto).toList()) - .orElseGet(() -> incomeService.getTransactionsByEmail(email).stream().map(incomeMapper::toDto).toList())); + if(date.isPresent()) + incomes = incomeService.getTransactionsByDate(date.get(), email).stream().map(incomeMapper::toDto).toList(); + else if(month.isPresent()) + incomes = incomeService.getTransactionsByMonth(Month.of(month.get()), email).stream().map(incomeMapper::toDto).toList(); + else if(startYear.isPresent() && endYear.isPresent()) + incomes = incomeService.getYearIntervalTransactions(email, startYear.get(), endYear.get()).stream().map(incomeMapper::toDto).toList(); + else if(lastUnit.isPresent()) { + + if(lastUnit.get().equalsIgnoreCase("week")) + incomes = incomeService.getLastWeekTransactions(email).stream().map(incomeMapper::toDto).toList(); + else if(lastUnit.get().equalsIgnoreCase("month")) + incomes = incomeService.getLastMonthTransactions(email).stream().map(incomeMapper::toDto).toList(); - if (!incomes.isEmpty()) { - return ResponseEntity.ok(incomes); - } else { - return ResponseEntity.ok(Collections.emptyList()); } + + return ResponseEntity.ok(incomes); } throw new TransactionsNotFoundException("The expenses have not been found"); diff --git a/src/main/java/com/faf223/expensetrackerfaf/repository/ExpenseRepository.java b/src/main/java/com/faf223/expensetrackerfaf/repository/ExpenseRepository.java index e9bbaa7..90a6ac5 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/repository/ExpenseRepository.java +++ b/src/main/java/com/faf223/expensetrackerfaf/repository/ExpenseRepository.java @@ -3,6 +3,7 @@ package com.faf223.expensetrackerfaf.repository; import com.faf223.expensetrackerfaf.model.Expense; import com.faf223.expensetrackerfaf.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.query.Procedure; import org.springframework.stereotype.Repository; import java.time.LocalDate; @@ -14,4 +15,16 @@ public interface ExpenseRepository extends JpaRepository { List findByDate(LocalDate date); List findByDateBetween(LocalDate start, LocalDate end); + + @Procedure(procedureName = "get_expenses_by_month") + List filterByMonth(int month); + + @Procedure(procedureName = "get_last_week_expenses") + List findLastWeek(); + + @Procedure(procedureName = "get_last_month_expenses") + List findLastMonth(); + + @Procedure(procedureName = "get_expenses_by_year_interval") + List filterByYearInterval(int start, int end); } diff --git a/src/main/java/com/faf223/expensetrackerfaf/repository/IncomeRepository.java b/src/main/java/com/faf223/expensetrackerfaf/repository/IncomeRepository.java index 48b038d..d1cd7a2 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/repository/IncomeRepository.java +++ b/src/main/java/com/faf223/expensetrackerfaf/repository/IncomeRepository.java @@ -3,7 +3,9 @@ package com.faf223.expensetrackerfaf.repository; import com.faf223.expensetrackerfaf.model.Income; import com.faf223.expensetrackerfaf.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.query.Procedure; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.List; @@ -14,4 +16,20 @@ public interface IncomeRepository extends JpaRepository { List findByDate(LocalDate date); List findByDateBetween(LocalDate start, LocalDate end); + + @Transactional(readOnly = true) + @Procedure(procedureName = "get_incomes_by_month") + List filterByMonth(int month); + + @Transactional(readOnly = true) + @Procedure(procedureName = "get_last_week_incomes") + List findLastWeek(); + + @Transactional(readOnly = true) + @Procedure(procedureName = "get_last_month_incomes") + List findLastMonth(); + + @Transactional(readOnly = true) + @Procedure(procedureName = "get_incomes_by_year_interval") + List filterByYearInterval(int start, int end); } diff --git a/src/main/java/com/faf223/expensetrackerfaf/service/CredentialService.java b/src/main/java/com/faf223/expensetrackerfaf/service/CredentialService.java new file mode 100644 index 0000000..180f029 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/service/CredentialService.java @@ -0,0 +1,20 @@ +package com.faf223.expensetrackerfaf.service; + +import com.faf223.expensetrackerfaf.model.Credential; +import com.faf223.expensetrackerfaf.repository.CredentialRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class CredentialService { + + private final CredentialRepository credentialRepository; + + public Optional findByEmail(String email) { + return credentialRepository.findByEmail(email); + } + +} diff --git a/src/main/java/com/faf223/expensetrackerfaf/service/ExpenseService.java b/src/main/java/com/faf223/expensetrackerfaf/service/ExpenseService.java index 119fd7a..1ded82e 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/service/ExpenseService.java +++ b/src/main/java/com/faf223/expensetrackerfaf/service/ExpenseService.java @@ -4,6 +4,7 @@ import com.faf223.expensetrackerfaf.model.*; import com.faf223.expensetrackerfaf.repository.CredentialRepository; import com.faf223.expensetrackerfaf.repository.ExpenseRepository; import com.faf223.expensetrackerfaf.repository.UserRepository; +import com.faf223.expensetrackerfaf.util.TransactionFilter; import com.faf223.expensetrackerfaf.util.exceptions.UserNotAuthenticatedException; import com.faf223.expensetrackerfaf.util.exceptions.UserNotFoundException; import lombok.RequiredArgsConstructor; @@ -11,6 +12,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.time.Month; @@ -25,6 +27,7 @@ public class ExpenseService implements ITransactionService { private final ExpenseRepository expenseRepository; private final CredentialRepository credentialRepository; private final UserRepository userRepository; + private final TransactionFilter transactionFilter; public void createOrUpdate(IMoneyTransaction expense) { expenseRepository.save((Expense) expense); @@ -47,37 +50,47 @@ public class ExpenseService implements ITransactionService { @Override public List getTransactionsByDate(LocalDate date, String email) { - return getTransactionsByDate(date) - .stream() - .filter(transaction -> { - Optional credential = credentialRepository.findByEmail(email); - if(credential.isEmpty()) - throw new UserNotFoundException("The user has not been found"); - return credential.get().getUser().equals(transaction.getUser()); - }) - .toList(); + return (List) transactionFilter.filterByEmail(getTransactionsByDate(date), email); } - // TODO: store transaction month in a separate field in the DB and change this logic @Override public List getTransactionsByMonth(Month month) { - LocalDate startOfMonth = LocalDate.of(LocalDate.now().getYear(), month, 1); - LocalDate endOfMonth = startOfMonth.plusMonths(1).minusDays(1); - - return expenseRepository.findByDateBetween(startOfMonth, endOfMonth); + return expenseRepository.filterByMonth(month.getValue()); } @Override public List getTransactionsByMonth(Month month, String email) { - return getTransactionsByMonth(month) - .stream() - .filter(transaction -> { - Optional credential = credentialRepository.findByEmail(email); - if(credential.isEmpty()) - throw new UserNotFoundException("The user has not been found"); - return credential.get().getUser().equals(transaction.getUser()); - }) - .toList(); + return (List) transactionFilter.filterByEmail(getTransactionsByMonth(month), email); + } + + @Override + public List getLastWeekTransactions() { + return expenseRepository.findLastWeek(); + } + + @Override + public List getLastWeekTransactions(String email) { + return (List) transactionFilter.filterByEmail(getLastWeekTransactions(), email); + } + + @Override + public List getLastMonthTransactions() { + return expenseRepository.findLastMonth(); + } + + @Override + public List getLastMonthTransactions(String email) { + return (List) transactionFilter.filterByEmail(getLastMonthTransactions(), email); + } + + @Override + public List getYearIntervalTransactions(int start, int end) { + return expenseRepository.filterByYearInterval(start, end); + } + + @Override + public List getYearIntervalTransactions(String email, int start, int end) { + return (List) transactionFilter.filterByEmail(getYearIntervalTransactions(start, end), email); } public List getTransactions() { @@ -113,4 +126,5 @@ public class ExpenseService implements ITransactionService { throw new UserNotAuthenticatedException("You are not authenticated"); } + } \ No newline at end of file diff --git a/src/main/java/com/faf223/expensetrackerfaf/service/ITransactionService.java b/src/main/java/com/faf223/expensetrackerfaf/service/ITransactionService.java index 051c3fe..54097c2 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/service/ITransactionService.java +++ b/src/main/java/com/faf223/expensetrackerfaf/service/ITransactionService.java @@ -17,6 +17,12 @@ public interface ITransactionService { List getTransactionsByDate(LocalDate date, String email); List getTransactionsByMonth(Month month); List getTransactionsByMonth(Month month, String email); + List getLastWeekTransactions(); + List getLastWeekTransactions(String email); + List getLastMonthTransactions(); + List getLastMonthTransactions(String email); + List getYearIntervalTransactions(int start, int end); + List getYearIntervalTransactions(String email, int start, int end); IMoneyTransaction getTransactionById(long id); void deleteTransactionById(long it); boolean belongsToUser(IMoneyTransaction transaction); diff --git a/src/main/java/com/faf223/expensetrackerfaf/service/IncomeService.java b/src/main/java/com/faf223/expensetrackerfaf/service/IncomeService.java index 97fe35e..5b56d4b 100644 --- a/src/main/java/com/faf223/expensetrackerfaf/service/IncomeService.java +++ b/src/main/java/com/faf223/expensetrackerfaf/service/IncomeService.java @@ -7,6 +7,7 @@ import com.faf223.expensetrackerfaf.model.User; import com.faf223.expensetrackerfaf.repository.CredentialRepository; import com.faf223.expensetrackerfaf.repository.IncomeRepository; import com.faf223.expensetrackerfaf.repository.UserRepository; +import com.faf223.expensetrackerfaf.util.TransactionFilter; import com.faf223.expensetrackerfaf.util.exceptions.UserNotAuthenticatedException; import com.faf223.expensetrackerfaf.util.exceptions.UserNotFoundException; import lombok.RequiredArgsConstructor; @@ -28,6 +29,7 @@ public class IncomeService implements ITransactionService { private final IncomeRepository incomeRepository; private final CredentialRepository credentialRepository; private final UserRepository userRepository; + private final TransactionFilter transactionFilter; public void createOrUpdate(IMoneyTransaction income) { incomeRepository.save((Income) income); @@ -54,37 +56,47 @@ public class IncomeService implements ITransactionService { @Override public List getTransactionsByDate(LocalDate date, String email) { - return getTransactionsByDate(date) - .stream() - .filter(transaction -> { - Optional credential = credentialRepository.findByEmail(email); - if(credential.isEmpty()) - throw new UserNotFoundException("The user has not been found"); - return credential.get().getUser().equals(transaction.getUser()); - }) - .toList(); + return (List) transactionFilter.filterByEmail(getTransactionsByDate(date), email); } - // TODO: store transaction month in a separate field in the DB and change this logic @Override public List getTransactionsByMonth(Month month) { - LocalDate startOfMonth = LocalDate.of(LocalDate.now().getYear(), month, 1); - LocalDate endOfMonth = startOfMonth.plusMonths(1).minusDays(1); - - return incomeRepository.findByDateBetween(startOfMonth, endOfMonth); + return incomeRepository.filterByMonth(month.getValue()); } @Override public List getTransactionsByMonth(Month month, String email) { - return getTransactionsByMonth(month) - .stream() - .filter(transaction -> { - Optional credential = credentialRepository.findByEmail(email); - if(credential.isEmpty()) - throw new UserNotFoundException("The user has not been found"); - return credential.get().getUser().equals(transaction.getUser()); - }) - .toList(); + return (List) transactionFilter.filterByEmail(getTransactionsByMonth(month), email); + } + + @Override + public List getLastWeekTransactions() { + return incomeRepository.findLastWeek(); + } + + @Override + public List getLastWeekTransactions(String email) { + return (List) transactionFilter.filterByEmail(getLastWeekTransactions(), email); + } + + @Override + public List getLastMonthTransactions() { + return incomeRepository.findLastMonth(); + } + + @Override + public List getLastMonthTransactions(String email) { + return (List) transactionFilter.filterByEmail(getLastMonthTransactions(), email); + } + + @Override + public List getYearIntervalTransactions(int start, int end) { + return incomeRepository.filterByYearInterval(start, end); + } + + @Override + public List getYearIntervalTransactions(String email, int start, int end) { + return (List) transactionFilter.filterByEmail(getYearIntervalTransactions(start, end), email); } public Income getTransactionById(long id) { diff --git a/src/main/java/com/faf223/expensetrackerfaf/util/TransactionFilter.java b/src/main/java/com/faf223/expensetrackerfaf/util/TransactionFilter.java new file mode 100644 index 0000000..f6380b5 --- /dev/null +++ b/src/main/java/com/faf223/expensetrackerfaf/util/TransactionFilter.java @@ -0,0 +1,31 @@ +package com.faf223.expensetrackerfaf.util; + +import com.faf223.expensetrackerfaf.model.Credential; +import com.faf223.expensetrackerfaf.model.IMoneyTransaction; +import com.faf223.expensetrackerfaf.service.CredentialService; +import com.faf223.expensetrackerfaf.util.exceptions.UserNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class TransactionFilter { + + private final CredentialService credentialService; + + public List filterByEmail(List transactions, String email) { + return transactions + .stream() + .filter(transaction -> { + Optional credential = credentialService.findByEmail(email); + if(credential.isEmpty()) + throw new UserNotFoundException("The user has not been found"); + return credential.get().getUser().equals(transaction.getUser()); + }) + .toList(); + } + +}