continue developing account page, start transaction pages

This commit is contained in:
Lucas Mathews
2024-05-29 15:38:25 +02:00
parent 01bbffa561
commit b70ba6ae2e
11 changed files with 238 additions and 32 deletions

6
.gitignore vendored
View File

@@ -1,4 +1,8 @@
flask_session flask_session/
application/__pycache__/
application/session_data.json
bank.db
test_database.db
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

View File

@@ -301,6 +301,13 @@ paths:
required: true required: true
schema: schema:
type: string type: string
- name: otp_code
in: query
description: OTP to verify
required: true
schema:
type: integer
format: int32
- name: description - name: description
in: query in: query
description: Account description description: Account description

View File

@@ -1,9 +1,17 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Account Page
import tkinter as tk import tkinter as tk
from tkinter import ttk, messagebox
import customtkinter
from config import CONFIG
from connection import get_transactions, format_balance, get_account
import sys import sys
import os
import customtkinter
from tkinter import ttk, messagebox
from config import CONFIG
from connection import get_transactions, format_balance, get_account, generate_otp, update_account
description_entry = None
notes_entry = None
otp_entry = None
################# #################
### Functions ### ### Functions ###
@@ -45,10 +53,10 @@ def display_account_info(account_id):
messagebox.showerror("Error", "Could not fetch account details.") messagebox.showerror("Error", "Could not fetch account details.")
return return
account = account_info['data'] account = account_info['data']
print(account)
if 'description' not in account: if 'description' not in account:
messagebox.showerror("Error", "Account description not found.") messagebox.showerror("Error", "Account description not found.")
return return
global account_description
account_description = account['description'] account_description = account['description']
fields = {'Account ID': account_id, 'Description': account_description, 'Balance': format_balance(account['balance']), 'Account Type': account['account_type']} fields = {'Account ID': account_id, 'Description': account_description, 'Balance': format_balance(account['balance']), 'Account Type': account['account_type']}
for i, (key, value) in enumerate(fields.items()): for i, (key, value) in enumerate(fields.items()):
@@ -57,6 +65,90 @@ def display_account_info(account_id):
label_key.grid(row=0, column=i*2, sticky='w', padx=10) label_key.grid(row=0, column=i*2, sticky='w', padx=10)
label_value.grid(row=0, column=i*2+1, sticky='w', padx=10) label_value.grid(row=0, column=i*2+1, sticky='w', padx=10)
def on_transaction_double_click(event):
"""Handles double-click event on a transaction in the table."""
try:
selected_transaction = transactions_table.item(transactions_table.selection())
transaction_id = selected_transaction['values'][0]
command = f"python application\\transaction.py {transaction_id} \"{selected_transaction['values'][4]}\""
return_code = os.system(command)
if return_code != 0:
print(f"Error: The command failed with return code {return_code}")
except Exception as e:
print(f"Error: {e}")
def add_transaction():
"""Open the add transaction dialog."""
command = f"python application\\new_transaction.py {account_id}"
return_code = os.system(command)
if return_code != 0:
print(f"Error: The command failed with return code {return_code}")
def edit_account_details():
"""Opens a new window for editing the account details."""
global edit_window, otp_entry, description_entry, notes_entry
edit_window = customtkinter.CTkToplevel(root)
edit_window.title("Edit Account Details")
edit_window.iconbitmap("application/luxbank.ico")
edit_window.geometry("300x350")
edit_window.attributes('-topmost', True)
description_label = customtkinter.CTkLabel(edit_window, text="Description: ")
description_entry = customtkinter.CTkEntry(edit_window)
description_label.pack()
description_entry.pack()
notes_label = customtkinter.CTkLabel(edit_window, text="Notes: ")
notes_entry = customtkinter.CTkEntry(edit_window)
notes_label.pack()
notes_entry.pack()
customtkinter.CTkLabel(edit_window, text=" ").pack() # Add space under the address box
otp_button = customtkinter.CTkButton(edit_window, text="Get OTP Code", command=generate_otp)
otp_button.pack()
otp_label = customtkinter.CTkLabel(edit_window, text="OTP Code: ")
otp_entry = customtkinter.CTkEntry(edit_window)
otp_label.pack()
otp_entry.pack()
save_button = customtkinter.CTkButton(edit_window, text="Verify OTP and Save", command=save_details)
save_button.pack()
edit_window.lift()
def save_details():
"""Saves the edited account details."""
global edit_window, otp_entry, description_entry, notes_entry, account_description
description = description_entry.get() if description_entry.get() != '' else None
notes = notes_entry.get() if notes_entry.get() != '' else None
otp_code = otp_entry.get() if otp_entry.get() != '' else None
if not otp_code:
messagebox.showerror("Error", "OTP code must be entered.")
return
if not messagebox.askyesno("Confirmation", "Are you sure you want to save the changes?"):
return
try:
result = update_account(account_id, otp_code, description, notes)
if 'success' in result and result['success']:
display_account_info(account_id)
messagebox.showinfo("Success", "Account details have been updated.")
root.title(f"Transactions for: {account_description}") # Update the window title
welcome_label.configure(text=f"Transactions for: {account_description}")
edit_window.destroy()
else:
if result['message'] == "Invalid OTP.":
messagebox.showerror("Error", "Invalid OTP code. Please try again.")
else:
messagebox.showerror("Error", f"Could not update account details: {result['message']}")
except Exception as e:
messagebox.showerror("Error", f"Could not update account details: {str(e)}")
############## ##############
### Layout ### ### Layout ###
############## ##############
@@ -89,7 +181,7 @@ button_frame = customtkinter.CTkFrame(root)
button_frame.pack(fill=tk.X, pady=10) button_frame.pack(fill=tk.X, pady=10)
add_transaction_button = customtkinter.CTkButton(button_frame, text="Add Transaction", command=add_transaction) add_transaction_button = customtkinter.CTkButton(button_frame, text="Add Transaction", command=add_transaction)
add_transaction_button.grid(row=0, column=0, padx=10) add_transaction_button.grid(row=0, column=0, padx=10)
request_otp_button = customtkinter.CTkButton(button_frame, text="Request OTP", command=request_otp) request_otp_button = customtkinter.CTkButton(button_frame, text="Request OTP", command=generate_otp)
request_otp_button.grid(row=0, column=1, padx=10) request_otp_button.grid(row=0, column=1, padx=10)
edit_account_details_button = customtkinter.CTkButton(button_frame, text="Edit Account Details", command=edit_account_details) edit_account_details_button = customtkinter.CTkButton(button_frame, text="Edit Account Details", command=edit_account_details)
edit_account_details_button.grid(row=0, column=2, padx=10) edit_account_details_button.grid(row=0, column=2, padx=10)

