Extensive commit; see notes.

* Replaced `forms` module with subpackage; added some initial goodies (many of
  which are currently just imports from `edbob`).

* Added/edited various CRUD templates for consistency.

* Renamed `customer_groups` module and template folder to `customergroups`.

* Modified several view modules so their Pyramid configuration is more
  "extensible."  This just means routes and views are defined as two separate
  steps, so that derived applications may inherit the route definitions if they
  so choose.

* Added Employee CRUD views; added Email Address field to index view.

* Updated `people` view module so it no longer derives from that of `edbob`.

* Added support for, and some implementations of, extra key lookup abilities to
  CRUD views.  This allows URLs to use a "natural" key (e.g. Customer ID
  instead of UUID), for cases where that is more helpful.

* Product CRUD now uses autocomplete for Brand field.  Also, price fields no
  longer appear within an editable fieldset.

* Within Store index view, default sort is now ID instead of Name.

* Added Contact and Phone Number fields to Vendor CRUD views; added Contact and
  Email Address fields to index view.
This commit is contained in:
Lance Edgar 2013-05-21 21:51:41 -07:00
parent 931700131f
commit c422b900c6
22 changed files with 471 additions and 105 deletions

View file

@ -28,33 +28,40 @@
from sqlalchemy import and_
import edbob
from edbob.pyramid.forms import AssociationProxyField, EnumFieldRenderer
from edbob.pyramid.views import SearchableAlchemyGridView
import rattail
from rattail.pyramid.views import CrudView
from rattail.pyramid.grids import EnumSearchFilter
from rattail.pyramid.forms import AssociationProxyField, EnumFieldRenderer
from rattail.db.model import (
Employee, EmployeePhoneNumber, EmployeeEmailAddress, Person)
from rattail.enum import EMPLOYEE_STATUS, EMPLOYEE_STATUS_CURRENT
class EmployeesGrid(SearchableAlchemyGridView):
mapped_class = rattail.Employee
mapped_class = Employee
config_prefix = 'employees'
sort = 'first_name'
def join_map(self):
return {
'phone':
lambda q: q.outerjoin(rattail.EmployeePhoneNumber, and_(
rattail.EmployeePhoneNumber.parent_uuid == rattail.Employee.uuid,
rattail.EmployeePhoneNumber.preference == 1)),
lambda q: q.outerjoin(EmployeePhoneNumber, and_(
EmployeePhoneNumber.parent_uuid == Employee.uuid,
EmployeePhoneNumber.preference == 1)),
'email':
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(edbob.Person.first_name),
last_name=self.filter_ilike(edbob.Person.last_name),
phone=self.filter_ilike(rattail.EmployeePhoneNumber.number))
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'):
kwargs.update(dict(
exact=['id', 'status']))
@ -66,27 +73,29 @@ class EmployeesGrid(SearchableAlchemyGridView):
filter_type_first_name='lk',
include_filter_last_name=True,
filter_type_last_name='lk',
filter_label_phone="Phone Number")
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(rattail.EMPLOYEE_STATUS),
status=rattail.EMPLOYEE_STATUS_CURRENT))
filter_factory_status=EnumSearchFilter(EMPLOYEE_STATUS),
status=EMPLOYEE_STATUS_CURRENT))
return self.make_filter_config(**kwargs)
def sort_map(self):
return self.make_sort_map(
first_name=self.sorter(edbob.Person.first_name),
last_name=self.sorter(edbob.Person.last_name),
phone=self.sorter(rattail.EmployeePhoneNumber.number))
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):
q = self.make_query()
q = q.join(edbob.Person)
q = q.join(Person)
if not self.request.has_perm('employees.edit'):
q = q.filter(rattail.Employee.status == rattail.EMPLOYEE_STATUS_CURRENT)
q = q.filter(Employee.status == EMPLOYEE_STATUS_CURRENT)
return q
def grid(self):
@ -99,18 +108,73 @@ class EmployeesGrid(SearchableAlchemyGridView):
g.first_name,
g.last_name,
g.phone.label("Phone Number"),
g.status.with_renderer(EnumFieldRenderer(rattail.EMPLOYEE_STATUS)),
g.email.label("Email Address"),
g.status.with_renderer(EnumFieldRenderer(EMPLOYEE_STATUS)),
],
readonly=True)
# Hide ID and Status fields for unprivileged users.
if not self.request.has_perm('employees.edit'):
del g.id
del g.status
if self.request.has_perm('employees.read'):
g.clickable = True
g.click_route_name = 'employee.read'
if self.request.has_perm('employees.update'):
g.editable = True
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 includeme(config):
class EmployeeCrud(CrudView):
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(
include=[
fs.id.label("ID"),
fs.first_name,
fs.last_name,
fs.phone.label("Phone Number").readonly(),
fs.email.label("Email Address").readonly(),
fs.status.with_renderer(EnumFieldRenderer(EMPLOYEE_STATUS)),
])
return fs
def add_routes(config):
config.add_route('employees', '/employees')
config.add_route('employee.create', '/employees/new')
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):
add_routes(config)
config.add_route('employees', '/employees')
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')