Skip to content
198 changes: 175 additions & 23 deletions abcbank/account.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,195 @@
from abcbank.transaction import Transaction
from transaction import Transaction
from abc import abstractmethod
from date_provider import DateProvider
import datetime

CHECKING = 0
SAVINGS = 1
MAXI_SAVINGS = 2
"""
Changes:
1. added acctType data-type to identify account type
2. created savings, checking, and maxi classes that inherit parent class account
3. overrode interest_earned() method in each class, decorated as an abstract method in Account
4. added logic to handle daily interest, this is handled by changing the sum_transaction method
5. also added logic to recognize when a year has past and the customer gets year interest on the Maxi account
6. Added test interest earned method in order to test interest calculations without considering DateProvider.now()
7. Interest rate accrual now occurs on a daily basis along with year returns from the MAxi Savings Account
"""


class Account:
def __init__(self, accountType):
self.accountType = accountType
class Account(object):
def __init__(self):
self.transactions = []
self.type = "Default"

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

def withdraw(self, amount):
if (amount <= 0):
if amount <= 0:
raise ValueError("amount must be greater than zero")
else:
self.transactions.append(Transaction(-amount))

@abstractmethod
def interestEarned(self):
amount = self.sumTransactions()
if self.accountType == SAVINGS:
if (amount <= 1000):
return amount * 0.001
"""
To be implemented in subclass
"""

def sum_transactions(self, checkAllTransactions=True, num_days=1):
"""
Essentially returns sum of transactions across all accounts, if not checking all transactions will check total
in the past num_days (mainly for interest accural)
num_days is defaulted to 1 day
"""
total = 0

if checkAllTransactions:
return sum([t.amount for t in self.transactions])
else:
for trans in self.transactions:
if DateProvider.now() - trans.transactionDate <= datetime.timedelta(days=num_days):
total += trans.amount
return total

class SavingsAccount(Account):
# rate changes can be made here (immutable types)
interest_rates = (.001, .002)
rate_thresholds = (1000,)

def __init__(self):
super(SavingsAccount, self).__init__()
self.type = "Savings Account"
self.interest_timer = DateProvider.now()

def interestEarned(self):
"""
returns --> total interest earned from this savings account
"""
amount = self.sum_transactions()
now = DateProvider.now()

if now - self.interest_timer > datetime.timedelta(days=1):
if amount <= SavingsAccount.rate_thresholds:
self.interest_timer = DateProvider.now()
return amount * SavingsAccount.interest_rates[0]

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
self.interest_timer = DateProvider.now()
return 1 + (amount - SavingsAccount.rate_thresholds[0]) * SavingsAccount.interest_rates[1]

else:
return 0

def testInterestEarned(self):
"""
This method is used to test interest calculations not dependent on time
"""
amount = self.sum_transactions()
if amount <= SavingsAccount.rate_thresholds[0]:
self.interest_timer = DateProvider.now()
return amount * SavingsAccount.interest_rates[0]

else:
self.interest_timer = DateProvider.now()
return 1 + (amount - SavingsAccount.rate_thresholds[0]) * SavingsAccount.interest_rates[1]


class CheckingAccount(Account):
# rate changes can be made here (immutable types)
interest_rates = (.001,)

def __init__(self):
super(CheckingAccount, self).__init__()
self.type = "Checking Account"
self.interest_timer = DateProvider.now()

def interestEarned(self):
"""
returns --> total interest earned from this checking account per year
"""
# daily accrual of interest
if DateProvider.now() - self.interest_timer > datetime.timedelta(days=1):
self.interest_timer = DateProvider.now() # reset timer
return self.sum_transactions(False) * CheckingAccount.interest_rates
else:
return 0

def testInterestEarned(self):
"""
To test interest earned on all transaction not time dependent
"""
return self.sum_transactions(True) * CheckingAccount.interest_rates

class MaxiSavings(Account):

# cannot be change
rate_thresholds = (1000, 2000)

def __init__(self):
super(MaxiSavings, self).__init__()
self.type = "Maxi Savings Account"
self.interest_rates = [.02, .05, .1]
self.interest_timer = DateProvider.now()
self.interest_timer_daily = DateProvider.now()
self.interest_timer_tenDay = DateProvider.now()

