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
82 changes: 75 additions & 7 deletions abcbank/account.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,111 @@
from abcbank.transaction import Transaction
from datetime import datetime, timedelta
from abcbank.transaction import Transaction, DEPOSIT, WITHDRAWL

CHECKING = 0
SAVINGS = 1
MAXI_SAVINGS = 2

ACCT_TYPE_NAME = {
CHECKING: "Checking Account",
SAVINGS: "Savings Account",
MAXI_SAVINGS: "Maxi Savings Account",
}

class Account(object):
''' An Account belongs to a Customer.
'''

class Account:
def __init__(self, accountType):
'''Create an Account.

:param int accountType: an account type, e.g. account.CHECKING
'''
self.accountType = accountType
self.transactions = []

def deposit(self, amount):
''' Deposit `amount` dollars into this account.
The `amount` is a positive dollar value taken from the customer.

:param float amount: deposit amount in USD
'''
if (amount <= 0):
raise ValueError("amount must be greater than zero")
raise ValueError("deposit amount must be greater than zero")
else:
self.transactions.append(Transaction(amount))

def withdraw(self, amount):
''' Withdraw `amount` dollars from this account.
The `amount` is a positive dollar value given to the customer.

:param float amount: withdraw amount in USD
'''
if (amount <= 0):
raise ValueError("amount must be greater than zero")
raise ValueError("withdraw amount must be greater than zero")
else:
self.transactions.append(Transaction(-amount))

def interestEarned(self):
''' Calculate the interest earned on all transactions.
This computes interest on all deposits and withdrawls during the period.

:returns: interest earned in USD
'''
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:
elif self.accountType == MAXI_SAVINGS:
# NOTE: This was the former maxi-savings plan
"""
if (amount <= 1000):
return amount * 0.02
elif (amount <= 2000):
return 20 + (amount - 1000) * 0.05
else:
return 70 + (amount - 2000) * 0.1
else:
"""
# Give a lower rate when withdrawls were made in the last 10 days.
if self.transactionHistory(10, transType=WITHDRAWL):
return amount * 0.001
else:
return amount * 0.05
elif self.accountType == CHECKING:
return amount * 0.001
else:
raise ValueError("account has an invalid account type")

def sumTransactions(self, checkAllTransactions=True):
return sum([t.amount for t in self.transactions])
''' Computes all deposits and withdrawls during the period.

:returns: current monies without interest in USD
'''
return sum([t.amount for t in self.transactions])

def accountTypeText(self):
''' Give an account type description text.

:returns: a string for the account type name
'''
accountType = ""
if self.accountType in ACCT_TYPE_NAME:
accountType = ACCT_TYPE_NAME[self.accountType]
return accountType

def transactionHistory(self, daysOld, transType=None):
''' Account Transaction history.

:param int daysOld: find transaction that are no more than N days old
:param transType: a Transaction type code, e.g. transaction.DEPOSIT
'''
transList = []
# Filter out older older transactions.
now = datetime.now()
transList = [ v for v in self.transactions if (now - v.transactionDate) <= timedelta(days=daysOld) ]
# Filter out transaction of the right type.
if transType:
transList = [ v for v in self.transactions if v.eventType() == transType ]
return transList

39 changes: 25 additions & 14 deletions abcbank/bank.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,36 @@
class Bank:
class Bank(object):
''' A Bank branch has one or more Customers.
'''

def __init__(self):
''' Create a bank branch.
'''
self.customers = []

def addCustomer(self, customer):
''' Add a new customer to the branch.

:param Customer customer: add a new Customer object
'''
self.customers.append(customer)

def customerSummary(self):
summary = "Customer Summary"
for customer in self.customers:
summary = 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")
''' List a summary of customers who have accounts at this branch.

:returns: a customer summary string
'''
summary = "Customer Summary\n - "
return summary + "\n - ".join([ str(customer) for customer in self.customers ])

def totalInterestPaid(self):
''' Calculate the total interest paid by the bank during this period.

:returns: total interest in USD
:rtype: float
'''
total = 0
for c in self.customers:
total += c.totalInterestEarned()
return total
def getFirstCustomer(self):
try:
self.customers = None
return self.customers[0].name
except Exception as e:
print(e)
return "Error"

