TransactionServiceImpl.java

package no.ntnu.idi.stud.savingsapp.bank.service.impl;

import java.math.BigDecimal;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import no.ntnu.idi.stud.savingsapp.bank.dto.TransactionDTO;
import no.ntnu.idi.stud.savingsapp.bank.model.Account;
import no.ntnu.idi.stud.savingsapp.bank.model.Transaction;
import no.ntnu.idi.stud.savingsapp.bank.repository.AccountRepository;
import no.ntnu.idi.stud.savingsapp.bank.repository.TransactionRepository;
import no.ntnu.idi.stud.savingsapp.bank.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

/**
 * Implementation of the {@link TransactionService} interface for transaction related
 * operations.
 */
@Service
@Slf4j
public class TransactionServiceImpl implements TransactionService {

	@Autowired
	private TransactionRepository transactionRepository;

	@Autowired
	private AccountRepository accountRepository;

	/**
	 * Performs and saves a transaction between two accounts.
	 * @param transactionRequest The transaction to be performed, containing the bban of
	 * the creditor and debitor accounts in addition to the amount that is being
	 * transferred.
	 */
	@Override
	public void saveTransaction(TransactionDTO transactionRequest) {

		boolean negativeTransactionAmount = (transactionRequest.getAmount().signum() == -1);

		if (negativeTransactionAmount) {
			log.error("[TransactionService:saveTransaction] Negative transfer amount: {}",
					transactionRequest.getAmount());
			throw new ResponseStatusException(HttpStatusCode.valueOf(400), "Negative transfer amount");
		}

		Optional<Account> debtorAccount = accountRepository.findAccountByBban(transactionRequest.getDebtorBBAN());
		if (debtorAccount.isEmpty()) {
			log.error("[TransactionService:saveTransaction] Debtor account not found: {}",
					transactionRequest.getDebtorBBAN());
			throw new ResponseStatusException(HttpStatusCode.valueOf(404), "Debtor account not found");
		}
		Optional<Account> creditorAccount = accountRepository.findAccountByBban(transactionRequest.getCreditorBBAN());
		if (creditorAccount.isEmpty()) {
			log.error("[TransactionService:saveTransaction] Creditor account not found: {}",
					transactionRequest.getDebtorBBAN());
			throw new ResponseStatusException(HttpStatusCode.valueOf(404), "Creditor account not found");
		}

		boolean negativeFunds = ((debtorAccount.get().getBalance().subtract(transactionRequest.getAmount()))
			.signum() == -1);
		if (negativeFunds) {
			log.error("[TransactionService:saveTransaction] Insufficient funds: {}", transactionRequest.getAmount());
			throw new ResponseStatusException(HttpStatusCode.valueOf(402), "Insufficient funds");
		}

		try {
			Transaction savedTransaction = new Transaction();
			savedTransaction.setDebtorAccount(debtorAccount.get());
			savedTransaction.setCreditorAccount(creditorAccount.get());
			savedTransaction.setAmount(transactionRequest.getAmount());

			BigDecimal debtorBalance = (debtorAccount.get().getBalance()).subtract(transactionRequest.getAmount());

			BigDecimal creditorBalance = (creditorAccount.get().getBalance().add(transactionRequest.getAmount()));

			accountRepository.updateBalance(debtorBalance, debtorAccount.get().getBban());
			accountRepository.updateBalance(creditorBalance, creditorAccount.get().getBban());
			transactionRepository.save(savedTransaction);
			log.info("[TransactionService:saveTransaction] saved transaction with id: {}", savedTransaction.getId());
		}
		catch (Exception e) {
			log.error("[TransactionService:saveTransaction] Transaction failed");
			throw new ResponseStatusException(HttpStatusCode.valueOf(400), e.getMessage());
		}
	}

}