Initial Commit: Still Trying things out
There is Router in the view.__init__ to handle routing. Trying out Qt Still. Looks good.
This commit is contained in:
56
.gitignore
vendored
Normal file
56
.gitignore
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# PyCharm
|
||||
.idea/
|
||||
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@ -0,0 +1,2 @@
|
||||
include *.txt *.ini *.cfg *.rst
|
||||
recursive-include brewman *.ico *.png *.css *.gif *.jpg *.txt *.js *.html *.map *.eot *.svg *.ttf *.ui
|
||||
31
setup.py
Normal file
31
setup.py
Normal file
@ -0,0 +1,31 @@
|
||||
import os
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
README = open(os.path.join(here, 'README.txt')).read()
|
||||
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
|
||||
|
||||
requires = [
|
||||
'SQLAlchemy', 'PyQt5',
|
||||
]
|
||||
|
||||
setup(name='summer',
|
||||
version='3.4',
|
||||
description='summer',
|
||||
long_description=README + '\n\n' + CHANGES,
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
],
|
||||
author='Tanshu',
|
||||
author_email='summer@tanshu.com',
|
||||
url='http://tanshu.com',
|
||||
keywords='',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=requires,
|
||||
tests_require=requires,
|
||||
test_suite="summer",
|
||||
)
|
||||
|
||||
304
summer/QtDesignerFiles/MainWindow.ui
Normal file
304
summer/QtDesignerFiles/MainWindow.ui
Normal file
@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>810</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Point of Sale</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QPushButton" name="btnLogin">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnSwipeLogin">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>10</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Swipe Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnSales">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>10</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sales</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnCustomers">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>490</x>
|
||||
<y>10</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Customers</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnProducts">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>650</x>
|
||||
<y>10</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Products</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnUsers">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>650</x>
|
||||
<y>120</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Users</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnProductGroups">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>120</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Product Groups</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnAdjustAdvance">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>490</x>
|
||||
<y>120</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Adjust Advance</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnReceiveAdvance">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>120</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Receive Advance</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnOpenBill">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>120</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Bill</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnSalesDetail">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>490</x>
|
||||
<y>230</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sales Detail</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnDiscountReport">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>340</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Discount Report</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnRoles">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>230</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Roles</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnSalesAnalysis">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>230</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sales Analysis</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnCashierCheckout">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>170</x>
|
||||
<y>230</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cashier Checkout</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnChangePassword">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>330</x>
|
||||
<y>340</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Change Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnVoids">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>340</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Voids / Reprints</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnBillDetails">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>650</x>
|
||||
<y>230</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Bill Details</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnExit">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>650</x>
|
||||
<y>340</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Exit</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="btnManagement">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>490</x>
|
||||
<y>340</y>
|
||||
<width>150</width>
|
||||
<height>100</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Management</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>btnLogin</tabstop>
|
||||
<tabstop>btnSwipeLogin</tabstop>
|
||||
<tabstop>btnSales</tabstop>
|
||||
<tabstop>btnCustomers</tabstop>
|
||||
<tabstop>btnProducts</tabstop>
|
||||
<tabstop>btnProductGroups</tabstop>
|
||||
<tabstop>btnOpenBill</tabstop>
|
||||
<tabstop>btnReceiveAdvance</tabstop>
|
||||
<tabstop>btnAdjustAdvance</tabstop>
|
||||
<tabstop>btnUsers</tabstop>
|
||||
<tabstop>btnRoles</tabstop>
|
||||
<tabstop>btnCashierCheckout</tabstop>
|
||||
<tabstop>btnSalesAnalysis</tabstop>
|
||||
<tabstop>btnSalesDetail</tabstop>
|
||||
<tabstop>btnBillDetails</tabstop>
|
||||
<tabstop>btnVoids</tabstop>
|
||||
<tabstop>btnDiscountReport</tabstop>
|
||||
<tabstop>btnChangePassword</tabstop>
|
||||
<tabstop>btnManagement</tabstop>
|
||||
<tabstop>btnExit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
98
summer/QtDesignerFiles/RoleDetail.ui
Normal file
98
summer/QtDesignerFiles/RoleDetail.ui
Normal file
@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UserDetails</class>
|
||||
<widget class="QDialog" name="UserDetails">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>397</width>
|
||||
<height>310</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>User Details</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="txtName"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Permissions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QListView" name="lvwPermissions"/>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>txtName</tabstop>
|
||||
<tabstop>lvwPermissions</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>UserDetails</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>227</x>
|
||||
<y>331</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>UserDetails</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>295</x>
|
||||
<y>337</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
37
summer/QtDesignerFiles/RoleList.ui
Normal file
37
summer/QtDesignerFiles/RoleList.ui
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>RoleList</class>
|
||||
<widget class="QMainWindow" name="RoleList">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Roles</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QTableView" name="tblRoles"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btnAddNew">
|
||||
<property name="text">
|
||||
<string>Add Role</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
141
summer/QtDesignerFiles/UserDetail.ui
Normal file
141
summer/QtDesignerFiles/UserDetail.ui
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UserDetails</class>
|
||||
<widget class="QDialog" name="UserDetails">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::WindowModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>397</width>
|
||||
<height>353</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>User Details</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="txtName"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="txtPassword">
|
||||
<property name="inputMask">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="chkLockedOut">
|
||||
<property name="text">
|
||||
<string>Locked Out</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QPushButton" name="btnSwipe">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Swipe Login</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QListView" name="lvwRoles"/>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Roles</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>txtName</tabstop>
|
||||
<tabstop>txtPassword</tabstop>
|
||||
<tabstop>chkLockedOut</tabstop>
|
||||
<tabstop>lvwRoles</tabstop>
|
||||
<tabstop>btnSwipe</tabstop>
|
||||
<tabstop>buttonBox</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>UserDetails</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>227</x>
|
||||
<y>331</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>UserDetails</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>295</x>
|
||||
<y>337</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
37
summer/QtDesignerFiles/UserList.ui
Normal file
37
summer/QtDesignerFiles/UserList.ui
Normal file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>UserList</class>
|
||||
<widget class="QMainWindow" name="UserList">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Users</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QTableView" name="tblUsers"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btnAddNew">
|
||||
<property name="text">
|
||||
<string>Add User</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
19
summer/__init__.py
Normal file
19
summer/__init__.py
Normal file
@ -0,0 +1,19 @@
|
||||
from PyQt5 import QtWidgets
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
from summer.models import initialize_sql
|
||||
from summer.templates.MainWindow import MainWindow
|
||||
|
||||
from summer.views import Router
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
engine = create_engine('postgresql://postgres:123456@localhost:5432/summer')
|
||||
initialize_sql(engine)
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
router = Router()
|
||||
window = MainWindow(router)
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
80
summer/models/__init__.py
Normal file
80
summer/models/__init__.py
Normal file
@ -0,0 +1,80 @@
|
||||
__author__ = 'tanshu'
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
|
||||
Session = sessionmaker(expire_on_commit=False)
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def session_scope():
|
||||
"""Provide a transactional scope around a series of operations."""
|
||||
session = Session()
|
||||
try:
|
||||
yield session
|
||||
session.commit()
|
||||
except:
|
||||
session.rollback()
|
||||
raise
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def initialize_sql(engine):
|
||||
Session.configure(bind=engine)
|
||||
Base.metadata.bind = engine
|
||||
if not schema_exists(engine):
|
||||
fixtures(engine)
|
||||
|
||||
|
||||
def schema_exists(engine):
|
||||
from models.master import DbSetting
|
||||
|
||||
with engine.connect() as connection:
|
||||
return engine.dialect.has_table(connection, DbSetting.__tablename__)
|
||||
|
||||
|
||||
def fixtures(engine):
|
||||
import uuid
|
||||
from .auth import Permission, Role, User, role_group, user_role
|
||||
from .master import Tax, Modifier, ProductGroup, FoodTable, DbSetting
|
||||
from .voucher import RunningTable
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
with session_scope() as DBSession:
|
||||
user = User('Admin', '123456', False, id=uuid.UUID('8de98592-76d9-c74d-bb3f-d6184d388b5a'))
|
||||
DBSession.add(user)
|
||||
permissions = [Permission('Users', uuid.UUID('c5b7d9d7-f178-0e45-8ea4-bf4e08ec901b')),
|
||||
Permission('Taxes', uuid.UUID('c830872a-ba1b-4593-800e-dd0573eea930')),
|
||||
Permission('Product Groups', uuid.UUID('08413a22-cf88-fd43-b2b7-365d2951d99f')),
|
||||
Permission('Products', uuid.UUID('74fa6d21-eebb-e14c-8153-bebc57190ab4')),
|
||||
Permission('Food Tables', uuid.UUID('f31a8e97-9ee0-4dbb-9016-637c03fbb646')),
|
||||
Permission('Modifiers', uuid.UUID('0e88b9f8-3ed6-4304-b3cf-5a66c96ac650'))]
|
||||
owner = Role('Owner', uuid.UUID('52e08c0c-048a-784f-be10-6e129ad4b5d4'))
|
||||
DBSession.add(owner)
|
||||
for permission in permissions:
|
||||
DBSession.add(permission)
|
||||
owner.permissions.append(permission)
|
||||
user.roles.append(owner)
|
||||
tax = Tax('Tax Free', 0, '08c3101d-cdab-449f-9574-a025c8ae5556', True)
|
||||
modifiers = [Modifier('Spicy', True, uuid.UUID('d1b76d14-ab02-45c8-8a37-3197113e7bd5')),
|
||||
Modifier('Mild', True, uuid.UUID('c49ed960-6635-4347-8b30-41258f372894')),
|
||||
Modifier('Less Oily', True, uuid.UUID('972593ee-2196-4978-a4b2-e91e9e5cac5d'))]
|
||||
for modifier in modifiers:
|
||||
DBSession.add(modifier)
|
||||
pgrs = [ProductGroup('Beer', 1, 'Beer', 0, True),
|
||||
ProductGroup('Whisky', 1, 'Alcohol', 0, True),
|
||||
ProductGroup('Wine', 1, 'Alcohol', 0, True),
|
||||
ProductGroup('Main Course', 1, 'Food', 0, True),
|
||||
ProductGroup('Appetizers', 1, 'Food', 0, True)]
|
||||
for pgr in pgrs:
|
||||
DBSession.add(pgr)
|
||||
|
||||
for i in range(1, 20):
|
||||
DBSession.add(FoodTable('Table No ' + str(i), 'Test Location'))
|
||||
DBSession.add(tax)
|
||||
137
summer/models/auth.py
Normal file
137
summer/models/auth.py
Normal file
@ -0,0 +1,137 @@
|
||||
__author__ = 'tanshu'
|
||||
|
||||
import uuid
|
||||
from hashlib import md5
|
||||
|
||||
from sqlalchemy.schema import ForeignKey, Table
|
||||
from sqlalchemy import Column, Boolean, Unicode
|
||||
from sqlalchemy.orm import synonym, relationship
|
||||
|
||||
from models.guidtype import GUID
|
||||
from models import Base
|
||||
|
||||
|
||||
def encrypt(val):
|
||||
return md5(val.encode('utf-8') + "Salt".encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
user_role = Table(
|
||||
'auth_user_roles', Base.metadata,
|
||||
Column('user_role_id', GUID(), primary_key=True, default=uuid.uuid4),
|
||||
Column('user_id', GUID(), ForeignKey('auth_users.user_id')),
|
||||
Column('role_id', GUID(), ForeignKey('auth_roles.role_id'))
|
||||
)
|
||||
|
||||
role_group = Table(
|
||||
'auth_permission_roles', Base.metadata,
|
||||
Column('permission_role_id', GUID(), primary_key=True, default=uuid.uuid4),
|
||||
Column('permission_id', GUID(), ForeignKey('auth_permissions.permission_id')),
|
||||
Column('role_id', GUID(), ForeignKey('auth_roles.role_id'))
|
||||
)
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'auth_users'
|
||||
|
||||
id = Column('user_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True)
|
||||
_password = Column('password', Unicode(60))
|
||||
locked_out = Column('locked_out', Boolean)
|
||||
|
||||
roles = relationship('Role', secondary=user_role)
|
||||
|
||||
def _get_password(self):
|
||||
return self._password
|
||||
|
||||
def _set_password(self, password):
|
||||
self._password = encrypt(password)
|
||||
|
||||
password = property(_get_password, _set_password)
|
||||
password = synonym('_password', descriptor=password)
|
||||
|
||||
|
||||
@property
|
||||
def __name__(self):
|
||||
return self.name
|
||||
|
||||
def __init__(self, name=None, password=None, locked_out=None, id=None):
|
||||
self.name = name
|
||||
self.password = password
|
||||
self.locked_out = locked_out
|
||||
self.id = id
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name, *, session=None):
|
||||
return session.query(cls).filter(cls.name.ilike(name)).first()
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id, *, session=None):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return session.query(cls).filter(cls.id == id).one()
|
||||
|
||||
@classmethod
|
||||
def auth(cls, name, password):
|
||||
user = cls.by_name(name)
|
||||
if not user:
|
||||
return False, None
|
||||
if user.password != encrypt(password) or user.locked_out:
|
||||
return False, None
|
||||
else:
|
||||
return True, user
|
||||
|
||||
@classmethod
|
||||
def list(cls, *, session=None):
|
||||
return session.query(cls).order_by(cls.name).all()
|
||||
|
||||
@classmethod
|
||||
def query(cls, *, session=None):
|
||||
return session.query(cls)
|
||||
|
||||
@classmethod
|
||||
def filtered_list(cls, name, *, session=None):
|
||||
query = session.query(cls)
|
||||
for item in name.split():
|
||||
query = query.filter(cls.name.ilike('%' + item + '%'))
|
||||
return query.order_by(cls.name)
|
||||
|
||||
|
||||
class Role(Base):
|
||||
__tablename__ = 'auth_roles'
|
||||
|
||||
id = Column('role_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True)
|
||||
|
||||
def __init__(self, name=None, id=None):
|
||||
self.name = name
|
||||
self.id = id
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id, *, session=None):
|
||||
return session.query(cls).filter(cls.id == id).one()
|
||||
|
||||
@classmethod
|
||||
def list(cls, *, session=None):
|
||||
return session.query(cls).order_by(cls.name).all()
|
||||
|
||||
|
||||
class Permission(Base):
|
||||
__tablename__ = 'auth_permissions'
|
||||
|
||||
id = Column('permission_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True)
|
||||
|
||||
groups = relationship('Role', secondary=role_group, backref='permissions')
|
||||
|
||||
def __init__(self, name=None, id=None):
|
||||
self.name = name
|
||||
self.id = id
|
||||
|
||||
@classmethod
|
||||
def list(cls, *, session=None):
|
||||
return session.query(cls).order_by(cls.name).all()
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id, *, session=None):
|
||||
return session.query(cls).filter(cls.id == id).one()
|
||||
|
||||
60
summer/models/guidtype.py
Normal file
60
summer/models/guidtype.py
Normal file
@ -0,0 +1,60 @@
|
||||
__author__ = 'tanshu'
|
||||
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.types import TypeDecorator, CHAR, Binary
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.dialects.sqlite import BLOB
|
||||
|
||||
|
||||
class GUID(TypeDecorator):
|
||||
"""Platform-independent GUID type.
|
||||
|
||||
Uses Postgresql's UUID type, otherwise uses
|
||||
CHAR(32), storing as stringified hex values.
|
||||
|
||||
"""
|
||||
impl = Binary
|
||||
|
||||
# if dialect.value == 'postgresql':
|
||||
# impl = CHAR
|
||||
# elif dialect.value == 'mysql':
|
||||
# impl = MSBinary
|
||||
# elif dialect.valie == 'sqlite':
|
||||
# impl = Binary
|
||||
# else:
|
||||
# impl = Binary
|
||||
|
||||
def load_dialect_impl(self, dialect):
|
||||
if dialect.name == 'postgresql':
|
||||
return dialect.type_descriptor(UUID())
|
||||
elif dialect.name == 'sqlite':
|
||||
return dialect.type_descriptor(BLOB())
|
||||
else:
|
||||
return dialect.type_descriptor(CHAR(32))
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is None:
|
||||
return None
|
||||
elif dialect.name == 'postgresql':
|
||||
return str(value)
|
||||
elif not isinstance(value, uuid.UUID):
|
||||
raise ValueError('value %s is not a valid uuid.UUID' % value)
|
||||
else:
|
||||
return value.bytes
|
||||
# if not isinstance(value, uuid.UUID):
|
||||
# return "%.32x" % uuid.UUID(value)
|
||||
# else:
|
||||
# # hexstring
|
||||
# return "%.32x" % value
|
||||
|
||||
def process_result_value(self, value, dialect=None):
|
||||
if value is None:
|
||||
return None
|
||||
elif isinstance(value, bytes):
|
||||
return uuid.UUID(bytes=value)
|
||||
else:
|
||||
return uuid.UUID(value)
|
||||
|
||||
def is_mutable(self):
|
||||
return False
|
||||
331
summer/models/master.py
Normal file
331
summer/models/master.py
Normal file
@ -0,0 +1,331 @@
|
||||
__author__ = 'tanshu'
|
||||
|
||||
import uuid
|
||||
from sqlalchemy import UniqueConstraint, Column, Integer, Unicode, Numeric, Boolean, ForeignKey, func, PickleType
|
||||
from sqlalchemy.orm import relationship
|
||||
from models import Base
|
||||
from models.guidtype import GUID
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = 'products'
|
||||
__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)
|
||||
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)
|
||||
|
||||
service_charge = Column('service_charge', Numeric, nullable=False)
|
||||
sc_taxable = Column('sc_taxable', Boolean, nullable=False)
|
||||
|
||||
is_active = Column('is_active', Boolean, nullable=False)
|
||||
is_fixture = Column('is_fixture', Boolean, nullable=False)
|
||||
sort_order = Column('sort_order', Integer, nullable=False)
|
||||
|
||||
service_tax = relationship('Tax', foreign_keys=service_tax_id, backref='products_service_tax')
|
||||
vat = relationship('Tax', foreign_keys=vat_id, backref='products_vat')
|
||||
|
||||
def __init__(self, code=None, name=None, units=None, product_group_id=None, price=None, happy_hour=None,
|
||||
service_tax_id=None, vat_id=None, service_charge=None, sc_taxable=None, is_active=None,
|
||||
sort_order=None, is_fixture=False):
|
||||
self.code = code
|
||||
self.name = name
|
||||
self.units = units
|
||||
self.product_group_id = product_group_id
|
||||
self.price = price
|
||||
self.happy_hour = happy_hour
|
||||
self.service_tax_id = service_tax_id
|
||||
self.vat_id = vat_id
|
||||
self.service_charge = service_charge
|
||||
self.sc_taxable = sc_taxable
|
||||
self.is_active = is_active
|
||||
self.sort_order = sort_order
|
||||
self.is_fixture = is_fixture
|
||||
|
||||
@property
|
||||
def full_name(self):
|
||||
return "{0} ({1})".format(self.name, self.units)
|
||||
|
||||
@classmethod
|
||||
def list(cls, name, active, *, DBSession=None):
|
||||
query = DBSession.query(cls)
|
||||
if active is not None:
|
||||
query = query.filter(cls.is_active != active)
|
||||
for item in name.split():
|
||||
query = query.filter(cls.name.ilike('%' + item + '%'))
|
||||
return query.order_by(cls.name)
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name, *, DBSession=None):
|
||||
return DBSession.query(cls).filter(cls.name == name).first()
|
||||
|
||||
@classmethod
|
||||
def by_full_name(cls, full_name, *, DBSession=None):
|
||||
return DBSession.query(cls).filter(cls.name + ' (' + cls.units + ')' == full_name).first()
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id, *, DBSession=None):
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def query(cls, *, DBSession=None):
|
||||
return DBSession.query(cls)
|
||||
|
||||
def create(self, *, DBSession=None):
|
||||
code = DBSession.query(func.max(Product.code)).one()[0]
|
||||
if code is None:
|
||||
self.code = 1
|
||||
else:
|
||||
self.code = code + 1
|
||||
DBSession.add(self)
|
||||
return self
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
if self.is_fixture:
|
||||
return False, ('Fixture', "{0} cannot be edited or deleted.".format(self.name))
|
||||
if self.is_active:
|
||||
return False, ('Active', 'Product is active')
|
||||
if len(self.inventories) > 0 and not advanced_delete:
|
||||
return False, ('In Use', 'Product has entries')
|
||||
return True, ''
|
||||
|
||||
@classmethod
|
||||
def suspense(cls):
|
||||
return uuid.UUID('aa79a643-9ddc-4790-ac7f-a41f9efb4c15')
|
||||
|
||||
|
||||
class ProductGroup(Base):
|
||||
__tablename__ = 'product_groups'
|
||||
|
||||
id = Column('product_group_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||
max_discount = Column('max_discount', Numeric, nullable=False)
|
||||
header = Column('header', Unicode(255), nullable=False)
|
||||
|
||||
is_active = Column('is_active', Boolean, nullable=False)
|
||||
is_fixture = Column('is_fixture', Boolean, nullable=False)
|
||||
sort_order = Column('sort_order', Integer, nullable=False)
|
||||
|
||||
products = relationship('Product', backref='product_group')
|
||||
|
||||
def __init__(self, name=None, max_discount=1, header=None, sort_order=None, is_active=True, id=None,
|
||||
is_fixture=False):
|
||||
self.name = name
|
||||
self.max_discount = max_discount
|
||||
self.header = header
|
||||
self.sort_order = sort_order
|
||||
self.is_active = is_active
|
||||
if id is not None and not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
self.id = id
|
||||
self.is_fixture = is_fixture
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(cls).order_by(cls.name)
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
if self.is_fixture:
|
||||
return False, ('Fixture', "{0} cannot be edited or deleted.".format(self.name))
|
||||
#if len(self.inventories) > 0 and not advanced_delete:
|
||||
# return False, 'Product has entries'
|
||||
return True, ''
|
||||
|
||||
|
||||
class Tax(Base):
|
||||
__tablename__ = 'taxes'
|
||||
|
||||
id = Column('tax_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||
rate = Column('rate', Numeric, nullable=False)
|
||||
is_fixture = Column('is_fixture', Boolean, nullable=False)
|
||||
|
||||
def __init__(self, name=None, rate=None, id=None, is_fixture=False):
|
||||
if id is not None and not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.rate = rate
|
||||
self.is_fixture = is_fixture
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
return DBSession.query(cls).filter(cls.name == name).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(cls).order_by(cls.name).all()
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
if self.is_fixture:
|
||||
return False, ('Fixture', "{0} cannot be edited or deleted.".format(self.name))
|
||||
#if len(self.inventories) > 0 and not advanced_delete:
|
||||
# return False, 'Product has entries'
|
||||
return True, ''
|
||||
|
||||
@classmethod
|
||||
def tax_free_id(cls):
|
||||
return uuid.UUID('08c3101d-cdab-449f-9574-a025c8ae5556')
|
||||
|
||||
@classmethod
|
||||
def tax_free(cls):
|
||||
return {'TaxID': uuid.UUID('08c3101d-cdab-449f-9574-a025c8ae5556')}
|
||||
|
||||
|
||||
class DbSetting(Base):
|
||||
__tablename__ = 'auth_settings'
|
||||
|
||||
id = Column('setting_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||
data = Column('data', PickleType)
|
||||
|
||||
def __init__(self, id=None, name=None, data=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.data = data
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
return DBSession.query(cls).filter(cls.name == name).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(cls).order_by(cls.name).all()
|
||||
|
||||
|
||||
class FoodTable(Base):
|
||||
__tablename__ = 'food_tables'
|
||||
|
||||
id = Column('food_table_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||
location = Column('location', Unicode(255), nullable=False)
|
||||
|
||||
def __init__(self, name=None, location=None, id=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.location = location
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
return DBSession.query(cls).filter(cls.name == name).first()
|
||||
|
||||
@classmethod
|
||||
def by_normalized_name(cls, name):
|
||||
return DBSession.query(cls).filter(cls.name == name.replace('-', ' ')).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(cls).order_by(cls.name).all()
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
#if len(self.inventories) > 0 and not advanced_delete:
|
||||
# return False, 'Product has entries'
|
||||
return True, ''
|
||||
|
||||
|
||||
class TableStatus:
|
||||
def __init__(self, id, status):
|
||||
self.id = id
|
||||
self.status = status
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
list = []
|
||||
list.append(TableStatus(1, 'Locked'))
|
||||
list.append(TableStatus(2, 'Running'))
|
||||
list.append(TableStatus(3, 'Billed'))
|
||||
return list
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
list = cls.list()
|
||||
for item in list:
|
||||
if item.name == name:
|
||||
return item
|
||||
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
list = cls.list()
|
||||
for item in list:
|
||||
if item.id == id:
|
||||
return item
|
||||
|
||||
|
||||
class Modifier(Base):
|
||||
__tablename__ = 'modifiers'
|
||||
|
||||
id = Column('modifier_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
name = Column('name', Unicode(255), unique=True, nullable=False)
|
||||
is_active = Column('is_active', Boolean, nullable=False)
|
||||
|
||||
groups = relationship('ModifierProductGroup', backref='modifier')
|
||||
|
||||
def __init__(self, name=None, is_active=None, id=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.is_active = is_active
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
if not isinstance(id, uuid.UUID):
|
||||
id = uuid.UUID(id)
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
return DBSession.query(cls).filter(cls.name == name).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
return DBSession.query(cls).order_by(cls.name).all()
|
||||
|
||||
def can_delete(self, advanced_delete):
|
||||
#if len(self.inventories) > 0 and not advanced_delete:
|
||||
# return False, 'Product has entries'
|
||||
return True, ''
|
||||
|
||||
|
||||
class ModifierProductGroup(Base):
|
||||
__tablename__ = 'modifier_product_groups'
|
||||
|
||||
id = Column('modifier_product_groups_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
modifier_id = Column('modifier_id', GUID(), ForeignKey('modifiers.modifier_id'), nullable=False)
|
||||
product_group_id = Column('product_group_id', GUID(), ForeignKey('product_groups.product_group_id'), nullable=True)
|
||||
|
||||
group = relationship('ProductGroup', backref='modifiers')
|
||||
|
||||
def __init__(self, modifier_id=None, product_group_id=None, id=None):
|
||||
self.id = id
|
||||
self.modifier_id = modifier_id
|
||||
self.product_group_id = product_group_id
|
||||
40
summer/models/validation_exception.py
Normal file
40
summer/models/validation_exception.py
Normal file
@ -0,0 +1,40 @@
|
||||
import json
|
||||
|
||||
__author__ = 'tanshu'
|
||||
|
||||
from sqlalchemy.exc import OperationalError, IntegrityError
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
def __init__(self, header, message, Errors=None):
|
||||
self.header = header
|
||||
self.message = message
|
||||
# Call the base class constructor with the parameters it needs
|
||||
Exception.__init__(self, (header, message))
|
||||
|
||||
# Now for your custom code...
|
||||
self.Errors = Errors
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
def json(self):
|
||||
return (self.header, self.message)
|
||||
#
|
||||
#
|
||||
# def TryCatchFunction(f):
|
||||
# def _decorator(self, *args, **kwargs):
|
||||
# try:
|
||||
# return f(self, *args, **kwargs)
|
||||
# except ValidationError as ex:
|
||||
# transaction.abort()
|
||||
# response = Response(json.dumps(ex.json()))
|
||||
# response.status_int = 500
|
||||
# return response
|
||||
# except (ValueError, KeyError, AttributeError, TypeError, OperationalError, IntegrityError) as ex:
|
||||
# transaction.abort()
|
||||
# response = Response(json.dumps(('Failed validation', str(ex))))
|
||||
# response.status_int = 500
|
||||
# return response
|
||||
#
|
||||
# return _decorator
|
||||
262
summer/models/voucher.py
Normal file
262
summer/models/voucher.py
Normal file
@ -0,0 +1,262 @@
|
||||
__author__ = 'tanshu'
|
||||
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy import Column, Integer, DateTime, Numeric, ForeignKey, Boolean, UniqueConstraint
|
||||
from sqlalchemy.orm import relationship, synonym, backref
|
||||
|
||||
from models.master import Tax
|
||||
from models.guidtype import GUID
|
||||
from models import Base
|
||||
|
||||
|
||||
class VoucherType:
|
||||
def __init__(self, id, name, show_in_choices, group):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.show_in_choices = show_in_choices
|
||||
self.group = group
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
list = []
|
||||
list.append(VoucherType(1, 'Regular Bill', True, 2))
|
||||
list.append(VoucherType(2, 'No Charge', True, 3))
|
||||
list.append(VoucherType(3, 'Take Away', True, 2))
|
||||
list.append(VoucherType(4, 'Staff Bill', True, 4))
|
||||
return list
|
||||
|
||||
@classmethod
|
||||
def by_name(cls, name):
|
||||
list = cls.list()
|
||||
for item in list:
|
||||
if item.name == name:
|
||||
return item
|
||||
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
list = cls.list()
|
||||
for item in list:
|
||||
if item.id == id:
|
||||
return item
|
||||
|
||||
|
||||
class Voucher(Base):
|
||||
__tablename__ = 'vouchers'
|
||||
__tableagrs__ = (UniqueConstraint('voucher_type', 'bill_id'))
|
||||
id = Column('voucher_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
date = Column('date', DateTime(timezone=True), nullable=False)
|
||||
creation_date = Column('creation_date', DateTime(timezone=True), nullable=False)
|
||||
last_edit_date = Column('last_edit_date', DateTime(timezone=True), nullable=False)
|
||||
_type = Column('voucher_type', Integer, nullable=False)
|
||||
kot_id = Column('kot_id', Integer, unique=True, nullable=False)
|
||||
bill_id = Column('bill_id', Integer, nullable=True)
|
||||
pax = Column('pax', Integer, nullable=False)
|
||||
per_chair = Column('per_chair', Boolean, nullable=False)
|
||||
|
||||
user_id = Column('user_id', GUID(), ForeignKey('auth_users.user_id'), nullable=False)
|
||||
food_table_id = Column('table_id', GUID(), ForeignKey('food_tables.food_table_id'), nullable=False)
|
||||
|
||||
user = relationship('User', primaryjoin="User.id==Voucher.user_id", cascade=None)
|
||||
food_table = relationship('FoodTable', primaryjoin="FoodTable.id==Voucher.food_table_id", cascade=None)
|
||||
|
||||
kots = relationship('Kot', backref='voucher', cascade="delete, delete-orphan", cascade_backrefs=False)
|
||||
|
||||
def _get_type(self):
|
||||
return self._type
|
||||
# for item in VoucherType.list():
|
||||
# if self._type == item.id:
|
||||
# return item
|
||||
|
||||
def _set_type(self, value):
|
||||
if type(value) == int:
|
||||
self._type = value
|
||||
else:
|
||||
self._type = value.id
|
||||
|
||||
type = property(_get_type, _set_type)
|
||||
type = synonym('_type', descriptor=type)
|
||||
|
||||
@property
|
||||
def __name__(self):
|
||||
return self.name
|
||||
|
||||
def __init__(self, date=None, creation_date=None, last_edit_date=None, type=None, kot_id=None, bill_id=None,
|
||||
user_id=None, food_table_id=None, pax=None, per_seat=None, id=None):
|
||||
self.id = id
|
||||
self.date = date
|
||||
self.creation_date = creation_date
|
||||
self.last_edit_date = last_edit_date
|
||||
self.type = type
|
||||
self.kot_id = kot_id
|
||||
self.bill_id = bill_id
|
||||
self.user_id = user_id
|
||||
self.food_table_id = food_table_id
|
||||
self.pax = pax
|
||||
self.per_seat = per_seat
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id, *, DBSession=None):
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls, *, DBSession=None):
|
||||
return DBSession.query(cls).all()
|
||||
|
||||
@classmethod
|
||||
def query(cls, *, DBSession=None):
|
||||
return DBSession.query(cls)
|
||||
|
||||
|
||||
class Kot(Base):
|
||||
__tablename__ = 'kots'
|
||||
|
||||
id = Column('kot_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
voucher_id = Column('voucher_id', GUID(), ForeignKey('vouchers.voucher_id'), nullable=False)
|
||||
code = Column('code', Integer, unique=True, nullable=False)
|
||||
food_table_id = Column('table_id', GUID(), ForeignKey('food_tables.food_table_id'), nullable=False)
|
||||
date = Column('date', DateTime, nullable=False)
|
||||
user_id = Column('user_id', GUID(), ForeignKey('auth_users.user_id'), nullable=False)
|
||||
|
||||
food_table = relationship('FoodTable', primaryjoin="FoodTable.id==Kot.food_table_id", cascade=None)
|
||||
|
||||
@hybrid_property
|
||||
def signed_amount(self):
|
||||
return self.debit * self.amount
|
||||
|
||||
@property
|
||||
def __name__(self):
|
||||
return self.name
|
||||
|
||||
def __init__(self, code=None, food_table_id=None, date=None, user_id=None, voucher_id=None, id=None):
|
||||
self.id = id
|
||||
self.voucher_id = voucher_id
|
||||
self.code = code
|
||||
self.food_table_id = food_table_id
|
||||
self.date = date
|
||||
self.user_id = user_id
|
||||
|
||||
@classmethod
|
||||
def by_id(cls, id):
|
||||
return DBSession.query(cls).filter(cls.id == id).first()
|
||||
|
||||
@classmethod
|
||||
def list(cls, voucher_id):
|
||||
return DBSession.query(cls).filter(cls.voucher_id == voucher_id).all()
|
||||
|
||||
@classmethod
|
||||
def query(cls):
|
||||
return DBSession.query(cls)
|
||||
|
||||
|
||||
class Inventory(Base):
|
||||
__tablename__ = 'inventories'
|
||||
# __tableagrs__ = (UniqueConstraint('VoucherID', 'ProductID'))
|
||||
id = Column('inventory_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
kot_id = Column('kot_id', GUID(), ForeignKey('kots.kot_id'), nullable=False)
|
||||
chair = Column('chair', Integer, nullable=False)
|
||||
product_id = Column('product_id', GUID(), ForeignKey('products.product_id'), nullable=False)
|
||||
quantity = Column('quantity', Numeric, nullable=False)
|
||||
price = Column('rate', Numeric, nullable=False)
|
||||
discount = Column('discount', Numeric, nullable=False)
|
||||
is_happy_hour = Column('is_happy_hour', Boolean, nullable=False)
|
||||
_service_tax_rate = Column('service_tax_rate', Numeric, nullable=False)
|
||||
service_tax_id = Column('service_tax_id', GUID(), ForeignKey('taxes.tax_id'), nullable=False)
|
||||
_vat_rate = Column('vat_rate', Numeric, nullable=False)
|
||||
vat_id = Column('vat_id', GUID(), ForeignKey('taxes.tax_id'), nullable=False)
|
||||
|
||||
service_charge = Column('service_charge', Numeric, nullable=False)
|
||||
sc_taxable = Column('sc_taxable', Boolean, nullable=False)
|
||||
|
||||
service_tax = relationship('Tax', foreign_keys=service_tax_id)
|
||||
vat = relationship('Tax', foreign_keys=vat_id)
|
||||
|
||||
def __init__(self, chair=0, product_id=None, quantity=None, price=None, discount=None, is_happy_hour=None,
|
||||
service_tax_id=None, service_tax_rate=None, vat_id=None, vat_rate=None, service_charge=None,
|
||||
sc_taxable=None, kot_id=None, id=None):
|
||||
self.id = id
|
||||
self.kot_id = kot_id
|
||||
self.chair = chair
|
||||
self.product_id = product_id
|
||||
self.quantity = quantity
|
||||
self.price = price
|
||||
self.discount = discount
|
||||
self.is_happy_hour = is_happy_hour
|
||||
self.service_tax_id = service_tax_id
|
||||
self.service_tax_rate = service_tax_rate
|
||||
self.vat_id = vat_id
|
||||
self.vat_rate = vat_rate
|
||||
self.service_charge = service_charge
|
||||
self.sc_taxable = sc_taxable
|
||||
|
||||
def _get_service_tax_rate(self):
|
||||
if self._service_tax_rate is not None:
|
||||
return self._service_tax_rate
|
||||
if self.service_tax is not None:
|
||||
return self.service_tax.rate
|
||||
if self.service_tax_id is not None:
|
||||
return Tax.by_id(self.service_tax_id).rate
|
||||
return None
|
||||
|
||||
def _set_service_tax_rate(self, value):
|
||||
self._service_tax_rate = value
|
||||
|
||||
service_tax_rate = property(_get_service_tax_rate, _set_service_tax_rate)
|
||||
service_tax_rate = synonym('_service_tax_rate', descriptor=service_tax_rate)
|
||||
|
||||
def _get_vat_rate(self):
|
||||
if self._vat_rate is not None:
|
||||
return self._vat_rate
|
||||
if self.vat is not None:
|
||||
return self.vat.rate
|
||||
if self.vat_id is not None:
|
||||
return Tax.by_id(self.vat_id).rate
|
||||
return None
|
||||
|
||||
def _set_vat_rate(self, value):
|
||||
self._vat_rate = value
|
||||
|
||||
vat_rate = property(_get_vat_rate, _set_vat_rate)
|
||||
vat_rate = synonym('_vat_rate', descriptor=vat_rate)
|
||||
|
||||
@hybrid_property
|
||||
def amount(self):
|
||||
if self.is_happy_hour:
|
||||
return 0
|
||||
base = self.quantity * self.rate * (1 - self.discount)
|
||||
if self.sc_taxable:
|
||||
return base * (1 + self.service_charge) * (1 + self.service_tax_rate + self.vat_rate)
|
||||
return base * (1 + self.service_charge + self.service_tax_rate + self.vat_rate)
|
||||
|
||||
|
||||
class RunningTable(Base):
|
||||
__tablename__ = 'running_tables'
|
||||
id = Column('running_table_id', GUID(), primary_key=True, default=uuid.uuid4)
|
||||
voucher_id = Column('voucher_id', GUID(), ForeignKey('vouchers.voucher_id'), nullable=False, unique=True)
|
||||
food_table_id = Column('food_table_id', GUID(), ForeignKey('food_tables.food_table_id'), nullable=False,
|
||||
unique=True)
|
||||
_status = Column('table_status', Integer, nullable=False)
|
||||
|
||||
food_table = relationship('FoodTable', backref=backref('info', uselist=False))
|
||||
|
||||
def _get_status(self):
|
||||
for item in RunningTable.list():
|
||||
if self._status == item.id:
|
||||
return item
|
||||
|
||||
def _set_status(self, value):
|
||||
if type(value) == int:
|
||||
self._status = value
|
||||
else:
|
||||
self._status = value.id
|
||||
|
||||
status = property(_get_status, _set_status)
|
||||
status = synonym('_status', descriptor=status)
|
||||
|
||||
def __init__(self, id=None, voucher_id=None, food_table_id=None, status=None):
|
||||
self.id = id
|
||||
self.voucher_id = voucher_id
|
||||
self.food_table_id = food_table_id
|
||||
self.status = status
|
||||
21
summer/templates/MainWindow.py
Normal file
21
summer/templates/MainWindow.py
Normal file
@ -0,0 +1,21 @@
|
||||
__author__ = 'tanshu'
|
||||
from PyQt5 import uic
|
||||
|
||||
|
||||
# package, resource = 'summer:QtDesignerFiles/MainWindow.ui'.split(':', 1)
|
||||
# file = pkg_resources.resource_stream(package, resource)
|
||||
file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/MainWindow.ui'
|
||||
base, form = uic.loadUiType(file)
|
||||
|
||||
|
||||
class MainWindow(base, form):
|
||||
def __init__(self, router, parent=None):
|
||||
super(MainWindow, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.btnExit.clicked.connect(self.handle_exit)
|
||||
self.btnUsers.clicked.connect(router.show_users)
|
||||
self.btnRoles.clicked.connect(router.show_roles)
|
||||
|
||||
def handle_exit(self):
|
||||
self.close()
|
||||
|
||||
56
summer/templates/RoleDetail.py
Normal file
56
summer/templates/RoleDetail.py
Normal file
@ -0,0 +1,56 @@
|
||||
__author__ = 'tanshu'
|
||||
from PyQt5 import uic, QtCore
|
||||
|
||||
|
||||
file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/RoleDetail.ui'
|
||||
base, form = uic.loadUiType(file)
|
||||
|
||||
|
||||
class RoleDetail(base, form):
|
||||
def __init__(self, router, role, parent=None):
|
||||
super(RoleDetail, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.router = router
|
||||
self.role = role
|
||||
self.txtName.setText(role['Name'])
|
||||
self.accepted.connect(self.save_role)
|
||||
self.lvwPermissions.setModel(RolesListModel(self.role['Permissions']))
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
|
||||
|
||||
def save_role(self):
|
||||
self.role['Name'] = self.txtName.text()
|
||||
self.router.save_role(self.role)
|
||||
|
||||
|
||||
class RolesListModel(QtCore.QAbstractListModel):
|
||||
def __init__(self, permissions=None, parent=None):
|
||||
QtCore.QAbstractListModel.__init__(self, parent)
|
||||
self.permissions = permissions if permissions is not None else []
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.permissions)
|
||||
|
||||
def data(self, index, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
item = self.permissions[index.row()]
|
||||
return item['Name']
|
||||
elif role == QtCore.Qt.CheckStateRole:
|
||||
item = self.permissions[index.row()]
|
||||
return QtCore.Qt.Checked if item['Enabled'] else QtCore.Qt.Unchecked
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return 'Permissions'
|
||||
else:
|
||||
return section + 1
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
if role == QtCore.Qt.CheckStateRole:
|
||||
item = self.permissions[index.row()]
|
||||
item['Enabled'] = False if value == 0 else True
|
||||
return True
|
||||
|
||||
|
||||
def flags(self, index):
|
||||
return QtCore.QAbstractListModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable
|
||||
63
summer/templates/RoleList.py
Normal file
63
summer/templates/RoleList.py
Normal file
@ -0,0 +1,63 @@
|
||||
__author__ = 'tanshu'
|
||||
from PyQt5 import uic, QtCore, QtWidgets
|
||||
|
||||
|
||||
file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/RoleList.ui'
|
||||
base, form = uic.loadUiType(file)
|
||||
|
||||
|
||||
class RoleList(base, form):
|
||||
def __init__(self, router, roles, parent=None):
|
||||
super(RoleList, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.router = router
|
||||
self.roles = roles
|
||||
self.model = RolesTableModel(roles)
|
||||
self.tblRoles.setModel(self.model)
|
||||
self.tblRoles.doubleClicked.connect(self.show_role)
|
||||
self.btnAddNew.clicked.connect(self.add_new_role)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
|
||||
self.tblRoles.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
|
||||
def update_data(self, data):
|
||||
self.roles = data
|
||||
self.model = RolesTableModel(self.roles)
|
||||
self.tblRoles.setModel(self.model)
|
||||
|
||||
def show_role(self, index):
|
||||
role = self.model.roles[index.row()]
|
||||
self.router.show_role(role['RoleID'])
|
||||
|
||||
def add_new_role(self):
|
||||
self.router.show_role(None)
|
||||
|
||||
|
||||
class RolesTableModel(QtCore.QAbstractTableModel):
|
||||
def __init__(self, roles=None, parent=None):
|
||||
QtCore.QAbstractTableModel.__init__(self, parent)
|
||||
self.roles = roles if roles is not None else []
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.roles)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return 2
|
||||
|
||||
def data(self, index, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
item = self.roles[index.row()]
|
||||
column = index.column()
|
||||
if column == 0:
|
||||
return item['Name']
|
||||
elif column == 1:
|
||||
return ", ".join(item['Permissions'])
|
||||
|
||||
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 'Permissions'
|
||||
else:
|
||||
return section + 1
|
||||
60
summer/templates/UserDetail.py
Normal file
60
summer/templates/UserDetail.py
Normal file
@ -0,0 +1,60 @@
|
||||
__author__ = 'tanshu'
|
||||
from PyQt5 import uic, QtCore
|
||||
|
||||
|
||||
file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/UserDetail.ui'
|
||||
base, form = uic.loadUiType(file)
|
||||
|
||||
|
||||
class UserDetail(base, form):
|
||||
def __init__(self, router, user, parent=None):
|
||||
super(UserDetail, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.router = router
|
||||
self.user = user
|
||||
self.txtName.setText(user['Name'])
|
||||
self.txtPassword.setText('')
|
||||
self.chkLockedOut.setCheckState(QtCore.Qt.Checked if user['LockedOut'] else QtCore.Qt.Unchecked)
|
||||
self.accepted.connect(self.save_user)
|
||||
self.lvwRoles.setModel(RolesListModel(self.user['Roles']))
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
|
||||
|
||||
def save_user(self):
|
||||
self.user['Name'] = self.txtName.text()
|
||||
self.user['Password'] = self.txtPassword.text()
|
||||
self.user['LockedOut'] = self.chkLockedOut.isChecked()
|
||||
self.router.save_user(self.user)
|
||||
|
||||
|
||||
class RolesListModel(QtCore.QAbstractListModel):
|
||||
def __init__(self, roles=None, parent=None):
|
||||
QtCore.QAbstractListModel.__init__(self, parent)
|
||||
self.roles = roles if roles is not None else []
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.roles)
|
||||
|
||||
def data(self, index, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
item = self.roles[index.row()]
|
||||
return item['Name']
|
||||
elif role == QtCore.Qt.CheckStateRole:
|
||||
item = self.roles[index.row()]
|
||||
return QtCore.Qt.Checked if item['Enabled'] else QtCore.Qt.Unchecked
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
if orientation == QtCore.Qt.Horizontal:
|
||||
return 'Roles'
|
||||
else:
|
||||
return section + 1
|
||||
|
||||
def setData(self, index, value, role=QtCore.Qt.EditRole):
|
||||
if role == QtCore.Qt.CheckStateRole:
|
||||
item = self.roles[index.row()]
|
||||
item['Enabled'] = False if value == 0 else True
|
||||
return True
|
||||
|
||||
|
||||
def flags(self, index):
|
||||
return QtCore.QAbstractListModel.flags(self, index) | QtCore.Qt.ItemIsUserCheckable
|
||||
67
summer/templates/UserList.py
Normal file
67
summer/templates/UserList.py
Normal file
@ -0,0 +1,67 @@
|
||||
__author__ = 'tanshu'
|
||||
from PyQt5 import uic, QtCore, QtWidgets
|
||||
|
||||
|
||||
file = '/home/tanshu/Programming/summer/summer/QtDesignerFiles/UserList.ui'
|
||||
base, form = uic.loadUiType(file)
|
||||
|
||||
|
||||
class UserList(base, form):
|
||||
def __init__(self, router, users, parent=None):
|
||||
super(UserList, self).__init__()
|
||||
self.setupUi(self)
|
||||
self.router = router
|
||||
self.users = users
|
||||
self.model = UsersTableModel(users)
|
||||
self.tblUsers.setModel(self.model)
|
||||
self.tblUsers.doubleClicked.connect(self.show_user)
|
||||
self.btnAddNew.clicked.connect(self.add_new_user)
|
||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
|
||||
self.tblUsers.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
|
||||
|
||||
def update_data(self, data):
|
||||
self.users = data
|
||||
self.model = UsersTableModel(self.users)
|
||||
self.tblUsers.setModel(self.model)
|
||||
|
||||
def show_user(self, index):
|
||||
user = self.model.users[index.row()]
|
||||
self.router.show_user(user['UserID'])
|
||||
|
||||
def add_new_user(self):
|
||||
self.router.show_user(None)
|
||||
|
||||
|
||||
class UsersTableModel(QtCore.QAbstractTableModel):
|
||||
def __init__(self, users=None, parent=None):
|
||||
QtCore.QAbstractTableModel.__init__(self, parent)
|
||||
self.users = users if users is not None else []
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.users)
|
||||
|
||||
def columnCount(self, parent):
|
||||
return 3
|
||||
|
||||
def data(self, index, role):
|
||||
if role == QtCore.Qt.DisplayRole:
|
||||
user = self.users[index.row()]
|
||||
column = index.column()
|
||||
if column == 0:
|
||||
return user['Name']
|
||||
elif column == 1:
|
||||
return user['LockedOut']
|
||||
elif column == 2:
|
||||
return ", ".join(user['Roles'])
|
||||
|
||||
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 'Locked Out'
|
||||
elif section == 2:
|
||||
return 'Roles'
|
||||
else:
|
||||
return section + 1
|
||||
73
summer/views/__init__.py
Normal file
73
summer/views/__init__.py
Normal file
@ -0,0 +1,73 @@
|
||||
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.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
|
||||
|
||||
__author__ = 'tanshu'
|
||||
|
||||
|
||||
class Router():
|
||||
def __init__(self):
|
||||
self.user_list_form = None
|
||||
self.user_detail_form = None
|
||||
self.role_list_form = None
|
||||
self.role_detail_form = None
|
||||
|
||||
def show_users(self):
|
||||
users = user_list()
|
||||
if self.user_list_form is None:
|
||||
self.user_list_form = UserList(self, users)
|
||||
self.user_list_form.destroyed.connect(self.user_list_form_destroyed)
|
||||
self.user_list_form.show()
|
||||
|
||||
def user_list_form_destroyed(self):
|
||||
self.user_list_form = None
|
||||
|
||||
def show_user(self, id=None):
|
||||
user = user_info(id)
|
||||
if self.user_detail_form is None:
|
||||
self.user_detail_form = UserDetail(self, user)
|
||||
self.user_detail_form.destroyed.connect(self.user_detail_form_destroyed)
|
||||
self.user_detail_form.show()
|
||||
|
||||
def user_detail_form_destroyed(self):
|
||||
self.user_detail_form = None
|
||||
if self.user_list_form is not None:
|
||||
self.user_list_form.update_data(user_list())
|
||||
|
||||
def save_user(self, user):
|
||||
if 'UserID' not in user:
|
||||
save_user(user)
|
||||
else:
|
||||
update_user(user)
|
||||
|
||||
|
||||
def show_roles(self):
|
||||
roles = role_list()
|
||||
if self.role_list_form is None:
|
||||
self.role_list_form = RoleList(self, roles)
|
||||
self.role_list_form.destroyed.connect(self.role_list_form_destroyed)
|
||||
self.role_list_form.show()
|
||||
|
||||
def role_list_form_destroyed(self):
|
||||
self.role_list_form = None
|
||||
|
||||
def show_role(self, id=None):
|
||||
role = role_info(id)
|
||||
if self.role_detail_form is None:
|
||||
self.role_detail_form = RoleDetail(self, role)
|
||||
self.role_detail_form.destroyed.connect(self.role_detail_form_destroyed)
|
||||
self.role_detail_form.show()
|
||||
|
||||
def role_detail_form_destroyed(self):
|
||||
self.role_detail_form = None
|
||||
if self.role_list_form is not None:
|
||||
self.role_list_form.update_data(role_list())
|
||||
|
||||
def save_role(self, role):
|
||||
if 'RoleID' not in role:
|
||||
save_role(role)
|
||||
else:
|
||||
update_role(role)
|
||||
61
summer/views/role.py
Normal file
61
summer/views/role.py
Normal file
@ -0,0 +1,61 @@
|
||||
from summer.models import session_scope
|
||||
|
||||
__author__ = 'tanshu'
|
||||
|
||||
from summer.models.auth import Role, Permission
|
||||
|
||||
|
||||
def save_role(json):
|
||||
role = Role(json['Name'])
|
||||
with session_scope() as DBSession:
|
||||
DBSession.add(role)
|
||||
add_permissions(role, json['Permissions'], DBSession)
|
||||
return role_info(role.id)
|
||||
|
||||
|
||||
def update_role(json):
|
||||
with session_scope() as DBSession:
|
||||
role = Role.by_id(json['RoleID'], session=DBSession)
|
||||
role.name = json['Name']
|
||||
add_permissions(role, json['Permissions'], session=DBSession)
|
||||
return role_info(role.id)
|
||||
|
||||
|
||||
def add_permissions(role, permissions, *, session=None):
|
||||
for permission in permissions:
|
||||
id = permission['PermissionID']
|
||||
gp = [p for p in role.permissions if p.id == id]
|
||||
gp = None if len(gp) == 0 else gp[0]
|
||||
if permission['Enabled'] and gp is None:
|
||||
role.permissions.append(Permission.by_id(id, session=session))
|
||||
elif not permission['Enabled'] and gp:
|
||||
role.permissions.remove(gp)
|
||||
|
||||
|
||||
def show_list():
|
||||
with session_scope() as DBSession:
|
||||
list = Role.list(session=DBSession)
|
||||
roles = []
|
||||
for item in list:
|
||||
role = {'RoleID': item.id, 'Name': item.name, 'Permissions': []}
|
||||
for permission in item.permissions:
|
||||
role['Permissions'].append(permission.name)
|
||||
roles.append(role)
|
||||
return roles
|
||||
|
||||
|
||||
def role_info(id):
|
||||
with session_scope() as DBSession:
|
||||
if id is None:
|
||||
role = {'Name': '', 'Permissions': []}
|
||||
for item in Permission.list(session=DBSession):
|
||||
role['Permissions'].append({'PermissionID': item.id, 'Name': item.name, 'Enabled': False})
|
||||
else:
|
||||
role_object = Role.by_id(id, session=DBSession)
|
||||
role = {'RoleID': role_object.id, 'Name': role_object.name, 'Permissions': []}
|
||||
for item in Permission.list(session=DBSession):
|
||||
enabled = True if item in role_object.permissions else False
|
||||
role['Permissions'].append({'PermissionID': item.id, 'Name': item.name, 'Enabled': enabled})
|
||||
return role
|
||||
|
||||
|
||||
74
summer/views/user.py
Normal file
74
summer/views/user.py
Normal file
@ -0,0 +1,74 @@
|
||||
from summer.models import session_scope
|
||||
from summer.models.auth import User, Role
|
||||
from summer.models.validation_exception import ValidationError
|
||||
|
||||
|
||||
def save_user(json):
|
||||
user = User(json['Name'], json['Password'], json['LockedOut'])
|
||||
with session_scope() as DBSession:
|
||||
DBSession.add(user)
|
||||
add_roles(user, json['Roles'], session=DBSession)
|
||||
return user_info(user.id)
|
||||
|
||||
|
||||
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')
|
||||
has_permission = True
|
||||
if has_permission:
|
||||
user.name = json['Name']
|
||||
user.locked_out = json['LockedOut']
|
||||
add_roles(user, json['Roles'], session=DBSession)
|
||||
|
||||
if json['Password'] != '' and json['Password'] != user.password:
|
||||
user.password = json['Password']
|
||||
return user_info(user.id)
|
||||
|
||||
|
||||
def show_list():
|
||||
with session_scope() as DBSession:
|
||||
list = User.list(session=DBSession)
|
||||
users = []
|
||||
for item in list:
|
||||
user = {'UserID': item.id, 'Name': item.name, 'LockedOut': item.locked_out, 'Roles': []}
|
||||
for role in item.roles:
|
||||
user['Roles'].append(role.name)
|
||||
users.append(user)
|
||||
return users
|
||||
|
||||
|
||||
def user_info(id):
|
||||
with session_scope() as DBSession:
|
||||
if id is None:
|
||||
account = {'Name': '', 'LockedOut': False, 'Roles': []}
|
||||
for item in Role.list(session=DBSession):
|
||||
account['Roles'].append({'RoleID': item.id, 'Name': item.name, 'Enabled': False})
|
||||
return account
|
||||
|
||||
user = User.by_id(id, session=DBSession)
|
||||
|
||||
has_permission = True
|
||||
if has_permission:
|
||||
account = {'UserID': user.id, 'Name': user.name, 'Password': '', 'LockedOut': user.locked_out, 'Roles': []}
|
||||
for item in Role.list(session=DBSession):
|
||||
account['Roles'].append(
|
||||
{'RoleID': item.id, 'Name': item.name, 'Enabled': True if item in user.roles else False})
|
||||
# elif self.user.id == user.id:
|
||||
elif 1 == 1:
|
||||
account = {'UserID': user.id, 'Name': user.name, 'Password': '', 'LockedOut': user.locked_out, 'Roles': []}
|
||||
else:
|
||||
raise ValidationError("User can only update his/her password")
|
||||
return account
|
||||
|
||||
|
||||
def add_roles(user, roles, *, session=None):
|
||||
for role in roles:
|
||||
id = role['RoleID']
|
||||
ur = [r for r in user.roles if r.id == id]
|
||||
ur = None if len(ur) == 0 else ur[0]
|
||||
if role['Enabled'] and ur is None:
|
||||
user.roles.append(Role.by_id(id, session=session))
|
||||
elif not role['Enabled'] and ur:
|
||||
user.roles.remove(ur)
|
||||
Reference in New Issue
Block a user