diff --git a/README.md b/README.md index 756dd11..d38965b 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,4 @@ A dummy application for a bank; should provide various functions of a retail ban * A customer can transfer between their accounts * Change **Maxi-Savings accounts** to have an interest rate of 5% assuming no withdrawals in the past 10 days otherwise 0.1% -* Interest rates should accrue daily (incl. weekends), rates above are per-annum \ No newline at end of file +* Interest rates should accrue daily (incl. weekends), rates above are per-annum diff --git a/abcbank/account.py b/abcbank/account.py index e010009..bf39ef9 100644 --- a/abcbank/account.py +++ b/abcbank/account.py @@ -1,43 +1,71 @@ from abcbank.transaction import Transaction - -CHECKING = 0 -SAVINGS = 1 -MAXI_SAVINGS = 2 - +from datetime import datetime, timedelta class Account: def __init__(self, accountType): self.accountType = accountType + self.dateOpened = datetime.now() self.transactions = [] + self.balance = 0 + self.account_age_in_days = (datetime.now() - self.dateOpened).days + def deposit(self, amount): - if (amount <= 0): - raise ValueError("amount must be greater than zero") - else: - self.transactions.append(Transaction(amount)) + if not amount > 0: + raise ValueError("Must enter an amount greater than zero") + new_transaction = Transaction(amount,'deposit') + self.balance += amount + self.transactions.append(new_transaction) def withdraw(self, amount): - if (amount <= 0): - raise ValueError("amount must be greater than zero") + if not amount > 0: + raise ValueError("Must enter an amount greater than zero") + if not self.balance >= amount: + raise ValueError("Insufficient Funds") else: - self.transactions.append(Transaction(-amount)) + new_transaction = Transaction(amount,'withdrawal') + self.balance -= amount + self.transactions.append(new_transaction) - def interestEarned(self): - amount = self.sumTransactions() - if self.accountType == SAVINGS: - if (amount <= 1000): - return amount * 0.001 - else: - return 1 + (amount - 1000) * 0.002 - if self.accountType == MAXI_SAVINGS: - if (amount <= 1000): - return amount * 0.02 - elif (amount <= 2000): - return 20 + (amount - 1000) * 0.05 + + def sumTransactions(self, checkAllTransactions=True): + return sum([t.amount for t in self.transactions]) + + def dailyInterestRate(self): + default_interest = self.balance * 0.001 / 356 + if self.accountType == 'CHECKING': + interest = (default_interest) + + elif self.accountType == 'SAVINGS': + if self.balance <= 1000: + interest = default_interest else: - return 70 + (amount - 2000) * 0.1 + interest = (1 + (self.balance - 1000) * 0.002) / 356 + + elif self.accountType == 'MAXI_SAVINGS': + for t in self.transactions: + if t.action == "withdrawal" and (datetime.now() - t.transactionDate).days < 10: + interest = default_interest + else: + interest = self.balance * 0.05 / 356 + return interest + + def interestEarnedDaily(self): + interest = 0 + if self.account_age_in_days == 0: + pass else: - return amount * 0.001 + for day in range(1,self.account_age_in_days): + interest += self.dailyInterestRate() + return interest + + def accrueInterest(self): + if self.account_age_in_days>0: + for day in range(self.account_age_in_days-1): + self.balance += self.accruedDailyInterest() * self.interestEarnedDaily() + return self.balance + + + + - def sumTransactions(self, checkAllTransactions=True): - return sum([t.amount for t in self.transactions]) \ No newline at end of file diff --git a/abcbank/bank.py b/abcbank/bank.py index 44711fe..6081f17 100644 --- a/abcbank/bank.py +++ b/abcbank/bank.py @@ -1,25 +1,31 @@ +from abcbank.customer import Customer + class Bank: def __init__(self): self.customers = [] - def addCustomer(self, customer): - self.customers.append(customer) + def addCustomer(self, customer_name): + new_customer = Customer(customer_name) + self.customers.append(new_customer) + return new_customer + def customerSummary(self): summary = "Customer Summary" for customer in self.customers: - summary = summary + "\n - " + customer.name + " (" + self._format(customer.numAccs(), "account") + ")" + summary += "\n - " + customer.name + " (" + self._format(customer.numAccs(), "account") + ")" return summary + def _format(self, number, word): return str(number) + " " + (word if (number == 1) else word + "s") + def totalInterestPaid(self): total = 0 for c in self.customers: total += c.totalInterestEarned() return total + def getFirstCustomer(self): - try: - self.customers = None + if self.customers: return self.customers[0].name - except Exception as e: - print(e) + else: return "Error" \ No newline at end of file diff --git a/abcbank/customer.py b/abcbank/customer.py index 7cfd62a..6c7e7f7 100644 --- a/abcbank/customer.py +++ b/abcbank/customer.py @@ -1,4 +1,4 @@ -from account import CHECKING, SAVINGS, MAXI_SAVINGS +from abcbank.account import Account class Customer: @@ -6,15 +6,23 @@ def __init__(self, name): self.name = name self.accounts = [] - def openAccount(self, account): - self.accounts.append(account) - return self - + def openAccount(self, account_type): + is_valid = ['CHECKING','SAVINGS','MAXI_SAVINGS'] + if account_type in is_valid: + new_account = Account(account_type) + self.accounts.append(new_account) + return new_account + else: + raise ValueError("Invalid Account Type") + def numAccs(self): return len(self.accounts) def totalInterestEarned(self): - return sum([a.interestEarned() for a in self.accounts]) + return sum([a.interestEarnedDaily() for a in self.accounts]) + + def totalAssets(self): + return "Total Assets: " + _toDollars(sum([a.balance for a in self.accounts])) # This method gets a statement def getStatement(self): @@ -25,31 +33,27 @@ def getStatement(self): statement = "Statement for %s" % self.name for account in self.accounts: statement = statement + self.statementForAccount(account) - statement = statement + "\n\nTotal In All Accounts " + _toDollars(totalAcrossAllAccounts) + statement += "\n\nTotal In All Accounts " + _toDollars(totalAcrossAllAccounts) return statement def statementForAccount(self, account): accountType = "\n\n\n" - if account.accountType == CHECKING: + if account.accountType == 'CHECKING': accountType = "\n\nChecking Account\n" - if account.accountType == SAVINGS: + if account.accountType == 'SAVINGS': accountType = "\n\nSavings Account\n" - if account.accountType == MAXI_SAVINGS: + if account.accountType == 'MAXI_SAVINGS': accountType = "\n\nMaxi Savings Account\n" - transactionSummary = [self.withdrawalOrDepositText(t) + " " + _toDollars(abs(t.amount)) - for t in account.transactions] + transactionSummary = [t.action + " " + _toDollars(t.amount) for t in account.transactions] transactionSummary = " " + "\n ".join(transactionSummary) + "\n" totalSummary = "Total " + _toDollars(sum([t.amount for t in account.transactions])) return accountType + transactionSummary + totalSummary - def withdrawalOrDepositText(self, transaction): - if transaction.amount < 0: - return "withdrawal" - elif transaction.amount > 0: - return "deposit" - else: - return "N/A" - + def makeTransfer(self,amount,account_to,account_from): + deposit_to = account_to.deposit(amount) + withdraw_from = account_from.withdraw(amount) + return self.getStatement() def _toDollars(number): return "${:1.2f}".format(number) + diff --git a/abcbank/transaction.py b/abcbank/transaction.py index 8e5b5ad..0df6f1c 100644 --- a/abcbank/transaction.py +++ b/abcbank/transaction.py @@ -2,6 +2,8 @@ class Transaction: - def __init__(self, amount): + def __init__(self,amount,action): self.amount = amount - self.transactionDate = datetime.now() \ No newline at end of file + self.transactionDate = datetime.now() + self.action = action + diff --git a/bank_tests.py b/bank_tests.py new file mode 100644 index 0000000..5524cfe --- /dev/null +++ b/bank_tests.py @@ -0,0 +1,52 @@ +from nose.tools import assert_equals +from abcbank.account import Account +from abcbank.bank import Bank +from abcbank.customer import Customer +from abcbank.transaction import Transaction + + +def test_customer_summary(): + bank = Bank() + john = bank.addCustomer('John') + john_new_checking = john.openAccount('CHECKING') + assert_equals(bank.customerSummary(),"Customer Summary\n - John (1 account)") + +def test_checking_account(): + bank = Bank() + bill = bank.addCustomer("Bill") + bill_new_checking = bill.openAccount('CHECKING') + bill_new_checking.deposit(100.00) + bill_new_checking.account_age_in_days = 20 + assert_equals(round(bank.totalInterestPaid(),3), .005) + +def test_savings_account(): + bank = Bank() + bill = bank.addCustomer("Bill") + bill_new_maxi = bill.openAccount('MAXI_SAVINGS') + bill_new_maxi.deposit(1500.0) + bill_new_maxi.account_age_in_days = 10 + assert_equals(round(bill_new_maxi.interestEarnedDaily(),2),1.9) + +def test_maxi_savings_account(): + bank = Bank() + bill = bank.addCustomer("Bill") + bill_new_maxi = bill.openAccount('MAXI_SAVINGS') + bill_new_maxi.deposit(3000.0) + +def test_transfer(): + bank = Bank() + bill = bank.addCustomer("Bill") + bill_savings = bill.openAccount('SAVINGS') + bill_checking = bill.openAccount('CHECKING') + bill_savings.deposit(200.0) + bill_checking.deposit(100.0) + bill.makeTransfer(50.0,bill_savings,bill_checking) + assert_equals(bill_checking.balance,50.0) + assert_equals(bill_savings.balance,250.0) + +if __name__ == '__main__': + test_customer_summary() + test_checking_account() + test_savings_account() + test_maxi_savings_account() + test_transfer() \ No newline at end of file diff --git a/customer_tests.py b/customer_tests.py new file mode 100644 index 0000000..c6c0901 --- /dev/null +++ b/customer_tests.py @@ -0,0 +1,49 @@ +from nose.tools import assert_equals, nottest +from abcbank.bank import Bank +from abcbank.account import Account +from abcbank.customer import Customer +from abcbank.transaction import Transaction + + +def test_statement(): + bank = Bank() + henry = bank.addCustomer("Henry") + henry_checking=henry.openAccount('CHECKING') + henry_savings = henry.openAccount('SAVINGS') + henry_checkings.deposit(100.0) + henry_savings.deposit(4000.0) + henry_savings.withdraw(200.0) + assert_equals(henry.getStatement(), + "Statement for Henry" + + "\n\nChecking Account\n deposit $100.00\nTotal $100.00" + + "\n\nSavings Account\n deposit $4000.00\n withdrawal $200.00\nTotal $3800.00" + + "\n\nTotal In All Accounts $3900.00") + + +def test_oneAccount(): + bank = Bank() + oscar = bank.addCustomer("Oscar") + oscar_savings = oscar.openAccount('SAVINGS') + assert_equals(oscar.numAccs(), 1) + +def test_twoAccounts(): + bank = Bank() + oscar = bank.addCustomer("Oscar") + oscar_savings = oscar.openAccount('SAVINGS') + oscar_checking = oscar.openAccount('CHECKING') + assert_equals(oscar.numAccs(), 2) + +@nottest +def test_threeAccounts(): + bank = Bank() + oscar = bank.addCustomer("Oscar") + oscar_savings = oscar.openAccount('SAVINGS') + oscar_checking = oscar.openAccount('CHECKING') + oscar_checking = oscar.openAccount('MAXI_SAVINGS') + assert_equals(oscar.numAccs(), 3) + +if __name__ == '__main__': + test_oneAccount() + test_twoAccounts() + test_threeAccounts() + diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/bank_tests.py b/tests/bank_tests.py deleted file mode 100644 index 6de98db..0000000 --- a/tests/bank_tests.py +++ /dev/null @@ -1,38 +0,0 @@ -from nose.tools import assert_equals - -from account import Account, CHECKING, MAXI_SAVINGS, SAVINGS -from bank import Bank -from customer import Customer - - -def test_customer_summary(): - bank = Bank() - john = Customer("John").openAccount(Account(CHECKING)) - bank.addCustomer(john) - assert_equals(bank.customerSummary(), - "Customer Summary\n - John (1 account)") - - -def test_checking_account(): - bank = Bank() - checkingAccount = Account(CHECKING) - bill = Customer("Bill").openAccount(checkingAccount) - bank.addCustomer(bill) - checkingAccount.deposit(100.0) - assert_equals(bank.totalInterestPaid(), 0.1) - - -def test_savings_account(): - bank = Bank() - checkingAccount = Account(SAVINGS) - bank.addCustomer(Customer("Bill").openAccount(checkingAccount)) - checkingAccount.deposit(1500.0) - assert_equals(bank.totalInterestPaid(), 2.0) - - -def test_maxi_savings_account(): - bank = Bank() - checkingAccount = Account(MAXI_SAVINGS) - bank.addCustomer(Customer("Bill").openAccount(checkingAccount)) - checkingAccount.deposit(3000.0) - assert_equals(bank.totalInterestPaid(), 170.0) \ No newline at end of file diff --git a/tests/customer_tests.py b/tests/customer_tests.py deleted file mode 100644 index 0211a4f..0000000 --- a/tests/customer_tests.py +++ /dev/null @@ -1,36 +0,0 @@ -from nose.tools import assert_equals, nottest - -from account import Account, CHECKING, SAVINGS -from customer import Customer - - -def test_statement(): - checkingAccount = Account(CHECKING) - savingsAccount = Account(SAVINGS) - henry = Customer("Henry").openAccount(checkingAccount).openAccount(savingsAccount) - checkingAccount.deposit(100.0) - savingsAccount.deposit(4000.0) - savingsAccount.withdraw(200.0) - assert_equals(henry.getStatement(), - "Statement for Henry" + - "\n\nChecking Account\n deposit $100.00\nTotal $100.00" + - "\n\nSavings Account\n deposit $4000.00\n withdrawal $200.00\nTotal $3800.00" + - "\n\nTotal In All Accounts $3900.00") - - -def test_oneAccount(): - oscar = Customer("Oscar").openAccount(Account(SAVINGS)) - assert_equals(oscar.numAccs(), 1) - - -def test_twoAccounts(): - oscar = Customer("Oscar").openAccount(Account(SAVINGS)) - oscar.openAccount(Account(CHECKING)) - assert_equals(oscar.numAccs(), 2) - - -@nottest -def test_threeAccounts(): - oscar = Customer("Oscar").openAccount(Account(SAVINGS)) - oscar.openAccount(Account(CHECKING)) - assert_equals(oscar.numAccs(), 3) \ No newline at end of file diff --git a/tests/transaction_tests.py b/tests/transaction_tests.py deleted file mode 100644 index 62caa8a..0000000 --- a/tests/transaction_tests.py +++ /dev/null @@ -1,8 +0,0 @@ -from nose.tools import assert_is_instance - -from transaction import Transaction - - -def test_type(): - t = Transaction(5) - assert_is_instance(t, Transaction, "correct type") \ No newline at end of file diff --git a/transaction_tests.py b/transaction_tests.py new file mode 100644 index 0000000..cebdd19 --- /dev/null +++ b/transaction_tests.py @@ -0,0 +1,21 @@ +from nose.tools import assert_is_instance,assert_equal +from abcbank.account import Account +from abcbank.customer import Customer +from abcbank.transaction import Transaction +from abcbank.bank import Bank + + +def test_withdrawal(): + t = Transaction(5,'withdrawal') + assert_is_instance(t, Transaction) + assert_equal(t.action, 'withdrawal') + assert_equal(t.amount, 5) + +def test_deposit(): + t = Transaction(5,'deposit') + assert_is_instance(t, Transaction) + assert_equal(t.action, 'deposit') + +if __name__ == '__main__': + test_withdrawal() + test_deposit()