Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added __init__.py
Empty file.
152 changes: 125 additions & 27 deletions abcbank/account.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,141 @@
from abcbank.transaction import Transaction

CHECKING = 0
SAVINGS = 1
MAXI_SAVINGS = 2

from abcbank.transaction import *
from datetime import datetime

class Account:
def __init__(self, accountType):
self.accountType = accountType
self.transactions = []
#Abstract class shouldn't be created via constructor
def __init__(self):
raise NotImplementedError("Must create a specific account type")

#deposit
def deposit(self, amount):
if (amount <= 0):
raise ValueError("amount must be greater than zero")
else:
self.transactions.append(Transaction(amount))
self.pending.append(Transaction(amount))
self.transactionTotal += amount

#withdraw, but ensure that account has enough money
def withdraw(self, amount):
self.updateBalance()
if (amount <= 0):
raise ValueError("amount must be greater than zero")
elif (self.balance < amount):
raise ValueError("amount must not exceed current account balance")
else:
self.transactions.append(Transaction(-amount))
self.pending.append(Transaction(-amount))
self.transactionTotal -= amount

#calculate interest at current balance for given number of days.
#since account types are different this gets implemented separately for each account type
def calculateInterest(self, numdays):
raise NotImplementedError("Interest rate not implemented")

#Update balance while calculating interest according to transaction dates
def updateBalance(self):
if len(self.pending) == 0:
return self.balance
else:
days = 0
transaction = self.pending.pop(0)
if len(self.history) != 0:
days = (transaction.transactionDate - self.history[len(self.history) - 1].transactionDate).days
self.history.append(transaction)
self.balance += (self.calculateInterest(days, transaction.transactionDate) + transaction.amount)
return self.updateBalance()

#Total amount of interest earned (unnecessary, but is here due to confusion of what this function was meant to originally do)
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
else:
return 70 + (amount - 2000) * 0.1
self.updateBalance()
return self.balance - self.transactionTotal

#Account type to string, implemented in sub-classes
def accountString(self):
raise NotImplementedError("Account type string not implemented")

#Yearly interest, this is what "interestEarned" used to be
def yearlyInterest(self):
self.updateBalance()
return self.calculateInterest(365, datetime.now())

#Format account statement
def accountStatement(self):
self.updateBalance()
header = self.accountString()
transactions = "\nNone\n"
total = "Total " + toDollars(self.balance) + "\n"

if (len(self.history) != 0):
i = len(self.history) - 1
transactions = "\n"
while i >= 0:
transactions += " " + self.history[i].getType() + " " + toDollars(abs(self.history[i].amount)) + "\n"
i -= 1

return header + transactions + total

class CheckingAcc(Account):
def __init__(self):
self.balance = 0
self.transactionTotal = 0
self.history = []
self.pending = []

def calculateInterest(self, numdays, date):
yearPart = numdays/365
return round(self.balance * (pow(1.001, yearPart) - 1), 4)

def accountString(self):
return "\n\nChecking Account\n"

class SavingsAcc(Account):
def __init__(self):
self.balance = 0
self.transactionTotal = 0
self.history = []
self.pending = []

def calculateInterest(self, numdays, date):
yearPart = numdays/365
return round(min(1000,self.balance) * (pow(1.001, yearPart) - 1) + max(self.balance - 1000, 0) * (pow(1.002, yearPart) - 1), 4)

def accountString(self):
return "\n\nSavings Account\n"

class MaxiSavingsAcc(Account):
def __init__(self):
self.balance = 0
self.transactionTotal = 0
self.lastWithdrawal = None
self.history = []
self.pending = []

def withdraw(self, amount):
self.updateBalance()
if (amount <= 0):
raise ValueError("amount must be greater than zero")
elif (self.balance < amount):
raise ValueError("amount must not exceed current account balance")
else:
transaction = Transaction(-amount)
self.pending.append(transaction)
self.lastWithdrawal = transaction.transactionDate
self.transactionTotal -= amount

def calculateInterest(self, numdays, date):
if self.lastWithdrawal == None:
yearPart = numdays/365
return round(self.balance * (pow(1.05, yearPart) - 1), 4)

sinceWithdrawal = (date - self.lastWithdrawal).days

if ((sinceWithdrawal - numdays) > 10):
yearPart = numdays/365
return round(self.balance * (pow(1.05, yearPart) - 1), 4)
else:
return amount * 0.001
yearPart1 = min(numdays, (numdays + 10 - sinceWithdrawal))/365
yearPart2 = numdays/365 - yearPart1
return round(self.balance * (pow(1.001, yearPart1) - 1) + self.balance * (pow(1.05, yearPart2) - 1), 4)

def accountString(self):
return "\n\nMaxi Savings Account\n"