View File

@@ -1,3 +1,6 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Connection Page
import requests import requests
from requests.models import Response from requests.models import Response
from config import CONFIG from config import CONFIG
@@ -124,12 +127,12 @@ def get_account(account_id):
print(f"RequestException: {e}") print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."} return {'success': False, 'message': "Could not connect to the server. Please try again later."}
def update_account(account_id, description=None, notes=None): def update_account(account_id, otp_code:int, description=None, notes=None):
"""Updates the account details for the given account_id.""" """Updates the account details for the given account_id."""
try: try:
with open('application\\session_data.json', 'r') as f: with open('application\\session_data.json', 'r') as f:
session_data = json.load(f) session_data = json.load(f)
params = {'account_id': account_id} params = {'account_id': account_id, 'otp_code': otp_code}
if description is not None: if description is not None:
params['description'] = description params['description'] = description
if notes is not None: if notes is not None:
@@ -137,6 +140,11 @@ def update_account(account_id, description=None, notes=None):
response = requests.put(CONFIG["server"]["url"] + "/Account", cookies=session_data['session_cookie'], params=params) response = requests.put(CONFIG["server"]["url"] + "/Account", cookies=session_data['session_cookie'], params=params)
response.raise_for_status() response.raise_for_status()
return response.json() return response.json()
except requests.exceptions.HTTPError as e:
if response.status_code == 400:
return {'success': False, 'message': "Invalid OTP."}
print(f"HTTPError: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."}
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
print(f"RequestException: {e}") print(f"RequestException: {e}")
return {'success': False, 'message': "Could not connect to the server. Please try again later."} return {'success': False, 'message': "Could not connect to the server. Please try again later."}

