From 7f35fd828a6cadc895c076cef9e75563e8ed2fde Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 8 Dec 2016 15:10:25 -0600 Subject: [PATCH 001/136] Add fab tasks: release, update_production --- fabfile.py | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 fabfile.py diff --git a/fabfile.py b/fabfile.py new file mode 100644 index 0000000..8113230 --- /dev/null +++ b/fabfile.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +Fabric script for Rattail Demo +""" + +from __future__ import unicode_literals, absolute_import + +import shutil + +from fabric.api import * + +from rattail.fablib import cdvirtualenv, workon, python + + +env.roledefs = { + 'production': ['lance@rattailproject.org'], +} + + +@task +def release(): + """ + Release a new version of rattail-demo + """ + shutil.rmtree('rattail_demo.egg-info') + local('python setup.py sdist --formats=gztar upload') + + +@task +@roles('production') +def update_production(all='false'): + """ + Update production Rattail Demo server + """ + all = all.lower() == 'true' + if all: + with workon('demo'): + python.pip('pip', 'setuptools', 'wheel', 'ndg-httpsclient') + with cdvirtualenv('demo', 'src/rattail'): + sudo('git pull') + sudo('pip install --editable .') + with cdvirtualenv('demo', 'src/rattail-tempmon'): + sudo('git pull') + sudo('pip install --editable .') + with cdvirtualenv('demo', 'src/tailbone'): + sudo('git pull') + sudo('pip install --editable .') + + with cdvirtualenv('demo', 'src/rattail-demo'): + sudo('git pull') + if all: + sudo('pip install --upgrade --editable .') + else: + sudo('pip install --editable .') + with cd('/srv/envs/demo'): + sudo('bin/alembic --config=app/rattail.conf upgrade heads', user='rattail') + sudo('service apache2 restart') From 94993cf553343fb0838eca7fcd59f37510168ce9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 10:15:41 -0600 Subject: [PATCH 002/136] Add people.autocomplete view config --- rattail_demo/web/views/people.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rattail_demo/web/views/people.py b/rattail_demo/web/views/people.py index d17ea81..7157b7a 100644 --- a/rattail_demo/web/views/people.py +++ b/rattail_demo/web/views/people.py @@ -21,4 +21,10 @@ class PeopleView(base.PeopleView): def includeme(config): + + # autocomplete + config.add_route('people.autocomplete', '/people/autocomplete') + config.add_view(base.PeopleAutocomplete, route_name='people.autocomplete', + renderer='json', permission='people.list') + PeopleView.defaults(config) From 50509f8f52994998477f9edd662a362685637671 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 11:24:01 -0600 Subject: [PATCH 003/136] Add support for tempmon views --- rattail_demo/web/templates/menu.mako | 17 ++++++++++++ rattail_demo/web/views/__init__.py | 1 + rattail_demo/web/views/tempmon.py | 39 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 rattail_demo/web/views/tempmon.py diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 5571b55..522852d 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -73,6 +73,23 @@ % endif + % if request.has_any_perm('tempmon.clients.list', 'tempmon.probes.list', 'tempmon.readings.list'): +
  • + TempMon +
      + % if request.has_perm('tempmon.clients.list'): +
    • ${h.link_to("Clients", url('tempmon.clients'))}
    • + % endif + % if request.has_perm('tempmon.probes.list'): +
    • ${h.link_to("Probes", url('tempmon.probes'))}
    • + % endif + % if request.has_perm('tempmon.readings.list'): +
    • ${h.link_to("Readings", url('tempmon.readings'))}
    • + % endif +
    +
  • + % endif + % if request.has_any_perm('users.list', 'roles.list', 'settings.list'):
  • Admin diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 9fc60c9..2b5837a 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -42,6 +42,7 @@ def includeme(config): config.include('tailbone.views.settings') config.include('tailbone.views.stores') config.include('tailbone.views.subdepartments') + config.include('rattail_demo.web.views.tempmon') config.include('rattail_demo.web.views.users') config.include('tailbone.views.vendors') diff --git a/rattail_demo/web/views/tempmon.py b/rattail_demo/web/views/tempmon.py new file mode 100644 index 0000000..d02c050 --- /dev/null +++ b/rattail_demo/web/views/tempmon.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +""" +Tempmon views +""" + +from __future__ import unicode_literals, absolute_import + +from tailbone.views.tempmon.clients import TempmonClientView as BaseTempmonClientView +from tailbone.views.tempmon.probes import TempmonProbeView as BaseTempmonProbeView + + +class TempmonClientView(BaseTempmonClientView): + """ + Prevent edit/delete for 'demo' client + """ + + def editable_instance(self, client): + return client.config_key != 'demo' + + def deletable_instance(self, client): + return client.config_key != 'demo' + + +class TempmonProbeView(BaseTempmonProbeView): + """ + Prevent edit/delete for 'demo' probe + """ + + def editable_instance(self, probe): + return probe.config_key != 'demo' + + def deletable_instance(self, probe): + return probe.config_key != 'demo' + + +def includeme(config): + TempmonClientView.defaults(config) + TempmonProbeView.defaults(config) + config.include('tailbone.views.tempmon.readings') From c61f5c80532b625fa1165e647f0d2e2c83e2ecd9 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 11:56:58 -0600 Subject: [PATCH 004/136] Only allow 'demo' tempmon client to be restarted --- rattail_demo/web/views/tempmon.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rattail_demo/web/views/tempmon.py b/rattail_demo/web/views/tempmon.py index d02c050..fe72b81 100644 --- a/rattail_demo/web/views/tempmon.py +++ b/rattail_demo/web/views/tempmon.py @@ -20,6 +20,12 @@ class TempmonClientView(BaseTempmonClientView): def deletable_instance(self, client): return client.config_key != 'demo' + def restartable_client(self, client): + return client.config_key == 'demo' + + def get_restart_cmd(self, client): + return ['ssh', 'demo.rattailproject.org', 'sudo service demo-tempmon-client restart'] + class TempmonProbeView(BaseTempmonProbeView): """ From 365d5847a4b427b98573976b272243f44bb0fe78 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 14:16:37 -0600 Subject: [PATCH 005/136] Add email config views --- rattail_demo/config.py | 20 ++++++++++++++++++++ rattail_demo/emails.py | 9 +++++++++ rattail_demo/web/templates/menu.mako | 5 ++++- rattail_demo/web/views/__init__.py | 1 + setup.py | 3 +++ 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 rattail_demo/config.py create mode 100644 rattail_demo/emails.py diff --git a/rattail_demo/config.py b/rattail_demo/config.py new file mode 100644 index 0000000..1ff335b --- /dev/null +++ b/rattail_demo/config.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +Rattail Demo config extension +""" + +from __future__ import unicode_literals, absolute_import + +from rattail.config import ConfigExtension + + +class DemoConfigExtension(ConfigExtension): + """ + Rattail Demo config extension + """ + key = 'rattail-demo' + + def configure(self, config): + + # tell rattail where our stuff lives + config.setdefault('rattail.mail', 'emails', 'rattail_demo.emails') diff --git a/rattail_demo/emails.py b/rattail_demo/emails.py new file mode 100644 index 0000000..31841d2 --- /dev/null +++ b/rattail_demo/emails.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +""" +Email definitions +""" + +from __future__ import unicode_literals, absolute_import + +# just tempmon emails for now +from rattail_tempmon.emails import tempmon_low_temp, tempmon_high_temp, tempmon_critical_temp, tempmon_error diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 522852d..9cdc93d 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -90,7 +90,7 @@
  • % endif - % if request.has_any_perm('users.list', 'roles.list', 'settings.list'): + % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'settings.list'):
  • Admin
      @@ -100,6 +100,9 @@ % if request.has_perm('roles.list'):
    • ${h.link_to("Roles", url('roles'))}
    • % endif + % if request.has_perm('emailprofiles.list'): +
    • ${h.link_to("Email Profiles", url('emailprofiles'))}
    • + % endif % if request.has_perm('settings.list'):
    • ${h.link_to("Settings", url('settings'))}
    • % endif diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 2b5837a..0d10c6f 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -33,6 +33,7 @@ def includeme(config): config.include('tailbone.views.brands') config.include('tailbone.views.customers') config.include('tailbone.views.departments') + config.include('tailbone.views.email') config.include('tailbone.views.employees') config.include('tailbone.views.families') config.include('rattail_demo.web.views.people') diff --git a/setup.py b/setup.py index 53148a4..9726116 100644 --- a/setup.py +++ b/setup.py @@ -76,5 +76,8 @@ setup( 'paste.app_factory': [ 'main = rattail_demo.web.app:main', ], + 'rattail.config.extensions': [ + 'rattail-demo = rattail_demo.config:DemoConfigExtension', + ], }, ) From d812e5465a4edc17a2533c7995773545601328f8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 14:55:36 -0600 Subject: [PATCH 006/136] Add overrides for email profiles, settings views to make feedback email config readonly --- rattail_demo/emails.py | 2 +- rattail_demo/web/views/__init__.py | 4 ++-- rattail_demo/web/views/email.py | 25 +++++++++++++++++++++++++ rattail_demo/web/views/settings.py | 27 +++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 rattail_demo/web/views/email.py create mode 100644 rattail_demo/web/views/settings.py diff --git a/rattail_demo/emails.py b/rattail_demo/emails.py index 31841d2..a60bf7b 100644 --- a/rattail_demo/emails.py +++ b/rattail_demo/emails.py @@ -5,5 +5,5 @@ Email definitions from __future__ import unicode_literals, absolute_import -# just tempmon emails for now +from rattail.emails import user_feedback from rattail_tempmon.emails import tempmon_low_temp, tempmon_high_temp, tempmon_critical_temp, tempmon_error diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 0d10c6f..b8669db 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -33,14 +33,14 @@ def includeme(config): config.include('tailbone.views.brands') config.include('tailbone.views.customers') config.include('tailbone.views.departments') - config.include('tailbone.views.email') + config.include('rattail_demo.web.views.email') config.include('tailbone.views.employees') config.include('tailbone.views.families') config.include('rattail_demo.web.views.people') config.include('tailbone.views.products') config.include('tailbone.views.reportcodes') config.include('tailbone.views.roles') - config.include('tailbone.views.settings') + config.include('rattail_demo.web.views.settings') config.include('tailbone.views.stores') config.include('tailbone.views.subdepartments') config.include('rattail_demo.web.views.tempmon') diff --git a/rattail_demo/web/views/email.py b/rattail_demo/web/views/email.py new file mode 100644 index 0000000..7c2d1ca --- /dev/null +++ b/rattail_demo/web/views/email.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +""" +Email views +""" + +from __future__ import unicode_literals, absolute_import + +from tailbone.views import email as base + + +class ProfilesView(base.ProfilesView): + """ + Prevent edit/delete for 'feedback' email config + """ + + def editable_instance(self, profile): + return profile['key'] != 'user_feedback' + + def deletable_instance(self, profile): + return profile['key'] != 'user_feedback' + + +def includeme(config): + ProfilesView.defaults(config) + base.EmailPreview.defaults(config) diff --git a/rattail_demo/web/views/settings.py b/rattail_demo/web/views/settings.py new file mode 100644 index 0000000..9346227 --- /dev/null +++ b/rattail_demo/web/views/settings.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +""" +Settings views +""" + +from __future__ import unicode_literals, absolute_import + +import re + +from tailbone.views import settings as base + + +class SettingsView(base.SettingsView): + """ + Prevent edit/delete for 'feedback' email settings + """ + feedback = re.compile(r'^rattail\.mail\.user_feedback\..*') + + def editable_instance(self, setting): + return not bool(self.feedback.match(setting.name)) + + def deletable_instance(self, setting): + return not bool(self.feedback.match(setting.name)) + + +def includeme(config): + SettingsView.defaults(config) From 7a22b11e2e60dcde59069bad15a006f9b75871dd Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 15:40:21 -0600 Subject: [PATCH 007/136] Add support for messaging system --- rattail_demo/web/app.py | 2 +- rattail_demo/web/subscribers.py | 11 +++++++++++ rattail_demo/web/templates/menu.mako | 1 + rattail_demo/web/views/__init__.py | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 rattail_demo/web/subscribers.py diff --git a/rattail_demo/web/app.py b/rattail_demo/web/app.py index dbb35bb..a6716c8 100644 --- a/rattail_demo/web/app.py +++ b/rattail_demo/web/app.py @@ -26,7 +26,7 @@ def main(global_config, **settings): # bring in rest of rattail-demo etc. pyramid_config.include('tailbone.static') - pyramid_config.include('tailbone.subscribers') + pyramid_config.include('rattail_demo.web.subscribers') pyramid_config.include('rattail_demo.web.views') # configure PostgreSQL some more diff --git a/rattail_demo/web/subscribers.py b/rattail_demo/web/subscribers.py new file mode 100644 index 0000000..f50bf4b --- /dev/null +++ b/rattail_demo/web/subscribers.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +""" +Pyramid Event Subscribers +""" + +from __future__ import unicode_literals, absolute_import + + +def includeme(config): + config.include('tailbone.subscribers') + config.add_subscriber('tailbone.subscribers.add_inbox_count', 'pyramid.events.BeforeRender') diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 9cdc93d..b3d0807 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -119,6 +119,7 @@ % elif request.is_admin:
    • ${h.link_to("Become root", url('become_root'))}
    • % endif +
    • ${h.link_to("Messages{}".format(" ({})".format(inbox_count) if inbox_count else ''), url('messages.inbox'))}
    • ${h.link_to("Change Password", url('change_password'))}
    • ${h.link_to("Logout", url('logout'))}
    diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index b8669db..1819d94 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -36,6 +36,7 @@ def includeme(config): config.include('rattail_demo.web.views.email') config.include('tailbone.views.employees') config.include('tailbone.views.families') + config.include('tailbone.views.messages') config.include('rattail_demo.web.views.people') config.include('tailbone.views.products') config.include('tailbone.views.reportcodes') From fcb21b9671e9ff1d98dd1e91a090d13ddb822fa8 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 10 Dec 2016 16:04:34 -0600 Subject: [PATCH 008/136] Add initial support for employee schedule views --- rattail_demo/web/templates/menu.mako | 20 +++++++++++++++++++ rattail_demo/web/views/__init__.py | 3 ++- rattail_demo/web/views/employees.py | 30 ++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 rattail_demo/web/views/employees.py diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index b3d0807..e330925 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -2,6 +2,26 @@ <%def name="main_menu_items()"> + % if request.has_any_perm('schedule.view', 'schedule.viewall', 'schedule.edit', 'scheduledshifts.list'): +
  • + Time Clock +
      + % if request.has_perm('schedule.view'): +
    • ${h.link_to("Personal Schedule", url('schedule.employee'))}
    • + % endif + % if request.has_perm('schedule.viewall'): +
    • ${h.link_to("Full Schedule", url('schedule'))}
    • + % endif + % if request.has_perm('schedule.edit'): +
    • ${h.link_to("Edit Schedule", url('schedule.edit'))}
    • + % endif + % if request.has_perm('scheduledshifts.list'): +
    • ${h.link_to("Raw Scheduled Shifts", url('scheduledshifts'))}
    • + % endif +
    +
  • + % endif + % if request.has_any_perm('products.list', 'vendors.list', 'brands.list', 'families.list', 'reportcodes.list'):
  • Products diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 1819d94..b4c2f3d 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -34,7 +34,7 @@ def includeme(config): config.include('tailbone.views.customers') config.include('tailbone.views.departments') config.include('rattail_demo.web.views.email') - config.include('tailbone.views.employees') + config.include('rattail_demo.web.views.employees') config.include('tailbone.views.families') config.include('tailbone.views.messages') config.include('rattail_demo.web.views.people') @@ -42,6 +42,7 @@ def includeme(config): config.include('tailbone.views.reportcodes') config.include('tailbone.views.roles') config.include('rattail_demo.web.views.settings') + config.include('tailbone.views.shifts') config.include('tailbone.views.stores') config.include('tailbone.views.subdepartments') config.include('rattail_demo.web.views.tempmon') diff --git a/rattail_demo/web/views/employees.py b/rattail_demo/web/views/employees.py new file mode 100644 index 0000000..5fc99ae --- /dev/null +++ b/rattail_demo/web/views/employees.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +Employee views +""" + +from __future__ import unicode_literals, absolute_import + +from tailbone.views import employees as base + + +class EmployeesView(base.EmployeesView): + """ + Prevent edit/delete for Chuck Norris + """ + + def editable_instance(self, employee): + return employee.person_uuid != '30d1fe06bcf411e6a7c23ca9f40bc550' + + def deletable_instance(self, employee): + return employee.person_uuid != '30d1fe06bcf411e6a7c23ca9f40bc550' + + +def includeme(config): + + # autocomplete + config.add_route('employees.autocomplete', '/employees/autocomplete') + config.add_view(base.EmployeesAutocomplete, route_name='employees.autocomplete', + renderer='json', permission='employees.list') + + EmployeesView.defaults(config) From 9d9bc58a9969f34146cb5ef79145bd5b15f6cc02 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 11 Dec 2016 01:40:41 -0600 Subject: [PATCH 009/136] Add initial mobile template support Also add rattail-tempmon to list of packages on 'about' page --- rattail_demo/web/templates/mobile/home.mako | 7 +++++++ rattail_demo/web/views/__init__.py | 5 +++-- rattail_demo/web/views/common.py | 7 +++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 rattail_demo/web/templates/mobile/home.mako diff --git a/rattail_demo/web/templates/mobile/home.mako b/rattail_demo/web/templates/mobile/home.mako new file mode 100644 index 0000000..de5615c --- /dev/null +++ b/rattail_demo/web/templates/mobile/home.mako @@ -0,0 +1,7 @@ +## -*- coding: utf-8 -*- +<%inherit file="tailbone:templates/mobile/home.mako" /> + +
    + ${h.image(request.static_url('tailbone:static/img/home_logo.png'), "Rattail Logo", width='400')} +

    Welcome to the Rattail Demo

    +
    diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index b4c2f3d..71b9c58 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -21,9 +21,10 @@ def includeme(config): # TODO: merge these views into core/common config.add_route('home', '/') config.add_view(base.home, route_name='home', renderer='/home.mako') + config.add_route('mobile.home', '/mobile/') + config.add_view(base.home, route_name='mobile.home', renderer='/mobile/home.mako') config.add_route('bogus_error', '/bogus-error') - config.add_view(bogus_error, route_name='bogus_error', - permission='admin') + config.add_view(bogus_error, route_name='bogus_error', permission='admin') # core views config.include('rattail_demo.web.views.common') diff --git a/rattail_demo/web/views/common.py b/rattail_demo/web/views/common.py index 0098090..5139f24 100644 --- a/rattail_demo/web/views/common.py +++ b/rattail_demo/web/views/common.py @@ -15,6 +15,13 @@ class CommonView(base.CommonView): project_title = "Rattail Demo" project_version = rattail_demo.__version__ + def get_packages(self): + import rattail_tempmon + + packages = super(CommonView, self).get_packages() + packages['rattail-tempmon'] = rattail_tempmon.__version__ + return packages + def includeme(config): CommonView.defaults(config) From b5c81244ac0b84a8feec54081f405ed18040535a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 11 Dec 2016 18:08:39 -0600 Subject: [PATCH 010/136] Add initial support for mobile login / user menu etc. --- rattail_demo/web/templates/mobile/base.mako | 4 +++ rattail_demo/web/templates/mobile/login.mako | 6 ++++ rattail_demo/web/views/auth.py | 34 +++++++------------- 3 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 rattail_demo/web/templates/mobile/base.mako create mode 100644 rattail_demo/web/templates/mobile/login.mako diff --git a/rattail_demo/web/templates/mobile/base.mako b/rattail_demo/web/templates/mobile/base.mako new file mode 100644 index 0000000..3341118 --- /dev/null +++ b/rattail_demo/web/templates/mobile/base.mako @@ -0,0 +1,4 @@ +## -*- coding: utf-8 -*- +<%inherit file="tailbone:templates/mobile/base_external_toolbars.mako" /> + +${parent.body()} diff --git a/rattail_demo/web/templates/mobile/login.mako b/rattail_demo/web/templates/mobile/login.mako new file mode 100644 index 0000000..1848389 --- /dev/null +++ b/rattail_demo/web/templates/mobile/login.mako @@ -0,0 +1,6 @@ +## -*- coding: utf-8 -*- +<%inherit file="tailbone:templates/mobile/login.mako" /> + +

    Login with chuck / admin for full demo access.

    + +${parent.body()} diff --git a/rattail_demo/web/views/auth.py b/rattail_demo/web/views/auth.py index 53e031a..38c522a 100644 --- a/rattail_demo/web/views/auth.py +++ b/rattail_demo/web/views/auth.py @@ -5,32 +5,20 @@ Auth views from __future__ import unicode_literals, absolute_import -from pyramid import httpexceptions - from tailbone.views import auth as base -def change_password(request): - # prevent password change for 'chuck' - if request.user and request.user.username == 'chuck': - request.session.flash("Cannot change password for 'chuck' in Rattail Demo") - return httpexceptions.HTTPFound(location=request.get_referrer()) - return base.change_password(request) +class AuthenticationView(base.AuthenticationView): + """ + Prevent password change for 'chuck' user + """ + + def change_password(self): + if self.request.user and self.request.user.username == 'chuck': + self.request.session.flash("Cannot change password for 'chuck' in Rattail Demo") + return self.redirect(self.request.get_referrer()) + return super(AuthenticationView, self).change_password() def includeme(config): - # TODO: this is way too much duplication, surely.. - base.add_routes(config) - - config.add_forbidden_view(base.forbidden) - - config.add_view(base.login, route_name='login', - renderer='/login.mako') - - config.add_view(base.logout, route_name='logout') - - config.add_view(base.become_root, route_name='become_root') - config.add_view(base.stop_root, route_name='stop_root') - - config.add_view(change_password, route_name='change_password', - renderer='/change_password.mako') + AuthenticationView.defaults(config) From db9deffaa654d7660022a7986f0a69e1491f800a Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 11 Dec 2016 19:42:26 -0600 Subject: [PATCH 011/136] Add '+dev' suffix to all versions on about page --- rattail_demo/web/views/common.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rattail_demo/web/views/common.py b/rattail_demo/web/views/common.py index 5139f24..1cd5a52 100644 --- a/rattail_demo/web/views/common.py +++ b/rattail_demo/web/views/common.py @@ -13,13 +13,15 @@ import rattail_demo class CommonView(base.CommonView): project_title = "Rattail Demo" - project_version = rattail_demo.__version__ + project_version = rattail_demo.__version__ + '+dev' def get_packages(self): import rattail_tempmon packages = super(CommonView, self).get_packages() packages['rattail-tempmon'] = rattail_tempmon.__version__ + for key in packages: + packages[key] = packages[key] + '+dev' return packages From e9998cb5971c3ddd0baa0963cfbbf05b7cb8ddc2 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 11 Dec 2016 21:21:44 -0600 Subject: [PATCH 012/136] Add basic support for datasync views Still no actual daemon wired up yet, but can fake it for UI with this --- rattail_demo/web/templates/menu.mako | 5 ++++- rattail_demo/web/views/__init__.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index e330925..19c6c8d 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -110,7 +110,7 @@
  • % endif - % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'settings.list'): + % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list'):
  • Admin
      @@ -123,6 +123,9 @@ % if request.has_perm('emailprofiles.list'):
    • ${h.link_to("Email Profiles", url('emailprofiles'))}
    • % endif + % if request.has_perm('datasync.list'): +
    • ${h.link_to("DataSync Changes", url('datasyncchanges'))}
    • + % endif % if request.has_perm('settings.list'):
    • ${h.link_to("Settings", url('settings'))}
    • % endif diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 71b9c58..b18799b 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -33,6 +33,7 @@ def includeme(config): # main table views config.include('tailbone.views.brands') config.include('tailbone.views.customers') + config.include('tailbone.views.datasync') config.include('tailbone.views.departments') config.include('rattail_demo.web.views.email') config.include('rattail_demo.web.views.employees') From bae33491b65d39853971af4d0de6c25a287188be Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 12 Dec 2016 16:03:42 -0600 Subject: [PATCH 013/136] Refactor mobile views a bit, per tailbone changes --- rattail_demo/web/templates/mobile/base.mako | 4 ---- rattail_demo/web/views/__init__.py | 18 ------------------ 2 files changed, 22 deletions(-) delete mode 100644 rattail_demo/web/templates/mobile/base.mako diff --git a/rattail_demo/web/templates/mobile/base.mako b/rattail_demo/web/templates/mobile/base.mako deleted file mode 100644 index 3341118..0000000 --- a/rattail_demo/web/templates/mobile/base.mako +++ /dev/null @@ -1,4 +0,0 @@ -## -*- coding: utf-8 -*- -<%inherit file="tailbone:templates/mobile/base_external_toolbars.mako" /> - -${parent.body()} diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index b18799b..5bd6d9a 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -5,27 +5,9 @@ Web views from __future__ import unicode_literals, absolute_import -from tailbone import views as base - - -def bogus_error(request): - """ - A special view which simply raises an error, for the sake of testing - uncaught exception handling. - """ - raise Exception("Congratulations, you have triggered a bogus error.") - def includeme(config): - # TODO: merge these views into core/common - config.add_route('home', '/') - config.add_view(base.home, route_name='home', renderer='/home.mako') - config.add_route('mobile.home', '/mobile/') - config.add_view(base.home, route_name='mobile.home', renderer='/mobile/home.mako') - config.add_route('bogus_error', '/bogus-error') - config.add_view(bogus_error, route_name='bogus_error', permission='admin') - # core views config.include('rattail_demo.web.views.common') config.include('rattail_demo.web.views.auth') From 9b9bd8ea0f08296a98fd5d4865d413a2ef25c7e5 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 16 Dec 2016 21:23:35 -0600 Subject: [PATCH 014/136] Fetch a web page after restarting apache when updating production demo --- fabfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabfile.py b/fabfile.py index 8113230..a24448e 100644 --- a/fabfile.py +++ b/fabfile.py @@ -55,3 +55,5 @@ def update_production(all='false'): with cd('/srv/envs/demo'): sudo('bin/alembic --config=app/rattail.conf upgrade heads', user='rattail') sudo('service apache2 restart') + run('curl --output demo.html https://rattailproject.org/demo/') + run('rm demo.html') From 7947f89b5f2e9b5d88cd53220b4d377c1363cb25 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 11 Jan 2017 21:11:03 -0600 Subject: [PATCH 015/136] Add Piwik tracking code --- rattail_demo/web/templates/base.mako | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rattail_demo/web/templates/base.mako b/rattail_demo/web/templates/base.mako index deb08b7..09c3bf7 100644 --- a/rattail_demo/web/templates/base.mako +++ b/rattail_demo/web/templates/base.mako @@ -7,6 +7,24 @@ +<%def name="head_tags()"> + + + + + <%def name="header_logo()"> ${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", height='49')} From ed1cc301a35ac4a83e41d23fdc74a2522ba5f9ee Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 30 Mar 2017 21:45:03 -0500 Subject: [PATCH 016/136] Add base mobile template, for custom menu --- rattail_demo/web/templates/mobile/base.mako | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 rattail_demo/web/templates/mobile/base.mako diff --git a/rattail_demo/web/templates/mobile/base.mako b/rattail_demo/web/templates/mobile/base.mako new file mode 100644 index 0000000..e3ef1c6 --- /dev/null +++ b/rattail_demo/web/templates/mobile/base.mako @@ -0,0 +1,25 @@ +## -*- coding: utf-8 -*- +<%inherit file="tailbone:templates/mobile/base.mako" /> + +<%def name="mobile_usermenu()"> +
      +
        +
      • ${h.link_to("Home", url('mobile.home'))}
      • + % if request.is_root: +
      • ${h.link_to("Stop being root", url('stop_root'), **{'data-ajax': 'false'})}
      • + % elif request.is_admin: +
      • ${h.link_to("Become root", url('become_root'), **{'data-ajax': 'false'})}
      • + % endif + % if request.has_perm('customers.list'): +
      • ${h.link_to("Customers", url('mobile.customers'))}
      • + % endif + % if request.has_perm('products.list'): +
      • ${h.link_to("Products", url('mobile.products'))}
      • + % endif +
      • ${h.link_to("Logout", url('logout'), **{'data-ajax': 'false'})}
      • +
      • ${h.link_to("About {}".format(capture(self.app_title)), url('mobile.about'))}
      • +
      +
      + + +${parent.body()} From b9021afd96bd581bc52af83d61b4561eedc34e6c Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sat, 1 Apr 2017 16:31:52 -0500 Subject: [PATCH 017/136] Upgrade tempmon database when updating production demo --- fabfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fabfile.py b/fabfile.py index a24448e..629ed12 100644 --- a/fabfile.py +++ b/fabfile.py @@ -54,6 +54,8 @@ def update_production(all='false'): sudo('pip install --editable .') with cd('/srv/envs/demo'): sudo('bin/alembic --config=app/rattail.conf upgrade heads', user='rattail') + with cd('/srv/envs/demo'): + sudo('bin/alembic --config=app/tempmon.conf upgrade heads', user='rattail') sudo('service apache2 restart') run('curl --output demo.html https://rattailproject.org/demo/') run('rm demo.html') From fe19b5ae65cbfa478f7705845a78cbcd6b52dd0d Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 6 Jul 2017 16:26:04 -0500 Subject: [PATCH 018/136] Replace occurrence of `execfile()` --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9726116..7864af8 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) -execfile(os.path.join(here, 'rattail_demo', '_version.py')) +exec(open(os.path.join(here, 'rattail_demo', '_version.py')).read()) README = open(os.path.join(here, 'README.rst')).read() From 8df12a177456411229313e3aa585a9446e071141 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 13 Aug 2017 22:37:38 -0500 Subject: [PATCH 019/136] Add basic upgrades support, remove 'better' theme references --- rattail_demo/web/app.py | 5 ++-- rattail_demo/web/templates/base.mako | 4 ++-- rattail_demo/web/templates/menu.mako | 7 ++++-- rattail_demo/web/views/__init__.py | 4 +++- rattail_demo/web/views/upgrades.py | 35 ++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 rattail_demo/web/views/upgrades.py diff --git a/rattail_demo/web/app.py b/rattail_demo/web/app.py index a6716c8..38ea47d 100644 --- a/rattail_demo/web/app.py +++ b/rattail_demo/web/app.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8; -*- """ Pyramid web application """ @@ -15,9 +15,8 @@ def main(global_config, **settings): # set some defaults for PostgreSQL app.provide_postgresql_settings(settings) - # prefer demo templates over tailbone; use 'better' theme + # prefer demo templates over tailbone settings.setdefault('mako.directories', ['rattail_demo.web:templates', - 'tailbone:templates/themes/better', 'tailbone:templates',]) # make config objects diff --git a/rattail_demo/web/templates/base.mako b/rattail_demo/web/templates/base.mako index 09c3bf7..9083b95 100644 --- a/rattail_demo/web/templates/base.mako +++ b/rattail_demo/web/templates/base.mako @@ -1,5 +1,5 @@ -## -*- coding: utf-8 -*- -<%inherit file="tailbone:templates/themes/better/base.mako" /> +## -*- coding: utf-8; -*- +<%inherit file="tailbone:templates/base.mako" /> <%def name="global_title()">${"[STAGE] " if not request.rattail_config.production() else ''}Rattail Demo diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 19c6c8d..912c6e0 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -1,4 +1,4 @@ -## -*- coding: utf-8 -*- +## -*- coding: utf-8; -*- <%def name="main_menu_items()"> @@ -110,7 +110,7 @@ % endif - % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list'): + % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'):
    • Admin
        @@ -129,6 +129,9 @@ % if request.has_perm('settings.list'):
      • ${h.link_to("Settings", url('settings'))}
      • % endif + % if request.has_perm('upgrades.list'): +
      • ${h.link_to("Rattail Demo Upgrades", url('upgrades'))}
      • + % endif
    • % endif diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index 5bd6d9a..7499874 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8; -*- """ Web views """ @@ -11,6 +11,7 @@ def includeme(config): # core views config.include('rattail_demo.web.views.common') config.include('rattail_demo.web.views.auth') + config.include('tailbone.views.progress') # main table views config.include('tailbone.views.brands') @@ -30,6 +31,7 @@ def includeme(config): config.include('tailbone.views.stores') config.include('tailbone.views.subdepartments') config.include('rattail_demo.web.views.tempmon') + config.include('rattail_demo.web.views.upgrades') config.include('rattail_demo.web.views.users') config.include('tailbone.views.vendors') diff --git a/rattail_demo/web/views/upgrades.py b/rattail_demo/web/views/upgrades.py new file mode 100644 index 0000000..48961cc --- /dev/null +++ b/rattail_demo/web/views/upgrades.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8; -*- +""" +Upgrade views +""" + +from __future__ import unicode_literals, absolute_import + +import re + +from tailbone.views import upgrades as base + + +class UpgradeView(base.UpgradeView): + + def get_changelog_url(self, project, old_version, new_version): + + if project == 'rattail-demo': + if self.commit_hash_pattern.match(new_version): + if new_version == old_version: + return 'https://rattailproject.org/trac/log/rattail-demo/?rev={}&limit=100'.format( + new_version) + else: + return 'https://rattailproject.org/trac/log/rattail-demo/?rev={}&stop_rev={}&limit=100'.format( + new_version, old_version) + elif re.match(r'^\d+\.\d+\.\d+$', new_version): + return 'https://rattailproject.org/trac/browser/rattail-demo/CHANGES.rst?rev=v{}'.format( + new_version) + else: + return 'https://rattailproject.org/trac/browser/rattail-demo/CHANGES.rst' + + return super(UpgradeView, self).get_changelog_url(project, old_version, new_version) + + +def includeme(config): + UpgradeView.defaults(config) From 82d5bcc0ed06f45db6d9b689691a4ccd05a81f28 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 13 Aug 2017 23:12:38 -0500 Subject: [PATCH 020/136] Add support for user events --- rattail_demo/web/templates/menu.mako | 5 ++++- rattail_demo/web/views/users.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 912c6e0..d6f1e25 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -110,13 +110,16 @@ % endif - % if request.has_any_perm('users.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'): + % if request.has_any_perm('users.list', 'userevents.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'):
    • Admin
        % if request.has_perm('users.list'):
      • ${h.link_to("Users", url('users'))}
      • % endif + % if request.has_perm('userevents.list'): +
      • ${h.link_to("User Events", url('userevents'))}
      • + % endif % if request.has_perm('roles.list'):
      • ${h.link_to("Roles", url('roles'))}
      • % endif diff --git a/rattail_demo/web/views/users.py b/rattail_demo/web/views/users.py index fc227e4..73aed0d 100644 --- a/rattail_demo/web/views/users.py +++ b/rattail_demo/web/views/users.py @@ -22,3 +22,4 @@ class UsersView(base.UsersView): def includeme(config): UsersView.defaults(config) + base.UserEventsView.defaults(config) From d5a9b5c47102e5a541190118a1c23d8dd10f7f44 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 13 Aug 2017 23:47:17 -0500 Subject: [PATCH 021/136] Change how we prevent edit/delete for demo users to take new 'demo' user into account, but also more readable --- rattail_demo/web/views/users.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rattail_demo/web/views/users.py b/rattail_demo/web/views/users.py index 73aed0d..0cdf5d4 100644 --- a/rattail_demo/web/views/users.py +++ b/rattail_demo/web/views/users.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8; -*- """ User views """ @@ -14,10 +14,10 @@ class UsersView(base.UsersView): """ def editable_instance(self, user): - return user.uuid != '28eeee92bcf411e6a7c23ca9f40bc550' + return user.username not in ('chuck', 'demo') def deletable_instance(self, user): - return user.uuid != '28eeee92bcf411e6a7c23ca9f40bc550' + return user.username not in ('chuck', 'demo') def includeme(config): From 42e2373b08b3eb6d425efe54cfda78cd47e74187 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 13 Feb 2018 19:48:45 -0600 Subject: [PATCH 022/136] Add 'invoke' dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 7864af8..b617c03 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,7 @@ requires = [ # # package # low high + 'invoke', # 0.22.1 'psycopg2', # 2.6.2 'Tailbone', # 0.5.49 'xlrd', # 1.0.0 From 8ed906020b85efa74ca861fdbada55c79b154abd Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 26 Feb 2018 15:14:44 -0600 Subject: [PATCH 023/136] Replace rattail.fablib with rattail-fabric --- fabfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabfile.py b/fabfile.py index 629ed12..2fabf53 100644 --- a/fabfile.py +++ b/fabfile.py @@ -9,7 +9,7 @@ import shutil from fabric.api import * -from rattail.fablib import cdvirtualenv, workon, python +from rattail_fabric import cdvirtualenv, workon, python env.roledefs = { From caaf2842dcd46a2fca17f72cdc75b2c4d1a6d3a4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Sun, 3 Jun 2018 00:44:10 -0500 Subject: [PATCH 024/136] Rearrange menu a bit, add rattail-corepos dependency --- rattail_demo/web/templates/menu.mako | 30 ++++++++++------------------ setup.py | 1 + 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index d6f1e25..566da83 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -22,7 +22,7 @@ % endif - % if request.has_any_perm('products.list', 'vendors.list', 'brands.list', 'families.list', 'reportcodes.list'): + % if request.has_any_perm('products.list', 'vendors.list', 'departments.list', 'subdepartments.list', 'brands.list', 'families.list', 'reportcodes.list'):
      • Products
          @@ -32,6 +32,12 @@ % if request.has_perm('vendors.list'):
        • ${h.link_to("Vendors", url('vendors'))}
        • % endif + % if request.has_perm('departments.list'): +
        • ${h.link_to("Departments", url('departments'))}
        • + % endif + % if request.has_perm('subdepartments.list'): +
        • ${h.link_to("Subdepartments", url('subdepartments'))}
        • + % endif % if request.has_perm('brands.list'):
        • ${h.link_to("Brands", url('brands'))}
        • % endif @@ -62,23 +68,6 @@ % endif - % if request.has_any_perm('stores.list', 'departments.list', 'subdepartments.list'): -
        • - Company -
            - % if request.has_perm('stores.list'): -
          • ${h.link_to("Stores", url('stores'))}
          • - % endif - % if request.has_perm('departments.list'): -
          • ${h.link_to("Departments", url('departments'))}
          • - % endif - % if request.has_perm('subdepartments.list'): -
          • ${h.link_to("Subdepartments", url('subdepartments'))}
          • - % endif -
          -
        • - % endif - % if request.has_any_perm('batch.handheld.list', 'batch.inventory.list'):
        • Batches @@ -110,10 +99,13 @@
        • % endif - % if request.has_any_perm('users.list', 'userevents.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'): + % if request.has_any_perm('stores.list', 'users.list', 'userevents.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'):
        • Admin
            + % if request.has_perm('stores.list'): +
          • ${h.link_to("Stores", url('stores'))}
          • + % endif % if request.has_perm('users.list'):
          • ${h.link_to("Users", url('users'))}
          • % endif diff --git a/setup.py b/setup.py index b617c03..18e4bc7 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,7 @@ requires = [ 'invoke', # 0.22.1 'psycopg2', # 2.6.2 + 'rattail-corepos', # 0.1.0 'Tailbone', # 0.5.49 'xlrd', # 1.0.0 ] From ba95d397807c07405e82267b695f4c1dc849fdfc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 17 Aug 2018 11:58:11 -0500 Subject: [PATCH 025/136] Add version caps for pyramid etc. so that we don't have to cap things within tailbone --- setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/setup.py b/setup.py index 18e4bc7..2259dd2 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,16 @@ requires = [ # # package # low high + # TODO: Pyramid 1.9 looks like it breaks us..? playing it safe for now.. + 'pyramid<1.9', # 1.3b2 1.8.3 + + # apparently 2.0 removes the retry support, in which case we then need + # pyramid_retry .. but that requires pyramid 1.9 ... + 'pyramid_tm<2.0', # 0.3 1.1.1 + + # TODO: why do we need to cap this? breaks tailbone.db zope stuff somehow + 'zope.sqlalchemy<1.0', # 0.7 0.7.7 + 'invoke', # 0.22.1 'psycopg2', # 2.6.2 'rattail-corepos', # 0.1.0 From 12c8b3b8dd7f1415443a3269a04d41663bd54131 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 17 Aug 2018 12:57:24 -0500 Subject: [PATCH 026/136] Remove version caps for pyramid; stop auto-config for postgres error retry --- rattail_demo/web/app.py | 6 ------ setup.py | 10 ---------- 2 files changed, 16 deletions(-) diff --git a/rattail_demo/web/app.py b/rattail_demo/web/app.py index 38ea47d..b81b31a 100644 --- a/rattail_demo/web/app.py +++ b/rattail_demo/web/app.py @@ -12,9 +12,6 @@ def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - # set some defaults for PostgreSQL - app.provide_postgresql_settings(settings) - # prefer demo templates over tailbone settings.setdefault('mako.directories', ['rattail_demo.web:templates', 'tailbone:templates',]) @@ -28,7 +25,4 @@ def main(global_config, **settings): pyramid_config.include('rattail_demo.web.subscribers') pyramid_config.include('rattail_demo.web.views') - # configure PostgreSQL some more - app.configure_postgresql(pyramid_config) - return pyramid_config.make_wsgi_app() diff --git a/setup.py b/setup.py index 2259dd2..18e4bc7 100644 --- a/setup.py +++ b/setup.py @@ -43,16 +43,6 @@ requires = [ # # package # low high - # TODO: Pyramid 1.9 looks like it breaks us..? playing it safe for now.. - 'pyramid<1.9', # 1.3b2 1.8.3 - - # apparently 2.0 removes the retry support, in which case we then need - # pyramid_retry .. but that requires pyramid 1.9 ... - 'pyramid_tm<2.0', # 0.3 1.1.1 - - # TODO: why do we need to cap this? breaks tailbone.db zope stuff somehow - 'zope.sqlalchemy<1.0', # 0.7 0.7.7 - 'invoke', # 0.22.1 'psycopg2', # 2.6.2 'rattail-corepos', # 0.1.0 From e91858b5671ef798562c62e453faecaa3180ee5b Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 17 Aug 2018 13:08:48 -0500 Subject: [PATCH 027/136] Add (protected) email settings for upgrade success, failure --- rattail_demo/emails.py | 13 +++++++++++-- rattail_demo/web/views/email.py | 12 +++++++++--- rattail_demo/web/views/settings.py | 16 +++++++++++++--- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/rattail_demo/emails.py b/rattail_demo/emails.py index a60bf7b..b9c43c3 100644 --- a/rattail_demo/emails.py +++ b/rattail_demo/emails.py @@ -5,5 +5,14 @@ Email definitions from __future__ import unicode_literals, absolute_import -from rattail.emails import user_feedback -from rattail_tempmon.emails import tempmon_low_temp, tempmon_high_temp, tempmon_critical_temp, tempmon_error + +# bring in some common config from rattail +from rattail.emails import (upgrade_failure, + upgrade_success, + user_feedback) + +# bring in some common config from rattail_tempmon +from rattail.emails import (tempmon_low_temp, + tempmon_high_temp, + tempmon_critical_temp, + tempmon_error) diff --git a/rattail_demo/web/views/email.py b/rattail_demo/web/views/email.py index 7c2d1ca..4343a5b 100644 --- a/rattail_demo/web/views/email.py +++ b/rattail_demo/web/views/email.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8; -*- """ Email views """ @@ -13,11 +13,17 @@ class ProfilesView(base.ProfilesView): Prevent edit/delete for 'feedback' email config """ + protected = [ + 'user_feedback', + 'upgrade_success', + 'upgrade_failure', + ] + def editable_instance(self, profile): - return profile['key'] != 'user_feedback' + return profile['key'] not in self.protected def deletable_instance(self, profile): - return profile['key'] != 'user_feedback' + return profile['key'] not in self.protected def includeme(config): diff --git a/rattail_demo/web/views/settings.py b/rattail_demo/web/views/settings.py index 9346227..42bf9b1 100644 --- a/rattail_demo/web/views/settings.py +++ b/rattail_demo/web/views/settings.py @@ -14,13 +14,23 @@ class SettingsView(base.SettingsView): """ Prevent edit/delete for 'feedback' email settings """ - feedback = re.compile(r'^rattail\.mail\.user_feedback\..*') + protected = [ + re.compile(r'^rattail\.mail\.user_feedback\..*'), + re.compile(r'^rattail\.mail\.upgrade_success\..*'), + re.compile(r'^rattail\.mail\.upgrade_failure\..*'), + ] def editable_instance(self, setting): - return not bool(self.feedback.match(setting.name)) + for pattern in self.protected: + if pattern.match(setting.name): + return False + return True def deletable_instance(self, setting): - return not bool(self.feedback.match(setting.name)) + for pattern in self.protected: + if pattern.match(setting.name): + return False + return True def includeme(config): From 452dc934fa0392a10244431df1f7fe7ebd45bb95 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Fri, 17 Aug 2018 13:11:52 -0500 Subject: [PATCH 028/136] Fix import bug for email settings --- rattail_demo/emails.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rattail_demo/emails.py b/rattail_demo/emails.py index b9c43c3..a20619e 100644 --- a/rattail_demo/emails.py +++ b/rattail_demo/emails.py @@ -12,7 +12,7 @@ from rattail.emails import (upgrade_failure, user_feedback) # bring in some common config from rattail_tempmon -from rattail.emails import (tempmon_low_temp, - tempmon_high_temp, - tempmon_critical_temp, - tempmon_error) +from rattail_tempmon.emails import (tempmon_low_temp, + tempmon_high_temp, + tempmon_critical_temp, + tempmon_error) From e20a9b8123e8203551a1575277bf69fe587b72cb Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 22 Nov 2018 11:16:59 -0600 Subject: [PATCH 029/136] Add basic CORE-POS table views --- rattail_demo/web/app.py | 6 ++ rattail_demo/web/db.py | 14 +++ rattail_demo/web/templates/menu.mako | 29 ++++++ rattail_demo/web/views/__init__.py | 3 + rattail_demo/web/views/corepos/__init__.py | 18 ++++ rattail_demo/web/views/corepos/customers.py | 73 ++++++++++++++ rattail_demo/web/views/corepos/departments.py | 70 ++++++++++++++ rattail_demo/web/views/corepos/employees.py | 68 ++++++++++++++ rattail_demo/web/views/corepos/master.py | 30 ++++++ rattail_demo/web/views/corepos/products.py | 94 +++++++++++++++++++ .../web/views/corepos/subdepartments.py | 36 +++++++ .../web/views/corepos/transactions.py | 76 +++++++++++++++ rattail_demo/web/views/corepos/vendors.py | 39 ++++++++ 13 files changed, 556 insertions(+) create mode 100644 rattail_demo/web/db.py create mode 100644 rattail_demo/web/views/corepos/__init__.py create mode 100644 rattail_demo/web/views/corepos/customers.py create mode 100644 rattail_demo/web/views/corepos/departments.py create mode 100644 rattail_demo/web/views/corepos/employees.py create mode 100644 rattail_demo/web/views/corepos/master.py create mode 100644 rattail_demo/web/views/corepos/products.py create mode 100644 rattail_demo/web/views/corepos/subdepartments.py create mode 100644 rattail_demo/web/views/corepos/transactions.py create mode 100644 rattail_demo/web/views/corepos/vendors.py 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) From b7512e0b31e2918ff85f0f5f5bb56d80a986b8a4 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 22 Nov 2018 20:38:36 -0600 Subject: [PATCH 030/136] Add feature demo for importing Square transactions to CORE-POS also allow delete of CORE-POS transactions, for sake of demo --- .../corepos/transaction-details/index.mako | 11 ++++++++ rattail_demo/web/templates/menu.mako | 5 +++- rattail_demo/web/views/__init__.py | 1 + .../web/views/corepos/transactions.py | 27 +++++++++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 rattail_demo/web/templates/corepos/transaction-details/index.mako diff --git a/rattail_demo/web/templates/corepos/transaction-details/index.mako b/rattail_demo/web/templates/corepos/transaction-details/index.mako new file mode 100644 index 0000000..c4dce82 --- /dev/null +++ b/rattail_demo/web/templates/corepos/transaction-details/index.mako @@ -0,0 +1,11 @@ +## -*- coding: utf-8; -*- +<%inherit file="/master/index.mako" /> + +<%def name="context_menu_items()"> + ${parent.context_menu_items()} + % if request.has_perm('{}.import_file'.format(permission_prefix)): +
          • ${h.link_to("Import {} from Square CSV".format(model_title_plural), url('{}.import_square'.format(route_prefix)))}
          • + % endif + + +${parent.body()} diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 0f048b8..16833c4 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -97,7 +97,7 @@ % endif - % if request.has_any_perm('batch.handheld.list', 'batch.inventory.list'): + % if request.has_any_perm('batch.handheld.list', 'batch.inventory.list', 'batch.importer.list'):
          • Batches
              @@ -107,6 +107,9 @@ % if request.has_perm('batch.inventory.list'):
            • ${h.link_to("Inventory", url('batch.inventory'))}
            • % endif + % if request.has_perm('batch.importer.list'): +
            • ${h.link_to("Import / Export", url('batch.importer'))}
            • + % endif
          • % endif diff --git a/rattail_demo/web/views/__init__.py b/rattail_demo/web/views/__init__.py index d1dc8c7..f32d45f 100644 --- a/rattail_demo/web/views/__init__.py +++ b/rattail_demo/web/views/__init__.py @@ -41,3 +41,4 @@ def includeme(config): # batch views config.include('tailbone.views.handheld') config.include('tailbone.views.inventory') + config.include('tailbone.views.batch.importer') diff --git a/rattail_demo/web/views/corepos/transactions.py b/rattail_demo/web/views/corepos/transactions.py index 85ad24f..aa169b9 100644 --- a/rattail_demo/web/views/corepos/transactions.py +++ b/rattail_demo/web/views/corepos/transactions.py @@ -10,6 +10,8 @@ from corepos.trans.db import model as coretrans from .master import CoreMasterView from rattail_demo.web.db import CoreTransSession +from rattail_corepos.corepos.importing.square import FromSquareToCoreTrans + class TransactionDetailView(CoreMasterView): """ @@ -20,6 +22,9 @@ class TransactionDetailView(CoreMasterView): model_title = "CORE-POS Transaction Detail" url_prefix = '/corepos/transaction-details' route_prefix = 'corepos.transaction_details' + deletable = True + bulk_deletable = True + supports_import_batch_from_file = True labels = { 'store_row_id': "Store Row ID", @@ -43,6 +48,11 @@ class TransactionDetailView(CoreMasterView): 'total', ] + def get_bulk_delete_session(self): + from corepos.trans.db import Session as CoreTransSession + + return CoreTransSession() + def configure_grid(self, g): super(TransactionDetailView, self).configure_grid(g) @@ -71,6 +81,23 @@ class TransactionDetailView(CoreMasterView): f.set_type('discount', 'currency') f.set_type('total', 'currency') + def import_square(self): + return self.import_batch_from_file(FromSquareToCoreTrans, 'TransactionDetail', + importer_host_title="Square CSV") + + @classmethod + def defaults(cls, config): + route_prefix = cls.get_route_prefix() + url_prefix = cls.get_url_prefix() + permission_prefix = cls.get_permission_prefix() + + # import from square + config.add_route('{}.import_square'.format(route_prefix), '{}/import-square'.format(url_prefix)) + config.add_view(cls, attr='import_square', route_name='{}.import_square'.format(route_prefix), + permission='{}.import_file'.format(permission_prefix)) + + cls._defaults(config) + def includeme(config): TransactionDetailView.defaults(config) From 57c5f8ba5ea598d1e81682a52b3babc20f69f201 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 26 Nov 2018 22:08:29 -0600 Subject: [PATCH 031/136] Add support for basic web API app --- rattail_demo/web/webapi.py | 52 ++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 2 files changed, 53 insertions(+) create mode 100644 rattail_demo/web/webapi.py diff --git a/rattail_demo/web/webapi.py b/rattail_demo/web/webapi.py new file mode 100644 index 0000000..591b2e6 --- /dev/null +++ b/rattail_demo/web/webapi.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8; -*- +""" +Rattail Demo web API +""" + +from __future__ import unicode_literals, absolute_import + +from pyramid.config import Configurator +from pyramid.authentication import SessionAuthenticationPolicy + +from tailbone import app +from tailbone.auth import TailboneAuthorizationPolicy + + +def main(global_config, **settings): + """ + This function returns a Pyramid WSGI application. + """ + # make config objects + rattail_config = app.make_rattail_config(settings) + pyramid_config = Configurator(settings=settings, root_factory=app.Root) + + # configure user authorization / authentication + pyramid_config.set_authorization_policy(TailboneAuthorizationPolicy()) + pyramid_config.set_authentication_policy(SessionAuthenticationPolicy()) + + # always require CSRF token protection + pyramid_config.set_default_csrf_options(require_csrf=True, token='_csrf', header='X-XSRF-TOKEN') + + # bring in some Pyramid goodies + pyramid_config.include('tailbone.beaker') + pyramid_config.include('pyramid_tm') + pyramid_config.include('cornice') + + # bring in the pyramid_retry logic, if available + # TODO: pretty soon we can require this package, hopefully.. + try: + import pyramid_retry + except ImportError: + pass + else: + pyramid_config.include('pyramid_retry') + + # add some permissions magic + pyramid_config.add_directive('add_tailbone_permission_group', 'tailbone.auth.add_permission_group') + pyramid_config.add_directive('add_tailbone_permission', 'tailbone.auth.add_permission') + + # bring in some Tailbone + pyramid_config.include('tailbone.subscribers') + pyramid_config.include('tailbone.api') + + return pyramid_config.make_wsgi_app() diff --git a/setup.py b/setup.py index 18e4bc7..3881c09 100644 --- a/setup.py +++ b/setup.py @@ -77,6 +77,7 @@ setup( entry_points = { 'paste.app_factory': [ 'main = rattail_demo.web.app:main', + 'webapi = rattail_demo.web.webapi:main', ], 'rattail.config.extensions': [ 'rattail-demo = rattail_demo.config:DemoConfigExtension', From e3d9d7d4e52719f7cf716751bafdf953bfc12b02 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Mon, 26 Nov 2018 22:08:41 -0600 Subject: [PATCH 032/136] Tweak some menu option labels --- rattail_demo/web/templates/menu.mako | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index 16833c4..d8bdad0 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -148,14 +148,14 @@
          • ${h.link_to("Roles", url('roles'))}
          • % endif % if request.has_perm('emailprofiles.list'): -
          • ${h.link_to("Email Profiles", url('emailprofiles'))}
          • +
          • ${h.link_to("Email Settings", url('emailprofiles'))}
          • + % endif + % if request.has_perm('settings.list'): +
          • ${h.link_to("Raw Settings", url('settings'))}
          • % endif % if request.has_perm('datasync.list'):
          • ${h.link_to("DataSync Changes", url('datasyncchanges'))}
          • % endif - % if request.has_perm('settings.list'): -
          • ${h.link_to("Settings", url('settings'))}
          • - % endif % if request.has_perm('upgrades.list'):
          • ${h.link_to("Rattail Demo Upgrades", url('upgrades'))}
          • % endif From 46867b11549073cff633f89b14f1ceba2621a23e Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Nov 2018 17:57:46 -0600 Subject: [PATCH 033/136] Move base template to /base_meta.mako per upstream 'themes' feature --- .../web/templates/{base.mako => base_meta.mako} | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) rename rattail_demo/web/templates/{base.mako => base_meta.mako} (72%) diff --git a/rattail_demo/web/templates/base.mako b/rattail_demo/web/templates/base_meta.mako similarity index 72% rename from rattail_demo/web/templates/base.mako rename to rattail_demo/web/templates/base_meta.mako index 9083b95..02252b9 100644 --- a/rattail_demo/web/templates/base.mako +++ b/rattail_demo/web/templates/base_meta.mako @@ -1,11 +1,7 @@ ## -*- coding: utf-8; -*- -<%inherit file="tailbone:templates/base.mako" /> +<%inherit file="tailbone:templates/base_meta.mako" /> -<%def name="global_title()">${"[STAGE] " if not request.rattail_config.production() else ''}Rattail Demo - -<%def name="favicon()"> - - +<%def name="app_title()">Rattail Demo <%def name="head_tags()"> @@ -28,5 +24,3 @@ <%def name="header_logo()"> ${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", height='49')} - -${parent.body()} From b0a52dcec29d16b040ea60d3f68e90f30f3e5003 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 27 Nov 2018 18:20:56 -0600 Subject: [PATCH 034/136] Fix template references for app_title make sure we only look to /base_meta.mako for that --- rattail_demo/web/templates/mobile/base.mako | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rattail_demo/web/templates/mobile/base.mako b/rattail_demo/web/templates/mobile/base.mako index e3ef1c6..dbcb610 100644 --- a/rattail_demo/web/templates/mobile/base.mako +++ b/rattail_demo/web/templates/mobile/base.mako @@ -1,5 +1,6 @@ ## -*- coding: utf-8 -*- <%inherit file="tailbone:templates/mobile/base.mako" /> +<%namespace name="base_meta" file="/base_meta.mako" /> <%def name="mobile_usermenu()">
            @@ -17,7 +18,7 @@
          • ${h.link_to("Products", url('mobile.products'))}
          • % endif
          • ${h.link_to("Logout", url('logout'), **{'data-ajax': 'false'})}
          • -
          • ${h.link_to("About {}".format(capture(self.app_title)), url('mobile.about'))}
          • +
          • ${h.link_to("About {}".format(capture(base_meta.app_title)), url('mobile.about'))}
          From 9294d95ffb50ff1727fb07d3f8bdcff872c5bc91 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 28 Nov 2018 19:52:57 -0600 Subject: [PATCH 035/136] Add proper "retry" support for basic postgres restart --- rattail_demo/web/app.py | 3 +++ setup.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/rattail_demo/web/app.py b/rattail_demo/web/app.py index 0c58612..26858dc 100644 --- a/rattail_demo/web/app.py +++ b/rattail_demo/web/app.py @@ -14,6 +14,9 @@ def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ + # set some defaults for postgres + app.provide_postgresql_settings(settings) + # prefer demo templates over tailbone settings.setdefault('mako.directories', ['rattail_demo.web:templates', 'tailbone:templates',]) diff --git a/setup.py b/setup.py index 3881c09..b2a2a9c 100644 --- a/setup.py +++ b/setup.py @@ -43,6 +43,9 @@ requires = [ # # package # low high + # TODO: can remove this once tailbone requires it + 'pyramid_retry', # 1.0 + 'invoke', # 0.22.1 'psycopg2', # 2.6.2 'rattail-corepos', # 0.1.0 From 71c60c58a185b3668fe4053d51c319ff1f73c884 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 28 Nov 2018 21:41:00 -0600 Subject: [PATCH 036/136] Tweak style for header logo seemed necessary for sake of bobcat theme, but worked for default too.. --- rattail_demo/web/templates/base_meta.mako | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rattail_demo/web/templates/base_meta.mako b/rattail_demo/web/templates/base_meta.mako index 02252b9..211503d 100644 --- a/rattail_demo/web/templates/base_meta.mako +++ b/rattail_demo/web/templates/base_meta.mako @@ -22,5 +22,5 @@ <%def name="header_logo()"> - ${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", height='49')} + ${h.image(request.static_url('tailbone:static/img/rattail.ico'), "Header Logo", style="height: 55px;")} From 15c44196bc685e9248b18acc931c6b59f6e384dc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Wed, 28 Nov 2018 23:53:19 -0600 Subject: [PATCH 037/136] Add menu for new 'bobcat' theme --- .../web/templates/themes/bobcat/base.mako | 7 + .../web/templates/themes/bobcat/menu.mako | 198 ++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 rattail_demo/web/templates/themes/bobcat/base.mako create mode 100644 rattail_demo/web/templates/themes/bobcat/menu.mako diff --git a/rattail_demo/web/templates/themes/bobcat/base.mako b/rattail_demo/web/templates/themes/bobcat/base.mako new file mode 100644 index 0000000..2de6cc6 --- /dev/null +++ b/rattail_demo/web/templates/themes/bobcat/base.mako @@ -0,0 +1,7 @@ +## -*- coding: utf-8; -*- +<%inherit file="tailbone:templates/themes/bobcat/base.mako" /> + +## NOTE: we must define this base template even though we just +## inherit from upstream bobcat! + +${parent.body()} diff --git a/rattail_demo/web/templates/themes/bobcat/menu.mako b/rattail_demo/web/templates/themes/bobcat/menu.mako new file mode 100644 index 0000000..10fe535 --- /dev/null +++ b/rattail_demo/web/templates/themes/bobcat/menu.mako @@ -0,0 +1,198 @@ +## -*- coding: utf-8; -*- + +<%def name="main_menu_items()"> + + + + From 599bf7cc322bf0c51a9584d9db77ea9b072b63a1 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 29 Nov 2018 01:40:18 -0600 Subject: [PATCH 038/136] Refactor web app to use "simple" menus for sake of sharing between themes --- rattail_demo/config.py | 1 + rattail_demo/web/menus.py | 232 ++++++++++++++++++ rattail_demo/web/templates/menu.mako | 180 +++----------- .../web/templates/themes/bobcat/menu.mako | 177 +++---------- 4 files changed, 310 insertions(+), 280 deletions(-) create mode 100644 rattail_demo/web/menus.py diff --git a/rattail_demo/config.py b/rattail_demo/config.py index 1ff335b..8cd6ccb 100644 --- a/rattail_demo/config.py +++ b/rattail_demo/config.py @@ -18,3 +18,4 @@ class DemoConfigExtension(ConfigExtension): # tell rattail where our stuff lives config.setdefault('rattail.mail', 'emails', 'rattail_demo.emails') + config.setdefault('tailbone', 'menus', 'rattail_demo.web.menus') diff --git a/rattail_demo/web/menus.py b/rattail_demo/web/menus.py new file mode 100644 index 0000000..37a818a --- /dev/null +++ b/rattail_demo/web/menus.py @@ -0,0 +1,232 @@ +# -*- coding: utf-8; -*- +""" +Web Menus +""" + +from __future__ import unicode_literals, absolute_import + + +def simple_menus(request): + url = request.route_url + + menus = [ + { + 'title': "Time Clock", + 'type': 'menu', + 'items': [ + { + 'title': "Personal Schedule", + 'url': url('schedule.employee'), + 'perm': 'schedule.view', + }, + { + 'title': "Full Schedule", + 'url': url('schedule'), + 'perm': 'schedule.viewall', + }, + { + 'title': "Edit Schedule", + 'url': url('schedule.edit'), + 'perm': 'schedule.edit', + }, + { + 'title': "Raw Scheduled Shifts", + 'url': url('scheduledshifts'), + 'perm': 'scheduledshifts.list', + }, + ], + }, + { + 'title': "Products", + 'type': 'menu', + 'items': [ + { + 'title': "Products", + 'url': url('products'), + 'perm': 'products.list', + }, + { + 'title': "Vendors", + 'url': url('vendors'), + 'perm': 'vendors.list', + }, + { + 'title': "Departments", + 'url': url('departments'), + 'perm': 'departments.list', + }, + { + 'title': "Subdepartments", + 'url': url('subdepartments'), + 'perm': 'subdepartments.list', + }, + { + 'title': "Brands", + 'url': url('brands'), + 'perm': 'brands.list', + }, + { + 'title': "Families", + 'url': url('families'), + 'perm': 'families.list', + }, + { + 'title': "Report Codes", + 'url': url('reportcodes'), + 'perm': 'reportcodes.list', + }, + ], + }, + { + 'title': "People", + 'type': 'menu', + 'items': [ + { + 'title': "All People", + 'url': url('people'), + 'perm': 'people.list', + }, + { + 'title': "Customers", + 'url': url('customers'), + 'perm': 'customers.list', + }, + { + 'title': "Employees", + 'url': url('employees'), + 'perm': 'employees.list', + }, + ], + }, + { + 'title': "CORE-POS", + 'type': 'menu', + 'items': [ + { + 'title': "Departments", + 'url': url('corepos.departments'), + 'perm': 'corepos.departments.list', + }, + { + 'title': "Subdepartments", + 'url': url('corepos.subdepartments'), + 'perm': 'corepos.subdepartments.list', + }, + { + 'title': "Vendors", + 'url': url('corepos.vendors'), + 'perm': 'corepos.vendors.list', + }, + { + 'title': "Products", + 'url': url('corepos.products'), + 'perm': 'corepos.products.list', + }, + { + 'title': "Customers", + 'url': url('corepos.customers'), + 'perm': 'corepos.customers.list', + }, + { + 'title': "Employees", + 'url': url('corepos.employees'), + 'perm': 'corepos.employees.list', + }, + { + 'title': "Transaction Details", + 'url': url('corepos.transaction_details'), + 'perm': 'corepos.transaction_details.list', + }, + ], + }, + { + 'title': "Batches", + 'type': 'menu', + 'items': [ + { + 'title': "Handheld", + 'url': url('batch.handheld'), + 'perm': 'batch.handheld.list', + }, + { + 'title': "Inventory", + 'url': url('batch.inventory'), + 'perm': 'batch.inventory.list', + }, + { + 'title': "Import / Export", + 'url': url('batch.importer'), + 'perm': 'batch.importer.list', + }, + ], + }, + { + 'title': "TempMon", + 'type': 'menu', + 'items': [ + { + 'title': "Clients", + 'url': url('tempmon.clients'), + 'perm': 'tempmon.clients.list', + }, + { + 'title': "Probes", + 'url': url('tempmon.probes'), + 'perm': 'tempmon.probes.list', + }, + { + 'title': "Readings", + 'url': url('tempmon.readings'), + 'perm': 'tempmon.readings.list', + }, + ], + }, + { + 'title': "Admin", + 'type': 'menu', + 'items': [ + { + 'title': "Stores", + 'url': url('stores'), + 'perm': 'stores.list', + }, + { + 'title': "Users", + 'url': url('users'), + 'perm': 'users.list', + }, + { + 'title': "User Events", + 'url': url('userevents'), + 'perm': 'userevents.list', + }, + { + 'title': "Roles", + 'url': url('roles'), + 'perm': 'roles.list', + }, + { + 'title': "Email Settings", + 'url': url('emailprofiles'), + 'perm': 'emailprofiles.list', + }, + { + 'title': "Raw Settings", + 'url': url('settings'), + 'perm': 'settings.list', + }, + { + 'title': "DataSync Changes", + 'url': url('datasyncchanges'), + 'perm': 'datasync.list', + }, + { + 'title': "Rattail Demo Upgrades", + 'url': url('upgrades'), + 'perm': 'upgrades.list', + }, + ], + }, + ] + + return menus diff --git a/rattail_demo/web/templates/menu.mako b/rattail_demo/web/templates/menu.mako index d8bdad0..966e724 100644 --- a/rattail_demo/web/templates/menu.mako +++ b/rattail_demo/web/templates/menu.mako @@ -1,161 +1,58 @@ ## -*- coding: utf-8; -*- +<%def name="make_simple_menu()"> + + % for topitem in menus: +
        • + ${topitem.title} +
            + % for subitem in topitem.items: +
          • ${h.link_to(subitem.title, subitem.url)}
          • + % endfor +
          +
        • + % endfor + + ## User Menu + % if request.user: +
        • + ${request.user}${" ({})".format(inbox_count) if inbox_count else ''} +
            + % if request.is_root: +
          • ${h.link_to("Stop being root", url('stop_root'))}
          • + % elif request.is_admin: +
          • ${h.link_to("Become root", url('become_root'))}
          • + % endif +
          • ${h.link_to("Messages{}".format(" ({})".format(inbox_count) if inbox_count else ''), url('messages.inbox'))}
          • +
          • ${h.link_to("Change Password", url('change_password'))}
          • +
          • ${h.link_to("Logout", url('logout'))}
          • +
          +
        • + % else: +
        • ${h.link_to("Login", url('login'))}
        • + % endif + + + <%def name="main_menu_items()"> - % if request.has_any_perm('schedule.view', 'schedule.viewall', 'schedule.edit', 'scheduledshifts.list'): -
        • - Time Clock -
            - % if request.has_perm('schedule.view'): -
          • ${h.link_to("Personal Schedule", url('schedule.employee'))}
          • - % endif - % if request.has_perm('schedule.viewall'): -
          • ${h.link_to("Full Schedule", url('schedule'))}
          • - % endif - % if request.has_perm('schedule.edit'): -
          • ${h.link_to("Edit Schedule", url('schedule.edit'))}
          • - % endif - % if request.has_perm('scheduledshifts.list'): -
          • ${h.link_to("Raw Scheduled Shifts", url('scheduledshifts'))}
          • - % endif -
          -
        • - % endif + % if request.rattail_config.getbool('tailbone', 'menus.simple', default=False): + ${self.make_simple_menu()} + % else: - % if request.has_any_perm('products.list', 'vendors.list', 'departments.list', 'subdepartments.list', 'brands.list', 'families.list', 'reportcodes.list'): -
        • - Products -
            - % if request.has_perm('products.list'): -
          • ${h.link_to("Products", url('products'))}
          • - % endif - % if request.has_perm('vendors.list'): -
          • ${h.link_to("Vendors", url('vendors'))}
          • - % endif - % if request.has_perm('departments.list'): -
          • ${h.link_to("Departments", url('departments'))}
          • - % endif - % if request.has_perm('subdepartments.list'): -
          • ${h.link_to("Subdepartments", url('subdepartments'))}
          • - % endif - % if request.has_perm('brands.list'): -
          • ${h.link_to("Brands", url('brands'))}
          • - % endif - % if request.has_perm('families.list'): -
          • ${h.link_to("Families", url('families'))}
          • - % endif - % if request.has_perm('reportcodes.list'): -
          • ${h.link_to("Report Codes", url('reportcodes'))}
          • - % endif -
          -
        • - % endif - - % if request.has_any_perm('people.list', 'customers.list', 'employees.list'): -
        • - People -
            - % if request.has_perm('people.list'): -
          • ${h.link_to("All People", url('people'))}
          • - % endif - % if request.has_perm('customers.list'): -
          • ${h.link_to("Customers", url('customers'))}
          • - % endif - % if request.has_perm('employees.list'): -
          • ${h.link_to("Employees", url('employees'))}
          • - % endif -
          -
        • - % 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', 'batch.importer.list'): -
        • - Batches -
            - % if request.has_perm('batch.handheld.list'): -
          • ${h.link_to("Handheld", url('batch.handheld'))}
          • - % endif - % if request.has_perm('batch.inventory.list'): -
          • ${h.link_to("Inventory", url('batch.inventory'))}
          • - % endif - % if request.has_perm('batch.importer.list'): -
          • ${h.link_to("Import / Export", url('batch.importer'))}
          • - % endif -
          -
        • - % endif - - % if request.has_any_perm('tempmon.clients.list', 'tempmon.probes.list', 'tempmon.readings.list'): -
        • - TempMon -
            - % if request.has_perm('tempmon.clients.list'): -
          • ${h.link_to("Clients", url('tempmon.clients'))}
          • - % endif - % if request.has_perm('tempmon.probes.list'): -
          • ${h.link_to("Probes", url('tempmon.probes'))}
          • - % endif - % if request.has_perm('tempmon.readings.list'): -
          • ${h.link_to("Readings", url('tempmon.readings'))}
          • - % endif -
          -
        • - % endif - - % if request.has_any_perm('stores.list', 'users.list', 'userevents.list', 'roles.list', 'emailprofiles.list', 'datasync.list', 'settings.list', 'upgrades.list'): + % if request.has_any_perm('users.list', 'roles.list', 'settings.list', 'upgrades.list'):
        • Admin
            - % if request.has_perm('stores.list'): -
          • ${h.link_to("Stores", url('stores'))}
          • - % endif % if request.has_perm('users.list'):
          • ${h.link_to("Users", url('users'))}
          • % endif - % if request.has_perm('userevents.list'): -
          • ${h.link_to("User Events", url('userevents'))}
          • - % endif % if request.has_perm('roles.list'):
          • ${h.link_to("Roles", url('roles'))}
          • % endif - % if request.has_perm('emailprofiles.list'): -
          • ${h.link_to("Email Settings", url('emailprofiles'))}
          • - % endif % if request.has_perm('settings.list'):
          • ${h.link_to("Raw Settings", url('settings'))}
          • % endif - % if request.has_perm('datasync.list'): -
          • ${h.link_to("DataSync Changes", url('datasyncchanges'))}
          • - % endif % if request.has_perm('upgrades.list'):
          • ${h.link_to("Rattail Demo Upgrades", url('upgrades'))}
          • % endif @@ -181,4 +78,5 @@
          • ${h.link_to("Login", url('login'))}
          • % endif + % endif diff --git a/rattail_demo/web/templates/themes/bobcat/menu.mako b/rattail_demo/web/templates/themes/bobcat/menu.mako index 10fe535..5cd4fe3 100644 --- a/rattail_demo/web/templates/themes/bobcat/menu.mako +++ b/rattail_demo/web/templates/themes/bobcat/menu.mako @@ -1,170 +1,67 @@ ## -*- coding: utf-8; -*- -<%def name="main_menu_items()"> +<%def name="make_simple_menu()"> + + - ## People - % if request.has_any_perm('people.list', 'customers.list', 'employees.list'): - - % endif +<%def name="main_menu_items()"> - ## CORE-POS - % 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'): - - % endif + % if request.rattail_config.getbool('tailbone', 'menus.simple', default=False): + ${self.make_simple_menu()} + % else: - ## Batches - % if request.has_any_perm('batch.handheld.list', 'batch.inventory.list', 'batch.importer.list'): - - % endif - - ## TempMon - % if request.has_any_perm('tempmon.clients.list', 'tempmon.probes.list', 'tempmon.readings.list'): - - % endif +