diff --git a/bank_class_diagram.png b/bank_class_diagram.png new file mode 100644 index 000000000..dcecd3c41 Binary files /dev/null and b/bank_class_diagram.png differ diff --git a/bank_extension_class_diagram.png b/bank_extension_class_diagram.png new file mode 100644 index 000000000..fd0762dd2 Binary files /dev/null and b/bank_extension_class_diagram.png differ diff --git a/src/main/java/com/booleanuk/core/BankAccount.java b/src/main/java/com/booleanuk/core/BankAccount.java new file mode 100644 index 000000000..a7c818bf8 --- /dev/null +++ b/src/main/java/com/booleanuk/core/BankAccount.java @@ -0,0 +1,96 @@ +package com.booleanuk.core; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public abstract class BankAccount { + private final String accountNumber; + private final String branchNumber; + private final List transactions; + + public BankAccount(int branchNumber) { + Random random = new Random(); + int randomAccountNumber = random.nextInt(99999999); + this.accountNumber = "0".repeat((int) (8 - Math.ceil(Math.log10(randomAccountNumber)))) + randomAccountNumber; + if (branchNumber > 999999) { + System.out.println("Invalid branch code (must be 6 digit number). Branch code will be set to 0."); + branchNumber = 0; + } + this.branchNumber = "0".repeat((int) (6 - Math.ceil(Math.log10(branchNumber)))) + branchNumber; + this.transactions = new ArrayList<>(); + } + + public BankAccount(String accountNumber, String branchNumber) { + // todo: make sure numbers are OK length + this.accountNumber = accountNumber; + this.branchNumber = branchNumber; + this.transactions = new ArrayList<>(); + } + + public boolean withdraw(double amount, LocalDateTime dateTime) { + double balance = this.getBalance(); + if (amount <= balance) { + balance -= amount; + transactions.add(new Transaction(-amount, balance, dateTime)); + } + return amount <= balance; + } + + public boolean withdraw(double amount) { + return this.withdraw(amount, LocalDateTime.now()); + } + + // maybe can be void... or maybe consider overflow? + public boolean deposit(double amount, LocalDateTime dateTime) { + double balance = this.getBalance(); + balance += amount; + transactions.add(new Transaction(amount, balance, dateTime)); + return true; + } + + public boolean deposit(double amount) { + return this.deposit(amount, LocalDateTime.now()); + } + + // TODO Fix formatting + public String generateBankStatement() { + StringBuilder sb = new StringBuilder(); + sb.append("date\t||credit\t||debit\t||balance\n"); + + for (Transaction t : transactions.reversed()) { + sb.append(t.getDateTime().getDayOfMonth()).append("/") + .append(t.getDateTime().getMonthValue()).append("/") + .append(t.getDateTime().getYear()).append("||"); + if (t.getAmount() > 0) { + sb.append(String.format("%.2f", Math.abs(t.getAmount()))).append("||\t"); + } else { + sb.append("\t||").append(String.format("%.2f", Math.abs(t.getAmount()))); + } + sb.append("||").append(String.format("%.2f", t.getCurrentBalance())).append("\n"); + } + + return sb.toString(); + } + + public String getAccountNumber() { + return accountNumber; + } + + public double getBalance() { + double balance = 0; + for (Transaction t : transactions) { + balance += t.getAmount(); + } + return balance; + } + + public List getTransactions() { + return transactions; + } + + public String getBranchNumber() { + return branchNumber; + } +} diff --git a/src/main/java/com/booleanuk/core/BankSystem.java b/src/main/java/com/booleanuk/core/BankSystem.java new file mode 100644 index 000000000..34fd687dd --- /dev/null +++ b/src/main/java/com/booleanuk/core/BankSystem.java @@ -0,0 +1,41 @@ +package com.booleanuk.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BankSystem { + private List overdraftRequests; + private Map savingsAccounts; + private Map currentAccounts; + + public BankSystem() { + overdraftRequests = new ArrayList<>(); + savingsAccounts = new HashMap<>(); + currentAccounts = new HashMap<>(); + } + + // maybe void? + public boolean approve(OverdraftRequest or) { + return savingsAccounts.get(or.accountNumber + or.branchNumber).setMaxOverdraft(or.getAmount()); + } + + public void addCurrentAccount(CurrentAccount b) { + currentAccounts.put(b.getAccountNumber() + b.getBranchNumber(), b); + } + + public void addSavingsAccount(SavingsAccount b) { + savingsAccounts.put(b.getAccountNumber() + b.getBranchNumber(), b); + } + + public Map getBankAccounts() { + Map bankAccounts = new HashMap<>(currentAccounts); + bankAccounts.putAll(savingsAccounts); + return bankAccounts; + } + + public List getOverdraftRequests() { + return overdraftRequests; + } +} diff --git a/src/main/java/com/booleanuk/core/CurrentAccount.java b/src/main/java/com/booleanuk/core/CurrentAccount.java new file mode 100644 index 000000000..46fa2f528 --- /dev/null +++ b/src/main/java/com/booleanuk/core/CurrentAccount.java @@ -0,0 +1,11 @@ +package com.booleanuk.core; + +public class CurrentAccount extends BankAccount { + public CurrentAccount() { + super(1); + } + + public CurrentAccount(int branchNumber) { + super(branchNumber); + } +} diff --git a/src/main/java/com/booleanuk/core/OverdraftRequest.java b/src/main/java/com/booleanuk/core/OverdraftRequest.java new file mode 100644 index 000000000..3d6c915d4 --- /dev/null +++ b/src/main/java/com/booleanuk/core/OverdraftRequest.java @@ -0,0 +1,25 @@ +package com.booleanuk.core; + +public class OverdraftRequest { + String accountNumber; + String branchNumber; + double amount; + + public OverdraftRequest(String accountNumber, String branchNumber, double amount) { + this.accountNumber = accountNumber; + this.branchNumber = branchNumber; + this.amount = amount; + } + + public String getAccountNumber() { + return accountNumber; + } + + public String getBranchNumber() { + return branchNumber; + } + + public double getAmount() { + return amount; + } +} diff --git a/src/main/java/com/booleanuk/core/SavingsAccount.java b/src/main/java/com/booleanuk/core/SavingsAccount.java new file mode 100644 index 000000000..e26dace9c --- /dev/null +++ b/src/main/java/com/booleanuk/core/SavingsAccount.java @@ -0,0 +1,49 @@ +package com.booleanuk.core; + +import java.time.LocalDateTime; + +public class SavingsAccount extends BankAccount { + private double maxOverdraft; + + public SavingsAccount(String accountNumber, String branchNumber) { + super(accountNumber, branchNumber); + this.maxOverdraft = 0; + } + + public SavingsAccount(int branchNumber) { + super(branchNumber); + this.maxOverdraft = 0; + } + + @Override + public boolean withdraw(double amount, LocalDateTime dateTime) { + if (getBalance() - amount < - maxOverdraft) { + return false; + } + getTransactions().add(new Transaction(-amount, getBalance() - amount, dateTime)); + return true; + } + + @Override + public boolean withdraw(double amount) { + return this.withdraw(amount, LocalDateTime.now()); + } + + // probably won't be used, but useful in theory + public boolean hasOverdraft() { + return maxOverdraft > 0; + } + + public double getMaxOverdraft() { + return maxOverdraft; + } + + public boolean setMaxOverdraft(double maxOverdraft) { + if (maxOverdraft < 0) { + System.out.println("Cannot set negative overdraft."); + return false; + } + this.maxOverdraft = maxOverdraft; + return true; + } +} diff --git a/src/main/java/com/booleanuk/core/Transaction.java b/src/main/java/com/booleanuk/core/Transaction.java new file mode 100644 index 000000000..3f19c6e62 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Transaction.java @@ -0,0 +1,27 @@ +package com.booleanuk.core; + +import java.time.LocalDateTime; + +public class Transaction { + private final double amount; + private final double currentBalance; + private final LocalDateTime dateTime; + + public Transaction(double amount, double currentBalance, LocalDateTime dateTime) { + this.amount = amount; + this.currentBalance = currentBalance; + this.dateTime = dateTime; + } + + public double getAmount() { + return amount; + } + + public double getCurrentBalance() { + return currentBalance; + } + + public LocalDateTime getDateTime() { + return dateTime; + } +} diff --git a/src/test/java/com/booleanuk/core/TestBankAccount.java b/src/test/java/com/booleanuk/core/TestBankAccount.java new file mode 100644 index 000000000..16debf23b --- /dev/null +++ b/src/test/java/com/booleanuk/core/TestBankAccount.java @@ -0,0 +1,142 @@ +package com.booleanuk.core; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +public class TestBankAccount { + @Test + public void newAccountInstancesAreValid() { + BankAccount currentAccount = new CurrentAccount(999999); + assertEquals(8, currentAccount.getAccountNumber().length()); + assertEquals(6, currentAccount.getBranchNumber().length()); + assertEquals(0, currentAccount.getBalance()); + assertEquals(0, currentAccount.getTransactions().size()); + } + @Test + public void withdrawTooMuchShouldFail() { + BankAccount currentAccount = new CurrentAccount(); + double amount = 1000; + assertFalse(currentAccount.withdraw(amount)); + } + + @Test + public void canWithdrawWithinReason() { + BankAccount account = new CurrentAccount(); + account.deposit(1000); + assertTrue(account.withdraw(500)); + } + + @Test + public void withdrawingRemovesMoney() { + BankAccount account = new CurrentAccount(); + account.deposit(1000); + assertTrue(account.withdraw(500)); + assertEquals(500, account.getBalance()); + } + @Test + public void withdrawingRemovesMoneyOnlyIfValidAmount() { + BankAccount account = new CurrentAccount(); + account.deposit(1000); + assertFalse(account.withdraw(2000)); + assertEquals(1000, account.getBalance()); + } + + @Test + public void depositAddsTheRightAmount() { + BankAccount account = new CurrentAccount(); + assertTrue(account.deposit(1000)); + assertEquals(1000, account.getBalance()); + } + + @Test + public void transactionsAreStored() { + BankAccount account = new CurrentAccount(); + assertTrue(account.deposit(1000)); + assertTrue(account.withdraw(100)); + assertEquals(2, account.getTransactions().size()); + } + + @Test + public void transactionsAreStoredCorrectly() { + BankAccount account = new CurrentAccount(); + LocalDateTime ldt = LocalDateTime.now(); + assertTrue(account.deposit(1000)); + assertTrue(account.withdraw(100)); + assertEquals(2, account.getTransactions().size()); + LocalDateTime depTime = account.getTransactions().getFirst().getDateTime(); + LocalDateTime wdrTime = account.getTransactions().getLast().getDateTime(); + // deposit time correct within 1 s + assertEquals(ldt.toLocalDate(), depTime.toLocalDate()); + assertEquals(ldt.getHour(), depTime.getHour()); + assertEquals(ldt.getMinute(), depTime.getMinute()); + assertEquals(ldt.getSecond(), depTime.getSecond()); + // withdraw time correct within 1 s + assertEquals(ldt.toLocalDate(), wdrTime.toLocalDate()); + assertEquals(ldt.getHour(), wdrTime.getHour()); + assertEquals(ldt.getMinute(), wdrTime.getMinute()); + assertEquals(ldt.getSecond(), wdrTime.getSecond()); + // deposit amounts correct + Transaction dep = account.getTransactions().getFirst(); + assertEquals(1000, dep.getAmount()); + assertEquals(1000, dep.getCurrentBalance()); + // withdraw amounts correct + Transaction wdr = account.getTransactions().getLast(); + assertEquals(-100, wdr.getAmount()); + assertEquals(900, wdr.getCurrentBalance()); + } + + @Test + public void generateStatementOnNoTransactions() { + BankAccount account = new CurrentAccount(); + assertEquals( + "date||credit||debit||balance", + account.generateBankStatement().replaceAll("\\s+","")); + } + + @Test + public void generateStatementOnSomeTransactions() { + BankAccount account = new CurrentAccount(); + LocalDateTime ldt1 = LocalDateTime.of(2012, 1, 14, 13, 54); + LocalDateTime ldt2 = LocalDateTime.of(2012, 1, 13, 22, 18); + LocalDateTime ldt3 = LocalDateTime.of(2012, 1, 10, 11, 27); + account.deposit(1000, ldt3); + account.withdraw(100, ldt2); + account.withdraw(200, ldt1); +// System.out.println(account.generateBankStatement()); + + String sb = "date||credit||debit||balance" + + "14/1/2012" + + "||||200.00||700.00" + + "13/1/2012" + + "||||100.00||900.00" + + "10/1/2012" + + "||1000.00||||1000.00".replaceAll("\\s+",""); + + assertEquals(sb, account.generateBankStatement().replaceAll("\\s+","")); + } + + @Test + public void overdraftNeedsToBePositive() { + SavingsAccount account = new SavingsAccount(523611); + assertFalse(account.setMaxOverdraft(-50)); + } + + @Test + public void cannotGoBeyondOverdraftLimit() { + SavingsAccount account = new SavingsAccount(123456); + account.deposit(100); + assertFalse(account.withdraw(200)); + assertEquals(100, account.getBalance()); + } + + @Test + public void canHaveNegativeBalanceWithinOverdraft() { + SavingsAccount account = new SavingsAccount(123456); + account.deposit(100); + account.setMaxOverdraft(200); + assertTrue(account.withdraw(200)); + assertEquals(-100, account.getBalance()); + } +} diff --git a/src/test/java/com/booleanuk/core/TestBankSystem.java b/src/test/java/com/booleanuk/core/TestBankSystem.java new file mode 100644 index 000000000..2eba21a52 --- /dev/null +++ b/src/test/java/com/booleanuk/core/TestBankSystem.java @@ -0,0 +1,16 @@ +package com.booleanuk.core; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +public class TestBankSystem { + @Test + public void approvingOverdraftRequestWorksCorrectly() { + BankSystem bs = new BankSystem(); + SavingsAccount account = new SavingsAccount("12345678", "123456"); + bs.addSavingsAccount(account); + OverdraftRequest or = new OverdraftRequest(account.getAccountNumber(), account.getBranchNumber(), 500); + bs.approve(or); + assertTrue(account.hasOverdraft()); + } +} diff --git a/src/test/java/com/booleanuk/core/TestTransaction.java b/src/test/java/com/booleanuk/core/TestTransaction.java new file mode 100644 index 000000000..1af8e03fc --- /dev/null +++ b/src/test/java/com/booleanuk/core/TestTransaction.java @@ -0,0 +1,19 @@ +package com.booleanuk.core; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +public class TestTransaction { + @Test + public void canCreateTransaction() { + double amount = 1000; + double currentBalance = 100; + LocalDateTime dt = LocalDateTime.now(); + Transaction t = new Transaction(amount, currentBalance + amount, dt); + assertEquals(1000, t.getAmount()); + assertEquals(1100, t.getCurrentBalance()); + assertEquals(dt, t.getDateTime()); + } +}