Skip to content
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());
}
}