diff --git a/gui/main_gui.ui b/gui/main_gui.ui
index defbf8a..64a48cb 100644
--- a/gui/main_gui.ui
+++ b/gui/main_gui.ui
@@ -35,7 +35,7 @@
10
0
401
- 171
+ 176
@@ -53,6 +53,11 @@
-
+
+
+ 11
+
+
Pension value
@@ -60,6 +65,11 @@
-
+
+
+ 11
+
+
true
@@ -82,6 +92,11 @@
-
+
+
+ 10
+
+
Investment mix (funds 50% / shares 50%)
@@ -108,6 +123,11 @@
-
+
+
+ 11
+
+
Annual share trades
@@ -115,6 +135,11 @@
-
+
+
+ 11
+
+
Annual fund trades
@@ -122,6 +147,11 @@
-
+
+
+ 11
+
+
true
@@ -132,6 +162,11 @@
false
+
+
+ 10
+
+
Calculate
@@ -139,6 +174,11 @@
-
+
+
+ 11
+
+
true
@@ -151,6 +191,11 @@
true
+
+
+ 10
+
+
@@ -44,6 +49,11 @@
24
+
+
+ 10
+
+
OK
@@ -57,6 +67,11 @@
24
+
+
+ 10
+
+
Save
diff --git a/gui/platform_edit.ui b/gui/platform_edit.ui
index ddf9a0e..fe3a57d 100644
--- a/gui/platform_edit.ui
+++ b/gui/platform_edit.ui
@@ -6,32 +6,20 @@
0
0
- 498
- 303
+ 630
+ 567
-
-
- 498
- 303
-
-
-
-
- 498
- 303
-
-
Platform Editor
- 20
+ 10
20
- 461
- 243
+ 611
+ 241
@@ -75,6 +63,11 @@
false
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -92,7 +85,7 @@
-
- false
+ true
true
@@ -101,6 +94,11 @@
-
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -117,6 +115,11 @@
-
+
+
+ 11
+
+
Share dealing discount # of trades
@@ -127,6 +130,11 @@
-
+
+
+ 11
+
+
Share platform monthly fee cap
@@ -134,6 +142,11 @@
-
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -150,6 +163,11 @@
-
+
+
+ 11
+
+
Share dealing fee*
@@ -160,6 +178,11 @@
false
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -186,6 +209,11 @@
-
+
+
+ 11
+
+
Fund dealing fee*
@@ -195,10 +223,21 @@
-
-
+
+
+
+ 11
+
+
+
-
+
+
+ 11
+
+
Share dealing discount amount
@@ -206,6 +245,11 @@
-
+
+
+ 11
+
+
Share platform fee*
@@ -213,6 +257,11 @@
-
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -229,6 +278,11 @@
-
+
+
+ 11
+
+
Platform name
@@ -239,6 +293,11 @@
false
+
+
+ 11
+
+
QAbstractSpinBox::ButtonSymbols::NoButtons
@@ -261,12 +320,17 @@
- 350
- 270
+ 482
+ 534
141
24
+
+
+ 10
+
+
Save
@@ -274,8 +338,8 @@
- 10
- 280
+ 8
+ 262
191
21
@@ -287,7 +351,7 @@
- 437
+ 577
10
61
16
@@ -300,6 +364,147 @@
Qt::AlignmentFlag::AlignCenter
+
+
+
+ 11
+ 309
+ 611
+ 31
+
+
+
+
+ 0
+
+
-
+
+
+
+ 11
+
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ %
+
+
+ 100.000000000000000
+
+
+
+ -
+
+
+
+ 11
+
+
+
+ on the first
+
+
+
+ -
+
+
+
+ 11
+
+
+
+ the fee is
+
+
+
+ -
+
+
+
+ 11
+
+
+
+ QAbstractSpinBox::ButtonSymbols::NoButtons
+
+
+ QAbstractSpinBox::CorrectionMode::CorrectToNearestValue
+
+
+ £
+
+
+ 9999999.000000000000000
+
+
+
+
+
+
+
+ false
+
+
+
+ 532
+ 481
+ 91
+ 24
+
+
+
+
+ 10
+
+
+
+ Add row
+
+
+
+
+ false
+
+
+
+ 440
+ 481
+ 91
+ 24
+
+
+
+
+ 10
+
+
+
+ Remove row
+
+
+
+
+
+ 6
+ 479
+ 421
+ 21
+
+
+
+
+ 10
+
+
+
+ on the value above £ there is no charge
+
+
@@ -321,7 +526,6 @@
share_deal_fee_box
share_deal_reduce_trades_box
share_deal_reduce_amount_box
- save_but
plat_name_check
fund_deal_fee_check
share_plat_fee_check
@@ -329,6 +533,11 @@
share_deal_fee_check
share_deal_reduce_trades_check
share_deal_reduce_amount_check
+ first_tier_box
+ first_tier_fee_box
+ del_row_but
+ add_row_but
+ save_but
diff --git a/src/main_window.py b/src/main_window.py
index 24a5eea..cd9b0f0 100644
--- a/src/main_window.py
+++ b/src/main_window.py
@@ -66,7 +66,6 @@ class SIPPCompare(QMainWindow):
def init_variables(self):
self.optional_boxes = self.platform_win.get_optional_boxes()
self.fund_plat_fee = self.platform_win.get_fund_plat_fee()
- self.fund_deal_fee = self.platform_win.get_fund_deal_fee()
self.share_plat_fee = self.platform_win.get_share_plat_fee()
self.share_deal_fee = self.platform_win.get_share_deal_fee()
@@ -77,16 +76,21 @@ class SIPPCompare(QMainWindow):
self.plat_name = None
if self.optional_boxes[1]:
+ self.fund_deal_fee = self.platform_win.get_fund_deal_fee()
+ else:
+ self.fund_deal_fee = None
+
+ if self.optional_boxes[2]:
self.share_plat_max_fee = self.platform_win.get_share_plat_max_fee()
else:
self.share_plat_max_fee = None
- if self.optional_boxes[2]:
+ if self.optional_boxes[3]:
self.share_deal_reduce_trades = self.platform_win.get_share_deal_reduce_trades()
else:
self.share_deal_reduce_trades = None
- if self.optional_boxes[3]:
+ if self.optional_boxes[4]:
self.share_deal_reduce_amount = self.platform_win.get_share_deal_reduce_amount()
else:
self.share_deal_reduce_amount = None
@@ -101,7 +105,8 @@ class SIPPCompare(QMainWindow):
slider_val: int = self.mix_slider.value()
funds_value = (slider_val / 100) * value_num
fund_trades_num = int(self.fund_trades_combo.currentText())
- self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
+ if self.fund_deal_fee is not None:
+ self.fund_deal_fees = fund_trades_num * self.fund_deal_fee
for i in range(1, len(self.fund_plat_fee[0])):
band = self.fund_plat_fee[0][i]
@@ -120,15 +125,15 @@ class SIPPCompare(QMainWindow):
if self.share_plat_max_fee is not None:
if (self.share_plat_fee * shares_value / 12) > self.share_plat_max_fee:
self.share_plat_fees = self.share_plat_max_fee * 12
- else:
- self.share_plat_fees = self.share_plat_fee * shares_value
+ else:
+ self.share_plat_fees = self.share_plat_fee * shares_value
share_trades_num = int(self.share_trades_combo.currentText())
if self.share_deal_reduce_trades is not None:
if (share_trades_num / 12) >= self.share_deal_reduce_trades:
self.share_deal_fees = self.share_deal_reduce_amount * share_trades_num
- else:
- self.share_deal_fees = self.share_deal_fee * share_trades_num
+ else:
+ self.share_deal_fees = self.share_deal_fee * share_trades_num
self.show_output_win()
diff --git a/src/platform_edit.py b/src/platform_edit.py
index 55f4e4a..06573cc 100644
--- a/src/platform_edit.py
+++ b/src/platform_edit.py
@@ -1,8 +1,9 @@
from PyQt6.QtCore import QRegularExpression
-from PyQt6.QtGui import QRegularExpressionValidator
-from PyQt6.QtWidgets import QWidget
+from PyQt6.QtGui import QRegularExpressionValidator, QFont
+from PyQt6.QtWidgets import QWidget, QLabel
from PyQt6 import uic
+from widgets.fastedit_spinbox import FastEditQDoubleSpinBox
import main_window
@@ -16,11 +17,7 @@ class PlatformEdit(QWidget):
# Create main window object, passing this instance as param
self.main_win = main_window.SIPPCompare(self)
- # TODO: Make fund_plat_fee user-defined
- self.fund_plat_fee = [
- [0, 250000, 1000000, 2000000],
- [0, 0.25, 0.1, 0.05]
- ]
+ self.fund_plat_fee = []
self.plat_name = ""
self.fund_deal_fee = 0.0
self.share_plat_fee = 0.0
@@ -28,19 +25,22 @@ class PlatformEdit(QWidget):
self.share_deal_fee = 0.0
self.share_deal_reduce_trades = 0.0
self.share_deal_reduce_amount = 0.0
+ self.widgets_list_list = []
+
+ self.fund_fee_rows = 1
# Debugging feature: set with "--DEBUG_AUTOFILL" cmd argument
self.autofill = autofill
if autofill:
self.save_but.setEnabled(True)
self.required_fields = [
- self.fund_deal_fee_box,
self.share_plat_fee_box,
self.share_deal_fee_box
]
self.optional_fields = [
self.plat_name_box,
+ self.fund_deal_fee_box,
self.share_plat_max_fee_box,
self.share_deal_reduce_trades_box,
self.share_deal_reduce_amount_box
@@ -48,12 +48,14 @@ class PlatformEdit(QWidget):
self.optional_check_boxes = [
self.plat_name_check,
+ self.fund_deal_fee_check,
self.share_plat_max_fee_check,
self.share_deal_reduce_trades_check,
self.share_deal_reduce_amount_check
]
self.check_boxes_ticked = [
+ True,
True,
False,
False,
@@ -74,8 +76,14 @@ class PlatformEdit(QWidget):
for check_box in self.optional_check_boxes:
check_box.checkStateChanged.connect(self.check_valid)
+ self.first_tier_box.valueChanged.connect(self.check_valid)
+ self.first_tier_fee_box.valueChanged.connect(self.check_valid)
+ self.first_tier_box.valueChanged.connect(self.update_tier_labels)
+
# NOTE: Signal defined in UI file to close window when save button clicked
self.save_but.clicked.connect(self.init_variables)
+ self.add_row_but.clicked.connect(self.add_row)
+ self.del_row_but.clicked.connect(self.remove_row)
# Set validators
# Regex accepts any characters that match [a-Z], [0-9] or _
@@ -83,11 +91,28 @@ class PlatformEdit(QWidget):
QRegularExpressionValidator(QRegularExpression("\\w*"))
)
+ def create_plat_fee_struct(self):
+ plat_fee_struct = [[0], [0]]
+ plat_fee_struct[0].append(self.first_tier_box.value())
+ plat_fee_struct[1].append(self.first_tier_fee_box.value())
+
+ for i in range(len(self.widgets_list_list)):
+ band = self.widgets_list_list[i][1].value()
+ fee = self.widgets_list_list[i][3].value()
+ plat_fee_struct[0].append(band)
+ plat_fee_struct[1].append(fee)
+
+ return plat_fee_struct
+
# 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"
+ self.fund_plat_fee = [
+ [0, 250000, 1000000, 2000000],
+ [0, 0.25, 0.1, 0.05]
+ ]
self.fund_deal_fee = 1.50
self.share_plat_fee = 0.0025
self.share_plat_max_fee = 3.50
@@ -96,6 +121,7 @@ class PlatformEdit(QWidget):
self.share_deal_reduce_amount = 3.50
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())
@@ -116,6 +142,7 @@ class PlatformEdit(QWidget):
# It's also called when any field emits a textChanged() or valueChanged() signal
def check_valid(self):
valid = True
+ tiers_valid = True
# Check all required fields have a non-zero value
for field in self.required_fields:
@@ -145,11 +172,118 @@ class PlatformEdit(QWidget):
input_box_item.setEnabled(False)
self.check_boxes_ticked[i] = False
- if valid:
+ if self.first_tier_fee_box.value() == 0:
+ tiers_valid = False
+
+ if self.fund_fee_rows > 1:
+ if self.widgets_list_list[0][1].value() <= self.first_tier_box.value():
+ tiers_valid = False
+ if self.widgets_list_list[0][3].value() == 0:
+ tiers_valid = False
+
+ for i in range(len(self.widgets_list_list) - 1, 0, -1):
+ if self.widgets_list_list[i][1].value() <= self.widgets_list_list[i-1][1].value():
+ tiers_valid = False
+ if self.widgets_list_list[i][3].value() == 0:
+ tiers_valid = False
+
+ if tiers_valid and self.fund_fee_rows < 6:
+ self.add_row_but.setEnabled(True)
+ else:
+ self.add_row_but.setEnabled(False)
+
+ if valid and tiers_valid:
self.save_but.setEnabled(True)
else:
self.save_but.setEnabled(False)
+ def update_tier_labels(self):
+ if self.fund_fee_rows > 1:
+ prev_value = self.first_tier_box.value()
+ self.widgets_list_list[0][0].setText(f"between £{int(prev_value)} and")
+
+ for i in range(len(self.widgets_list_list) - 1, 0, -1):
+ prev_value = self.widgets_list_list[i-1][1].value()
+ self.widgets_list_list[i][0].setText(f"between £{int(prev_value)} and")
+
+ if self.fund_fee_rows > 1:
+ max_band = self.widgets_list_list[self.fund_fee_rows - 2][1].value()
+ else:
+ 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):
+ widgets = []
+ font = QFont()
+ font.setPointSize(11)
+
+ 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(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)
+
+ # 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
+
+ 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")
+
+ if self.fund_fee_rows > 1:
+ self.del_row_but.setEnabled(True)
+
+ if self.fund_fee_rows > 5:
+ self.add_row_but.setEnabled(False)
+
+ self.check_valid()
+
+ # TODO: Tab order
+
+ def remove_row(self):
+ for widget in self.widgets_list_list[self.fund_fee_rows - 2]:
+ self.gridLayout_2.removeWidget(widget)
+ widget.hide()
+ self.widgets_list_list.pop()
+ self.fund_fee_rows -= 1
+ self.gridLayoutWidget_2.setGeometry(11, 309, 611, int(round(28.5 * self.fund_fee_rows, 0)))
+
+ if self.fund_fee_rows < 2:
+ self.del_row_but.setEnabled(False)
+
+ if self.fund_fee_rows < 6:
+ self.add_row_but.setEnabled(True)
+
+ self.check_valid()
+ self.update_tier_labels()
+
# Getter functions (is this necessary? maybe directly reading class vars would be best...)
def get_optional_boxes(self):
return self.check_boxes_ticked