diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java new file mode 100644 index 000000000..387fd81ee --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/AuditLog.java @@ -0,0 +1,22 @@ +package com.codedifferently.lesson17.bank; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +public class AuditLog { + + private final List entries = new ArrayList<>(); + + public void record(String message) { + entries.add(LocalDateTime.now() + " - " + message); + } + + public List getEntries() { + return entries; + } + + public void printLog() { + entries.forEach(System.out::println); + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java index 8cbcd3cc0..a8b42b72f 100644 --- a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java @@ -11,6 +11,12 @@ public class BankAtm { private final Map customerById = new HashMap<>(); private final Map accountByNumber = new HashMap<>(); + private final AuditLog auditLog; + + public BankAtm(AuditLog auditLog) { + + this.auditLog = auditLog; + } /** * Adds a checking account to the bank. @@ -40,14 +46,35 @@ public Set findAccountsByCustomerId(UUID customerId) { } /** - * Deposits funds into an account. + * Deposits funds into the specified account. This method supports both cash (Double) and + * MoneyOrder objects as deposit sources. * - * @param accountNumber The account number. - * @param amount The amount to deposit. + * @param accountNumber the account to deposit into + * @param source the deposit source; must be a Double (cash) or MoneyOrder */ - public void depositFunds(String accountNumber, double amount) { - CheckingAccount account = getAccountOrThrow(accountNumber); - account.deposit(amount); + public void depositFunds(String accountNumber, Object source) { + // Retrieve the target account using the provided account number + CheckingAccount targetAccount = (CheckingAccount) accountByNumber.get(accountNumber); + + // If the deposit source is a MoneyOrder, extract the amount and deposit it + if (source instanceof MoneyOrder newMoneyOrder) { + if (targetAccount != null) { + double amount = newMoneyOrder.getAmount(); + targetAccount.deposit(amount); + auditLog.record("Deposited $" + amount + " via MoneyOrder to account " + accountNumber); + } + } + // If the deposit source is cash (Double), proceed with a standard deposit + else if (source instanceof Double amount) { + if (targetAccount != null) { + targetAccount.deposit(amount); + auditLog.record("Deposited $" + amount + " in cash to account " + accountNumber); + } + } + // If the source type is unsupported, throw an exception + else { + throw new IllegalArgumentException("Unsupported deposit type"); + } } /** @@ -59,6 +86,11 @@ public void depositFunds(String accountNumber, double amount) { public void depositFunds(String accountNumber, Check check) { CheckingAccount account = getAccountOrThrow(accountNumber); check.depositFunds(account); + auditLog.record("Deposited check" + check + "into account" + accountNumber); + } + + public void printAuditLog() { + auditLog.printLog(); } /** @@ -70,6 +102,7 @@ public void depositFunds(String accountNumber, Check check) { public void withdrawFunds(String accountNumber, double amount) { CheckingAccount account = getAccountOrThrow(accountNumber); account.withdraw(amount); + auditLog.record("Withdrew $" + amount + "from account" + accountNumber); } /** diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java new file mode 100644 index 000000000..0e8fa765c --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverter.java @@ -0,0 +1,5 @@ +package com.codedifferently.lesson17.bank; + +public interface CurrencyConverter { + double convert(double amount, String fromCurrency, String toCurrency); +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverterException.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverterException.java new file mode 100644 index 000000000..ec7613d40 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/CurrencyConverterException.java @@ -0,0 +1,7 @@ +package com.codedifferently.lesson17.bank; + +public class CurrencyConverterException extends RuntimeException { + public CurrencyConverterException(String fromCurrency, String toCurrency) { + super("Unsupported conversion from " + fromCurrency + " to " + toCurrency); + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java new file mode 100644 index 000000000..3fd57301e --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/MoneyOrder.java @@ -0,0 +1,24 @@ +package com.codedifferently.lesson17.bank; + +public class MoneyOrder { + private final double amount; + private final String sourceAccountNumber; + + public MoneyOrder(CheckingAccount sourceAccount, double amount) { + this.amount = amount; + this.sourceAccountNumber = sourceAccount.getAccountNumber(); + sourceAccount.withdraw(amount); // Withdraw immediately + } + + public double getAmount() { + return amount; + } + + public String getSourceAccountNumber() { + return sourceAccountNumber; + } + + public void depositTo(CheckingAccount destinationAccount) { + destinationAccount.deposit(amount); + } +} diff --git a/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SimpleCurrencyConverter.java b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SimpleCurrencyConverter.java new file mode 100644 index 000000000..8ff847de4 --- /dev/null +++ b/lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/SimpleCurrencyConverter.java @@ -0,0 +1,25 @@ +package com.codedifferently.lesson17.bank; + +import java.util.Map; + +public class SimpleCurrencyConverter implements CurrencyConverter { + private final Map exchangeRates; + + public SimpleCurrencyConverter(Map exchangeRates) { + this.exchangeRates = exchangeRates; + } + + @Override + public double convert(double amount, String fromCurrency, String toCurrency) { + if (fromCurrency.equals(toCurrency)) return amount; + + String key = fromCurrency + "_TO_" + toCurrency; + Double rate = exchangeRates.get(key); + + if (rate == null) { + throw new CurrencyConverterException(fromCurrency, toCurrency); + } + + return amount * rate; + } +} diff --git a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java index fa4a913a2..df039983a 100644 --- a/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java +++ b/lesson_17/bank/bank_app/src/test/java/com/codedifferently/lesson17/bank/BankAtmTest.java @@ -1,14 +1,19 @@ package com.codedifferently.lesson17.bank; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import com.codedifferently.lesson17.bank.exceptions.AccountNotFoundException; import com.codedifferently.lesson17.bank.exceptions.CheckVoidedException; -import java.util.Set; -import java.util.UUID; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; class BankAtmTest { @@ -18,18 +23,29 @@ class BankAtmTest { private Customer customer1; private Customer customer2; + Map rates = + Map.of( + "USD_TO_EUR", 0.85, + "EUR_TO_USD", 1.18); + SimpleCurrencyConverter converter = new SimpleCurrencyConverter(rates); + @BeforeEach void setUp() { - classUnderTest = new BankAtm(); + AuditLog auditLog = new AuditLog(); + classUnderTest = new BankAtm(auditLog); customer1 = new Customer(UUID.randomUUID(), "John Doe"); customer2 = new Customer(UUID.randomUUID(), "Jane Smith"); + account1 = new CheckingAccount("123456789", Set.of(customer1), 100.0); account2 = new CheckingAccount("987654321", Set.of(customer1, customer2), 200.0); + customer1.addAccount(account1); customer1.addAccount(account2); customer2.addAccount(account2); + classUnderTest.addAccount(account1); classUnderTest.addAccount(account2); + } @Test @@ -107,4 +123,34 @@ void testWithdrawFunds_AccountNotFound() { .isThrownBy(() -> classUnderTest.withdrawFunds(nonExistingAccountNumber, 50.0)) .withMessage("Account not found"); } + + @Test + void testDeposit_LogsToConsole() { + classUnderTest.depositFunds(account1.getAccountNumber(), 50.0); + + assertThat(account1.getBalance()).isEqualTo(150.0); + + System.out.println("🧾 Audit Log:"); + classUnderTest.printAuditLog(); + } + + @Test + public void testConvert_USDToEUR() { + double result = converter.convert(100.0, "USD", "EUR"); + assertEquals(85.0, result, 0.001); + } + + @Test + public void testConvert_SameCurrency() { + double result = converter.convert(100.0, "USD", "USD"); + assertEquals(100.0, result, 0.001); + } + + @Test + public void testUnsupportedConversion_ThrowsException() { + Exception exception = + assertThrows( + CurrencyConverterException.class, () -> converter.convert(100.0, "USD", "JPY")); + assertTrue(exception.getMessage().contains("Unsupported conversion")); + } }