Skip to main content
3 of 6
edited tags; edited title
200_success
  • 145.6k
  • 22
  • 191
  • 481

OOP bank account program in Python 3

Python beginner here. Building an OOP bank account program with SQLite and starting to struggle a bit with its design. From a design standpoint I have a file (bank) which calls ATM. ATM simulates an ATM, and is the file which then calls bank_account or credit_card depending on the account data passed in from bank.

To initially setup an account I decided to put this into a different file, for example bank_account_setup or for credit_card, credit_card_setup. These would create_account, help setup pin etc so that the account is created and ready to use. Then the actual bank_account or credit_card contains other functions, like, deposit, withdraw, get_balance etc. Also, send_email is in another file

My question is basically around my design. Is there a way to structure this better? How about my setup files to create bank or credit card accounts? Is that a good or bad idea? Also, another issue I am having is that when I run bank I pass in account type to ATM. In ATM I then had to know what class I am using in advance and instantiate that inside ATM. Could I handle that dynamically? (Also, the code does work - just concerned with bad design).

Here's my calling file bank:

import atm
        
class BankAccount:

    def __init__(self, name, social, account_number, balance, acctype):
        self.name = name
        self.social = social
        self.account_number = account_number
        self.balance = balance
        self.acctype = acctype

if __name__ == '__main__':

    obj1 = atm.ATM.main_menu( "Frank Smith", 135063522, 5544, 850, 'credit_card', 4400110022004400)

Here's ATM, which calls the other files:

import sqlite3, smtplib
import bank_account
import secrets
import send_email
import credit_card
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders

conn = sqlite3.connect('bank_account.db')
c = conn.cursor()

class ATM:

    def get_pin(account_number, acctype, user_pin):
        with conn:
            db_pin =  c.execute("SELECT pin from {} WHERE account_number=:account_number".format(acctype),
                      {'account_number':account_number})
            db_pin = c.fetchone()
            if db_pin is not None:
                return(db_pin[0])
            else:
                print("db pin is None")
                pass

    def set_pin(account_number, acctype, input_code):
        with conn:
            get_code = ATM.get_user_code(account_number, acctype)
            if get_code is None:
                pass
                print("You need to request an authorization code first before you set your pin")
            else:    
                if get_code !=input_code:
                    print("Authorization code not valid")
                else:    
                    pin = input("Please set your 4 digit pin: ")
                    if len(pin) < 4 or len(pin) >4 or len(pin) == 4 and pin.isdigit()==False:
                        print("This is not a 4 digit pin")
                    else:
                        print("pin accepted")
                        c.execute("""UPDATE {} SET pin=:pin
                                WHERE account_number =:account_number""".format(acctype),
                                  {'account_number':account_number, 'pin':pin})
                        print("Pin for account has been updated")


    def main_menu(name, social, account_number, balance, acctype, card_no=None):
        
#        obj1 =  bank_account.BankAccount(name, social, account_number, balance, acctype)    
#       obj1 =  credit_card.CreditCard(name, social, account_number, balance, acctype, card_no)         
        user_pin = int(input("\nATM Home Screen. Please enter your pin code: "))
        db_pin = ATM.get_pin(account_number, acctype, user_pin)
        if user_pin != db_pin and db_pin != '':
            print("No pin match")
        elif db_pin is '':
            print("Pin has not been set")
            print("First request an authorization code and use that to set the pin")
        else:
            user_pin == db_pin
            print("\nPin accepted continue \n ")
            print("""""""ATM Menu, choose an option""""""")  
            print("\n1 - Deposit funds")
            print("2 - Withdraw funds")
            print("3 - Check balance")
            print("4 - Reset Pin")
            print("5 - Exit")
            
            while True:
                try:
                    choice = int(input("Please enter a number: "))
                except ValueError:
                    print("This is not a number")
                if choice >= 1 and choice <=5:
                    if choice == 1:
                        amount = input("\nPlease enter the deposit amount: ")
                        if amount != '' and amount.isdigit():
                            int(amount)
                            obj1.deposit( account_number, acctype, amount)
                        else:
                            print("Please enter a valid number")
                            
                    elif choice == 2:
                        amount = input("Please enter the withdrawl amount: ")
                        if amount != '' and amount.isdigit():
                            int(amount)
                            obj1.withdraw(account_number, acctype, amount)
                        else:
                            print("Please enter a valid number")
                            
                    elif choice ==3:
                        obj1.get_balance(account_number, acctype)
                        
                    elif choice ==4:
                        new_pin = input("Please enter a new 4 digit pin: ")
                        if new_pin != '' and new_pin.isdigit():
                            int(new_pin)
                        obj1.set_reset_pin(account_number, acctype, new_pin)
                        
                    elif choice ==5:
                        break
                                
            else:
                print("Not a valid number")

A piece of bank_account_setup:

import sqlite3
import secrets
import getpass
import smtplib, sqlite3
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import send_email

conn = sqlite3.connect('bank_account.db')
c = conn.cursor()        

class BankAccount:

    def __init__(self, name, social, account_number, balance, acctype):
        self.name = name
        self.social = social
        self.account_number = account_number
        self.balance = balance
        self.acctype = acctype
        
    """ create different accounts based on account type passed in """         
    def create_account(self, name, social, account_number, balance, acctype, card_no=None, credit_score=None, credit_limit=None):
        self.rate = None
        with conn:
