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 + +
  • + % 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)