diff --git a/rattail_demo/web/app.py b/rattail_demo/web/app.py
index b81b31a..0c58612 100644
--- a/rattail_demo/web/app.py
+++ b/rattail_demo/web/app.py
@@ -7,6 +7,8 @@ from __future__ import unicode_literals, absolute_import
from tailbone import app
+from rattail_demo.web.db import CoreSession, CoreTransSession
+
def main(global_config, **settings):
"""
@@ -20,6 +22,10 @@ def main(global_config, **settings):
rattail_config = app.make_rattail_config(settings)
pyramid_config = app.make_pyramid_config(settings)
+ # configure database sessions
+ CoreSession.configure(bind=rattail_config.corepos_engine)
+ CoreTransSession.configure(bind=rattail_config.coretrans_engine)
+
# bring in rest of rattail-demo etc.
pyramid_config.include('tailbone.static')
pyramid_config.include('rattail_demo.web.subscribers')
diff --git a/rattail_demo/web/db.py b/rattail_demo/web/db.py
new file mode 100644
index 0000000..c11d157
--- /dev/null
+++ b/rattail_demo/web/db.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8; -*-
+"""
+Web Database Sessions
+"""
+
+from sqlalchemy.orm import sessionmaker, scoped_session
+from zope.sqlalchemy import register
+
+
+CoreSession = scoped_session(sessionmaker())
+register(CoreSession)
+
+CoreTransSession = scoped_session(sessionmaker())
+register(CoreTransSession)
diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako
index 566da83..0f048b8 100644
--- a/rattail_demo/web/templates/menu.mako
+++ b/rattail_demo/web/templates/menu.mako
@@ -68,6 +68,35 @@
% endif
+ % if request.has_any_perm('corepos.departments.list', 'corepos.subdepartments.list', 'corepos.vendors.list', 'corepos.products.list', 'corepos.customers.list', 'corepos.employees.list', 'corepos.transaction_details.list'):
+
+ CORE-POS
+
+ % if request.has_perm('corepos.departments.list'):
+ - ${h.link_to("Departments", url('corepos.departments'))}
+ % endif
+ % if request.has_perm('corepos.subdepartments.list'):
+ - ${h.link_to("Subdepartments", url('corepos.subdepartments'))}
+ % endif
+ % if request.has_perm('corepos.vendors.list'):
+ - ${h.link_to("Vendors", url('corepos.vendors'))}
+ % endif
+ % if request.has_perm('corepos.products.list'):
+ - ${h.link_to("Products", url('corepos.products'))}
+ % endif
+ % if request.has_perm('corepos.customers.list'):
+ - ${h.link_to("Customers", url('corepos.customers'))}
+ % endif
+ % if request.has_perm('corepos.employees.list'):
+ - ${h.link_to("Employees", url('corepos.employees'))}
+ % endif
+ % if request.has_perm('corepos.transaction_details.list'):
+ - ${h.link_to("Transaction Details", url('corepos.transaction_details'))}
+ % endif
+
+
+ % endif
+
% if request.has_any_perm('batch.handheld.list', 'batch.inventory.list'):
Batches
diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py
index 7499874..d1dc8c7 100644
--- a/rattail_demo/web/views/__init__.py
+++ b/rattail_demo/web/views/__init__.py
@@ -35,6 +35,9 @@ def includeme(config):
config.include('rattail_demo.web.views.users')
config.include('tailbone.views.vendors')
+ # core-pos views
+ config.include('rattail_demo.web.views.corepos')
+
# batch views
config.include('tailbone.views.handheld')
config.include('tailbone.views.inventory')
diff --git a/rattail_demo/web/views/corepos/__init__.py b/rattail_demo/web/views/corepos/__init__.py
new file mode 100644
index 0000000..07f319c
--- /dev/null
+++ b/rattail_demo/web/views/corepos/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS Views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from .master import CoreMasterView
+
+
+def includeme(config):
+ config.include('rattail_demo.web.views.corepos.departments')
+ config.include('rattail_demo.web.views.corepos.subdepartments')
+ config.include('rattail_demo.web.views.corepos.vendors')
+ config.include('rattail_demo.web.views.corepos.products')
+ config.include('rattail_demo.web.views.corepos.customers')
+ config.include('rattail_demo.web.views.corepos.employees')
+ config.include('rattail_demo.web.views.corepos.transactions')
diff --git a/rattail_demo/web/views/corepos/customers.py b/rattail_demo/web/views/corepos/customers.py
new file mode 100644
index 0000000..87e563d
--- /dev/null
+++ b/rattail_demo/web/views/corepos/customers.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS customer views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class CustomerView(CoreMasterView):
+ """
+ Base class for customer views.
+ """
+ model_class = corepos.Customer
+ model_title = "CORE-POS Customer"
+ url_prefix = '/core-pos/customers'
+ route_prefix = 'corepos.customers'
+
+ labels = {
+ 'id': "ID",
+ 'CardNo': "Card No.",
+ 'personNum': "Person No.",
+ 'LastName': "Last Name",
+ 'FirstName': "First Name",
+ 'CashBack': "Cash Back",
+ 'MemDiscountLimit': "Member Discount Limit",
+ 'ChargeLimit': "Charge Limit",
+ 'ChargeOk': "Charge OK",
+ 'WriteChecks': "Write Checks",
+ 'StoreCoupons': "Store Coupons",
+ 'memType': "Member Type No.",
+ 'NumberOfChecks': "Number of Checks",
+ 'memCoupons': "Member Coupons",
+ 'blueLine': "Blue Line",
+ 'LastChange': "Last Change",
+ }
+
+ grid_columns = [
+ 'CardNo',
+ 'FirstName',
+ 'LastName',
+ 'ChargeOk',
+ 'ChargeLimit',
+ 'Balance',
+ 'WriteChecks',
+ 'Purchases',
+ ]
+
+ def configure_grid(self, g):
+ super(CustomerView, self).configure_grid(g)
+
+ g.filters['FirstName'].default_active = True
+ g.filters['FirstName'].default_verb = 'contains'
+
+ g.filters['LastName'].default_active = True
+ g.filters['LastName'].default_verb = 'contains'
+
+ g.set_type('ChargeLimit', 'currency')
+ g.set_type('Balance', 'currency')
+ g.set_type('Purchases', 'currency')
+
+ g.set_sort_defaults('CardNo')
+
+ g.set_link('CardNo')
+ g.set_link('FirstName')
+ g.set_link('LastName')
+
+
+def includeme(config):
+ CustomerView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/departments.py b/rattail_demo/web/views/corepos/departments.py
new file mode 100644
index 0000000..772e4bd
--- /dev/null
+++ b/rattail_demo/web/views/corepos/departments.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS department views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class DepartmentView(CoreMasterView):
+ """
+ Base class for department views.
+ """
+ model_class = corepos.Department
+ model_title = "CORE-POS Department"
+ url_prefix = '/core-pos/departments'
+ route_prefix = 'corepos.departments'
+
+ labels = {
+ 'dept_no': "Number",
+ 'dept_name': "Name",
+ 'dept_tax': "Tax",
+ 'dept_fs': "FS",
+ 'dept_limit': "Limit",
+ 'dept_minimum': "Minimum",
+ 'dept_discount': "Discount",
+ 'dept_see_id': "See ID",
+ 'modifiedby': "Modified by",
+ 'salesCode': "Sales Code",
+ 'memberOnly': "Member Only",
+ }
+
+ grid_columns = [
+ 'dept_no',
+ 'dept_name',
+ 'dept_tax',
+ 'dept_fs',
+ 'dept_limit',
+ 'dept_minimum',
+ 'dept_discount',
+ 'dept_see_id',
+ 'modified',
+ 'modifiedby',
+ 'margin',
+ 'salesCode',
+ 'memberOnly',
+ ]
+
+ def configure_grid(self, g):
+ super(DepartmentView, self).configure_grid(g)
+
+ g.filters['dept_no'].default_active = True
+ g.filters['dept_no'].default_verb = 'equal'
+
+ g.filters['dept_name'].default_active = True
+ g.filters['dept_name'].default_verb = 'contains'
+
+ g.set_type('modified', 'datetime_local')
+
+ g.set_sort_defaults('dept_no')
+
+ g.set_link('dept_no')
+ g.set_link('dept_name')
+
+
+def includeme(config):
+ DepartmentView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/employees.py b/rattail_demo/web/views/corepos/employees.py
new file mode 100644
index 0000000..a202d43
--- /dev/null
+++ b/rattail_demo/web/views/corepos/employees.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS employee views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class EmployeeView(CoreMasterView):
+ """
+ Base class for employee views.
+ """
+ model_class = corepos.Employee
+ model_title = "CORE-POS Employee"
+ url_prefix = '/core-pos/employees'
+ route_prefix = 'corepos.employees'
+
+ labels = {
+ 'emp_no': "Number",
+ 'CashierPassword': "Cashier Password",
+ 'AdminPassword': "Admin Password",
+ 'FirstName': "First Name",
+ 'LastName': "Last Name",
+ 'JobTitle': "Job Title",
+ 'EmpActive': "Active",
+ 'frontendsecurity': "Frontend Security",
+ 'backendsecurity': "Backend Security",
+ 'birthdate': "Birth Date",
+ }
+
+ grid_columns = [
+ 'emp_no',
+ 'FirstName',
+ 'LastName',
+ 'JobTitle',
+ 'EmpActive',
+ 'birthdate',
+ ]
+
+ def configure_grid(self, g):
+ super(EmployeeView, self).configure_grid(g)
+
+ g.filters['EmpActive'].default_active = True
+ g.filters['EmpActive'].default_verb = 'is_true'
+
+ g.filters['FirstName'].default_active = True
+ g.filters['FirstName'].default_verb = 'contains'
+
+ g.filters['LastName'].default_active = True
+ g.filters['LastName'].default_verb = 'contains'
+
+ g.set_sort_defaults('emp_no')
+
+ g.set_link('emp_no')
+ g.set_link('FirstName')
+ g.set_link('LastName')
+
+ def grid_extra_class(self, employee, i):
+ if not employee.EmpActive:
+ return 'warning'
+
+
+def includeme(config):
+ EmployeeView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/master.py b/rattail_demo/web/views/corepos/master.py
new file mode 100644
index 0000000..87b3263
--- /dev/null
+++ b/rattail_demo/web/views/corepos/master.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS master view
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+# import six
+
+from tailbone.views import MasterView
+
+from rattail_demo.web.db import CoreSession
+
+
+class CoreMasterView(MasterView):
+ """
+ Master base class for CORE-POS views
+ """
+ Session = CoreSession
+ # model_key = 'pk'
+ creatable = False
+ editable = False
+ deletable = False
+
+ # # TODO: would be nice to find a way around this somehow
+ # # must encode all search values as utf-8
+ # use_byte_string_filters = True
+
+ # def get_action_route_kwargs(self, row):
+ # return {'pk': six.text_type(row.pk)}
diff --git a/rattail_demo/web/views/corepos/products.py b/rattail_demo/web/views/corepos/products.py
new file mode 100644
index 0000000..4cfa1a1
--- /dev/null
+++ b/rattail_demo/web/views/corepos/products.py
@@ -0,0 +1,94 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS product views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class ProductView(CoreMasterView):
+ """
+ Base class for product views.
+ """
+ model_class = corepos.Product
+ model_title = "CORE-POS Product"
+ url_prefix = '/core-pos/products'
+ route_prefix = 'corepos.products'
+
+ labels = {
+ 'id': "ID",
+ 'upc': "UPC",
+ 'pricemethod': "Price Method",
+ 'groupprice': "Group Price",
+ 'specialpricemethod': "Special Price Method",
+ 'specialgroupprice': "Special Group Price",
+ 'specialquantity': "Special Quantity",
+ 'dept_no': "Dept. No.",
+ 'foodstamp': "Food Stamp",
+ 'scaleprice': "Scale Price",
+ 'mixmatchcode': "Mix Match Code",
+ 'tareweight': "Tare Weight",
+ 'discounttype': "Discount Type",
+ 'unitofmeasure': "Unit of Measure",
+ 'qttyEnforced': "Qty. Enforced",
+ 'idEnforced': "ID Enforced",
+ 'inUse': "In Use",
+ 'numflag': "Num. Flag",
+ 'subdept': "Subdept. No.",
+ 'default_vendor_id': "Default Vendor ID",
+ 'current_origin_id': "Current Origin ID",
+ }
+
+ grid_columns = [
+ 'upc',
+ 'brand',
+ 'description',
+ 'size',
+ 'department',
+ 'vendor',
+ 'normal_price',
+ 'cost',
+ ]
+
+ def configure_grid(self, g):
+ super(ProductView, self).configure_grid(g)
+
+ g.set_joiner('department', lambda q: q.outerjoin(corepos.Department))
+ g.set_sorter('department', corepos.Department.dept_name)
+
+ g.set_joiner('vendor', lambda q: q.outerjoin(corepos.Vendor))
+ g.set_sorter('vendor', corepos.Vendor.vendorName)
+
+ g.filters['upc'].default_active = True
+ g.filters['upc'].default_verb = 'equal'
+
+ g.set_type('cost', 'currency')
+ g.set_type('normal_price', 'currency')
+
+ g.set_sort_defaults('upc')
+
+ g.set_link('upc')
+ g.set_link('brand')
+ g.set_link('description')
+
+ def configure_form(self, f):
+ super(ProductView, self).configure_form(f)
+
+ f.set_type('start_date', 'datetime_local')
+ f.set_type('end_date', 'datetime_local')
+ f.set_type('modified', 'datetime_local')
+
+ f.set_type('normal_price', 'currency')
+ f.set_type('groupprice', 'currency')
+ f.set_type('special_price', 'currency')
+ f.set_type('specialgroupprice', 'currency')
+ f.set_type('cost', 'currency')
+ f.set_type('deposit', 'currency')
+
+
+def includeme(config):
+ ProductView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/subdepartments.py b/rattail_demo/web/views/corepos/subdepartments.py
new file mode 100644
index 0000000..efa6a5f
--- /dev/null
+++ b/rattail_demo/web/views/corepos/subdepartments.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS subdepartment views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class SubdepartmentView(CoreMasterView):
+ """
+ Base class for subdepartment views.
+ """
+ model_class = corepos.Subdepartment
+ model_title = "CORE-POS Subdepartment"
+ url_prefix = '/core-pos/subdepartments'
+ route_prefix = 'corepos.subdepartments'
+
+ labels = {
+ 'subdept_no': "Number",
+ 'subdept_name': "Name",
+ 'dept_ID': "Dept. No.",
+ }
+
+ grid_columns = [
+ 'subdept_no',
+ 'subdept_name',
+ 'department',
+ ]
+
+
+def includeme(config):
+ SubdepartmentView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/transactions.py b/rattail_demo/web/views/corepos/transactions.py
new file mode 100644
index 0000000..85ad24f
--- /dev/null
+++ b/rattail_demo/web/views/corepos/transactions.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS transaction views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.trans.db import model as coretrans
+
+from .master import CoreMasterView
+from rattail_demo.web.db import CoreTransSession
+
+
+class TransactionDetailView(CoreMasterView):
+ """
+ Master view for transaction details.
+ """
+ Session = CoreTransSession
+ model_class = coretrans.TransactionDetail
+ model_title = "CORE-POS Transaction Detail"
+ url_prefix = '/corepos/transaction-details'
+ route_prefix = 'corepos.transaction_details'
+
+ labels = {
+ 'store_row_id': "Store Row ID",
+ 'store_id': "Store ID",
+ 'pos_row_id': "POS Row ID",
+ 'transaction_id': "Transaction ID",
+ 'upc': "UPC",
+ }
+
+ grid_columns = [
+ 'date_time',
+ 'register_number',
+ 'transaction_number',
+ 'card_number',
+ 'upc',
+ 'department_number',
+ 'description',
+ 'quantity',
+ 'unit_price',
+ 'discount',
+ 'total',
+ ]
+
+ def configure_grid(self, g):
+ super(TransactionDetailView, self).configure_grid(g)
+
+ g.set_type('date_time', 'datetime_local')
+ g.set_type('quantity', 'quantity')
+ g.set_type('unit_price', 'currency')
+ g.set_type('discount', 'currency')
+ g.set_type('total', 'currency')
+
+ g.set_sort_defaults('date_time', 'desc')
+
+ g.set_label('register_number', "Register")
+ g.set_label('transaction_number', "Trans. No.")
+ g.set_label('card_number', "Card No.")
+ g.set_label('department_number', "Dept. No.")
+
+ g.set_link('upc')
+ g.set_link('description')
+
+ def configure_form(self, f):
+ super(TransactionDetailView, self).configure_form(f)
+
+ f.set_type('date_time', 'datetime_local')
+ f.set_type('quantity', 'quantity')
+ f.set_type('unit_price', 'currency')
+ f.set_type('discount', 'currency')
+ f.set_type('total', 'currency')
+
+
+def includeme(config):
+ TransactionDetailView.defaults(config)
diff --git a/rattail_demo/web/views/corepos/vendors.py b/rattail_demo/web/views/corepos/vendors.py
new file mode 100644
index 0000000..25aa909
--- /dev/null
+++ b/rattail_demo/web/views/corepos/vendors.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8; -*-
+"""
+CORE-POS vendor views
+"""
+
+from __future__ import unicode_literals, absolute_import
+
+from corepos.db import model as corepos
+
+from .master import CoreMasterView
+
+
+class VendorView(CoreMasterView):
+ """
+ Base class for vendor views.
+ """
+ model_class = corepos.Vendor
+ model_title = "CORE-POS Vendor"
+ url_prefix = '/core-pos/vendors'
+ route_prefix = 'corepos.vendors'
+
+ labels = {
+ 'vendorID': "ID",
+ 'vendorName': "Name",
+ 'vendorAbbreviation': "Abbreviation",
+ 'discountRate': "Discount Rate",
+ }
+
+ grid_columns = [
+ 'vendorID',
+ 'vendorName',
+ 'vendorAbbreviation',
+ 'discountRate',
+ 'contact',
+ ]
+
+
+def includeme(config):
+ VendorView.defaults(config)