#            account_found = BankAccount.get_account(self, account_number, acctype)   
#            if not account_found:
            if acctype == 'bank_account':
                c.execute("INSERT INTO {} VALUES (:name, :social, :account_number, :balance, :pin)".format(acctype),
                          {'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'pin':''})  
                print("New account: {} has been created, acc # is: {}".format(acctype, account_number))
                
            elif acctype == 'savings_account':
                c.execute("INSERT INTO {} VALUES (:name, :social, :account_number, :balance, :rate)".format(acctype),
                      {'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'rate':''})
                print("New account: {} has been created, acc # is: {}".format(acctype, account_number))
                
            elif acctype == 'credit_card':
                c.execute("INSERT INTO credit_card VALUES (:name, :social, :account_number, :balance, :card_no,:credit_score, :credit_limit, :pin)",
                  {'name':name, 'social': social,'account_number': account_number, 'balance':balance, 'card_no'
                   :card_no, 'credit_score':credit_score, 'credit_limit':credit_limit, 'pin':'' })
                print("New account: {} has been created, acc # is: {}".format(acctype, account_number))                                  
        conn.commit()

Piece of bank_account:

import sqlite3
import secrets
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import send_email

conn = sqlite3.connect('bank_account.db')
c = conn.cursor()        

class BankAccount:

    def __init__(self, name, social, account_number, balance, acctype):
        self.name = name
        self.social = social
        self.account_number = account_number
        self.balance = balance
        self.acctype = acctype
        

    def get_balance(self, account_number, acctype):
        with conn:
            balance = c.execute("SELECT balance from {} WHERE account_number=:account_number".format(acctype),
                                {'account_number':account_number})
            balance = c.fetchone()
            print("The balance for account number: {} is ${}".format(account_number, balance[0]))
            notif_set = BankAccount.get_notif(self, account_number, acctype)
            if notif_set is None:
                print("No notifications are set for this user")
            else:    
                notif_balance = notif_set[4]
                name = notif_set[0]
                if notif_balance == 1:
                    notify = send_email.send_email(account_number, acctype, 'Balance', balance, balance, name)
        
        return(balance[0])
    
    """ Deposit funds into the account number + acctype for the account passed in """
    def deposit(self, account_number, acctype, amount):
        with conn:

            """ Check acct exists before making deposit """
            account_found = BankAccount.get_account(self, account_number, acctype)
            if account_found:
                existing_bal = account_found[3]
                c.execute("""UPDATE {} SET balance=balance +:amount
                        WHERE account_number =:account_number""".format(acctype),
                          {'account_number':account_number, 'amount':amount})
                new_bal = existing_bal + (int(amount))
                print("${} has been deposited to account {} and the new balance is ${}".format(amount, account_number, existing_bal + (int(amount))))

               # Check email configurations are turned on for deposits 
                notif_set = BankAccount.get_notif(self, account_number, acctype)
                if notif_set is None:
                    print("No notifications are set for this user")
                else:    
                    notif_deposits = notif_set[5]
                    name = notif_set[0]
                    if notif_deposits == 1:
                        notify = send_email.send_email(account_number, acctype, 'Deposit', amount, new_bal, name)

Piece of credit_card:

from bank_account import BankAccount
import sqlite3

conn = sqlite3.connect('bank_account.db')
c = conn.cursor()     
        
class CreditCard(BankAccount):
    def __init__(self, name, social, account_number, balance, acctype, card_no, credit_score=None, credit_limit=None):
        super().__init__(name, social, account_number, balance, acctype)
        self.card_no = card_no
        self.credit_score = credit_score
        self.credit_limit = credit_limit

    """ set credit limit, check if acct exists, then call get credit limit """
    def set_credit_limit(self, account_number, acctype, credit_score):
        with conn:
            account_found = BankAccount.get_account(self, account_number, acctype)
            if account_found:
                credit_limit = CreditCard.set_credit_limit_helper(self, account_number, credit_score)
                if credit_limit:
                    c.execute("""UPDATE credit_card SET credit_limit=:credit_limit
                            WHERE account_number =:account_number """,
                              {'account_number':account_number, 'credit_limit':credit_limit})
                    print("Account number {} credit limit is set to {}".format(account_number, credit_limit))    
            conn.commit()    
                
    def withdraw(self, account_number, acctype, amount):
        with conn:
            account_found = BankAccount.get_account(self, account_number, acctype)
            if account_found:
                balance = account_found[3]
                credit_limit  = CreditCard.get_credit_limit(self, account_number)
                amount_left = credit_limit - (int(balance))
                
                if balance - (int(amount)) < credit_limit:
                     print("Your balance is: {}, and your credit limit is: {}".format(balance, credit_limit))
                     print("The max you can withdraw is {}".format(amount_left))
                else:    
                    existing_bal = account_found[3]
                    c.execute("""UPDATE credit_card SET balance=balance -:amount
                            WHERE account_number =:account_number""",
                              {'account_number':account_number, 'amount':amount})
                    print("${} has been withdrawn from account {} and the new balance is ${}".format(amount, account_number, existing_bal - (int(amount))))

                    notif_set = BankAccount.get_notif(self, account_number, acctype)
                    if notif_set is None:
                        print("No notifications have been set for this acct")
                    else:
                        notif_withdraw = notif_set[7]
                        if notif_withdraw == 1:
                            notify = BankAccount.send_email(self,account_number, acctype, 'Withdraw', amount, existing_bal - amount)
            conn.commit()
anfield
  • 173
  • 1
  • 6