# def getFirstCustomer(self): There is no use for this method, and it was buggy!
97 changes: 73 additions & 24 deletions abcbank/customer.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,104 @@
from account import CHECKING, SAVINGS, MAXI_SAVINGS
from abcbank.account import CHECKING, SAVINGS, MAXI_SAVINGS


class Customer:
class Customer(object):
''' A Customer of the bank.
'''

def __init__(self, name):
''' Create a new customer who has one or more accounts.
'''
self.name = name
self.accounts = []

def __str__(self):
return "{} ({} account{})".format(self.name, self.numAccs(),
('s' if self.numAccs() != 1 else ''))

def openAccount(self, account):
''' Open a new account for this customer.
The method returns this object rather than NoneType, like sorted() vs. sort().

:param account: a new Account object
:returns: this Customer object
'''
self.accounts.append(account)
return self

def numAccs(self):
''' Show the number of accounts this customer has.

:returns: number of accounts
:rtype: int
'''
return len(self.accounts)

def totalInterestEarned(self):
''' Compute the total interest earned by this customer across all accounts.

:returns: total interest in USD
:rtype: float
'''
return sum([a.interestEarned() for a in self.accounts])

# This method gets a statement
def getStatement(self):
''' Get a bank statement for this customer.

:returns: a bank statement string
'''
# DELETE: These lines do nothing and 1988 is before Python ever existed!
# JIRA-123 Change by Joe Bloggs 29/7/1988 start
statement = None # reset statement to null here
## 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])
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)
accountStatements = [ self.statementForAccount(account) for account in self.accounts ]

statement = ("Statement for {}".format(self.name) + ''.join(accountStatements)
+ "\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))
''' Prepare a statement for a single account belonging to this customer.

:param Account account: pass the Account object
:returns: a statement string for the account
'''
transactionSummary = [t.eventText() + " " + _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
return "\n\n{}\n".format(account.accountTypeText()) + transactionSummary + totalSummary

def ownsAccount(self, account):
'''Does this customer own the `account`?

:parameter Account account: the account to check
:returns: True when this Customer owns the Account
'''
# Use the "is" operator to match the exact Account object.
# TODO: Replace this with a clear Account.id
return any([ account is v for v in self.accounts ])

def withdrawalOrDepositText(self, transaction):
if transaction.amount < 0:
return "withdrawal"
elif transaction.amount > 0:
return "deposit"
else:
return "N/A"
def transfer(self, fromAccount, toAccount, amount):
''' Transfer money from one account to another belonging to this customer.
The `amount` should be a postive value in USD.

:param fromAccount: transfer from this Account
:param toAccount: transfer to this Account
:param float amount: transfer this amount in USD
'''
if not (self.ownsAccount(fromAccount) and self.ownsAccount(toAccount)):
raise ValueError("Customer cannot transfer between those accounts")

fromAccount.withdraw(amount)
toAccount.deposit(amount)


def _toDollars(number):
''' Format an amount in USD to a printed USD string, e.g. 12.01 to "$12.01".

:param float number: amount in USD
:returns: a USD string to print
'''
return "${:1.2f}".format(number)
37 changes: 35 additions & 2 deletions abcbank/transaction.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
from datetime import datetime

DEPOSIT = "deposit"
WITHDRAWL = "withdrawal"

class Transaction(object):
''' A single Transaction on an account.

This expects the real numeric amount in USD.
A deposit is positive.
A withdrawl is negative.
'''

class Transaction:
def __init__(self, amount):
''' Create a new transaction for the amount.

:param float amount: amount deposited or withdrawn in USD
'''
self.amount = amount
self.transactionDate = datetime.now()
self.transactionDate = datetime.now()

def eventType(self):
''' Transaction event type code

:returns: event type string
'''
if self.amount < 0:
return WITHDRAWL
elif self.amount > 0:
return DEPOSIT
else:
return "N/A"

def eventText(self):
''' Transaction event text describes what happened.

:returns: event description string
'''
return self.eventType()

Loading