Convert (most?) basic views to use master view pattern.

This commit is contained in:
Lance Edgar 2015-12-07 15:08:14 -06:00
parent ab40685175
commit 16bbbb4a75
35 changed files with 363 additions and 1187 deletions

View file

@ -1,16 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Brands", url('brands'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Brand", url('brand.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Brand", url('brand.read', uuid=form.fieldset.model.uuid))}</li>
% endif
% if version_count is not Undefined and request.has_perm('brand.versions.view'):
<li>${h.link_to("View Change History ({0})".format(version_count), url('brand.versions', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Brands</%def>
<%def name="context_menu_items()">
% if request.has_perm('brands.create'):
<li>${h.link_to("Create a new Brand", url('brand.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,16 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Categories", url('categories'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Category", url('category.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Category", url('category.read', uuid=form.fieldset.model.uuid))}</li>
% endif
% if version_count is not Undefined and request.has_perm('category.versions.view'):
<li>${h.link_to("View Change History ({0})".format(version_count), url('category.versions', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Categories</%def>
<%def name="context_menu_items()">
% if request.has_perm('categories.create'):
<li>${h.link_to("Create a new Category", url('category.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Customer Groups", url('customer_groups'))}</li>
% if form.readonly and request.has_perm('customer_groups.update'):
<li>${h.link_to("Edit this Customer Group", url('customer_group.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Customer Group", url('customer_group.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Customer Groups</%def>
<%def name="context_menu_items()">
% if request.has_perm('customer_groups.create'):
<li>${h.link_to("Create a new Customer Group", url('customer_group.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Customers", url('customers'))}</li>
% if form.readonly and request.has_perm('customers.update'):
<li>${h.link_to("Edit this Customer", url('customer.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Customer", url('customer.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,6 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Customers</%def>
${parent.body()}

View file

@ -1,12 +1,10 @@
## -*- coding: utf-8 -*- ## -*- coding: utf-8 -*-
<%inherit file="/customers/crud.mako" /> <%inherit file="/master/view.mako" />
${parent.body()} ${parent.body()}
<% customer = form.fieldset.model %>
<h2>People</h2> <h2>People</h2>
% if customer.people: % if instance.people:
<p>Customer account is associated with the following people:</p> <p>Customer account is associated with the following people:</p>
<div class="grid clickable"> <div class="grid clickable">
<table> <table>
@ -15,8 +13,8 @@ ${parent.body()}
<th>Last Name</th> <th>Last Name</th>
</thead> </thead>
<tbody> <tbody>
% for i, person in enumerate(customer.people, 1): % for i, person in enumerate(instance.people, 1):
<tr class="${'odd' if i % 2 else 'even'}" url="${url('person.read', uuid=person.uuid)}"> <tr class="${'odd' if i % 2 else 'even'}" url="${url('people.view', uuid=person.uuid)}">
<td>${person.first_name or ''}</td> <td>${person.first_name or ''}</td>
<td>${person.last_name or ''}</td> <td>${person.last_name or ''}</td>
</tr> </tr>
@ -29,7 +27,7 @@ ${parent.body()}
% endif % endif
<h2>Groups</h2> <h2>Groups</h2>
% if customer.groups: % if instance.groups:
<p>Customer account belongs to the following groups:</p> <p>Customer account belongs to the following groups:</p>
<div class="grid clickable"> <div class="grid clickable">
<table> <table>
@ -38,8 +36,8 @@ ${parent.body()}
<th>Name</th> <th>Name</th>
</thead> </thead>
<tbody> <tbody>
% for i, group in enumerate(customer.groups, 1): % for i, group in enumerate(instance.groups, 1):
<tr class="${'odd' if i % 2 else 'even'}" url="${url('customer_group.read', uuid=group.uuid)}"> <tr class="${'odd' if i % 2 else 'even'}" url="${url('customergroups.view', uuid=group.uuid)}">
<td>${group.id}</td> <td>${group.id}</td>
<td>${group.name or ''}</td> <td>${group.name or ''}</td>
</tr> </tr>

View file

@ -1,16 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Departments", url('departments'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Department", url('department.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Department", url('department.read', uuid=form.fieldset.model.uuid))}</li>
% endif
% if version_count is not Undefined and request.has_perm('department.versions.view'):
<li>${h.link_to("View Change History ({0})".format(version_count), url('department.versions', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Departments</%def>
<%def name="context_menu_items()">
% if request.has_perm('departments.create'):
<li>${h.link_to("Create a new Department", url('department.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Employees", url('employees'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Employee", url('employee.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Employee", url('employee.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,6 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Employees</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Families", url('families'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Family", url('family.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Family", url('family.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Families</%def>
<%def name="context_menu_items()">
% if request.has_perm('families.create'):
<li>${h.link_to("Create a new Family", url('family.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Report Codes", url('reportcodes'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Report Code", url('reportcode.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Report Code", url('reportcode.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Report Codes</%def>
<%def name="context_menu_items()">
% if request.has_perm('reportcodes.create'):
<li>${h.link_to("Create a new Report Code", url('reportcode.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,13 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Stores", url('stores'))}</li>
% if form.readonly and request.has_perm('stores.update'):
<li>${h.link_to("Edit this Store", url('store.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Store", url('store.read', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Stores</%def>
<%def name="context_menu_items()">
% if request.has_perm('stores.create'):
<li>${h.link_to("Create a new Store", url('store.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,16 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Subdepartments", url('subdepartments'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Subdepartment", url('subdepartment.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Subdepartment", url('subdepartment.read', uuid=form.fieldset.model.uuid))}</li>
% endif
% if version_count is not Undefined and request.has_perm('subdepartment.versions.view'):
<li>${h.link_to("View Change History ({0})".format(version_count), url('subdepartment.versions', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Subdepartments</%def>
<%def name="context_menu_items()">
% if request.has_perm('subdepartments.create'):
<li>${h.link_to("Create a new Subdepartment", url('subdepartment.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,16 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/crud.mako" />
<%def name="context_menu_items()">
<li>${h.link_to("Back to Vendors", url('vendors'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Vendor", url('vendor.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Vendor", url('vendor.read', uuid=form.fieldset.model.uuid))}</li>
% endif
% if version_count is not Undefined and request.has_perm('vendor.versions.view'):
<li>${h.link_to("View Change History ({0})".format(version_count), url('vendor.versions', uuid=form.fieldset.model.uuid))}</li>
% endif
</%def>
${parent.body()}

View file

@ -1,12 +0,0 @@
## -*- coding: utf-8 -*-
<%inherit file="/grid.mako" />
<%def name="title()">Vendors</%def>
<%def name="context_menu_items()">
% if request.has_perm('vendors.create'):
<li>${h.link_to("Create a new Vendor", url('vendor.create'))}</li>
% endif
</%def>
${parent.body()}

View file

@ -24,58 +24,31 @@
Brand Views Brand Views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from rattail.db import model from rattail.db import model
from rattail.db.model import Brand
from . import SearchableAlchemyGridView, CrudView, AutocompleteView from tailbone.views import MasterView, AutocompleteView
from .continuum import VersionView, version_defaults from tailbone.views.continuum import VersionView, version_defaults
class BrandsGrid(SearchableAlchemyGridView): class BrandsView(MasterView):
"""
Master view for the Brand class.
"""
model_class = model.Brand
mapped_class = Brand def configure_grid(self, g):
config_prefix = 'brands' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'name'
def filter_map(self):
return self.make_filter_map(ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.name, g.name,
], ],
readonly=True) readonly=True)
if self.request.has_perm('brands.read'):
g.viewable = True
g.view_route_name = 'brand.read'
if self.request.has_perm('brands.update'):
g.editable = True
g.edit_route_name = 'brand.update'
if self.request.has_perm('brands.delete'):
g.deletable = True
g.delete_route_name = 'brand.delete'
return g
def configure_fieldset(self, fs):
class BrandCrud(CrudView):
mapped_class = Brand
home_route = 'brands'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.name, fs.name,
@ -88,49 +61,21 @@ class BrandVersionView(VersionView):
View which shows version history for a brand. View which shows version history for a brand.
""" """
parent_class = model.Brand parent_class = model.Brand
route_model_view = 'brand.read' route_model_view = 'brands.view'
class BrandsAutocomplete(AutocompleteView): class BrandsAutocomplete(AutocompleteView):
mapped_class = Brand mapped_class = model.Brand
fieldname = 'name' fieldname = 'name'
def add_routes(config):
config.add_route('brands', '/brands')
config.add_route('brands.autocomplete', '/brands/autocomplete')
config.add_route('brand.create', '/brands/new')
config.add_route('brand.read', '/brands/{uuid}')
config.add_route('brand.update', '/brands/{uuid}/edit')
config.add_route('brand.delete', '/brands/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config)
config.add_view(BrandsGrid, # autocomplete
route_name='brands', config.add_route('brands.autocomplete', '/brands/autocomplete')
renderer='/brands/index.mako', config.add_view(BrandsAutocomplete, route_name='brands.autocomplete',
permission='brands.list') renderer='json', permission='brands.list')
config.add_view(BrandsAutocomplete,
route_name='brands.autocomplete',
renderer='json',
permission='brands.list')
config.add_view(BrandCrud, attr='create',
route_name='brand.create',
renderer='/brands/crud.mako',
permission='brands.create')
config.add_view(BrandCrud, attr='read',
route_name='brand.read',
renderer='/brands/crud.mako',
permission='brands.read')
config.add_view(BrandCrud, attr='update',
route_name='brand.update',
renderer='/brands/crud.mako',
permission='brands.update')
config.add_view(BrandCrud, attr='delete',
route_name='brand.delete',
permission='brands.delete')
BrandsView.defaults(config)
version_defaults(config, BrandVersionView, 'brand') version_defaults(config, BrandVersionView, 'brand')

View file

@ -24,34 +24,26 @@
Category Views Category Views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from rattail.db import model from rattail.db import model
from rattail.db.model import Category
from . import SearchableAlchemyGridView, CrudView from tailbone.views import MasterView
from .continuum import VersionView, version_defaults from tailbone.views.continuum import VersionView, version_defaults
class CategoriesGrid(SearchableAlchemyGridView): class CategoriesView(MasterView):
"""
Master view for the Category class.
"""
model_class = model.Category
model_title_plural = "Categories"
route_prefix = 'categories'
mapped_class = Category def configure_grid(self, g):
config_prefix = 'categories' g.filters['name'].default_active = True
sort = 'number' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'number'
def filter_map(self):
return self.make_filter_map(exact=['number'], ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('number', 'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.number, g.number,
@ -59,25 +51,8 @@ class CategoriesGrid(SearchableAlchemyGridView):
g.department, g.department,
], ],
readonly=True) readonly=True)
if self.request.has_perm('categories.read'):
g.viewable = True
g.view_route_name = 'category.read'
if self.request.has_perm('categories.update'):
g.editable = True
g.edit_route_name = 'category.update'
if self.request.has_perm('categories.delete'):
g.deletable = True
g.delete_route_name = 'category.delete'
return g
def configure_fieldset(self, fs):
class CategoryCrud(CrudView):
mapped_class = Category
home_route = 'categories'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.number, fs.number,
@ -94,32 +69,9 @@ class CategoryVersionView(VersionView):
parent_class = model.Category parent_class = model.Category
model_title_plural = "Categories" model_title_plural = "Categories"
route_model_list = 'categories' route_model_list = 'categories'
route_model_view = 'category.read' route_model_view = 'categories.view'
def add_routes(config):
config.add_route('categories', '/categories')
config.add_route('category.create', '/categories/new')
config.add_route('category.read', '/categories/{uuid}')
config.add_route('category.update', '/categories/{uuid}/edit')
config.add_route('category.delete', '/categories/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config) CategoriesView.defaults(config)
# list
config.add_view(CategoriesGrid, route_name='categories',
renderer='/categories/index.mako', permission='categories.list')
# crud
config.add_view(CategoryCrud, attr='create', route_name='category.create',
renderer='/categories/crud.mako', permission='categories.create')
config.add_view(CategoryCrud, attr='read', route_name='category.read',
renderer='/categories/crud.mako', permission='categories.read')
config.add_view(CategoryCrud, attr='update', route_name='category.update',
renderer='/categories/crud.mako', permission='categories.update')
config.add_view(CategoryCrud, attr='delete', route_name='category.delete',
permission='categories.delete')
version_defaults(config, CategoryVersionView, 'category', template_prefix='/categories') version_defaults(config, CategoryVersionView, 'category', template_prefix='/categories')

View file

@ -1,9 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2015 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -21,99 +20,50 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # along with Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
CustomerGroup Views CustomerGroup Views
""" """
from . import SearchableAlchemyGridView, CrudView from __future__ import unicode_literals, absolute_import
from ..db import Session from rattail.db import model
from rattail.db.model import CustomerGroup, CustomerGroupAssignment
from tailbone.db import Session
from tailbone.views import MasterView
class CustomerGroupsGrid(SearchableAlchemyGridView): class CustomerGroupsView(MasterView):
"""
Master view for the CustomerGroup class.
"""
model_class = model.CustomerGroup
model_title = "Customer Group"
mapped_class = CustomerGroup def configure_grid(self, g):
config_prefix = 'customer_groups' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'name'
def filter_map(self):
return self.make_filter_map(ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('id', 'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.id.label("ID"), g.id.label("ID"),
g.name, g.name,
], ],
readonly=True) readonly=True)
if self.request.has_perm('customer_groups.read'):
g.viewable = True
g.view_route_name = 'customer_group.read'
if self.request.has_perm('customer_groups.update'):
g.editable = True
g.edit_route_name = 'customer_group.update'
if self.request.has_perm('customer_groups.delete'):
g.deletable = True
g.delete_route_name = 'customer_group.delete'
return g
def configure_fieldset(self, fs):
class CustomerGroupCrud(CrudView):
mapped_class = CustomerGroup
home_route = 'customer_groups'
pretty_name = "Customer Group"
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.id.label("ID"), fs.id.label("ID"),
fs.name, fs.name,
]) ])
return fs
def pre_delete(self, group): def before_delete(self, group):
# First remove customer associations. # First remove customer associations.
q = Session.query(CustomerGroupAssignment)\ q = Session.query(model.CustomerGroupAssignment)\
.filter(CustomerGroupAssignment.group == group) .filter(model.CustomerGroupAssignment.group == group)
for assignment in q: for assignment in q:
Session.delete(assignment) Session.delete(assignment)
def add_routes(config):
config.add_route('customer_groups', '/customer-groups')
config.add_route('customer_group.create', '/customer-groups/new')
config.add_route('customer_group.read', '/customer-groups/{uuid}')
config.add_route('customer_group.update', '/customer-groups/{uuid}/edit')
config.add_route('customer_group.delete', '/customer-groups/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config) CustomerGroupsView.defaults(config)
config.add_view(CustomerGroupsGrid, route_name='customer_groups',
renderer='/customergroups/index.mako',
permission='customer_groups.list')
config.add_view(CustomerGroupCrud, attr='create', route_name='customer_group.create',
renderer='/customergroups/crud.mako',
permission='customer_groups.create')
config.add_view(CustomerGroupCrud, attr='read', route_name='customer_group.read',
renderer='/customergroups/crud.mako',
permission='customer_groups.read')
config.add_view(CustomerGroupCrud, attr='update', route_name='customer_group.update',
renderer='/customergroups/crud.mako',
permission='customer_groups.update')
config.add_view(CustomerGroupCrud, attr='delete', route_name='customer_group.delete',
permission='customer_groups.delete')

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2014 Lance Edgar # Copyright © 2010-2015 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -20,65 +20,59 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # along with Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Customer Views Customer Views
""" """
from __future__ import unicode_literals, absolute_import
import re import re
from sqlalchemy import func, and_ import sqlalchemy as sa
from sqlalchemy.orm import joinedload from sqlalchemy import orm
from tailbone.views import SearchableAlchemyGridView, CrudView, AutocompleteView from pyramid.httpexceptions import HTTPNotFound
from tailbone.forms import EnumFieldRenderer
from tailbone import forms
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView, AutocompleteView
from rattail import enum from rattail import enum
from rattail.db import model from rattail.db import model
class CustomersGrid(SearchableAlchemyGridView): class CustomersView(MasterView):
"""
Master view for the Customer class.
"""
model_class = model.Customer
mapped_class = model.Customer def configure_grid(self, g):
config_prefix = 'customers'
sort = 'name'
def join_map(self): g.joiners['email'] = lambda q: q.outerjoin(model.CustomerEmailAddress, sa.and_(
return {
'email':
lambda q: q.outerjoin(model.CustomerEmailAddress, and_(
model.CustomerEmailAddress.parent_uuid == model.Customer.uuid, model.CustomerEmailAddress.parent_uuid == model.Customer.uuid,
model.CustomerEmailAddress.preference == 1)), model.CustomerEmailAddress.preference == 1))
'phone': g.joiners['phone'] = lambda q: q.outerjoin(model.CustomerPhoneNumber, sa.and_(
lambda q: q.outerjoin(model.CustomerPhoneNumber, and_(
model.CustomerPhoneNumber.parent_uuid == model.Customer.uuid, model.CustomerPhoneNumber.parent_uuid == model.Customer.uuid,
model.CustomerPhoneNumber.preference == 1)), model.CustomerPhoneNumber.preference == 1))
}
def filter_map(self): g.filters['email'] = g.make_filter('email', model.CustomerEmailAddress.address,
return self.make_filter_map( label="Email Address")
exact=['id'], g.filters['phone'] = g.make_filter('phone', model.CustomerPhoneNumber.number,
name=self.filter_ilike_and_soundex(model.Customer.name), label="Phone Number")
email=self.filter_ilike(model.CustomerEmailAddress.address),
phone=self.filter_ilike(model.CustomerPhoneNumber.number))
def filter_config(self): # TODO
return self.make_filter_config( # name=self.filter_ilike_and_soundex(model.Customer.name),
include_filter_name=True,
filter_type_name='lk',
filter_label_phone="Phone Number",
filter_label_email="Email Address",
filter_label_id="ID")
def sort_map(self): g.filters['name'].default_active = True
return self.make_sort_map( g.filters['name'].default_verb = 'contains'
'id', 'name', g.filters['id'].label = "ID"
email=self.sorter(model.CustomerEmailAddress.address),
phone=self.sorter(model.CustomerPhoneNumber.number)) g.sorters['email'] = lambda q, d: q.order_by(getattr(model.CustomerEmailAddress.address, d)())
g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.CustomerPhoneNumber.number, d)())
g.default_sortkey = 'name'
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.id.label("ID"), g.id.label("ID"),
@ -88,42 +82,38 @@ class CustomersGrid(SearchableAlchemyGridView):
], ],
readonly=True) readonly=True)
if self.request.has_perm('customers.read'): def get_instance(self):
g.viewable = True try:
g.view_route_name = 'customer.read' instance = super(CustomersView, self).get_instance()
if self.request.has_perm('customers.update'): except HTTPNotFound:
g.editable = True pass
g.edit_route_name = 'customer.update' else:
if self.request.has_perm('customers.delete'): if instance:
g.deletable = True return instance
g.delete_route_name = 'customer.delete'
return g key = self.request.matchdict['uuid']
# search by Customer.id
instance = self.Session.query(model.Customer)\
.filter(model.Customer.id == key)\
.first()
if instance:
return instance
class CustomerCrud(CrudView): # search by CustomerPerson.uuid
instance = self.Session.query(model.CustomerPerson).get(key)
if instance:
return instance.customer
mapped_class = model.Customer # search by CustomerGroupAssignment.uuid
home_route = 'customers' instance = self.Session.query(model.CustomerGroupAssignment).get(key)
if instance:
return instance.customer
def get_model(self, key): raise HTTPNotFound
customer = super(CustomerCrud, self).get_model(key)
if customer:
return customer
customer = Session.query(model.Customer).filter_by(id=key).first()
if customer:
return customer
person = Session.query(model.CustomerPerson).get(key)
if person:
return person.customer
group = Session.query(model.CustomerGroupAssignment).get(key)
if group:
return group.customer
return None
def fieldset(self, model): def configure_fieldset(self, fs):
fs = self.make_fieldset(model) fs.email_preference.set(renderer=forms.EnumFieldRenderer(enum.EMAIL_PREFERENCE))
fs.email_preference.set(renderer=EnumFieldRenderer(enum.EMAIL_PREFERENCE))
fs.configure( fs.configure(
include=[ include=[
fs.id.label("ID"), fs.id.label("ID"),
@ -132,7 +122,6 @@ class CustomerCrud(CrudView):
fs.email.label("Email Address").readonly(), fs.email.label("Email Address").readonly(),
fs.email_preference, fs.email_preference,
]) ])
return fs
class CustomerNameAutocomplete(AutocompleteView): class CustomerNameAutocomplete(AutocompleteView):
@ -140,7 +129,7 @@ class CustomerNameAutocomplete(AutocompleteView):
Autocomplete view which operates on customer name. Autocomplete view which operates on customer name.
""" """
mapped_class = model.Customer mapped_class = model.Customer
fieldname = u'name' fieldname = 'name'
class CustomerPhoneAutocomplete(AutocompleteView): class CustomerPhoneAutocomplete(AutocompleteView):
@ -154,19 +143,19 @@ class CustomerPhoneAutocomplete(AutocompleteView):
order to be able to perform smarter matching. However normalizing the order to be able to perform smarter matching. However normalizing the
database value currently uses the PG SQL ``regexp_replace()`` function. database value currently uses the PG SQL ``regexp_replace()`` function.
""" """
invalid_pattern = re.compile(ur'\D') invalid_pattern = re.compile(r'\D')
def prepare_term(self, term): def prepare_term(self, term):
return self.invalid_pattern.sub(u'', term) return self.invalid_pattern.sub('', term)
def query(self, term): def query(self, term):
return Session.query(model.CustomerPhoneNumber)\ return Session.query(model.CustomerPhoneNumber)\
.filter(func.regexp_replace(model.CustomerPhoneNumber.number, ur'\D', u'', u'g').like(u'%{0}%'.format(term)))\ .filter(sa.func.regexp_replace(model.CustomerPhoneNumber.number, r'\D', '', 'g').like('%{0}%'.format(term)))\
.order_by(model.CustomerPhoneNumber.number)\ .order_by(model.CustomerPhoneNumber.number)\
.options(joinedload(model.CustomerPhoneNumber.customer)) .options(orm.joinedload(model.CustomerPhoneNumber.customer))
def display(self, phone): def display(self, phone):
return u"{0} {1}".format(phone.number, phone.customer) return "{0} {1}".format(phone.number, phone.customer)
def value(self, phone): def value(self, phone):
return phone.customer.uuid return phone.customer.uuid
@ -176,54 +165,30 @@ def customer_info(request):
""" """
View which returns simple dictionary of info for a particular customer. View which returns simple dictionary of info for a particular customer.
""" """
uuid = request.params.get(u'uuid') uuid = request.params.get('uuid')
customer = Session.query(model.Customer).get(uuid) if uuid else None customer = Session.query(model.Customer).get(uuid) if uuid else None
if not customer: if not customer:
return {} return {}
return { return {
u'uuid': customer.uuid, 'uuid': customer.uuid,
u'name': customer.name, 'name': customer.name,
u'phone_number': customer.phone.number if customer.phone else u'', 'phone_number': customer.phone.number if customer.phone else '',
} }
def add_routes(config):
config.add_route(u'customers', u'/customers')
config.add_route(u'customer.create', u'/customers/new')
config.add_route(u'customer.info', u'/customers/info')
config.add_route(u'customers.autocomplete', u'/customers/autocomplete')
config.add_route(u'customers.autocomplete.phone', u'/customers/autocomplete/phone')
config.add_route(u'customer.read', u'/customers/{uuid}')
config.add_route(u'customer.update', u'/customers/{uuid}/edit')
config.add_route(u'customer.delete', u'/customers/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config)
config.add_view(CustomersGrid, route_name='customers', # autocomplete
renderer='/customers/index.mako', config.add_route('customers.autocomplete', '/customers/autocomplete')
permission='customers.list') config.add_view(CustomerNameAutocomplete, route_name='customers.autocomplete',
renderer='json', permission='customers.list')
config.add_route('customers.autocomplete.phone', '/customers/autocomplete/phone')
config.add_view(CustomerPhoneAutocomplete, route_name='customers.autocomplete.phone',
renderer='json', permission='customers.list')
config.add_view(CustomerCrud, attr='create', route_name='customer.create', # info
renderer='/customers/crud.mako', config.add_route('customer.info', '/customers/info')
permission='customers.create') config.add_view(customer_info, route_name='customer.info',
config.add_view(CustomerCrud, attr='read', route_name='customer.read', renderer='json', permission='customers.view')
renderer='/customers/read.mako',
permission='customers.read')
config.add_view(CustomerCrud, attr='update', route_name='customer.update',
renderer='/customers/crud.mako',
permission='customers.update')
config.add_view(CustomerCrud, attr='delete', route_name='customer.delete',
permission='customers.delete')
config.add_view(CustomerNameAutocomplete, route_name=u'customers.autocomplete', CustomersView.defaults(config)
renderer=u'json',
permission=u'customers.list')
config.add_view(CustomerPhoneAutocomplete, route_name=u'customers.autocomplete.phone',
renderer=u'json',
permission=u'customers.list')
config.add_view(customer_info, route_name=u'customer.info',
renderer=u'json',
permission=u'customers.read')

View file

@ -27,56 +27,29 @@ Department Views
from __future__ import unicode_literals from __future__ import unicode_literals
from rattail.db import model from rattail.db import model
from rattail.db.model import Department, Product, ProductCost, Vendor
from . import SearchableAlchemyGridView, CrudView, AlchemyGridView, AutocompleteView from tailbone.views import MasterView, AutocompleteView, AlchemyGridView
from .continuum import VersionView, version_defaults from tailbone.views.continuum import VersionView, version_defaults
class DepartmentsGrid(SearchableAlchemyGridView): class DepartmentsView(MasterView):
"""
Master view for the Department class.
"""
model_class = model.Department
mapped_class = Department def configure_grid(self, g):
config_prefix = 'departments' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'number'
def filter_map(self):
return self.make_filter_map(ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('number', 'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.number, g.number,
g.name, g.name,
], ],
readonly=True) readonly=True)
if self.request.has_perm('departments.read'):
g.viewable = True
g.view_route_name = 'department.read'
if self.request.has_perm('departments.update'):
g.editable = True
g.edit_route_name = 'department.update'
if self.request.has_perm('departments.delete'):
g.deletable = True
g.delete_route_name = 'department.delete'
return g
def configure_fieldset(self, fs):
class DepartmentCrud(CrudView):
mapped_class = Department
home_route = 'departments'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.number, fs.number,
@ -90,25 +63,24 @@ class DepartmentVersionView(VersionView):
View which shows version history for a department. View which shows version history for a department.
""" """
parent_class = model.Department parent_class = model.Department
route_model_view = 'department.read' route_model_view = 'departments.view'
class DepartmentsByVendorGrid(AlchemyGridView): class DepartmentsByVendorGrid(AlchemyGridView):
mapped_class = Department mapped_class = model.Department
config_prefix = 'departments.by_vendor' config_prefix = 'departments.by_vendor'
checkboxes = True checkboxes = True
partial_only = True partial_only = True
def query(self): def query(self):
q = self.make_query() return self.make_query()\
q = q.outerjoin(Product) .outerjoin(model.Product)\
q = q.join(ProductCost) .join(model.ProductCost)\
q = q.join(Vendor) .join(model.Vendor)\
q = q.filter(Vendor.uuid == self.request.params['uuid']) .filter(model.Vendor.uuid == self.request.params['uuid'])\
q = q.distinct() .distinct()\
q = q.order_by(Department.name) .order_by(model.Department.name)
return q
def grid(self): def grid(self):
g = self.make_grid() g = self.make_grid()
@ -122,43 +94,21 @@ class DepartmentsByVendorGrid(AlchemyGridView):
class DepartmentsAutocomplete(AutocompleteView): class DepartmentsAutocomplete(AutocompleteView):
mapped_class = Department mapped_class = model.Department
fieldname = 'name' fieldname = 'name'
def add_routes(config):
config.add_route('departments', '/departments')
config.add_route('departments.autocomplete', '/departments/autocomplete')
config.add_route('departments.by_vendor', '/departments/by-vendor')
config.add_route('department.create', '/departments/new')
config.add_route('department.read', '/departments/{uuid}')
config.add_route('department.update', '/departments/{uuid}/edit')
config.add_route('department.delete', '/departments/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config)
# list
config.add_view(DepartmentsGrid, route_name='departments',
renderer='/departments/index.mako', permission='departments.list')
# autocomplete # autocomplete
config.add_route('departments.autocomplete', '/departments/autocomplete')
config.add_view(DepartmentsAutocomplete, route_name='departments.autocomplete', config.add_view(DepartmentsAutocomplete, route_name='departments.autocomplete',
renderer='json', permission='departments.list') renderer='json', permission='departments.list')
# departments by vendor list # departments by vendor list
config.add_route('departments.by_vendor', '/departments/by-vendor')
config.add_view(DepartmentsByVendorGrid,route_name='departments.by_vendor', config.add_view(DepartmentsByVendorGrid,route_name='departments.by_vendor',
permission='departments.list') permission='departments.list')
# crud DepartmentsView.defaults(config)
config.add_view(DepartmentCrud, attr='create', route_name='department.create',
renderer='/departments/crud.mako', permission='departments.create')
config.add_view(DepartmentCrud, attr='read', route_name='department.read',
renderer='/departments/crud.mako', permission='departments.read')
config.add_view(DepartmentCrud, attr='update', route_name='department.update',
renderer='/departments/crud.mako', permission='departments.update')
config.add_view(DepartmentCrud, attr='delete', route_name='department.delete',
permission='departments.delete')
version_defaults(config, DepartmentVersionView, 'department') version_defaults(config, DepartmentVersionView, 'department')

View file

@ -24,88 +24,68 @@
Employee Views Employee Views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from sqlalchemy import and_ import sqlalchemy as sa
from rattail.db import model
from rattail import enum from rattail import enum
from rattail.db import model
from tailbone.views import AutocompleteView from tailbone import forms
from tailbone.views import MasterView, AutocompleteView
from . import SearchableAlchemyGridView, CrudView from tailbone.newgrids.filters import EnumValueRenderer
from ..grids.search import EnumSearchFilter
from ..forms import AssociationProxyField, EnumFieldRenderer
from rattail.db.model import (
Employee, EmployeePhoneNumber, EmployeeEmailAddress, Person)
from rattail.enum import EMPLOYEE_STATUS, EMPLOYEE_STATUS_CURRENT
class EmployeesView(MasterView):
"""
Master view for the Employee class.
"""
model_class = model.Employee
class EmployeesGrid(SearchableAlchemyGridView): def configure_grid(self, g):
mapped_class = Employee g.joiners['phone'] = lambda q: q.outerjoin(model.EmployeePhoneNumber, sa.and_(
config_prefix = 'employees' model.EmployeePhoneNumber.parent_uuid == model.Employee.uuid,
sort = 'first_name' model.EmployeePhoneNumber.preference == 1))
g.joiners['email'] = lambda q: q.outerjoin(model.EmployeeEmailAddress, sa.and_(
model.EmployeeEmailAddress.parent_uuid == model.Employee.uuid,
model.EmployeeEmailAddress.preference == 1))
def join_map(self): g.filters['first_name'] = g.make_filter('first_name', model.Person.first_name)
return { g.filters['last_name'] = g.make_filter('last_name', model.Person.last_name)
'phone':
lambda q: q.outerjoin(EmployeePhoneNumber, and_( g.filters['email'] = g.make_filter('email', model.EmployeeEmailAddress.address,
EmployeePhoneNumber.parent_uuid == Employee.uuid, label="Email Address")
EmployeePhoneNumber.preference == 1)), g.filters['phone'] = g.make_filter('phone', model.EmployeePhoneNumber.number,
'email': label="Phone Number")
lambda q: q.outerjoin(EmployeeEmailAddress, and_(
EmployeeEmailAddress.parent_uuid == Employee.uuid,
EmployeeEmailAddress.preference == 1)),
}
def filter_map(self):
kwargs = dict(
first_name=self.filter_ilike(Person.first_name),
last_name=self.filter_ilike(Person.last_name),
phone=self.filter_ilike(EmployeePhoneNumber.number),
email=self.filter_ilike(EmployeeEmailAddress.address))
if self.request.has_perm('employees.edit'): if self.request.has_perm('employees.edit'):
kwargs.update(dict( g.filters['id'].label = "ID"
exact=['id', 'status'])) g.filters['status'].default_active = True
return self.make_filter_map(**kwargs) g.filters['status'].default_verb = 'equal'
g.filters['status'].default_value = enum.EMPLOYEE_STATUS_CURRENT
g.filters['status'].set_value_renderer(EnumValueRenderer(enum.EMPLOYEE_STATUS))
else:
del g.filters['id']
del g.filters['status']
def filter_config(self): g.filters['first_name'].default_active = True
kwargs = dict( g.filters['first_name'].default_verb = 'contains'
include_filter_first_name=True,
filter_type_first_name='lk',
include_filter_last_name=True,
filter_type_last_name='lk',
filter_label_phone="Phone Number",
filter_label_email="Email Address")
if self.request.has_perm('employees.edit'):
kwargs.update(dict(
filter_label_id="ID",
include_filter_status=True,
filter_type_status='is',
filter_factory_status=EnumSearchFilter(EMPLOYEE_STATUS),
status=EMPLOYEE_STATUS_CURRENT))
return self.make_filter_config(**kwargs)
def sort_map(self): g.filters['last_name'].default_active = True
return self.make_sort_map( g.filters['last_name'].default_verb = 'contains'
first_name=self.sorter(Person.first_name),
last_name=self.sorter(Person.last_name),
phone=self.sorter(EmployeePhoneNumber.number),
email=self.sorter(EmployeeEmailAddress.address))
def query(self): g.sorters['first_name'] = lambda q, d: q.order_by(getattr(model.Person.first_name, d)())
q = self.make_query() g.sorters['last_name'] = lambda q, d: q.order_by(getattr(model.Person.last_name, d)())
q = q.join(Person)
if not self.request.has_perm('employees.edit'): g.sorters['email'] = lambda q, d: q.order_by(getattr(model.EmployeeEmailAddress.address, d)())
q = q.filter(Employee.status == EMPLOYEE_STATUS_CURRENT) g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.EmployeePhoneNumber.number, d)())
return q
g.default_sortkey = 'first_name'
g.append(forms.AssociationProxyField('first_name'))
g.append(forms.AssociationProxyField('last_name'))
def grid(self):
g = self.make_grid()
g.append(AssociationProxyField('first_name'))
g.append(AssociationProxyField('last_name'))
g.configure( g.configure(
include=[ include=[
g.id.label("ID"), g.id.label("ID"),
@ -113,38 +93,24 @@ class EmployeesGrid(SearchableAlchemyGridView):
g.last_name, g.last_name,
g.phone.label("Phone Number"), g.phone.label("Phone Number"),
g.email.label("Email Address"), g.email.label("Email Address"),
g.status.with_renderer(EnumFieldRenderer(EMPLOYEE_STATUS)), g.status.with_renderer(forms.EnumFieldRenderer(enum.EMPLOYEE_STATUS)),
], ],
readonly=True) readonly=True)
# Hide ID and Status fields for unprivileged users.
if not self.request.has_perm('employees.edit'): if not self.request.has_perm('employees.edit'):
del g.id del g.id
del g.status del g.status
if self.request.has_perm('employees.read'): def query(self, session):
g.viewable = True q = session.query(model.Employee).join(model.Person)
g.view_route_name = 'employee.read' if not self.request.has_perm('employees.edit'):
if self.request.has_perm('employees.update'): q = q.filter(model.Employee.status == enum.EMPLOYEE_STATUS_CURRENT)
g.editable = True return q
g.edit_route_name = 'employee.update'
if self.request.has_perm('employees.delete'):
g.deletable = True
g.delete_route_name = 'employee.delete'
return g def configure_fieldset(self, fs):
fs.append(forms.AssociationProxyField('first_name'))
fs.append(forms.AssociationProxyField('last_name'))
class EmployeeCrud(CrudView): fs.append(forms.AssociationProxyField('display_name'))
mapped_class = Employee
home_route = 'employees'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.append(AssociationProxyField('first_name'))
fs.append(AssociationProxyField('last_name'))
fs.append(AssociationProxyField('display_name'))
fs.configure( fs.configure(
include=[ include=[
fs.id.label("ID"), fs.id.label("ID"),
@ -153,9 +119,8 @@ class EmployeeCrud(CrudView):
fs.display_name, fs.display_name,
fs.phone.label("Phone Number").readonly(), fs.phone.label("Phone Number").readonly(),
fs.email.label("Email Address").readonly(), fs.email.label("Email Address").readonly(),
fs.status.with_renderer(EnumFieldRenderer(EMPLOYEE_STATUS)), fs.status.with_renderer(forms.EnumFieldRenderer(enum.EMPLOYEE_STATUS)),
]) ])
return fs
class EmployeesAutocomplete(AutocompleteView): class EmployeesAutocomplete(AutocompleteView):
@ -174,33 +139,11 @@ class EmployeesAutocomplete(AutocompleteView):
return person.employee.uuid return person.employee.uuid
def add_routes(config):
config.add_route('employees', '/employees')
config.add_route('employee.create', '/employees/new')
config.add_route('employees.autocomplete', '/employees/autocomplete')
config.add_route('employee.read', '/employees/{uuid}')
config.add_route('employee.update', '/employees/{uuid}/edit')
config.add_route('employee.delete', '/employees/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config)
config.add_view(EmployeesGrid, route_name='employees',
renderer='/employees/index.mako',
permission='employees.list')
config.add_view(EmployeeCrud, attr='create', route_name='employee.create',
renderer='/employees/crud.mako',
permission='employees.create')
config.add_view(EmployeeCrud, attr='read', route_name='employee.read',
renderer='/employees/crud.mako',
permission='employees.read')
config.add_view(EmployeeCrud, attr='update', route_name='employee.update',
renderer='/employees/crud.mako',
permission='employees.update')
config.add_view(EmployeeCrud, attr='delete', route_name='employee.delete',
permission='employees.delete')
# autocomplete # autocomplete
config.add_route('employees.autocomplete', '/employees/autocomplete')
config.add_view(EmployeesAutocomplete, route_name='employees.autocomplete', config.add_view(EmployeesAutocomplete, route_name='employees.autocomplete',
renderer='json', permission='employees.list') renderer='json', permission='employees.list')
EmployeesView.defaults(config)

View file

@ -1,9 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2015 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -21,62 +20,37 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # along with Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Family Views Family Views
""" """
from . import SearchableAlchemyGridView, CrudView from __future__ import unicode_literals, absolute_import
from tailbone.views import MasterView
from rattail.db import model from rattail.db import model
class FamiliesGrid(SearchableAlchemyGridView): class FamiliesView(MasterView):
"""
Master view for the Family class.
"""
model_class = model.Family
model_title_plural = "Families"
route_prefix = 'families'
mapped_class = model.Family def configure_grid(self, g):
config_prefix = 'families' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'code'
def filter_map(self):
return self.make_filter_map(
exact=['code'],
ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('code', 'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.code, g.code,
g.name, g.name,
], ],
readonly=True) readonly=True)
if self.request.has_perm('families.read'):
g.viewable = True
g.view_route_name = 'family.read'
if self.request.has_perm('families.update'):
g.editable = True
g.edit_route_name = 'family.update'
if self.request.has_perm('families.delete'):
g.deletable = True
g.delete_route_name = 'family.delete'
return g
def configure_fieldset(self, fs):
class FamilyCrud(CrudView):
mapped_class = model.Family
home_route = 'families'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.code, fs.code,
@ -85,29 +59,5 @@ class FamilyCrud(CrudView):
return fs return fs
def add_routes(config):
config.add_route('families', '/families')
config.add_route('family.create', '/families/new')
config.add_route('family.read', '/families/{uuid}')
config.add_route('family.update', '/families/{uuid}/edit')
config.add_route('family.delete', '/families/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config) FamiliesView.defaults(config)
config.add_view(FamiliesGrid, route_name='families',
renderer='/families/index.mako',
permission='families.list')
config.add_view(FamilyCrud, attr='create', route_name='family.create',
renderer='/families/crud.mako',
permission='families.create')
config.add_view(FamilyCrud, attr='read', route_name='family.read',
renderer='/families/crud.mako',
permission='families.read')
config.add_view(FamilyCrud, attr='update', route_name='family.update',
renderer='/families/crud.mako',
permission='families.update')
config.add_view(FamilyCrud, attr='delete', route_name='family.delete',
permission='families.delete')

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2014 Lance Edgar # Copyright © 2010-2015 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -20,62 +20,36 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # along with Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Report Code Views Report Code Views
""" """
from tailbone.views import SearchableAlchemyGridView, CrudView from __future__ import unicode_literals, absolute_import
from tailbone.views import MasterView
from rattail.db import model from rattail.db import model
class ReportCodesGrid(SearchableAlchemyGridView): class ReportCodesView(MasterView):
"""
Master view for the ReportCode class.
"""
model_class = model.ReportCode
model_title = "Report Code"
mapped_class = model.ReportCode def configure_grid(self, g):
config_prefix = u'reportcodes' g.filters['name'].default_active = True
sort = u'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'code'
def filter_map(self):
return self.make_filter_map(
exact=[u'code'],
ilike=[u'name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name=u'lk')
def sort_map(self):
return self.make_sort_map(u'code', u'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.code, g.code,
g.name, g.name,
], ],
readonly=True) readonly=True)
if self.request.has_perm(u'reportcodes.read'):
g.viewable = True
g.view_route_name = u'reportcode.read'
if self.request.has_perm(u'reportcodes.update'):
g.editable = True
g.edit_route_name = u'reportcode.update'
if self.request.has_perm(u'reportcodes.delete'):
g.deletable = True
g.delete_route_name = u'reportcode.delete'
return g
def configure_fieldset(self, fs):
class ReportCodeCrud(CrudView):
mapped_class = model.ReportCode
home_route = u'reportcodes'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.code, fs.code,
@ -84,29 +58,5 @@ class ReportCodeCrud(CrudView):
return fs return fs
def add_routes(config):
config.add_route(u'reportcodes', u'/reportcodes')
config.add_route(u'reportcode.create', u'/reportcodes/new')
config.add_route(u'reportcode.read', u'/reportcodes/{uuid}')
config.add_route(u'reportcode.update', u'/reportcodes/{uuid}/edit')
config.add_route(u'reportcode.delete', u'/reportcodes/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config) ReportCodesView.defaults(config)
config.add_view(ReportCodesGrid, route_name=u'reportcodes',
renderer=u'/reportcodes/index.mako',
permission=u'reportcodes.list')
config.add_view(ReportCodeCrud, attr=u'create', route_name=u'reportcode.create',
renderer=u'/reportcodes/crud.mako',
permission=u'reportcodes.create')
config.add_view(ReportCodeCrud, attr=u'read', route_name=u'reportcode.read',
renderer=u'/reportcodes/crud.mako',
permission=u'reportcodes.read')
config.add_view(ReportCodeCrud, attr=u'update', route_name=u'reportcode.update',
renderer=u'/reportcodes/crud.mako',
permission=u'reportcodes.update')
config.add_view(ReportCodeCrud, attr=u'delete', route_name=u'reportcode.delete',
permission=u'reportcodes.delete')

View file

@ -1,9 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2012 Lance Edgar # Copyright © 2010-2015 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -21,56 +20,48 @@
# along with Rattail. If not, see <http://www.gnu.org/licenses/>. # along with Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
""" """
Store Views Store Views
""" """
from sqlalchemy import and_ from __future__ import unicode_literals, absolute_import
from . import SearchableAlchemyGridView, CrudView import sqlalchemy as sa
from rattail.db.model import Store, StoreEmailAddress, StorePhoneNumber
from rattail.db import model
from tailbone.views import MasterView
class StoresGrid(SearchableAlchemyGridView): class StoresView(MasterView):
"""
Master view for the Store class.
"""
model_class = model.Store
mapped_class = Store def configure_grid(self, g):
config_prefix = 'stores'
sort = 'id'
def join_map(self): g.joiners['email'] = lambda q: q.outerjoin(model.StoreEmailAddress, sa.and_(
return { model.StoreEmailAddress.parent_uuid == model.Store.uuid,
'email': model.StoreEmailAddress.preference == 1))
lambda q: q.outerjoin(StoreEmailAddress, and_( g.joiners['phone'] = lambda q: q.outerjoin(model.StorePhoneNumber, sa.and_(
StoreEmailAddress.parent_uuid == Store.uuid, model.StorePhoneNumber.parent_uuid == model.Store.uuid,
StoreEmailAddress.preference == 1)), model.StorePhoneNumber.preference == 1))
'phone':
lambda q: q.outerjoin(StorePhoneNumber, and_(
StorePhoneNumber.parent_uuid == Store.uuid,
StorePhoneNumber.preference == 1)),
}
def filter_map(self): g.filters['email'] = g.make_filter('email', model.StoreEmailAddress.address,
return self.make_filter_map( label="Email Address")
exact=['id'], g.filters['phone'] = g.make_filter('phone', model.StorePhoneNumber.number,
ilike=['name'], label="Phone Number")
email=self.filter_ilike(StoreEmailAddress.address),
phone=self.filter_ilike(StorePhoneNumber.number))
def filter_config(self): g.filters['name'].default_active = True
return self.make_filter_config( g.filters['name'].default_verb = 'contains'
include_filter_name=True, g.filters['id'].label = "ID"
filter_type_name='lk',
filter_label_id="ID")
def sort_map(self): g.sorters['email'] = lambda q, d: q.order_by(getattr(model.StoreEmailAddress.address, d)())
return self.make_sort_map( g.sorters['phone'] = lambda q, d: q.order_by(getattr(model.StorePhoneNumber.number, d)())
'id', 'name',
email=self.sorter(StoreEmailAddress.address), g.default_sortkey = 'id'
phone=self.sorter(StorePhoneNumber.number))
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.id.label("ID"), g.id.label("ID"),
@ -79,24 +70,8 @@ class StoresGrid(SearchableAlchemyGridView):
g.email.label("Email Address"), g.email.label("Email Address"),
], ],
readonly=True) readonly=True)
g.viewable = True
g.view_route_name = 'store.read'
if self.request.has_perm('stores.update'):
g.editable = True
g.edit_route_name = 'store.update'
if self.request.has_perm('stores.delete'):
g.deletable = True
g.delete_route_name = 'store.delete'
return g
def configure_fieldset(self, fs):
class StoreCrud(CrudView):
mapped_class = Store
home_route = 'stores'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.id.label("ID"), fs.id.label("ID"),
@ -105,31 +80,7 @@ class StoreCrud(CrudView):
fs.phone.label("Phone Number").readonly(), fs.phone.label("Phone Number").readonly(),
fs.email.label("Email Address").readonly(), fs.email.label("Email Address").readonly(),
]) ])
return fs
def includeme(config): def includeme(config):
StoresView.defaults(config)
config.add_route('stores', '/stores')
config.add_view(StoresGrid, route_name='stores',
renderer='/stores/index.mako',
permission='stores.list')
config.add_route('store.create', '/stores/new')
config.add_view(StoreCrud, attr='create', route_name='store.create',
renderer='/stores/crud.mako',
permission='stores.create')
config.add_route('store.read', '/stores/{uuid}')
config.add_view(StoreCrud, attr='read', route_name='store.read',
renderer='/stores/crud.mako',
permission='stores.read')
config.add_route('store.update', '/stores/{uuid}/edit')
config.add_view(StoreCrud, attr='update', route_name='store.update',
renderer='/stores/crud.mako',
permission='stores.update')
config.add_route('store.delete', '/stores/{uuid}/delete')
config.add_view(StoreCrud, attr='delete', route_name='store.delete',
permission='stores.delete')

View file

@ -27,31 +27,21 @@ Subdepartment Views
from __future__ import unicode_literals from __future__ import unicode_literals
from rattail.db import model from rattail.db import model
from rattail.db.model import Subdepartment
from . import SearchableAlchemyGridView, CrudView from tailbone.views import MasterView
from .continuum import VersionView, version_defaults from tailbone.views.continuum import VersionView, version_defaults
class SubdepartmentsGrid(SearchableAlchemyGridView): class SubdepartmentsView(MasterView):
"""
Master view for the Subdepartment class.
"""
model_class = model.Subdepartment
mapped_class = Subdepartment def configure_grid(self, g):
config_prefix = 'subdepartments' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'name'
def filter_map(self):
return self.make_filter_map(ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk')
def sort_map(self):
return self.make_sort_map('number', 'name')
def grid(self):
g = self.make_grid()
g.configure( g.configure(
include=[ include=[
g.number, g.number,
@ -59,25 +49,8 @@ class SubdepartmentsGrid(SearchableAlchemyGridView):
g.department, g.department,
], ],
readonly=True) readonly=True)
if self.request.has_perm('subdepartments.read'):
g.viewable = True
g.view_route_name = 'subdepartment.read'
if self.request.has_perm('subdepartments.update'):
g.editable = True
g.edit_route_name = 'subdepartment.update'
if self.request.has_perm('subdepartments.delete'):
g.deletable = True
g.delete_route_name = 'subdepartment.delete'
return g
def configure_fieldset(self, fs):
class SubdepartmentCrud(CrudView):
mapped_class = Subdepartment
home_route = 'subdepartments'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.configure( fs.configure(
include=[ include=[
fs.number, fs.number,
@ -92,32 +65,9 @@ class SubdepartmentVersionView(VersionView):
View which shows version history for a subdepartment. View which shows version history for a subdepartment.
""" """
parent_class = model.Subdepartment parent_class = model.Subdepartment
route_model_view = 'subdepartment.read' route_model_view = 'subdepartments.view'
def add_routes(config):
config.add_route('subdepartments', '/subdepartments')
config.add_route('subdepartment.create', '/subdepartments/new')
config.add_route('subdepartment.read', '/subdepartments/{uuid}')
config.add_route('subdepartment.update', '/subdepartments/{uuid}/edit')
config.add_route('subdepartment.delete', '/subdepartments/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config) SubdepartmentsView.defaults(config)
# list
config.add_view(SubdepartmentsGrid, route_name='subdepartments',
renderer='/subdepartments/index.mako', permission='subdepartments.list')
# crud
config.add_view(SubdepartmentCrud, attr='create', route_name='subdepartment.create',
renderer='/subdepartments/crud.mako', permission='subdepartments.create')
config.add_view(SubdepartmentCrud, attr='read', route_name='subdepartment.read',
renderer='/subdepartments/crud.mako', permission='subdepartments.read')
config.add_view(SubdepartmentCrud, attr='update', route_name='subdepartment.update',
renderer='/subdepartments/crud.mako', permission='subdepartments.update')
config.add_view(SubdepartmentCrud, attr='delete', route_name='subdepartment.delete',
permission='subdepartments.delete')
version_defaults(config, SubdepartmentVersionView, 'subdepartment') version_defaults(config, SubdepartmentVersionView, 'subdepartment')

View file

@ -24,10 +24,9 @@
Views pertaining to vendors Views pertaining to vendors
""" """
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from .core import (VendorsGrid, VendorCrud, VendorVersionView, from .core import VendorsView, VendorVersionView, VendorsAutocomplete
VendorsAutocomplete, add_routes)
def includeme(config): def includeme(config):

View file

@ -24,38 +24,29 @@
Vendor Views Vendor Views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals, absolute_import
from rattail.db import model from rattail.db import model
from rattail.db.model import Vendor
from tailbone import forms
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import SearchableAlchemyGridView, CrudView, AutocompleteView from tailbone.views import MasterView, AutocompleteView
from tailbone.views.continuum import VersionView, version_defaults from tailbone.views.continuum import VersionView, version_defaults
from tailbone.forms import AssociationProxyField, PersonFieldRenderer
class VendorsGrid(SearchableAlchemyGridView): class VendorsView(MasterView):
"""
Master view for the Vendor class.
"""
model_class = model.Vendor
mapped_class = Vendor def configure_grid(self, g):
config_prefix = 'vendors' g.filters['name'].default_active = True
sort = 'name' g.filters['name'].default_verb = 'contains'
g.filters['id'].label = "ID"
g.default_sortkey = 'name'
def filter_map(self): g.append(forms.AssociationProxyField('contact'))
return self.make_filter_map(exact=['id'], ilike=['name'])
def filter_config(self):
return self.make_filter_config(
include_filter_name=True,
filter_type_name='lk',
filter_label_id="ID")
def sort_map(self):
return self.make_sort_map('id', 'name')
def grid(self):
g = self.make_grid()
g.append(AssociationProxyField('contact'))
g.configure( g.configure(
include=[ include=[
g.id.label("ID"), g.id.label("ID"),
@ -65,26 +56,9 @@ class VendorsGrid(SearchableAlchemyGridView):
g.contact, g.contact,
], ],
readonly=True) readonly=True)
if self.request.has_perm('vendors.read'):
g.viewable = True
g.view_route_name = 'vendor.read'
if self.request.has_perm('vendors.update'):
g.editable = True
g.edit_route_name = 'vendor.update'
if self.request.has_perm('vendors.delete'):
g.deletable = True
g.delete_route_name = 'vendor.delete'
return g
def configure_fieldset(self, fs):
class VendorCrud(CrudView): fs.append(forms.AssociationProxyField('contact'))
mapped_class = Vendor
home_route = 'vendors'
def fieldset(self, model):
fs = self.make_fieldset(model)
fs.append(AssociationProxyField('contact'))
fs.configure( fs.configure(
include=[ include=[
fs.id.label("ID"), fs.id.label("ID"),
@ -94,12 +68,10 @@ class VendorCrud(CrudView):
fs.order_interval_days.label("Order Interval in Days"), fs.order_interval_days.label("Order Interval in Days"),
fs.phone.label("Phone Number").readonly(), fs.phone.label("Phone Number").readonly(),
fs.email.label("Email Address").readonly(), fs.email.label("Email Address").readonly(),
fs.contact.with_renderer(PersonFieldRenderer).readonly(), fs.contact.with_renderer(forms.PersonFieldRenderer).readonly(),
]) ])
return fs
def pre_delete(self, vendor):
def before_delete(self, vendor):
# Remove all product costs. # Remove all product costs.
q = Session.query(model.ProductCost).filter( q = Session.query(model.ProductCost).filter(
model.ProductCost.vendor == vendor) model.ProductCost.vendor == vendor)
@ -112,42 +84,21 @@ class VendorVersionView(VersionView):
View which shows version history for a vendor. View which shows version history for a vendor.
""" """
parent_class = model.Vendor parent_class = model.Vendor
route_model_view = 'vendor.read' route_model_view = 'vendors.view'
class VendorsAutocomplete(AutocompleteView): class VendorsAutocomplete(AutocompleteView):
mapped_class = Vendor mapped_class = model.Vendor
fieldname = 'name' fieldname = 'name'
def add_routes(config):
config.add_route('vendors', '/vendors')
config.add_route('vendors.autocomplete', '/vendors/autocomplete')
config.add_route('vendor.create', '/vendors/new')
config.add_route('vendor.read', '/vendors/{uuid}')
config.add_route('vendor.update', '/vendors/{uuid}/edit')
config.add_route('vendor.delete', '/vendors/{uuid}/delete')
def includeme(config): def includeme(config):
add_routes(config)
config.add_view(VendorsGrid, route_name='vendors', # autocomplete
renderer='/vendors/index.mako', config.add_route('vendors.autocomplete', '/vendors/autocomplete')
permission='vendors.list')
config.add_view(VendorsAutocomplete, route_name='vendors.autocomplete', config.add_view(VendorsAutocomplete, route_name='vendors.autocomplete',
renderer='json', permission='vendors.list') renderer='json', permission='vendors.list')
config.add_view(VendorCrud, attr='create', route_name='vendor.create',
renderer='/vendors/crud.mako',
permission='vendors.create')
config.add_view(VendorCrud, attr='read', route_name='vendor.read',
renderer='/vendors/crud.mako',
permission='vendors.read')
config.add_view(VendorCrud, attr='update', route_name='vendor.update',
renderer='/vendors/crud.mako',
permission='vendors.update')
config.add_view(VendorCrud, attr='delete', route_name='vendor.delete',
permission='vendors.delete')
VendorsView.defaults(config)
version_defaults(config, VendorVersionView, 'vendor') version_defaults(config, VendorVersionView, 'vendor')