Finish implementing new transacitions

This commit is contained in:
Lucas Mathews
2024-06-07 17:31:56 +02:00
parent 1e95d2b605
commit 21fe74853c
7 changed files with 135 additions and 98 deletions

14
api.yml
View File

@@ -416,7 +416,7 @@ paths:
description: Amount of transaction description: Amount of transaction
required: true required: true
schema: schema:
type: integer type: number
format: float format: float
- name: account_id - name: account_id
in: query in: query
@@ -430,12 +430,6 @@ paths:
required: true required: true
schema: schema:
type: string type: string
- name: description
in: query
description: Description of transaction
required: false
schema:
type: string
- name: otp_code - name: otp_code
in: query in: query
description: OTP to verify description: OTP to verify
@@ -443,6 +437,12 @@ paths:
schema: schema:
type: integer type: integer
format: int32 format: int32
- name: description
in: query
description: Description of transaction
required: false
schema:
type: string
responses: responses:
'200': '200':
description: Successful operation description: Successful operation

View File

@@ -169,20 +169,38 @@ def update_account(account_id, otp_code:int, description=None, notes=None):
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 new_transaction(ammount, account_id, recipient_account_id, otp_code, description): def new_transaction(account_id, description, amount, recipient_account_id, otp_code):
"""Creates a new transaction for the given account.""" """Creates a new transaction for the given account."""
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)
if description == "": # Prepare the query parameters
description = None params = {
params = {"account_id": account_id, "recipient_account_id": recipient_account_id, "amount": ammount, "description": description, "otp_code": otp_code} "amount": amount,
"account_id": account_id,
"recipient_account_id": recipient_account_id,
"otp_code": otp_code,
"description": description}
print(f"Params: {params}")
response = requests.post(CONFIG["server"]["url"] + "/Transaction", cookies=session_data['session_cookie'], params=params) response = requests.post(CONFIG["server"]["url"] + "/Transaction", cookies=session_data['session_cookie'], params=params)
response.raise_for_status() response.raise_for_status()
print(f"Response: {response.json()}")
return response.json() return response.json()
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404:
return {'success': False, 'message': 'Account not found.'}
elif e.response.status_code == 403:
return {'success': False, 'message': 'Invalid OTP code.'}
elif e.response.status_code == 400:
return {'success': False, 'message': 'Invalid request. Please check the OTP code and other details.'}
else:
print(f"HTTPError: {e}")
return {'success': False, 'message': f"Unexpected error occurred. Error: {e}"}
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': f"Could not connect to the server. Error: {e}"}
def generate_otp(): def generate_otp():
"""Generates a new OTP for the current client, which is sent by Email.""" """Generates a new OTP for the current client, which is sent by Email."""

View File