def sumTransactions(self, checkAllTransactions=True):
return sum([t.amount for t in self.transactions])
9 changes: 8 additions & 1 deletion abcbank/bank.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@ def totalInterestPaid(self):
for c in self.customers:
total += c.totalInterestEarned()
return total

def totalYearlyInterest(self):
total = 0
for c in self.customers:
total += c.customerYearlyInterest()
return total

def getFirstCustomer(self):
try:
self.customers = None
return self.customers[0].name
except Exception as e:
print(e)
return "Error"
return "Error"
52 changes: 23 additions & 29 deletions abcbank/customer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from account import CHECKING, SAVINGS, MAXI_SAVINGS
from abcbank.account import *
from abcbank.transaction import *


class Customer:
Expand All @@ -16,40 +17,33 @@ def numAccs(self):
def totalInterestEarned(self):
return sum([a.interestEarned() for a in self.accounts])

def depositTo(self, index_to, amount):
if (index_to >= len(self.accounts)):
raise ValueError("Invalid account index")
self.accounts[index_to].deposit(amount)

def withdrawFrom(self, index_from, amount):
if (index_from >= len(self.accounts)):
raise ValueError("Invalid account index")
self.accounts[index_from].withdraw(amount)

def customerYearlyInterest(self):
return sum([a.yearlyInterest() for a in self.accounts])

# This method gets a statement
def getStatement(self):
# JIRA-123 Change by Joe Bloggs 29/7/1988 start
statement = None # reset statement to null here
# JIRA-123 Change by Joe Bloggs 29/7/1988 end
totalAcrossAllAccounts = sum([a.sumTransactions() for a in self.accounts])
totalAcrossAllAccounts = sum([a.updateBalance() for a in self.accounts])
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 = statement + account.accountStatement()
statement = statement + "\n\nTotal In All Accounts " + toDollars(totalAcrossAllAccounts)
return statement

def statementForAccount(self, account):
accountType = "\n\n\n"
if account.accountType == CHECKING:
accountType = "\n\nChecking Account\n"
if account.accountType == SAVINGS:
accountType = "\n\nSavings Account\n"
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 = " " + "\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 _toDollars(number):
return "${:1.2f}".format(number)
def transfer(self, index_from, index_to, amount):
if ((index_from >= len(self.accounts)) or (index_to >= len(self.accounts))):
raise ValueError("Invalid account index")
self.accounts[index_from].withdraw(amount)
self.accounts[index_to].deposit(amount)
13 changes: 12 additions & 1 deletion abcbank/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,15 @@
class Transaction:
def __init__(self, amount):
self.amount = amount
self.transactionDate = datetime.now()
self.transactionDate = datetime.now()

def getType(self):
if self.amount < 0:
return "withdrawal"
elif self.amount > 0:
return "deposit"
else:
return "N\A"

def toDollars(number):
return "${:1.2f}".format(number)
74 changes: 74 additions & 0 deletions tests/account_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from nose.tools import assert_equals, raises, assert_true
import sys

sys.path.append('../abcbank')

from abcbank.account import *

@raises(NotImplementedError)
def test_constructor():
a = Account()

@raises(ValueError)
def test_deposit0():
a = CheckingAcc()
a.deposit(0)

@raises(ValueError)
def test_withdraw0():
a = CheckingAcc()
a.withdraw(0)

@raises(ValueError)
def test_withdraw_limit():
a = CheckingAcc()
a.withdraw(5)

def test_deposit():
a = SavingsAcc()
a.deposit(5)
assert_equals(a.updateBalance(), 5)

def test_withdraw():
a = SavingsAcc()
a.deposit(5)
a.withdraw(4)
assert_equals(a.updateBalance(), 1)

def test_checking_interest():
a = CheckingAcc()
a.deposit(1000)
a.updateBalance()
interest = a.calculateInterest(365, None)
assert_equals(interest, 1)

def test_checking_yearly_interest():
a = CheckingAcc()
a.deposit(1000)
interest = a.yearlyInterest()
assert_equals(interest, 1)

def test_savings_yearly_interest1():
a = SavingsAcc()
a.deposit(1000)
interest = a.yearlyInterest()
assert_equals(interest, 1)

def test_savings_yearly_interest2():
a = SavingsAcc()
a.deposit(2000)
interest = a.yearlyInterest()
assert_equals(interest, 3)

def test_maxi_yearly_interest1():
a = MaxiSavingsAcc()
a.deposit(1000)
interest = a.yearlyInterest()
assert_equals(interest, 50)

def test_maxi_yearly_interest2():
a = MaxiSavingsAcc()
a.deposit(1100)
a.withdraw(100)
interest = a.yearlyInterest()
assert_true(interest < 50)
Loading