diff --git a/summer/QtDesignerFiles/ProductGroupDetail.ui b/summer/QtDesignerFiles/ProductGroupDetail.ui
new file mode 100644
index 0000000..32ebab9
--- /dev/null
+++ b/summer/QtDesignerFiles/ProductGroupDetail.ui
@@ -0,0 +1,135 @@
+
+
+ ProductGroupDetails
+
+
+ Qt::WindowModal
+
+
+
+ 0
+ 0
+ 397
+ 231
+
+
+
+ Product Group Details
+
+
+ true
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+ -
+
+
+ Name
+
+
+
+ -
+
+
+ -
+
+
+ Maximum Discount
+
+
+
+ -
+
+
+ -
+
+
+ Header
+
+
+
+ -
+
+
+
+
+
+ QLineEdit::Normal
+
+
+
+ -
+
+
+ Sort Order
+
+
+
+ -
+
+
+ -
+
+
+ Is Active?
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+ txtName
+ sbMaxDiscount
+ txtHeader
+ sbSortOrder
+ chkIsActive
+ buttonBox
+
+
+
+
+ buttonBox
+ accepted()
+ ProductGroupDetails
+ accept()
+
+
+ 227
+ 331
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ ProductGroupDetails
+ reject()
+
+
+ 295
+ 337
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/summer/QtDesignerFiles/ProductGroupList.ui b/summer/QtDesignerFiles/ProductGroupList.ui
new file mode 100644
index 0000000..464baa8
--- /dev/null
+++ b/summer/QtDesignerFiles/ProductGroupList.ui
@@ -0,0 +1,37 @@
+
+
+ ProductGroupList
+
+
+ Qt::ApplicationModal
+
+
+
+ 0
+ 0
+ 800
+ 600
+
+
+
+ Product Groups
+
+
+
+ -
+
+
+ -
+
+
+ Add Product Group
+
+
+
+
+
+
+
+
+
+
diff --git a/summer/models/__init__.py b/summer/models/__init__.py
index 28233f6..cf04005 100644
--- a/summer/models/__init__.py
+++ b/summer/models/__init__.py
@@ -32,7 +32,7 @@ def initialize_sql(engine):
def schema_exists(engine):
- from models.master import DbSetting
+ from summer.models.master import DbSetting
with engine.connect() as connection:
return engine.dialect.has_table(connection, DbSetting.__tablename__)
diff --git a/summer/models/master.py b/summer/models/master.py
index 7bfb23b..f93c278 100644
--- a/summer/models/master.py
+++ b/summer/models/master.py
@@ -12,12 +12,11 @@ class Product(Base):
__tableagrs__ = (UniqueConstraint('name', 'units'))
id = Column('product_id', GUID(), primary_key=True, default=uuid.uuid4)
- code = Column('code', Integer, unique=True, nullable=False)
name = Column('name', Unicode(255), nullable=False)
units = Column('units', Unicode(255), nullable=False)
product_group_id = Column('product_group_id', GUID(), ForeignKey('product_groups.product_group_id'), nullable=False)
price = Column('price', Numeric, nullable=False)
- happy_hour = Column('happy_hour', Boolean, nullable=False)
+ has_happy_hour = Column('has_happy_hour', Boolean, nullable=False)
service_tax_id = Column('service_tax_id', GUID(), ForeignKey('taxes.tax_id'), nullable=False)
vat_id = Column('vat_id', GUID(), ForeignKey('taxes.tax_id'), nullable=False)
@@ -127,14 +126,10 @@ class ProductGroup(Base):
self.is_fixture = is_fixture
@classmethod
- def list(cls):
- return DBSession.query(cls).order_by(cls.name)
-
- @classmethod
- def by_id(cls, id):
+ def by_id(cls, id, * , session=None):
if not isinstance(id, uuid.UUID):
id = uuid.UUID(id)
- return DBSession.query(cls).filter(cls.id == id).first()
+ return session.query(cls).filter(cls.id == id).first()
def can_delete(self, advanced_delete):
if self.is_fixture:
diff --git a/summer/templates/MainWindow.py b/summer/templates/MainWindow.py
index 7070dda..4262603 100644
--- a/summer/templates/MainWindow.py
+++ b/summer/templates/MainWindow.py
@@ -15,6 +15,7 @@ class MainWindow(base, form):
self.btnExit.clicked.connect(self.handle_exit)
self.btnUsers.clicked.connect(router.show_users)
self.btnRoles.clicked.connect(router.show_roles)
+ self.btnProductGroups.clicked.connect(router.show_product_groups)
def handle_exit(self):
self.close()
diff --git a/summer/templates/ProductGroupDetail.py b/summer/templates/ProductGroupDetail.py
new file mode 100644
index 0000000..714554e
--- /dev/null
+++ b/summer/templates/ProductGroupDetail.py
@@ -0,0 +1,33 @@
+__author__ = 'tanshu'
+from PyQt5 import uic, QtCore
+
+
+file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/ProductGroupDetail.ui'
+base, form = uic.loadUiType(file)
+
+
+class ProductGroupDetail(base, form):
+ def __init__(self, router, product_group, parent=None):
+ super(ProductGroupDetail, self).__init__()
+ self.setupUi(self)
+ self.router = router
+ self.product_group = product_group
+ self.txtName.setText(product_group['Name'])
+ self.sbMaxDiscount.setMaximum(100)
+ self.sbMaxDiscount.setMinimum(0)
+ self.sbMaxDiscount.setValue(product_group['MaxDiscount'] * 100)
+ self.txtHeader.setText(product_group['Header'])
+ self.chkIsActive.setCheckState(QtCore.Qt.Checked if product_group['IsActive'] else QtCore.Qt.Unchecked)
+ self.sbSortOrder.setMinimum(0)
+ self.sbSortOrder.setValue(product_group['SortOrder'])
+ self.accepted.connect(self.save_product_group)
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+
+ def save_product_group(self):
+ self.product_group['Name'] = self.txtName.text()
+ self.product_group['MaxDiscount'] = self.sbMaxDiscount.value() / 100
+ self.product_group['Header'] = self.txtHeader.text()
+ self.product_group['IsActive'] = self.chkIsActive.isChecked()
+ self.product_group['SortOrder'] = self.sbSortOrder.value()
+
+ self.router.save_product_group(self.product_group)
diff --git a/summer/templates/ProductGroupList.py b/summer/templates/ProductGroupList.py
new file mode 100644
index 0000000..8f9c242
--- /dev/null
+++ b/summer/templates/ProductGroupList.py
@@ -0,0 +1,79 @@
+__author__ = 'tanshu'
+from PyQt5 import uic, QtCore, QtWidgets
+
+
+file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/ProductGroupList.ui'
+base, form = uic.loadUiType(file)
+
+
+class ProductGroupList(base, form):
+ def __init__(self, router, product_groups, parent=None):
+ super(ProductGroupList, self).__init__()
+ self.setupUi(self)
+ self.router = router
+ self.product_groups = product_groups
+ self.model = ProductGroupsTableModel(product_groups)
+ self.tblProductGroups.setModel(self.model)
+ self.tblProductGroups.doubleClicked.connect(self.show_product_group)
+ self.btnAddNew.clicked.connect(self.add_new_product_group)
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
+ self.tblProductGroups.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+
+ def update_data(self, data):
+ self.product_groups = data
+ self.model = ProductGroupsTableModel(self.product_groups)
+ self.tblProductGroups.setModel(self.model)
+
+ def show_product_group(self, index):
+ product_group = self.model.product_groups[index.row()]
+ self.router.show_product_group(product_group['ProductGroupID'])
+
+ def add_new_product_group(self):
+ self.router.show_product_group(None)
+
+
+class ProductGroupsTableModel(QtCore.QAbstractTableModel):
+ def __init__(self, product_groups=None, parent=None):
+ QtCore.QAbstractTableModel.__init__(self, parent)
+ self.product_groups = product_groups if product_groups is not None else []
+
+ def rowCount(self, parent):
+ return len(self.product_groups)
+
+ def columnCount(self, parent):
+ return 6
+
+ def data(self, index, role):
+ if role == QtCore.Qt.DisplayRole:
+ item = self.product_groups[index.row()]
+ column = index.column()
+ if column == 0:
+ return item['Name']
+ elif column == 1:
+ return '{0:,.2f}%'.format(item['MaxDiscount'] * 100)
+ elif column == 2:
+ return item['Header']
+ elif column == 3:
+ return item['IsActive']
+ elif column == 4:
+ return item['IsFixture']
+ elif column == 5:
+ return item['SortOrder']
+
+ def headerData(self, section, orientation, role):
+ if role == QtCore.Qt.DisplayRole:
+ if orientation == QtCore.Qt.Horizontal:
+ if section == 0:
+ return 'Name'
+ elif section == 1:
+ return 'Maximum Discount'
+ elif section == 2:
+ return 'Header'
+ elif section == 3:
+ return 'Is Active?'
+ elif section == 4:
+ return 'Is Fixture?'
+ elif section == 5:
+ return 'Sort Order'
+ else:
+ return section + 1
diff --git a/summer/templates/__init__.py b/summer/templates/__init__.py
new file mode 100644
index 0000000..87c77ab
--- /dev/null
+++ b/summer/templates/__init__.py
@@ -0,0 +1,7 @@
+__author__ = 'tanshu'
+from summer.templates.RoleDetail import RoleDetail
+from summer.templates.RoleList import RoleList
+from summer.templates.UserDetail import UserDetail
+from summer.templates.UserList import UserList
+from summer.templates.ProductGroupList import ProductGroupList
+from summer.templates.ProductGroupDetail import ProductGroupDetail
diff --git a/summer/views/__init__.py b/summer/views/__init__.py
index 34edfc7..bfa2d90 100644
--- a/summer/views/__init__.py
+++ b/summer/views/__init__.py
@@ -1,9 +1,8 @@
-from summer.templates.RoleDetail import RoleDetail
-from summer.templates.RoleList import RoleList
-from summer.templates.UserDetail import UserDetail
-from summer.templates.UserList import UserList
+from summer.templates import RoleDetail, RoleList, UserDetail, UserList, ProductGroupList, ProductGroupDetail
from summer.views.user import show_list as user_list, user_info, save_user, update_user
from summer.views.role import show_list as role_list, role_info, save_role, update_role
+from summer.views.product_group import show_list as product_group_list, product_group_info, save_product_group, \
+ update_product_group
__author__ = 'tanshu'
@@ -14,6 +13,8 @@ class Router():
self.user_detail_form = None
self.role_list_form = None
self.role_detail_form = None
+ self.product_group_list_form = None
+ self.product_group_detail_form = None
def show_users(self):
users = user_list()
@@ -71,3 +72,32 @@ class Router():
save_role(role)
else:
update_role(role)
+
+
+ def show_product_groups(self):
+ product_groups = product_group_list()
+ if self.product_group_list_form is None:
+ self.product_group_list_form = ProductGroupList(self, product_groups)
+ self.product_group_list_form.destroyed.connect(self.product_group_list_form_destroyed)
+ self.product_group_list_form.show()
+
+ def product_group_list_form_destroyed(self):
+ self.product_group_list_form = None
+
+ def show_product_group(self, id=None):
+ product_group = product_group_info(id)
+ if self.product_group_detail_form is None:
+ self.product_group_detail_form = ProductGroupDetail(self, product_group)
+ self.product_group_detail_form.destroyed.connect(self.product_group_detail_form_destroyed)
+ self.product_group_detail_form.show()
+
+ def product_group_detail_form_destroyed(self):
+ self.product_group_detail_form = None
+ if self.product_group_list_form is not None:
+ self.product_group_list_form.update_data(product_group_list())
+
+ def save_product_group(self, product_group):
+ if 'ProductGroupID' not in product_group:
+ save_product_group(product_group)
+ else:
+ update_product_group(product_group)
diff --git a/summer/views/product_group.py b/summer/views/product_group.py
new file mode 100644
index 0000000..f8785ba
--- /dev/null
+++ b/summer/views/product_group.py
@@ -0,0 +1,81 @@
+from decimal import Decimal, InvalidOperation
+
+from summer.models import session_scope
+from summer.models.validation_exception import ValidationError
+
+
+__author__ = 'tanshu'
+
+from summer.models.master import ProductGroup
+
+
+def save_product_group(json):
+ name = json['Name'].strip()
+ try:
+ max_discount = Decimal(json['MaxDiscount'])
+ if max_discount < 0 or max_discount > 1:
+ raise ValidationError("Fraction must be a decimal >= 0 <= 1")
+ except (ValueError, InvalidOperation):
+ raise ValidationError("Fraction must be a decimal >= 0 <= 1")
+ header = json['Header'].strip()
+ try:
+ sort_order = Decimal(json['SortOrder'])
+ if sort_order < 0:
+ raise ValidationError("Sort Order must be a decimal >= 0")
+ except (ValueError, InvalidOperation):
+ raise ValidationError("Sort Order must be a decimal >= 0")
+ is_active = json['IsActive']
+ item = ProductGroup(name, max_discount, header, sort_order, is_active)
+ with session_scope() as DBSession:
+ DBSession.add(item)
+ return product_group_info(item.id)
+
+
+def update_product_group(json):
+ with session_scope() as DBSession:
+ item = ProductGroup.by_id(json['ProductGroupID'], session=DBSession)
+ if item.is_fixture:
+ raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.name))
+ item.name = json['Name'].strip()
+ try:
+ max_discount = Decimal(json['MaxDiscount'])
+ if max_discount < 0 or max_discount > 1:
+ raise ValidationError("Fraction must be a decimal >= 0 <= 1")
+ except (ValueError, InvalidOperation):
+ raise ValidationError("Fraction must be a decimal >= 0 <= 1")
+ item.max_discount = max_discount
+ item.header = json['Header'].strip()
+ try:
+ sort_order = Decimal(json['SortOrder'])
+ if sort_order < 0:
+ raise ValidationError("Sort Order must be a decimal >= 0")
+ except (ValueError, InvalidOperation):
+ raise ValidationError("Sort Order must be a decimal >= 0")
+ item.sort_order = sort_order
+ item.is_active = json['IsActive']
+ return product_group_info(item.id)
+
+
+def show_list():
+ with session_scope() as DBSession:
+ list = DBSession.query(ProductGroup).order_by(ProductGroup.sort_order).order_by(ProductGroup.name)
+ product_groups = []
+ for item in list:
+ product_groups.append(
+ {'ProductGroupID': item.id, 'Name': item.name, 'MaxDiscount': item.max_discount, 'Header': item.header,
+ 'IsActive': item.is_active, 'IsFixture': item.is_fixture, 'SortOrder': item.sort_order})
+ return product_groups
+
+
+def product_group_info(id):
+ with session_scope() as DBSession:
+ if id is None:
+ item = {'Name': '', 'MaxDiscount': 1, 'Header': '', 'IsActive': True, 'IsFixture': False, 'SortOrder': 0}
+ else:
+ product_group = ProductGroup.by_id(id, session=DBSession)
+ return {'ProductGroupID': product_group.id, 'Name': product_group.name,
+ 'MaxDiscount': product_group.max_discount, 'Header': product_group.header,
+ 'IsActive': product_group.is_active, 'IsFixture': product_group.is_fixture,
+ 'SortOrder': product_group.sort_order}
+ return item
+
diff --git a/summer/views/role.py b/summer/views/role.py
index 78f3f6c..b3c3ea9 100644
--- a/summer/views/role.py
+++ b/summer/views/role.py
@@ -6,7 +6,7 @@ from summer.models.auth import Role, Permission
def save_role(json):
- role = Role(json['Name'])
+ role = Role(json['Name'].strip())
with session_scope() as DBSession:
DBSession.add(role)
add_permissions(role, json['Permissions'], DBSession)
@@ -16,7 +16,7 @@ def save_role(json):
def update_role(json):
with session_scope() as DBSession:
role = Role.by_id(json['RoleID'], session=DBSession)
- role.name = json['Name']
+ role.name = json['Name'].strip()
add_permissions(role, json['Permissions'], session=DBSession)
return role_info(role.id)
diff --git a/summer/views/user.py b/summer/views/user.py
index 7e236ce..0634da1 100644
--- a/summer/views/user.py
+++ b/summer/views/user.py
@@ -4,7 +4,7 @@ from summer.models.validation_exception import ValidationError
def save_user(json):
- user = User(json['Name'], json['Password'], json['LockedOut'])
+ user = User(json['Name'].strip(), json['Password'], json['LockedOut'])
with session_scope() as DBSession:
DBSession.add(user)
add_roles(user, json['Roles'], session=DBSession)
@@ -15,10 +15,10 @@ def update_user(json):
with session_scope() as DBSession:
user = User.by_id(json['UserID'], session=DBSession)
if user is None:
- raise ValidationError('User name / id not found')
+ raise ValidationError('User not found')
has_permission = True
if has_permission:
- user.name = json['Name']
+ user.name = json['Name'].strip()
user.locked_out = json['LockedOut']
add_roles(user, json['Roles'], session=DBSession)