@@ -24,64 +24,64 @@ else:
def submit_transaction(): def submit_transaction():
"""Submit a new transaction to the server.""" """Submit a new transaction to the server."""
account_id = account_combobox.get() amount = amount_text.get("1.0", "end").strip()
recipient_id = recipient_text.get("1.0", "end-1c") account_id = account_combobox.get().strip()
amount = amount_text.get("1.0", "end-1c") recipient_id = recipient_text.get("1.0", "end").strip()
description = description_text.get("1.0", "end-1c") description = description_text.get("1.0", "end").strip()
otp_code:int = otp_text.get("1.0", "end").strip()
if not amount or not recipient_id or not account_id: if not amount or not recipient_id or not account_id or not otp_code:
messagebox.showerror("Error", "Please fill in all fields.") messagebox.showerror("Error", "Please fill in manditory fields.")
return return
if verify_accounts(account_id, recipient_id, amount) is False:
# Convert amount to float return
try: if not otp_code.isdigit() or len(otp_code) != 6:
messagebox.showerror("Error", "Please enter a valid 6-digit OTP code.")
return
try: # Convert amount to float
amount = float(amount) amount = float(amount)
except ValueError: except ValueError:
messagebox.showerror("Error", "Invalid amount entered.") messagebox.showerror("Error", "Invalid amount entered.")
return return
try: # Convert otp code to int
account_verification = verify_accounts() otp_code = int(otp_code)
if account_verification is False: except ValueError:
messagebox.showerror("Error", "Could not verify account IDs.") messagebox.showerror("Error", "Invalid OTP code entered.")
return return
response = new_transaction(account_id, description, amount, recipient_id) response = new_transaction(account_id, description, amount, recipient_id, otp_code)
if response is None or "data" not in response: if response is None or "success" not in response:
messagebox.showerror("Error", "Could not submit transaction.") messagebox.showerror("Error", "Could not submit transaction.")
return return
transaction_id = response["data"] if not response['success']: # Check if the transaction was unsuccessful
otp = generate_otp(account_id, transaction_id) messagebox.showerror("Error", response['message']) # Show the error message in a popup
if otp is None or "data" not in otp:
messagebox.showerror("Error", "Could not generate OTP.")
return return
messagebox.showinfo("Success", f"Transaction submitted successfully. OTP: {otp['data']}") if response['success']: # Check if the transaction was successful
messagebox.showinfo("Success", "Transaction submitted successfully.")
root.destroy() # Close the window
return
def verify_accounts():
"""Verify that the account IDs are valid.""" def verify_accounts(account_id, recipient_id, amount):
try: """Verify that both account IDs are valid, and the from account has enough balance to make the transaction."""
account_id = account_combobox.get()
recipient_id = recipient_text.get("1.0", "end-1c")
account = get_account(account_id) account = get_account(account_id)
recipient_id = get_account(recipient_id) if account is None or "data" not in account or "balance" not in account["data"]:
except requests.exceptions.RequestException as e: messagebox.showerror("Error", "Invalid account ID.")
messagebox.showerror("Error", f"Failed to get account details: {e}")
return False return False
if account is None or recipient_id is None or "data" not in account or "data" not in recipient_id: recipient = get_account(recipient_id)
messagebox.showerror("Error", "Server did not return the expected response.") if recipient is None or "data" not in recipient:
messagebox.showerror("Error", "Invalid recipient ID.")
return False return False
if "account_id" not in account["data"]: try: # Convert amount to float
messagebox.showerror("Error", "Account ID not found.") amount = float(amount)
except ValueError:
messagebox.showerror("Error", "Invalid amount entered.")
return False return False
if "account_id" not in recipient_id["data"]: account_balance = account["data"]["balance"]
messagebox.showerror("Error", "Recipient Account ID not found.") if account_balance < amount:
messagebox.showerror("Error", "Insufficient balance.")
return False return False
amount = amount_text.get("1.0", "end-1c")
if account["data"]["balance"] < amount: # Check the client has the required balance
messagebox.showerror("Error", "Insufficient funds.")
return False
messagebox.showinfo("Success", "Accounts verified successfully.")
return True return True
def populate_accounts(client_id): def populate_accounts(client_id):
@@ -156,7 +156,7 @@ otp_text = customtkinter.CTkTextbox(root, height=2, width=250)
otp_text.pack(pady=5) otp_text.pack(pady=5)
# Create the submit button # Create the submit button
submit_button = customtkinter.CTkButton(root, text="Submit", command=submit_transaction) submit_button = customtkinter.CTkButton(root, text="Verify & Submit", command=submit_transaction)
submit_button.pack(pady=5) submit_button.pack(pady=5)
# Populate accounts combobox with the given client_id # Populate accounts combobox with the given client_id

View File

@@ -1 +1 @@
{"session_cookie": {"session": "0vm2PELvoXxLiOtpdLDR6M4-WHJq5xq5BX92b46UTkM"}, "client_id": "9755c18f"} {"session_cookie": {"session": "I5vE6-7HHC9fAildRSp-bxrPKTaglz4CWQgh1fdoFE4"}, "client_id": "9755c18f"}

BIN
bank.db

Binary file not shown.

View File

