Skip to content

feat: adds bank features. CurrencyConverter, Savings Account, Business Account - Xavier Cruz #539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.codedifferently.lesson17.bank;

public class AuditLog {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.codedifferently.lesson17.bank;

import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
import com.codedifferently.lesson17.bank.exceptions.UnsupportedCurrencyException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -44,10 +45,13 @@ public Set<CheckingAccount> findAccountsByCustomerId(UUID customerId) {
*
* @param accountNumber The account number.
* @param amount The amount to deposit.
* @throws UnsupportedCurrencyException
*/
public void depositFunds(String accountNumber, double amount) {
public void depositFunds(String accountNumber, double amount, String currencyType)
throws UnsupportedCurrencyException {
CheckingAccount account = getAccountOrThrow(accountNumber);
account.deposit(amount);
double convertedAmount = CurrencyConverter.convert(amount, currencyType, "USD");
account.deposit(convertedAmount);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.codedifferently.lesson17.bank;

import java.util.Set;

public class BusinessCheckingAccount extends CheckingAccount {
private boolean isActive;

/**
* Creates a new business checking account.
*
* @param accountNumber The account number.
* @param owners The owners of the account.
* @param initialBalance The initial balance of the account.
*/
public BusinessCheckingAccount(
String accountNumber, Set<Customer> owners, double initialBalance) {
super(accountNumber, owners, initialBalance);
isActive = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.codedifferently.lesson17.bank;

import com.codedifferently.lesson17.bank.exceptions.UnsupportedCurrencyException;
import java.util.Map;

public class CurrencyConverter {
private static final Map<String, Double> exchangeRates =
Map.of(
"USD", 1.0,
"EUR", 0.9,
"GBP", 0.8,
"JPY", 110.0);

public static double convert(double amount, String fromCurrency, String toCurrency)
throws UnsupportedCurrencyException {
if (!exchangeRates.containsKey(fromCurrency) || !exchangeRates.containsKey(toCurrency)) {
throw new UnsupportedCurrencyException("Unsupported currency type.");
}
double amountInUSD = amount / exchangeRates.get(fromCurrency);
return amountInUSD * exchangeRates.get(toCurrency);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ public class Customer {
private final UUID id;
private final String name;
private final Set<CheckingAccount> accounts = new HashSet<>();
private boolean isBusiness = false;

/**
* Creates a new customer.
*
* @param id The ID of the customer.
* @param name The name of the customer.
*/
public Customer(UUID id, String name, boolean isBusiness) {
this.id = id;
this.name = name;
this.isBusiness = isBusiness;
}

public Customer(UUID id, String name) {
this.id = id;
this.name = name;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.codedifferently.lesson17.bank;

public class MoneyOrder {
private double amount;
private CheckingAccount account;

/**
* Creates a new check.
*
* @param checkNumber The check number.
* @param amount The amount of the check.
* @param account The account the check is drawn on.
*/
public MoneyOrder(double amount, CheckingAccount account) {
if (amount < 0) {
throw new IllegalArgumentException("Money Order amount must be positive");
}
this.amount = amount;
this.account = account;

account.withdraw(amount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package com.codedifferently.lesson17.bank;

import com.codedifferently.lesson17.bank.exceptions.InsufficientFundsException;
import java.util.Set;

/** Represents a savings account. */
public class SavingsAccount {

private final Set<Customer> owners;
private final String accountNumber;
private double balance;
private boolean isActive;

/**
* Creates a new savings account.
*
* @param accountNumber The account number.
* @param owners The owners of the account.
* @param initialBalance The initial balance of the account.
*/
public SavingsAccount(String accountNumber, Set<Customer> owners, double initialBalance) {
this.accountNumber = accountNumber;
this.owners = owners;
this.balance = initialBalance;
isActive = true;
}

/**
* Gets the account number.
*
* @return The account number.
*/
public String getAccountNumber() {
return accountNumber;
}

/**
* Gets the owners of the account.
*
* @return The owners of the account.
*/
public Set<Customer> getOwners() {
return owners;
}

/**
* Deposits funds into the account.
*
* @param amount The amount to deposit.
*/
public void deposit(double amount) throws IllegalStateException {
if (isClosed()) {
throw new IllegalStateException("Cannot deposit to a closed account");
}
if (amount <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
balance += amount;
}

/**
* Withdraws funds from the account.
*
* @param amount
* @throws InsufficientFundsException
*/
public void withdraw(double amount) throws InsufficientFundsException {
if (isClosed()) {
throw new IllegalStateException("Cannot withdraw from a closed account");
}
if (amount <= 0) {
throw new IllegalStateException("Withdrawal amount must be positive");
}
if (balance < amount) {
throw new InsufficientFundsException("Account does not have enough funds for withdrawal");
}
balance -= amount;
}

/**
* Gets the balance of the account.
*
* @return The balance of the account.
*/
public double getBalance() {
return balance;
}

/** Closes the account. */
public void closeAccount() throws IllegalStateException {
if (balance > 0) {
throw new IllegalStateException("Cannot close account with a positive balance");
}
isActive = false;
}

/**
* Checks if the account is closed.
*
* @return True if the account is closed, otherwise false.
*/
public boolean isClosed() {
return !isActive;
}

@Override
public int hashCode() {
return accountNumber.hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj instanceof SavingsAccount other) {
return accountNumber.equals(other.accountNumber);
}
return false;
}

@Override
public String toString() {
return "SavingsAccount{"
+ "accountNumber='"
+ accountNumber
+ '\''
+ ", balance="
+ balance
+ ", isActive="
+ isActive
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.codedifferently.lesson17.bank.exceptions;

public class UnsupportedCurrencyException extends Exception {
public UnsupportedCurrencyException() {}

public UnsupportedCurrencyException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException;
import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException;
import com.codedifferently.lesson17.bank.exceptions.UnsupportedCurrencyException;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -59,7 +60,12 @@ void testFindAccountsByCustomerId() {
@Test
void testDepositFunds() {
// Act
classUnderTest.depositFunds(account1.getAccountNumber(), 50.0);
try {
classUnderTest.depositFunds(account1.getAccountNumber(), 50.0, "USD");
} catch (UnsupportedCurrencyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

// Assert
assertThat(account1.getBalance()).isEqualTo(150.0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.codedifferently.lesson17.bank;

import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.codedifferently.lesson17.bank.exceptions.InsufficientFundsException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SavingsAccountTest {

private SavingsAccount classUnderTest;
private Set<Customer> owners;

@BeforeEach
void setUp() {
owners = new HashSet<>();
owners.add(new Customer(UUID.randomUUID(), "John Doe"));
owners.add(new Customer(UUID.randomUUID(), "Jane Smith"));
classUnderTest = new SavingsAccount("123456789", owners, 100.0);
}

@Test
void getAccountNumber() {
assertEquals("123456789", classUnderTest.getAccountNumber());
}

@Test
void getOwners() {
assertEquals(owners, classUnderTest.getOwners());
}

@Test
void deposit() {
classUnderTest.deposit(50.0);
assertEquals(150.0, classUnderTest.getBalance());
}

@Test
void deposit_withNegativeAmount() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> classUnderTest.deposit(-50.0));
}

@Test
void withdraw() {
classUnderTest.withdraw(50.0);
assertEquals(50.0, classUnderTest.getBalance());
}

@Test
void withdraw_withNegativeAmount() {
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> classUnderTest.withdraw(-50.0))
.withMessage("Withdrawal amount must be positive");
}

@Test
void withdraw_withInsufficientBalance() {
assertThatExceptionOfType(InsufficientFundsException.class)
.isThrownBy(() -> classUnderTest.withdraw(150.0))
.withMessage("Account does not have enough funds for withdrawal");
}

@Test
void getBalance() {
assertEquals(100.0, classUnderTest.getBalance());
}

@Test
void closeAccount_withPositiveBalance() {
assertThatExceptionOfType(IllegalStateException.class)
.isThrownBy(() -> classUnderTest.closeAccount());
}

@Test
void isClosed() {
assertFalse(classUnderTest.isClosed());
classUnderTest.withdraw(100);
classUnderTest.closeAccount();
assertTrue(classUnderTest.isClosed());
}

@Test
void equals() {
SavingsAccount otherAccount = new SavingsAccount("123456789", owners, 200.0);
assertEquals(classUnderTest, otherAccount);
}

@Test
void hashCodeTest() {
SavingsAccount otherAccount = new SavingsAccount("123456789", owners, 200.0);
assertEquals(classUnderTest.hashCode(), otherAccount.hashCode());
}

@Test
void toStringTest() {
String expected = "SavingsAccount{accountNumber='123456789', balance=100.0, isActive=true}";
assertEquals(expected, classUnderTest.toString());
}
}