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:
parent
931700131f
commit
c422b900c6
22 changed files with 471 additions and 105 deletions
|
@ -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')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue