diff --git a/SIPPCompare.db b/SIPPCompare.db new file mode 100644 index 0000000..ef44f75 Binary files /dev/null and b/SIPPCompare.db differ diff --git a/src/db_handler.py b/src/db_handler.py index e8cd137..7e95c14 100644 --- a/src/db_handler.py +++ b/src/db_handler.py @@ -1,5 +1,7 @@ import os import sqlite3 +import data_struct +from data_struct import Platform class DBHandler: @@ -8,14 +10,15 @@ class DBHandler: self.cur.execute(""" CREATE TABLE "tblPlatforms" ( "PlatformID" INTEGER NOT NULL UNIQUE, - "PlatformName" TEXT NOT NULL, + "PlatformName" TEXT, + "IsEnabled" INTEGER NOT NULL, PRIMARY KEY("PlatformID") ) """) self.cur.execute(""" CREATE TABLE "tblFlatPlatFees" ( - "PlatformID" INTEGER NOT NULL, + "PlatformID" INTEGER NOT NULL UNIQUE, "SharePlatFee" REAL NOT NULL, "SharePlatMaxFee" REAL, PRIMARY KEY("PlatformID"), @@ -25,7 +28,7 @@ class DBHandler: self.cur.execute(""" CREATE TABLE "tblFlatDealFees" ( - "PlatformID" INTEGER NOT NULL, + "PlatformID" INTEGER NOT NULL UNIQUE, "FundDealFee" REAL, "ShareDealFee" REAL NOT NULL, "ShareDealReduceTrades" REAL, @@ -47,10 +50,12 @@ class DBHandler: self.cur.execute(""" CREATE TABLE "tblUserDetails" ( - "PensionValue" REAL, - "SliderValue" INTEGER, - "ShareTrades" INTEGER, - "FundTrades" INTEGER + "UserID" INTEGER NOT NULL UNIQUE, + "PensionValue" REAL NOT NULL, + "SliderValue" INTEGER NOT NULL, + "ShareTrades" INTEGER NOT NULL, + "FundTrades" INTEGER NOT NULL, + PRIMARY KEY("UserID") ) """) @@ -65,23 +70,71 @@ class DBHandler: create_tables() def retrieve_plat_list(self) -> list: - res = self.cur.execute("SELECT PlatformName FROM tblPlatforms") - res_list = res.fetchall() + res = self.cur.execute("SELECT PlatformName FROM tblPlatforms").fetchall() plat_name_list = [] - for platform in res_list: + for platform in res: plat_name_list.append(platform[0]) return plat_name_list + def retrieve_platforms(self) -> list[Platform]: + platforms_res = self.cur.execute(""" + SELECT + -- tblPlatforms + tblPlatforms.PlatformID, PlatformName, IsEnabled, + -- tblFlatPlatFees + SharePlatFee, SharePlatMaxFee, + -- tblFlatDealFees + FundDealFee, + ShareDealFee, + ShareDealReduceTrades, + ShareDealReduceAmount + FROM tblPlatforms + INNER JOIN tblFlatPlatFees ON + tblPlatforms.PlatformID = tblFlatPlatFees.PlatformID + INNER JOIN tblFlatDealFees ON + tblPlatforms.PlatformID = tblFlatDealFees.PlatformID + """).fetchall() + + platforms = [] + for platform in platforms_res: + # plat_id, plat_name, enabled, share_plat_fee, share_plat_max_fee, fund_deal_fee, + # share_deal_fee, share_deal_reduce_trades, share_deal_reduce_amount + this_platform = [platform[0], platform[1], platform[2], platform[3], platform[4], + platform[5], platform[6], platform[7], platform[8]] + platforms.append(this_platform) + + for platform in platforms: + platform.insert(1, [[], []]) + + fund_plat_fee_res = self.cur.execute("SELECT * FROM tblFundPlatFee").fetchall() + for i in range(len(fund_plat_fee_res)): + plat_id = fund_plat_fee_res[i][0] + platforms[plat_id][1][0].append(fund_plat_fee_res[i][1]) + platforms[plat_id][1][1].append(fund_plat_fee_res[i][2]) + + platform_obj_list: list[Platform] = [] + for platform in platforms: + platform_obj_list.append(Platform( + platform[0], platform[1], platform[2], platform[3], + platform[6], platform[4], platform[5], platform[7], + platform[8], platform[9] + )) + + return platform_obj_list + + def write_user_details(self, pension_val: float, slider_val: int, share_trades: int, fund_trades: int): - user_details_data = (pension_val, slider_val, share_trades, fund_trades) + # Hardcode UserID as 0 + user_details_data = (0, pension_val, slider_val, share_trades, fund_trades) res = self.cur.execute("SELECT EXISTS(SELECT 1 FROM tblUserDetails)").fetchone() if res[0] == 0: - self.cur.execute("INSERT INTO tblUserDetails VALUES (?, ?, ?, ?)", user_details_data) + self.cur.execute("INSERT INTO tblUserDetails VALUES (?, ?, ?, ?, ?)", user_details_data) else: self.cur.execute(""" UPDATE tblUserDetails SET + UserID = ?, PensionValue = ?, SliderValue = ?, ShareTrades = ?, @@ -95,12 +148,13 @@ class DBHandler: return {"NO_RECORD": None} res = self.cur.execute("SELECT * FROM tblUserDetails") - res_tuple = res.fetchone() - user_details_dict = { - "pension_val": res_tuple[0], - "slider_val": res_tuple[1], - "share_trades": res_tuple[2], - "fund_trades": res_tuple[3] + res_tuple: tuple = res.fetchone() + user_details_dict: dict[str, float | int] = { + "user_id": res_tuple[0], + "pension_val": res_tuple[1], + "slider_val": res_tuple[2], + "share_trades": res_tuple[3], + "fund_trades": res_tuple[4] } return user_details_dict diff --git a/src/main.py b/src/main.py index 14c9b4d..4ee5f19 100644 --- a/src/main.py +++ b/src/main.py @@ -3,18 +3,21 @@ from PyQt6.QtWidgets import QApplication import sys import platform_edit +import main_window app = QApplication(sys.argv) # Show platform edit window first, before main win # When debugging, can be useful to autofill values to save time -if len(sys.argv) > 1: +"""if len(sys.argv) > 1: if sys.argv[1] == "--DEBUG_AUTOFILL": window = platform_edit.PlatformEdit(True) else: window = platform_edit.PlatformEdit(False) else: - window = platform_edit.PlatformEdit(False) + window = platform_edit.PlatformEdit(False)""" +#plat_edit_win = platform_edit.PlatformEdit() +window = main_window.SIPPCompare() window.show() app.exec() diff --git a/src/main_window.py b/src/main_window.py index dc12513..f500469 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -9,8 +9,7 @@ import db_handler class SIPPCompare(QMainWindow): - # Receive instance of PlatformEdit() as parameter - def __init__(self, plat_edit_win: QWidget): + def __init__(self): super().__init__() # Import Qt Designer UI XML file uic.loadUi(resource_finder.get_res_path("gui/main_gui.ui"), self) @@ -36,14 +35,12 @@ class SIPPCompare(QMainWindow): # Create window objects self.db = db_handler.DBHandler() - self.platform_win = plat_edit_win self.platform_list_win = platform_list.PlatformList(self.db) self.output_win = output_window.OutputWindow() # Handle events self.calc_but.clicked.connect(self.calculate_fees) # Menu bar entry (File -> Edit Platforms) - #self.actionEdit_Platforms.triggered.connect(self.show_platform_edit) self.actionList_Platforms.triggered.connect(self.show_platform_list) # Update percentage mix label when slider moved self.mix_slider.valueChanged.connect(self.update_slider_lab) @@ -64,6 +61,8 @@ class SIPPCompare(QMainWindow): self.fund_trades_combo.setCurrentText(str(prev_session_data["fund_trades"])) self.calc_but.setFocus() + + # Display slider position as mix between two nums (funds/shares) def update_slider_lab(self): slider_val = self.mix_slider.value() @@ -167,9 +166,5 @@ class SIPPCompare(QMainWindow): ) self.output_win.show() - # Show the platform editor window (currently run-time only) - def show_platform_edit(self): - self.platform_win.show() - def show_platform_list(self): self.platform_list_win.show() diff --git a/src/platform_edit.py b/src/platform_edit.py index 492a9fb..07b8817 100644 --- a/src/platform_edit.py +++ b/src/platform_edit.py @@ -4,36 +4,30 @@ from PyQt6.QtWidgets import QWidget, QLabel from PyQt6 import uic from widgets.fastedit_spinbox import FastEditQDoubleSpinBox +from data_struct import Platform import main_window import resource_finder class PlatformEdit(QWidget): - def __init__(self, autofill: bool): + def __init__(self, plat: Platform): super().__init__() # Import Qt Designer UI XML file uic.loadUi(resource_finder.get_res_path("gui/platform_edit.ui"), self) self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico"))) # Initialise class variables - # Create main window object, passing this instance as param - self.main_win = main_window.SIPPCompare(self) - - self.fund_plat_fee = [] - self.plat_name = "" - self.fund_deal_fee = 0.0 - self.share_plat_fee = 0.0 - self.share_plat_max_fee = 0.0 - self.share_deal_fee = 0.0 - self.share_deal_reduce_trades = 0.0 - self.share_deal_reduce_amount = 0.0 + self.plat = plat + self.fund_plat_fee = self.plat.fund_plat_fee self.widgets_list_list = [] - self.fund_fee_rows = 1 + self.fund_fee_rows = len(self.plat.fund_plat_fee[0]) + """ # Debugging feature: set with "--DEBUG_AUTOFILL" cmd argument self.autofill = autofill if autofill: self.save_but.setEnabled(True) + """ self.required_fields = [ self.share_plat_fee_box, @@ -64,6 +58,54 @@ class PlatformEdit(QWidget): False ] + if self.plat.plat_name is None: + self.check_boxes_ticked[0] = False + self.plat_name_check.setChecked(False) + else: + self.check_boxes_ticked[0] = True + self.plat_name_check.setChecked(True) + self.plat_name_box.setText(self.plat.plat_name) + + if self.plat.fund_deal_fee is None: + self.check_boxes_ticked[1] = False + self.plat_fund_deal_fee_check.setChecked(False) + else: + self.check_boxes_ticked[1] = True + self.fund_deal_fee_check.setChecked(True) + self.fund_deal_fee_box.setValue(self.plat.fund_deal_fee) + + self.share_plat_fee_box.setValue(self.plat.share_plat_fee * 100) + + if self.plat.share_plat_max_fee is None: + self.check_boxes_ticked[2] = False + self.share_plat_max_fee_check.setChecked(False) + else: + self.check_boxes_ticked[2] = True + self.share_plat_max_fee_check.setChecked(True) + self.share_plat_max_fee_box.setValue(self.plat.share_plat_max_fee) + + self.share_deal_fee_box.setValue(self.plat.share_deal_fee) + + if self.plat.share_deal_reduce_trades is None: + self.check_boxes_ticked[3] = False + self.share_deal_reduce_trades_check.setChecked(False) + else: + self.check_boxes_ticked[3] = True + self.share_deal_reduce_trades_check.setChecked(True) + self.share_deal_reduce_trades_box.setValue(int(self.plat.share_deal_reduce_trades)) + + if self.plat.share_deal_reduce_trades is None: + self.check_boxes_ticked[4] = False + self.share_deal_reduce_amount_check.setChecked(False) + else: + self.check_boxes_ticked[4] = True + self.share_deal_reduce_amount_check.setChecked(True) + self.share_deal_reduce_amount_box.setValue(self.plat.share_deal_reduce_amount) + + self.first_tier_box.setValue(self.plat.fund_plat_fee[0][1]) + self.first_tier_fee_box.setValue(self.plat.fund_plat_fee[1][1]) + self.add_row(loading=True) + # Handle events for field in self.required_fields: field.valueChanged.connect(self.check_valid) @@ -108,6 +150,7 @@ class PlatformEdit(QWidget): # Get fee structure variables from user input when "Save" clicked def init_variables(self): + """ # If debugging, save time by hardcoding if self.autofill: self.plat_name = "AJBell" @@ -123,14 +166,15 @@ class PlatformEdit(QWidget): self.share_deal_reduce_amount = 3.50 self.check_boxes_ticked = [True, True, True, True, True] else: - self.plat_name = self.plat_name_box.text() - self.fund_plat_fee = self.create_plat_fee_struct() - self.fund_deal_fee = float(self.fund_deal_fee_box.value()) - self.share_plat_fee = float(self.share_plat_fee_box.value()) / 100 - self.share_plat_max_fee = float(self.share_plat_max_fee_box.value()) - self.share_deal_fee = float(self.share_deal_fee_box.value()) - self.share_deal_reduce_trades = float(self.share_deal_reduce_trades_box.value()) - self.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value()) + """ + self.plat_name = self.plat_name_box.text() + self.fund_plat_fee = self.create_plat_fee_struct() + self.fund_deal_fee = float(self.fund_deal_fee_box.value()) + self.share_plat_fee = float(self.share_plat_fee_box.value()) / 100 + self.share_plat_max_fee = float(self.share_plat_max_fee_box.value()) + self.share_deal_fee = float(self.share_deal_fee_box.value()) + self.share_deal_reduce_trades = float(self.share_deal_reduce_trades_box.value()) + self.share_deal_reduce_amount = float(self.share_deal_reduce_amount_box.value()) # Once user input is received show main window self.main_win.show() @@ -215,50 +259,63 @@ class PlatformEdit(QWidget): max_band = self.first_tier_box.value() self.val_above_lab.setText(f"on the value above £{int(max_band)} there is no charge") - def add_row(self): + def add_row(self, loading: bool = False): + if loading: + rows_needed = self.fund_fee_rows + else: + rows_needed = 1 + widgets = [] - font = QFont() - font.setPointSize(11) + for x in range(rows_needed): + font = QFont() + font.setPointSize(11) - widgets.append(QLabel(self.gridLayoutWidget_2)) - widgets[0].setFont(font) + widgets.append(QLabel(self.gridLayoutWidget_2)) + widgets[0].setFont(font) - widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2)) - widgets[1].setPrefix("£") - widgets[1].setMaximum(9999999) - widgets[1].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons) - widgets[1].setFont(font) - widgets[1].valueChanged.connect(self.check_valid) - widgets[1].valueChanged.connect(self.update_tier_labels) + widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2)) + widgets[1].setPrefix("£") + widgets[1].setMaximum(9999999) + widgets[1].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons) + widgets[1].setFont(font) + if loading: + widgets[1].setValue(self.plat.fund_plat_fee[0][x+1]) + widgets[1].valueChanged.connect(self.check_valid) + widgets[1].valueChanged.connect(self.update_tier_labels) - widgets.append(QLabel(self.gridLayoutWidget_2)) - widgets[2].setText(f"the fee is") - widgets[2].setFont(font) + widgets.append(QLabel(self.gridLayoutWidget_2)) + widgets[2].setText(f"the fee is") + widgets[2].setFont(font) - widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2)) - widgets[3].setSuffix("%") - widgets[3].setMaximum(100) - widgets[3].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons) - widgets[3].setFont(font) - widgets[3].valueChanged.connect(self.check_valid) + widgets.append(FastEditQDoubleSpinBox(self.gridLayoutWidget_2)) + widgets[3].setSuffix("%") + widgets[3].setMaximum(100) + widgets[3].setButtonSymbols(FastEditQDoubleSpinBox.ButtonSymbols.NoButtons) + widgets[3].setFont(font) + if loading: + widgets[3].setValue(self.plat.fund_plat_fee[1][x+1]) + widgets[3].valueChanged.connect(self.check_valid) - # TODO: why 28.5? - self.gridLayoutWidget_2.setGeometry(11, 309, 611, int(round(28.5 * (self.fund_fee_rows + 1), 0))) - for i in range(len(widgets)): - self.gridLayout_2.addWidget(widgets[i], self.fund_fee_rows, i, 1, 1) + # TODO: why 28.5? + self.gridLayoutWidget_2.setGeometry(11, 309, 611, int(round(28.5 * (self.fund_fee_rows + 1), 0))) + for i in range(len(widgets)): + self.gridLayout_2.addWidget(widgets[i], self.fund_fee_rows, i, 1, 1) - self.fund_fee_rows += 1 + if not loading: + self.fund_fee_rows += 1 - self.widgets_list_list.append(widgets) - cur_label_idx = self.gridLayout_2.indexOf(widgets[0]) - cur_box_idx = self.gridLayout_2.indexOf(widgets[1]) - cur_label_pos = list(self.gridLayout_2.getItemPosition(cur_label_idx))[:2] - cur_box_pos = list(self.gridLayout_2.getItemPosition(cur_box_idx))[:2] + self.widgets_list_list.append(widgets) + cur_label_idx = self.gridLayout_2.indexOf(widgets[0]) + cur_box_idx = self.gridLayout_2.indexOf(widgets[1]) + cur_label_pos = list(self.gridLayout_2.getItemPosition(cur_label_idx))[:2] + cur_box_pos = list(self.gridLayout_2.getItemPosition(cur_box_idx))[:2] - prev_box_row = cur_box_pos[0] - 1 - prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_pos[1]).widget() - cur_label_item = self.gridLayout_2.itemAtPosition(cur_label_pos[0], cur_label_pos[1]).widget() - cur_label_item.setText(f"between £{int(prev_box_item.value())} and") + prev_box_row = cur_box_pos[0] - 1 + prev_box_item = self.gridLayout_2.itemAtPosition(prev_box_row, cur_box_pos[1]).widget() + #if loading: + # prev_box_item.setValue(self.plat.fund_plat_fee[0][x+1]) + cur_label_item = self.gridLayout_2.itemAtPosition(cur_label_pos[0], cur_label_pos[1]).widget() + cur_label_item.setText(f"between £{int(prev_box_item.value())} and") if self.fund_fee_rows > 1: self.del_row_but.setEnabled(True) diff --git a/src/platform_list.py b/src/platform_list.py index 91cccc9..110f0a9 100644 --- a/src/platform_list.py +++ b/src/platform_list.py @@ -4,6 +4,8 @@ from PyQt6.QtCore import QRegularExpression from PyQt6 import uic import resource_finder +import data_struct +import platform_edit class PlatformRename(QWidget): @@ -28,8 +30,11 @@ class PlatformList(QWidget): self.setWindowIcon(QIcon(resource_finder.get_res_path("icon2.ico"))) self.plat_list_dialog = PlatformRename() + self.p_edit = None self.db = db self.plat_name_list = self.db.retrieve_plat_list() + self.plat_list = self.db.retrieve_platforms() + print(self.plat_list[1].fund_plat_fee) print(self.plat_name_list) for platform in self.plat_name_list: @@ -39,6 +44,29 @@ class PlatformList(QWidget): # Handle events self.add_plat_but.clicked.connect(self.add_platform) + self.del_plat_but.clicked.connect(self.remove_platform) + self.edit_plat_but.clicked.connect(self.edit_platform) + self.plat_enabled_check.checkStateChanged.connect(self.toggle_platform_state) + self.platListWidget.currentRowChanged.connect(self.get_enabled_state) def add_platform(self): self.plat_list_dialog.show() + + def get_enabled_state(self): + index = self.platListWidget.currentRow() + is_enabled = self.plat_list[index].enabled + if is_enabled: + self.plat_enabled_check.setChecked(True) + else: + self.plat_enabled_check.setChecked(False) + + def edit_platform(self): + index = self.platListWidget.currentRow() + self.p_edit = platform_edit.PlatformEdit(self.plat_list[index]) + self.p_edit.show() + + def toggle_platform_state(self): + return None + + def remove_platform(self): + return None