View File

@@ -1,3 +1,6 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Dashboard Page
from tkinter import messagebox, ttk from tkinter import messagebox, ttk
import customtkinter import customtkinter
import json import json
@@ -13,6 +16,9 @@ address_entry = None
otp_entry = None otp_entry = None
frame = None frame = None
fields = [('Name', 'name'), ('Client ID', 'client_id'), ('Email', 'email'), ('Phone', 'phone_number'),
('Address', 'address'), ('Account Opened', 'opening_timestamp')]
################# #################
### Functions ### ### Functions ###
################# #################
@@ -42,6 +48,7 @@ def exit_application():
else: else:
messagebox.showerror("Logout failed", json_response['message']) messagebox.showerror("Logout failed", json_response['message'])
def display_client_info(): def display_client_info():
"""Displays the client's information on the dashboard.""" """Displays the client's information on the dashboard."""
global frame global frame
@@ -50,7 +57,7 @@ def display_client_info():
widget.destroy() widget.destroy()
else: else:
frame = customtkinter.CTkFrame(root) frame = customtkinter.CTkFrame(root)
frame.pack(anchor='w', side='left', padx=20, pady=20) frame.grid(row=1, column=0, padx=20, pady=20)
try: try:
with open('application\\session_data.json', 'r') as f: with open('application\\session_data.json', 'r') as f:
@@ -59,12 +66,10 @@ def display_client_info():
client_info = get_client(client_id) client_info = get_client(client_id)
if client_info.get('success'): if client_info.get('success'):
client = client_info['data'] client = client_info['data']
fields = [('Name', 'name'), ('Client ID', 'client_id'), ('Email', 'email'), ('Phone', 'phone_number'),
('Address', 'address'), ('Account Opened', 'opening_timestamp')]
for i, (display_name, key) in enumerate(fields): for i, (display_name, key) in enumerate(fields):
value = client.get(key, 'N/A') value = client.get(key, 'N/A')
label_key = customtkinter.CTkLabel(frame, text=f"{display_name}: ", font=("Helvetica", 14)) label_key = customtkinter.CTkLabel(frame, text=f"{display_name}: ", font=("Helvetica", 14))
label_value = customtkinter.CTkLabel(frame, text=value, font=("Helvetica", 14)) label_value = customtkinter.CTkLabel(frame, text=value, font=("Helvetica", 14), anchor='w')
label_key.grid(row=i, column=0, sticky='e') label_key.grid(row=i, column=0, sticky='e')
label_value.grid(row=i, column=1, sticky='w') label_value.grid(row=i, column=1, sticky='w')
else: else:
@@ -73,16 +78,13 @@ def display_client_info():
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"Could not retrieve client information: {e}") messagebox.showerror("Error", f"Could not retrieve client information: {e}")
edit_button = customtkinter.CTkButton(frame, text="Edit Details", command=edit_details)
edit_button.grid(row=len(fields), column=0, columnspan=2)
def edit_details(): def edit_details():
"""Opens a new window for editing client details.""" """Opens a new window for editing client details."""
global edit_window, email_entry, phone_entry, address_entry, otp_entry global edit_window, email_entry, phone_entry, address_entry, otp_entry
edit_window = customtkinter.CTkToplevel(root) edit_window = customtkinter.CTkToplevel(root)
edit_window.title("Edit Details") edit_window.title("Edit Details")
edit_window.geometry("300x350")
edit_window.iconbitmap("application/luxbank.ico") edit_window.iconbitmap("application/luxbank.ico")
edit_window.geometry("300x350")
edit_window.attributes('-topmost', True) edit_window.attributes('-topmost', True)
email_label = customtkinter.CTkLabel(edit_window, text="Email: ") email_label = customtkinter.CTkLabel(edit_window, text="Email: ")
@@ -143,13 +145,14 @@ def save_details():
if result['message'] == "Invalid OTP.": if result['message'] == "Invalid OTP.":
messagebox.showerror("Error", "MFA details not correct. Please try again.") messagebox.showerror("Error", "MFA details not correct. Please try again.")
else: else:
messagebox.showerror("Update Failed", result.get('message', 'Unknown error')) messagebox.showerror("Error", f"Could not update client details: {result['message']}")
except Exception as e: except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}") messagebox.showerror("Error", f"Could not update client details: {str(e)}")
def populate_table(): def populate_table():
"""Populates the accounts table with client accounts.""" """Populates the accounts table with client accounts."""
for row in table.get_children(): # Clear the table before populating it
table.delete(row)
try: try:
with open('application\\session_data.json', 'r') as f: with open('application\\session_data.json', 'r') as f:
session_data = json.load(f) session_data = json.load(f)
@@ -178,6 +181,12 @@ def on_account_double_click(event):
except Exception as e: except Exception as e:
print(f"Error: {e}") print(f"Error: {e}")
def reload_info_and_accounts():
"""Reloads the client information and the accounts table."""
display_client_info()
populate_table()
############## ##############
### Layout ### ### Layout ###
############## ##############
@@ -192,29 +201,41 @@ customtkinter.set_appearance_mode(CONFIG["preferences"].get("dark_theme", "light
# Create a label for the title # Create a label for the title
welcome_label = customtkinter.CTkLabel(root, text="Welcome to the Luxbank Dashboard!", font=("Helvetica", 24)) welcome_label = customtkinter.CTkLabel(root, text="Welcome to the Luxbank Dashboard!", font=("Helvetica", 24))
welcome_label.pack(pady=20) welcome_label.grid(row=0, column=0, columnspan=2, pady=20)
display_client_info()
# Create a frame for buttons # Create a frame for buttons
button_frame = customtkinter.CTkFrame(root) button_frame = customtkinter.CTkFrame(root)
button_frame.pack(pady=15, side='top') button_frame.grid(row=1, column=0, columnspan=2, sticky='ew', pady=15)
# Create the OTP button # Create the OTP button
otp_button = customtkinter.CTkButton(button_frame, text="Get OTP Code", command=generate_otp) otp_button = customtkinter.CTkButton(button_frame, text="Get OTP Code", command=generate_otp)
otp_button.pack(side='left', padx=5) otp_button.grid(row=0, column=0, padx=5)
# Create the reload button
reload_button = customtkinter.CTkButton(button_frame, text="Reload", command=reload_info_and_accounts)
reload_button.grid(row=0, column=1, padx=5)
# Create the logout button # Create the logout button
logout_button = customtkinter.CTkButton(button_frame, text="Logout", command=logout) logout_button = customtkinter.CTkButton(button_frame, text="Logout", command=logout)
logout_button.pack(side='left', padx=5) logout_button.grid(row=0, column=2, padx=5)
# Create the exit button # Create the exit button
exit_button = customtkinter.CTkButton(button_frame, text="Exit", command=exit_application) exit_button = customtkinter.CTkButton(button_frame, text="Exit", command=exit_application)
exit_button.pack(side='left', padx=5) exit_button.grid(row=0, column=3, padx=5)
# Display client info after creating the buttons
frame = customtkinter.CTkFrame(root)
frame.grid(row=2, column=0, padx=20, pady=20, sticky='n') # Adjusted grid placement
display_client_info()
# Create a frame for the table # Create a frame for the table
table_frame = ttk.Frame(root) table_frame = ttk.Frame(root)
table_frame.pack(side='right', fill='both', expand=True) table_frame.grid(row=2, column=1, sticky='nsew') # Adjusted grid placement
# Configure the grid
root.grid_rowconfigure(2, weight=1) # Adjusted row index
root.grid_columnconfigure(1, weight=1)
# Create the table # Create the table
table = ttk.Treeview(table_frame, columns=('Name', 'Account ID', 'Balance', 'Account Type'), show='headings') table = ttk.Treeview(table_frame, columns=('Name', 'Account ID', 'Balance', 'Account Type'), show='headings')

View File

@@ -0,0 +1,36 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System New Transaction Page
import tkinter as tk
from tkinter import ttk, messagebox
import customtkinter
from config import CONFIG
from connection import
import sys
#################
### Functions ###
#################
def get_transactoin
##############
### Layout ###
##############
# Initialise the main window
root = customtkinter.CTk()
root.title(f"Transactions: {transaction_description}")
root.iconbitmap("application/luxbank.ico")
root.geometry("800x400")
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
else:
customtkinter.set_appearance_mode("light") # Set the style for light mode
# Display main window title
welcome_label = customtkinter.CTkLabel(root, text=f"Transactions for: {account_description}", font=("Helvetica", 24))
welcome_label.pack(pady=10)

View File

@@ -1 +1 @@
{"session_cookie": {"session": "CjjpiVUxx009emp27lUSxMpJ4Dt1sUi3fV-_VILXFrw"}, "client_id": "d18e5ae0"} {"session_cookie": {"session": "nwHUYOr9vg2nOaZmrYNmWgjMgJ47QLIz71_dX_kFH_o"}, "client_id": "d18e5ae0"}

View File

@@ -0,0 +1,36 @@
# Lucas Mathews - Fontys Student ID: 5023572
# Banking System Transaction Page
import tkinter as tk
from tkinter import ttk, messagebox
import customtkinter
from config import CONFIG
from connection import get_transaction, format_balance
import sys
#################
### Functions ###
#################
##############
### Layout ###
##############
# Initialise the main window
root = customtkinter.CTk()
root.title(f"Transactions: {transaction_description}")
root.iconbitmap("application/luxbank.ico")
root.geometry("800x400")
if CONFIG["preferences"]["dark_theme"] == "dark": # Check if dark mode is enabled
customtkinter.set_appearance_mode("dark") # Set the style for dark mode
else:
customtkinter.set_appearance_mode("light") # Set the style for light mode
# Display main window title
welcome_label = customtkinter.CTkLabel(root, text=f"Transactions: {transaction_description}", font=("Helvetica", 24))
welcome_label.pack(pady=10)

View File

@@ -260,9 +260,11 @@ def add_account(client_id:str, description:str, account_type:str, **kwargs):
return format_response(True, f"New account has been added: account_id: {account_id}"), 200 return format_response(True, f"New account has been added: account_id: {account_id}"), 200
@login_required @login_required
def update_account(account_id:str, **kwargs): def update_account(account_id:str, otp_code:str, **kwargs):
"""Updates an account in the database. If the account is not found, returns an error message.""" """Updates an account in the database. If the account is not found, returns an error message."""
current_client_id, is_admin = get_current_client() current_client_id, is_admin = get_current_client()
if not verify_otp(current_client_id, otp_code):
return format_response(False, "Invalid OTP."), 400
account_owner = session.query(Account).filter_by(account_id=account_id).one_or_none().client_id account_owner = session.query(Account).filter_by(account_id=account_id).one_or_none().client_id
if not is_admin and account_owner != current_client_id: if not is_admin and account_owner != current_client_id:
return format_response(False, "You can only view your own client information."), 403 return format_response(False, "You can only view your own client information."), 403

Binary file not shown.