def interestEarned(self):
"""
Note: rates can be adjusted by changing class variables interest_Rates and rate_thresholds
returns --> interest earned from the Maxi Savings account
"""
amount = self.sum_transactions(False, 365)

# yearly interest rate accrual
if DateProvider.now() - self.interest_timer > datetime.timedelta(days=365):
if amount <= self.rate_thresholds[0]:
self.interest_timer = DateProvider.now()
return amount * self.interest_rates[0]

elif amount < MaxiSavings.rate_thresholds[1]:
self.interest_timer = DateProvider.now()
return 20 + (amount - MaxiSavings.rate_thresholds[0]) * self.interest_rates[1]

else:
self.interest_timer = DateProvider.now()
return 70 + (amount - MaxiSavings.rate_thresholds[1]) * self.interest_rates[2]

# checking for daily interest
amount = self.sum_transactions(False)
if datetime.timedelta(days=1) < DateProvider.now() - self.interest_timer_daily < datetime.timedelta(days=365):
if self.interest_timer_tenDay + datetime.timedelta(days=1) > datetime.timedelta(days=10):
self.interest_timer_daily = DateProvider.now()
return amount * .05
else:
return 70 + (amount - 2000) * 0.1
self.interest_timer_daily = DateProvider.now()
return amount * .001
else:
return 0

def test_interest_earned(self):
"""
this method removes the time dependency just to check interest calculations
"""
amount = self.sum_transactions()
if amount <= self.rate_thresholds[0]:
self.interest_timer = DateProvider.now()
return amount * self.interest_rates[0]

elif amount < MaxiSavings.rate_thresholds[1]:
self.interest_timer = DateProvider.now()
return 20 + (amount - MaxiSavings.rate_thresholds[0]) * self.interest_rates[1]

else:
return amount * 0.001
self.interest_timer = DateProvider.now()
return 70 + (amount - MaxiSavings.rate_thresholds[1]) * self.interest_rates[2]


def sumTransactions(self, checkAllTransactions=True):
return sum([t.amount for t in self.transactions])
def set_interest_rate(self, ratesList):
"""
ratesList --> list of three interest rates
allows for dynamic changing of account interest rates
"""
MaxiSavings.interest_rates = ratesList
70 changes: 57 additions & 13 deletions abcbank/bank.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,69 @@
from customer import Customer


class Bank:
"""
Changes made:
1. spaced out functions
2. type check in addCustomer method (raising typeError)
3. imported Customer class
4. instantiated customers with list() instead of []
5. refactored methods to conform to PEP8 (lowercase and underscores)
6. made _format() static (can safely due this)
7. deleted self.customers = None in get_first_customer()
8. changed argument name in _format() for clarity of use
9. deleted total = 0 in total interest paid, allows this method to be called multiple times
10. added a clear_interest_paid() method to clear out interest paid record
"""

def __init__(self):
self.customers = []
self.customers = list()
self.interest_paid = 0

def addCustomer(self, customer):
def add_customer(self, customer):
"""
Customer can be a list of customers
"""
# check customer is a customer object
if not isinstance(customer, Customer):
raise TypeError(str(customer) + " is not a customer object")
self.customers.append(customer)
def customerSummary(self):

def customer_summary(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.num_accounts(), "account") + ")"
return summary
def _format(self, number, word):
return str(number) + " " + (word if (number == 1) else word + "s")
def totalInterestPaid(self):
total = 0

@staticmethod
def _format(number, type_acct):
"""
Descr: Used internal to class as a text formatting helper function
number: number of customer accounts
word: type of account
"""
return str(number) + " " + (type_acct if (number == 1) else type_acct + "s")

def total_interest_paid(self):
"""
Descr: keeps a running total of interest paid to customers until cleared
"""
for c in self.customers:
total += c.totalInterestEarned()
return total
def getFirstCustomer(self):
self.interest_paid += c.total_interest_earned()

print("Interest paid out by the bank to Customers....")
return self.interest_paid

def clear_interest_paid(self):
"""
Descr: zero out interest paid to customers
"""
self.interest_paid = 0
print("Record of interest paid out to customers cleared....")

def get_first_customer(self):
try:
self.customers = None
return self.customers[0].name
except Exception as e:
print(e)
return "Error"
return "Error no customers on file"
Loading