@@ -228,7 +228,7 @@ def change_password():
if client.hash == hash_old_password: if client.hash == hash_old_password:
client.hash = hash_new_password client.hash = hash_new_password
session.commit() session.commit()
delete_otp(client_id) delete_otp(current_client_id)
log_event(f"Password for client_id {client_id} has been updated by {current_client_id}.") log_event(f"Password for client_id {client_id} has been updated by {current_client_id}.")
return format_response(True, f"Password for client_id {client_id} has been updated."), 200 return format_response(True, f"Password for client_id {client_id} has been updated."), 200
else: else:
@@ -255,14 +255,14 @@ def get_accounts(client_id: str):
def get_account(account_id: str): def get_account(account_id: str):
"""Returns a specific account in the database. If the account is not found, returns an error message.""" """Returns a specific 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()
account_owner = session.query(Account).filter_by(account_id=account_id).one_or_none().client_id account = session.query(Account).filter_by(account_id=account_id).one_or_none()
if account is None:
return format_response(False, "Account not found."), 404
account_owner = account.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
account = session.query(Account).filter_by(account_id=account_id).one_or_none()
for account in session.query(Account).all():
if account.account_id == account_id:
return format_response(True, "", account.to_dict()), 200 return format_response(True, "", account.to_dict()), 200
return format_response(False, "Account not found."), 404
@login_required @login_required
def add_account(client_id:str, description:str, account_type:str, **kwargs): def add_account(client_id:str, description:str, account_type:str, **kwargs):
@@ -288,31 +288,44 @@ def add_account(client_id:str, description:str, account_type:str, **kwargs):
def update_account(account_id: str, otp_code: 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()
# Verify OTP
if not verify_otp(current_client_id, otp_code): if not verify_otp(current_client_id, otp_code):
return format_response(False, "Invalid OTP."), 400 return format_response(False, "Invalid OTP."), 400
account_owner = session.query(Account).filter_by(account_id=account_id).one_or_none().client_id
# Query the account once
account = session.query(Account).filter_by(account_id=account_id).one_or_none()
if account is None:
return format_response(False, "Account not found."), 404
account_owner = account.client_id
# Check permissions
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 update your own account information."), 403
for account in session.query(Account).all():
if account.account_id == account_id: # Update the account with provided kwargs
description = kwargs.get("description", None) description = kwargs.get("description")
account_type = kwargs.get("account_type", None) account_type = kwargs.get("account_type")
balance = kwargs.get("balance", None) balance = kwargs.get("balance")
enabled = kwargs.get("enabled", None) enabled = kwargs.get("enabled")
notes = kwargs.get("notes", None) notes = kwargs.get("notes")
if description:
if description is not None:
account.description = description account.description = description
if account_type: if account_type is not None:
account.account_type = account_type account.account_type = account_type
if balance: if balance is not None:
account.balance = balance account.balance = balance
if enabled: if enabled is not None:
account.enabled = enabled account.enabled = enabled
if notes: if notes is not None:
account.notes = notes account.notes = notes
# Commit the changes
session.commit() session.commit()
return format_response(True, f"account_id: {account_id} has been updated."), 200 return format_response(True, f"account_id: {account_id} has been updated."), 200
return format_response(False, "Account not found."), 404
################### ###################
### Transaction ### ### Transaction ###
@@ -332,30 +345,36 @@ def get_transaction(transaction_id:int):
return format_response(True, "", transaction.to_dict()), 200 return format_response(True, "", transaction.to_dict()), 200
@login_required @login_required
def add_transaction(amount:int, account_id, recipient_account_id, otp_code:int, **kwargs): def add_transaction(amount: float, account_id: str, recipient_account_id: str, otp_code: int, description: str):
"""Adds a new transaction to the database. If the account is not found, returns an error message.""" """Adds a new transaction to the database. If the account is not found, returns an error message."""
print(f"Adding transaction: amount: {amount}, account_id: {account_id}, recipient_account_id: {recipient_account_id}, otp_code: {otp_code}, description: {description}")
current_client_id, is_admin = get_current_client() current_client_id, is_admin = get_current_client()
if not is_admin and account_id != current_client_id: if not is_admin and account_id != 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
otp_verified = verify_otp(current_client_id, otp_code) # Verify if the OTP is correct otp_verified = verify_otp(current_client_id, otp_code)
if not otp_verified: if not otp_verified:
return format_response(False, "Invalid OTP."), 400 return format_response(False, "Invalid OTP."), 400
transaction_id = generate_uuid() transaction_id = generate_uuid()
for account in session.query(Account).all(): account_from = session.query(Account).filter_by(account_id=account_id).one_or_none()
if account.account_id == account_id: account_dest = session.query(Account).filter_by(account_id=recipient_account_id).one_or_none()
account_from = account
if account.account_id == recipient_account_id:
account_dest = account if account_from is None or account_dest is None:
if account_from.balance < amount: # Check if account has enough funds return format_response(False, "Account not found."), 404
if account_from.balance < amount:
return format_response(False, "Insufficient funds."), 400 return format_response(False, "Insufficient funds."), 400
account_from.balance -= amount # Perform the transaction delete_otp(current_client_id)
# Perform the transaction
account_from.balance -= amount
account_dest.balance += amount account_dest.balance += amount
transaction_type = "transfer" transaction_type = "transfer"
session.commit() session.commit()
description = kwargs.get("description", None) # Create the transaction record # Create the transaction record
new_transaction = Transaction(transaction_id, transaction_type, amount, timestamp(), description, account_id, recipient_account_id) new_transaction = Transaction(transaction_id, transaction_type, amount, timestamp(), description, account_id, recipient_account_id)
session.add(new_transaction) session.add(new_transaction)
session.commit() session.commit()
return format_response(True, f"New transaction has been added: transaction_id: {transaction_id}"), 200 return format_response(True, f"New transaction has been added: transaction_id: {transaction_id}"), 200
@login_required @login_required