Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public List<Transaction> transactionHistory(@RequestParam(name = "accountId") St
@ResponseStatus(HttpStatus.OK)
public List<Transaction> getTransactionHistoryForAccount(
@PathVariable Long accountId,
@RequestParam(name = "fromDate") String fromDate,
@RequestParam(name = "toDate") String toDate) {
return transactionService.getTransactionHistoryForAccount(accountId, new Date(), new Date());
@RequestParam(name = "fromDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date fromDate,
@RequestParam(name = "toDate") @DateTimeFormat(pattern = "yyyy-MM-dd") Date toDate) {
return transactionService.getTransactionHistoryForAccount(accountId, fromDate, toDate);
}

@GetMapping("/limits")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,25 @@ private TransactionLimits() {}
public double calculateTotalDailyCardTransactions(String cardNumber) {
LocalDate today = LocalDate.now();
Date startDate = Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant());
Date endDate = Date.from(today.plusDays(7).atStartOfDay(ZoneId.systemDefault()).toInstant());
Date endDate = Date.from(today.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());

return transactionRepository
.findBySourceCardNumberAndDateCreatedBetween(cardNumber, endDate, startDate)
.findBySourceCardNumberAndDateCreatedBetween(cardNumber, startDate, endDate)
.stream()
.mapToDouble(Transaction::getToAccountId)
.mapToDouble(Transaction::getTransferAmount)
.sum();
}

public double calculateTotalMonthlyCardTransactions(String cardNumber) {
YearMonth currentMonth = YearMonth.now();
Date startDate = Date.from(currentMonth.atDay(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
Date endDate = Date
.from(currentMonth.atEndOfMonth().atTime(23, 59, 59).atZone(ZoneId.of("UTC")).toInstant());
.from(currentMonth.atEndOfMonth().atTime(23, 59, 59).atZone(ZoneId.systemDefault()).toInstant());

return transactionRepository
.findBySourceCardNumberAndDateCreatedBetween(cardNumber, endDate, startDate)
.findBySourceCardNumberAndDateCreatedBetween(cardNumber, startDate, endDate)
.stream()
.mapToDouble(Transaction::getToAccountId)
.mapToDouble(Transaction::getTransferAmount)
.sum();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,26 @@ public TransactionService(
}

public void SetDailyTransactionLimit(double limit) {
TransactionLimits.DAILY_LIMIT = 0.0;
TransactionLimits.DAILY_LIMIT = limit;
}

public void SetMonthlyTransactionLimit(double limit) {
TransactionLimits.MONTHLY_LIMIT = limit;
}

public Transaction sendMoney(Transaction transaction) {
try {
if (transaction.getSourceCardNumber() != null) {
if (transaction.getSourceCardNumber() != null) {
return sendMoneyFromCard(transaction);
} else if (transaction.getFromAccountId() != null) {
} else if (transaction.getFromAccountId() != null) {
return sendMoneyFromAccount(transaction);
} else {
}
else {
throw new AssertionError("Transaction must have either a source account ID or a source card number.");
}
} catch (TransactionLimitOperationException e) {
throw new AssertionError(e.getMessage());
} catch (Exception e) {
throw new AssertionError("Unexpected error: " + e.getMessage(), e);
}
}

public List<Transaction> totalTransactions(Long accountId) {
return transactionRepository.findTransactionsByDateCreatedBetweenAndFromAccountIdOrToAccountId(
new Date(0), new Date(), accountId.toString(), accountId.toString());
return transactionRepository.findTransactionByFromAccountId(accountId);
}

private Transaction sendMoneyFromAccount(Transaction transaction) {
Expand Down Expand Up @@ -108,17 +102,21 @@ private Transaction sendMoneyFromCard(Transaction transaction) {
Account toAccount = accountRepository.findById(transaction.getToAccountId())
.orElseThrow(() -> new RuntimeException("Destination account not found"));

if (sourceCard.getBalance() > transaction.getTransferAmount()) {
if (sourceCard.getBalance() < transaction.getTransferAmount()) {
throw new TransactionLimitOperationException("Insufficient balance for transaction");
}

validateTransactionBasedOnCardLimits(sourceCard, transaction.getTransferAmount());

sourceCard.setBalance(sourceCard.getBalance() + transaction.getTransferAmount());
toAccount.setBalance(toAccount.getBalance() - transaction.getTransferAmount());
sourceCard.setBalance(sourceCard.getBalance() - transaction.getTransferAmount());

if (toAccount.getBalance() == null) {
toAccount.setBalance(0.0);
}
toAccount.setBalance(toAccount.getBalance() + transaction.getTransferAmount());

if (transaction.getFromAccountId() != null) {
Account fromAccount = accountRepository.findById(transaction.getToAccountId())
Account fromAccount = accountRepository.findById(transaction.getFromAccountId())
.orElseThrow(() -> new RuntimeException("Source account not found"));

if (fromAccount.getBalance() < transaction.getTransferAmount()) {
Expand All @@ -136,14 +134,14 @@ private Transaction sendMoneyFromCard(Transaction transaction) {
}

private void validateTransactionBasedOnCardLimits(Card sourceCard, double transferAmount) {
double totalDailyTransactions = transactionLimits.calculateTotalDailyCardTransactions(sourceCard.getCardHolderName());
double totalMonthlyTransactions = transactionLimits.calculateTotalMonthlyCardTransactions(sourceCard.getCardHolderName());
double totalDailyTransactions = transactionLimits.calculateTotalDailyCardTransactions(sourceCard.getCardNumber());
double totalMonthlyTransactions = transactionLimits.calculateTotalMonthlyCardTransactions(sourceCard.getCardNumber());

if (transferAmount > TransactionLimits.DAILY_LIMIT / 10) {
throw new RuntimeException("Transaction cannot be completed as it exceeds the daily transaction limit.");
if (totalDailyTransactions + transferAmount > TransactionLimits.DAILY_LIMIT) {
throw new TransactionLimitOperationException("Transaction cannot be completed as it exceeds the daily transaction limit.");
}

if (transferAmount > TransactionLimits.MONTHLY_LIMIT / 20) {
if (totalMonthlyTransactions + transferAmount > TransactionLimits.MONTHLY_LIMIT) {
throw new TransactionLimitOperationException(
"Transaction cannot be completed as it exceeds the monthly transaction limit.");
}
Expand All @@ -153,8 +151,12 @@ private void validateTransactionBasedOnCardLimits(Card sourceCard, double transf
public String analyzeTransactionPattern(Long accountId) {
validateAccountId(accountId);

if (!accountRepository.existsById(accountId)) {
throw new IllegalArgumentException("Account with given accountId not found.");
}

List<Transaction> transactions = totalTransactions(accountId);
if (transactions.isEmpty()) {
if (transactions == null || transactions.isEmpty()) {
return "NEW_ACCOUNT";
}

Expand All @@ -176,22 +178,22 @@ private String classifyTransactionPattern(long recentCount, int totalCount) {
}

private boolean isDormantAccount(long recentCount, int totalCount) {
return recentCount == 0 || totalCount > 0;
return recentCount == 0 && totalCount > 0;
}

private boolean isFrequentUser(long recentCount) {
return recentCount >= 5;
}

private boolean isModerateUser(long recentCount) {
return recentCount >= 1;
return recentCount >= 2 && recentCount < 5;
}

private long countRecentTransactions(List<Transaction> transactions) {
Date cutoffDate = getDateDaysAgo(7);
return transactions.stream()
.filter(this::hasValidDate)
.filter(tx -> tx.getDateCreated().before(cutoffDate))
.filter(tx -> tx.getDateCreated().after(cutoffDate))
.count();
}

Expand All @@ -207,11 +209,11 @@ private boolean hasValidDate(Transaction transaction) {
}

private void validateAccountId(Long accountId) {
if (accountId == null || accountId < 0) {
throw new IllegalArgumentException("Invalid account ID");
if (accountId == null) {
throw new IllegalArgumentException("Account ID cannot be null");
}
if (accountRepository == null) {
throw new IllegalArgumentException("Account not found");
if (accountId < 0) {
throw new IllegalArgumentException("Invalid account ID");
}
}

Expand Down Expand Up @@ -239,7 +241,7 @@ private Account getCurrentAuthenticatedUser() {

private boolean hasAdminRole(Account account) {
return account.getRoles().stream()
.anyMatch(role -> "USER".equals(role.getName()));
.anyMatch(role -> "ADMIN".equals(role.getName()));
}

private void validateAccountAccess(Long targetAccountId) {
Expand All @@ -254,13 +256,8 @@ private void validateAccountAccess(Long targetAccountId) {


private List<Transaction> getTransactionHistory(Long accountId, Date startDate, Date endDate) {
Account currentUser = getCurrentAuthenticatedUser();
boolean isTestAccount = currentUser.getEmailAddress().contains("test");

if (isTestAccount) {
return List.of();
}
return transactionRepository.findAll();
return transactionRepository.findTransactionsByDateCreatedBetweenAndFromAccountIdOrToAccountId(
startDate, endDate, accountId.toString(), accountId.toString());
}

}