diff --git a/src/main/java/com/booleanuk/core/Account.java b/src/main/java/com/booleanuk/core/Account.java new file mode 100644 index 000000000..1a01bc9d1 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Account.java @@ -0,0 +1,160 @@ +package com.booleanuk.core; + +import com.booleanuk.core.Users.BankManager; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class Account { + private List transactionList = new ArrayList<>(); + private BankManager bm; + private String accountId; + private String accountType; + private String branch; + private int balance = 0; + + // Constructor with BankManager (mostly for test and lack of service layer); ! or account has a manager + public Account(String accountType, int balance, String branch, BankManager bm) { + this.accountId = UUID.randomUUID().toString(); + this.branch = branch; + this.bm = bm; + setAccountType(accountType); + checkBalance(balance); + } + + // Constructor with balance + public Account(String accountType, int balance, String branch) { + this.accountId = UUID.randomUUID().toString(); + this.branch = branch; + setAccountType(accountType); + checkBalance(balance); + } + + // Constructor with no initial balance + public Account(String accountId, String accountType) { + this.accountId = UUID.randomUUID().toString(); + setAccountType(accountType); + checkBalance(balance); + } + + public void checkBalance(int balance) { + if (balance < 0) + throw new IllegalArgumentException("Initial balance cant be less than 0"); + else + setBalance(balance); + } + + public int deposit(int amount) { + if (amount <= 0){ + System.out.println("Deposit must be larger than 0.."); + return 0; + } + addTransaction(amount, "debit"); + return this.balance += amount; + } + + public int withdraw(int amount) { + if (amount < 0) { + System.out.println("Cant withdraw a negative value.."); + return 0; + } + + if (this.balance >= amount) { + balance -= amount; + addTransaction(amount, "credit"); + return amount; + } + + System.out.println("Cant withdraw amount, balance too low.."); + return 0; + } + + public void addTransaction(int amount, String type) { + + if (!type.toLowerCase().trim().equals("debit") && !type.toLowerCase().trim().equals("credit")) { + type = "unknown"; + } + + LocalDate today = LocalDate.now(); + Transaction transaction = new Transaction(amount, this.balance, today, type); // amount, date, type + transactionList.add(transaction); + } + + public void generateBankStatement() { + System.out.printf("\n%-20s %-10s %-10s %-10s\n", "Date", "Credit", "Debit", "Balance"); + for (Transaction t : transactionList) { + if (t.getType().equals("debit")) { + System.out.printf("%-20s %-10s %-10s %-10s\n", t.getDate(), " ", t.getAmount(), t.getBalance()); + } else if (t.getType().equals("credit")) { + System.out.printf("%-20s %-10s %-10s %-10s\n", t.getDate(), t.getAmount(), " ", t.getBalance()); + } + } + System.out.println("\n"); + } + + public void generateStatementForSMS() { + System.out.printf("%-10s %-10s %-10s %-10s\n", "Date", "Credit", "Debit", "Balance"); + for (Transaction t : transactionList) { + if (t.getType().equals("debit")) { + System.out.printf("%-10s %-10s %-10s %-10s\n", t.getDate().getMonth(), " ", t.getAmount(), t.getBalance()); + } else if (t.getType().equals("credit")) { + System.out.printf("%-10s %-10s %-10s %-10s\n", t.getDate().getMonth(), t.getAmount(), " ", t.getBalance()); + } + } + } + + public boolean requestOverdraft(int amount) { + return bm.verifyOverdraftRequest(this, amount); + } + + // GETTERS & SETTERS + + public String getBranch() { + return branch; + } + + public void setBranch(String branch) { + this.branch = branch; + } + + public String getAccountId() { + return accountId; + } + + public void setAccountId(String accountId) { + this.accountId = accountId; + } + + public int getBalance() { + return balance; + } + + public void setBalance(int balance) { + this.balance = balance; + } + + public String getAccountType() { + return accountType; + } + + public void setAccountType(String accountType) { + switch (accountType) { + case "savings", "current": + this.accountType = accountType; + break; + default: + throw new IllegalArgumentException("Unexpected value: " + accountType); + } + } + + public List getTransactionList() { + return transactionList; + } + + public void setTransactionList(List transactionList) { + this.transactionList = transactionList; + } + +} diff --git a/src/main/java/com/booleanuk/core/Branch.java b/src/main/java/com/booleanuk/core/Branch.java new file mode 100644 index 000000000..912c583a4 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Branch.java @@ -0,0 +1,45 @@ +package com.booleanuk.core; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.DoubleAccumulator; + +public class Branch { + private String id; + private String location; + private List accounts = new ArrayList<>(); + + public Branch(String location) { + this.id = UUID.randomUUID().toString(); + this.location = location; + } + + public void addAccount(Account acc) { + accounts.add(acc); + } + + public List getAccounts() { + return accounts; + } + + public void setAccounts(List accounts) { + this.accounts = accounts; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} 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..19c9445d4 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Transaction.java @@ -0,0 +1,57 @@ +package com.booleanuk.core; + +import java.time.LocalDate; +import java.util.Date; +import java.util.UUID; + +public class Transaction { + private String id; + private int amount; + private LocalDate date; + private String type; + private int balance; + + public Transaction(int amount, int balance, LocalDate date, String type) { + this.id = UUID.randomUUID().toString(); + this.amount = amount; + this.balance = balance; + this.date = date; + this.type = type; + } + + public int getBalance() { + return this.balance; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public LocalDate getDate() { + return this.date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } +} diff --git a/src/main/java/com/booleanuk/core/Users/BankManager.java b/src/main/java/com/booleanuk/core/Users/BankManager.java new file mode 100644 index 000000000..cb69ae726 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Users/BankManager.java @@ -0,0 +1,45 @@ +package com.booleanuk.core.Users; + +import com.booleanuk.core.Account; +import com.booleanuk.core.Branch; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class BankManager { + private String id; + List branches = new ArrayList<>(); + + public BankManager() { + this.id = UUID.randomUUID().toString(); + } + + // True if within balance range, else deny + public Boolean verifyOverdraftRequest(Account account, int amount) { + if (amount < 0) + return false; + + return amount <= account.getBalance(); + } + + public Branch addBranch(String branchLocation) { + Branch branch = new Branch(branchLocation); + branches.add(branch); + + return branch; + } + + public void addAccountToBranch(Account account) { + for (Branch branch : branches) { + if (account.getBranch().equalsIgnoreCase(branch.getLocation())) { + branch.addAccount(account); + return; + } + } + } + + public List getBranches() { + return this.branches; + } +} diff --git a/src/main/java/com/booleanuk/core/Users/Customer.java b/src/main/java/com/booleanuk/core/Users/Customer.java new file mode 100644 index 000000000..fae97969b --- /dev/null +++ b/src/main/java/com/booleanuk/core/Users/Customer.java @@ -0,0 +1,50 @@ +package com.booleanuk.core.Users; + +import com.booleanuk.core.Account; + +import java.util.UUID; + +public class Customer { + private String id; + private Account account; + + public Customer() { + this.id = UUID.randomUUID().toString(); + } + + public void createAccount(String type, int balance, String branch) { + this.account = new Account(type, balance, branch); + } + + // Mainly for testing interaction , Customer -> Account -> BankManager + public void createAccount(String type, int balance, String branch, BankManager bm) { + this.account = new Account(type, balance, branch, bm); + } + + public void viewBankStatements() { + this.account.generateBankStatement(); + } + + public void depositToAccount(int amount) { + this.account.deposit(amount); + } + + public void withdrawFromAccount(int amount) { + this.account.withdraw(amount); + } + + public boolean requestOverdraft(int amount) { + return account.requestOverdraft(amount); + } + + public void sendStatementAsSMS(String num) { + System.out.println("\n\nSend to number: " + num); + System.out.println("Sending SMS..."); + System.out.println("..."); + System.out.println("..."); + System.out.println("\nIncoming..."); + account.generateStatementForSMS(); + System.out.println("\n"); + } + +} diff --git a/src/main/java/com/booleanuk/core/Users/Engineer.java b/src/main/java/com/booleanuk/core/Users/Engineer.java new file mode 100644 index 000000000..86445f2af --- /dev/null +++ b/src/main/java/com/booleanuk/core/Users/Engineer.java @@ -0,0 +1,44 @@ +package com.booleanuk.core.Users; + +import com.booleanuk.core.Account; +import com.booleanuk.core.Transaction; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class Engineer { + private String id; + private List accountList = new ArrayList<>(); + + public Engineer(List list) { + this.id = UUID.randomUUID().toString(); + this.accountList = list; + } + + public int getAccountBalance(String id) { + Account account = null; + for (Account a: accountList) + if (a.getAccountId().equals(id)) + account = a; + + List list = null; + if (account != null) { + list = account.getTransactionList(); + } + + int balance = 0; + if (list != null) { + balance = list.getFirst().getBalance(); + + for (Transaction t : list) { + if (t.getType().equals("debit")) { + balance += t.getAmount(); + } else if (t.getType().equals("credit")) { + balance -= t.getAmount(); + } + } + } + return balance; + } +} diff --git a/src/main/java/com/booleanuk/core/bank-statement-printout.png b/src/main/java/com/booleanuk/core/bank-statement-printout.png new file mode 100644 index 000000000..697a2887a Binary files /dev/null and b/src/main/java/com/booleanuk/core/bank-statement-printout.png differ diff --git a/src/main/java/com/booleanuk/core/diagrams/domain-model-1.0.md b/src/main/java/com/booleanuk/core/diagrams/domain-model-1.0.md new file mode 100644 index 000000000..6ea3bf0f3 --- /dev/null +++ b/src/main/java/com/booleanuk/core/diagrams/domain-model-1.0.md @@ -0,0 +1,47 @@ +# Core + +| **Class** | **Member** | **Method** | **Scenario** | **Output** | +|-----------------|---------------------|-------------------------------|--------------------------------------------------------------------------|---------------| +| **Customer** | `customerId` | `createACurrentAccount()` | As a customer, I want to create a current account. | `bool` | +| | `List` | `createASavingsAccount()` | As a customer, I want to create a savings account. | `bool` | +| | `` | `depositToSavings()` | As a customer, I want to deposit to a savings account. | `bool` | +| | `` | `depositToCurrent()` | As a customer, I want to deposit to a current account. | `bool` | +| | `` | `withdrawFromCurrent()` | As a customer, I want to withdraw from current. | `int` | +| | `` | `withdrawFromSavings()` | As a customer, I want to withdraw from svaings. | `int` | +| **Account** | `accountId` | `deposit(amount)` | As a customer, I want to deposit funds. | `boolean` | +| | `balance` | `withdraw(amount)` | As a customer, I want to withdraw funds. | `boolean` | +| | `accountType` | `generateBankStatement()` | As a customer, I want to generate bank statements. | `void` | +| | `List` | `addTransaction(transaction)` | On account transaction, store the data for future statement. | `void` | +| **Transaction** | `transactionId` | `getTransactionDetails()` | As a customer, I want to view a transaction. | `Transaction` | +| | `amount` | `getTransactionAmount()` | As a customer, I want to see the transaction amount. | `decimal` | +| | `date` | `getTransactionDate()` | As a customer, I want to see the date of the transaction. | `Date` | +| | `type` | `getTransactionType()` | As a customer, I want to see the type (debit/credit) of the transaction. | `type` | + +# Extensions + +| **Class** | **Member** | **Method** | **Scenario** | **Output** | +|-----------------|-------------------------------------------------------------|-----------------------------------|------------------------------------------------------------------------------------|--------------------------------------------------------| +| **Engineer** | `List accounts` | `getAccountBalance()` | As an engineer, I want account balances to be calculated from transaction history. | Returns balance amount | +| **BankManager** | `List branches` | `-` | As a bank manager, I want accounts to be associated with specific branches. | **`BankManager` contains a list of `Branch` objects.** | +| | | `verifyOverdraftRequest(account)` | As a bank manager, I want to approve/reject overdraft requests. | `boolean` | +| **Customer** | `-` | `requestOverdraft(account)` | As a customer, I want to request an overdraft on my account. | `boolean` | +| | | | | | +| **Branch** | `branchId`, `location`, `List accounts` | `-` | As a bank manager, I want accounts to be associated with specific branches. | | + + +// Notes: +Extension: + +User (Abstract?) - Customer, Manager (though they currently do not share anything) + +Engineer story + want balances acounted based on history instaed of memoery + -> no member variable -> calculate from history of transaction list on each call ? + +BankManager + Accounts -> associate with Branches + Branches (parent) -> Branch + Approve or Reject overdraft Request + +Customer + Emergency fund -> overdraft request on account diff --git a/src/main/java/com/booleanuk/core/diagrams/initial-classDiagram.png b/src/main/java/com/booleanuk/core/diagrams/initial-classDiagram.png new file mode 100644 index 000000000..3a2f71d28 Binary files /dev/null and b/src/main/java/com/booleanuk/core/diagrams/initial-classDiagram.png differ diff --git a/src/test/java/com/booleanuk/core/AccountTest.java b/src/test/java/com/booleanuk/core/AccountTest.java new file mode 100644 index 000000000..8aa240a50 --- /dev/null +++ b/src/test/java/com/booleanuk/core/AccountTest.java @@ -0,0 +1,70 @@ +package com.booleanuk.core; + +import org.junit.jupiter.api.Test; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +public class AccountTest { + + @Test + public void testCreateCurrentAccountWithDataPassVerifications() { + Account acc = new Account("savings", 500, "rome"); + assertEquals("savings", acc.getAccountType()); + assertEquals(500, acc.getBalance()); + assertThrows(IllegalArgumentException.class, () -> acc.setAccountType("test")); + assertThrows(IllegalArgumentException.class, () -> new Account("current", -500, "rome")); + } + + @Test + public void depositAndUpdateAccountBalance() { + Account acc = new Account("savings", 444, "rome"); + + assertEquals(5444, acc.deposit(5000)); + assertEquals(5444, acc.getBalance()); + assertEquals(5500, acc.deposit(56)); + assertEquals(5500, acc.getBalance()); + assertEquals(0, acc.deposit(-54)); + assertEquals(0, acc.deposit(0)); + } + + @Test + public void withdrawAndUpdateAccountBalance() { + Account acc = new Account("savings", 444, "rome"); + + assertEquals(400 ,acc.withdraw(400)); + assertEquals(44, acc.getBalance()); + assertEquals(98, acc.deposit(54)); + assertEquals(4, acc.withdraw(4)); + assertEquals(94, acc.getBalance()); + } + + @Test + public void withdrawLessThanBalance() { + Account acc = new Account("savings", 444, "rome"); + + assertEquals(0, acc.withdraw(1000)); + assertEquals(0, acc.withdraw(-1000)); + assertEquals(0, acc.withdraw(0)); + assertEquals(44, acc.withdraw(44)); + assertEquals(400, acc.getBalance()); + assertEquals(400, acc.withdraw(400)); + assertEquals(0, acc.withdraw(1)); + } + + @Test + public void createTransaction() { + Account acc = new Account("savings", 500, "rome"); + acc.addTransaction(500, ""); // unknown type + acc.deposit(300); // deposit = debit + + List list = acc.getTransactionList(); + assertEquals(2, list.size()); + Transaction test = list.getFirst(); + Transaction test2 = list.getLast(); + assertEquals(500, test.getAmount()); + assertEquals("unknown", test.getType()); + assertEquals("debit", test2.getType()); + assertEquals(300, test2.getAmount()); + } + +} diff --git a/src/test/java/com/booleanuk/core/BankManagerTest.java b/src/test/java/com/booleanuk/core/BankManagerTest.java new file mode 100644 index 000000000..0182086f2 --- /dev/null +++ b/src/test/java/com/booleanuk/core/BankManagerTest.java @@ -0,0 +1,52 @@ +package com.booleanuk.core; + +import com.booleanuk.core.Users.BankManager; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class BankManagerTest { + + @Test + public void ifBalanceAboveThresholdApproveOverdraftElseDeny() { + Account acc = new Account("current", 1000, "rome"); + BankManager bm = new BankManager(); + + assertTrue(bm.verifyOverdraftRequest(acc, 1000)); + assertTrue(bm.verifyOverdraftRequest(acc, 999)); + assertFalse(bm.verifyOverdraftRequest(acc, 1100)); + assertFalse(bm.verifyOverdraftRequest(acc, 1001)); + assertTrue(bm.verifyOverdraftRequest(acc, 500)); + assertTrue(bm.verifyOverdraftRequest(acc, 0)); + assertFalse(bm.verifyOverdraftRequest(acc, -1000)); + } + + @Test + public void addBranchesAndVerify() { + BankManager bm = new BankManager(); + Branch b1 = bm.addBranch("rome"); + Branch b2 = bm.addBranch("berlin"); + + Account acc = new Account("current", 1000, "rome"); + Account acc2 = new Account("current", 2000, "berlin"); + Account acc3 = new Account("current", 4000, "rome"); + Account acc4 = new Account("current", 6000, "rome"); + + bm.addAccountToBranch(acc); + bm.addAccountToBranch(acc2); + bm.addAccountToBranch(acc3); + bm.addAccountToBranch(acc4); + + List list = bm.getBranches(); + assertEquals("rome", list.getFirst().getLocation()); + assertEquals("berlin", list.getLast().getLocation()); + + List accs = list.getFirst().getAccounts(); + assertEquals("rome", accs.getFirst().getBranch()); + assertEquals("rome", accs.get(1).getBranch()); + assertEquals("rome", accs.get(2).getBranch()); + } + +} diff --git a/src/test/java/com/booleanuk/core/CustomerTest.java b/src/test/java/com/booleanuk/core/CustomerTest.java new file mode 100644 index 000000000..3a6e207cc --- /dev/null +++ b/src/test/java/com/booleanuk/core/CustomerTest.java @@ -0,0 +1,50 @@ +package com.booleanuk.core; + +import com.booleanuk.core.Users.BankManager; +import com.booleanuk.core.Users.Customer; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class CustomerTest { + + @Test // Testing printout of method + public void testForStatementPrintout() { + + Customer c = new Customer(); + c.createAccount("savings", 500, "rome"); + c.depositToAccount(300); + c.depositToAccount(100); + c.withdrawFromAccount(150); + c.depositToAccount(500); + c.withdrawFromAccount(400); + + c.viewBankStatements(); + } + + @Test + public void testRequestOverdraft() { + BankManager bm = new BankManager(); + Customer c = new Customer(); + c.createAccount("current", 500, "rome", bm); + assertTrue(c.requestOverdraft(500)); + assertTrue(c.requestOverdraft(499)); + assertTrue(c.requestOverdraft(1)); + assertFalse(c.requestOverdraft(1000)); + assertFalse(c.requestOverdraft(1001)); + assertFalse(c.requestOverdraft(-151)); + } + + @Test // Testing printout of method + public void sendStatementAsSMS() { + Customer c = new Customer(); + c.createAccount("savings", 500, "rome"); + c.depositToAccount(300); + c.depositToAccount(100); + c.withdrawFromAccount(150); + c.depositToAccount(500); + c.withdrawFromAccount(400); + + c.sendStatementAsSMS("0708616843"); + } +} diff --git a/src/test/java/com/booleanuk/core/EngineerTest.java b/src/test/java/com/booleanuk/core/EngineerTest.java new file mode 100644 index 000000000..953e81b23 --- /dev/null +++ b/src/test/java/com/booleanuk/core/EngineerTest.java @@ -0,0 +1,27 @@ +package com.booleanuk.core; + +import com.booleanuk.core.Users.Engineer; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.ArrayList; +import java.util.List; + +public class EngineerTest { + + @Test + public void asEngineerCalculateAllHistoryTransactions() { + + Account account = new Account("current", 1000, "rome"); + account.deposit(500); + account.withdraw(300); + account.deposit(150); + account.deposit(550); + account.withdraw(1000); + List list = new ArrayList<>(); + list.add(account); + Engineer e = new Engineer(list); + + assertEquals(900, e.getAccountBalance(account.getAccountId())); // shall be 900 + } +}