Refactor time sheet, schedule filter forms to use colander/deform

also add "print employee schedule" feature, didn't realize that was missing
This commit is contained in:
Lance Edgar 2018-02-11 15:58:06 -06:00
parent d30e5e2b02
commit 4191e50456
10 changed files with 225 additions and 100 deletions

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -36,28 +36,29 @@ from rattail.db import model, api
from rattail.time import localtime, make_utc, get_sunday
from rattail.util import pretty_hours, hours_as_decimal
import formencode as fe
from pyramid_simpleform import Form
import colander
from deform import widget as dfwidget
from webhelpers2.html import tags, HTML
from tailbone import forms
from tailbone import forms2 as forms
from tailbone.db import Session
from tailbone.views import View
class ShiftFilter(fe.Schema):
allow_extra_fields = True
filter_extra_fields = True
store = forms.validators.ValidStore()
department = forms.validators.ValidDepartment()
date = fe.validators.DateConverter()
class ShiftFilter(colander.Schema):
store = colander.SchemaNode(forms.types.StoreType())
department = colander.SchemaNode(forms.types.DepartmentType())
date = colander.SchemaNode(colander.Date())
class EmployeeShiftFilter(fe.Schema):
allow_extra_fields = True
filter_extra_fields = True
employee = forms.validators.ValidEmployee()
date = fe.validators.DateConverter()
class EmployeeShiftFilter(colander.Schema):
employee = colander.SchemaNode(forms.types.EmployeeType())
date = colander.SchemaNode(colander.Date())
class TimeSheetView(View):
@ -166,47 +167,88 @@ class TimeSheetView(View):
Process a "shift filter" form if one was in fact POST'ed. If it was
then we store new context in session and redirect to display as normal.
"""
if self.request.method == 'POST':
if form.validate():
store = form.data['store']
self.request.session['timesheet.{}.store'.format(self.key)] = store.uuid if store else None
department = form.data['department']
self.request.session['timesheet.{}.department'.format(self.key)] = department.uuid if department else None
date = form.data['date']
self.request.session['timesheet.{}.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
raise self.redirect(self.request.current_route_url())
if form.validate(newstyle=True):
store = form.validated['store']
self.request.session['timesheet.{}.store'.format(self.key)] = store.uuid if store else None
department = form.validated['department']
self.request.session['timesheet.{}.department'.format(self.key)] = department.uuid if department else None
date = form.validated['date']
self.request.session['timesheet.{}.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
raise self.redirect(self.request.current_route_url())
def process_employee_filter_form(self, form):
"""
Process an "employee shift filter" form if one was in fact POST'ed. If it
was then we store new context in session and redirect to display as normal.
"""
if self.request.method == 'POST':
if form.validate():
employee = form.data['employee']
self.request.session['timesheet.{}.employee'.format(self.key)] = employee.uuid if employee else None
date = form.data['date']
self.request.session['timesheet.{}.employee.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
raise self.redirect(self.request.current_route_url())
if form.validate(newstyle=True):
employee = form.validated['employee']
self.request.session['timesheet.{}.employee'.format(self.key)] = employee.uuid if employee else None
date = form.validated['date']
self.request.session['timesheet.{}.employee.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
raise self.redirect(self.request.current_route_url())
def make_full_filter_form(self, context):
form = forms.Form(schema=ShiftFilter(), request=self.request)
stores = self.get_stores()
store_values = [(s.uuid, "{} - {}".format(s.id, s.name)) for s in stores]
store_values.insert(0, ('', "(all)"))
form.set_widget('store', forms.widgets.PlainSelectWidget(values=store_values))
if context['store']:
form.set_default('store', context['store'].uuid)
departments = self.get_departments()
department_values = [(d.uuid, d.name) for d in departments]
department_values.insert(0, ('', "(all)"))
form.set_widget('department', forms.widgets.PlainSelectWidget(values=department_values))
if context['department']:
form.set_default('department', context['department'].uuid)
form.set_type('date', 'date_jquery')
form.set_default('date', get_sunday(context['date']))
return form
def full(self):
"""
View a "full" timesheet/schedule, i.e. all employees but filterable by
store and/or department.
"""
form = Form(self.request, schema=ShiftFilter)
self.process_filter_form(form)
context = self.get_timesheet_context()
form = self.make_full_filter_form(context)
self.process_filter_form(form)
context['form'] = form
return self.render_full(**context)
def make_employee_filter_form(self, context):
"""
View time sheet for single employee.
"""
permission_prefix = self.key
form = forms.Form(schema=EmployeeShiftFilter(), request=self.request)
if self.request.has_perm('{}.viewall'.format(permission_prefix)):
employee_display = six.text_type(context['employee'] or '')
employees_url = self.request.route_url('employees.autocomplete')
form.set_widget('employee', forms.widgets.JQueryAutocompleteWidget(
field_display=employee_display, service_url=employees_url))
if context['employee']:
form.set_default('employee', context['employee'].uuid)
else:
form.set_widget('employee', forms.widgets.ReadonlyWidget())
form.set_default('employee', context['employee'].uuid)
form.set_type('date', 'date_jquery')
form.set_default('date', get_sunday(context['date']))
return form
def employee(self):
"""
View time sheet for single employee.
"""
form = Form(self.request, schema=EmployeeShiftFilter)
self.process_employee_filter_form(form)
context = self.get_employee_context()
form = self.make_employee_filter_form(context)
self.process_employee_filter_form(form)
context['form'] = form
return self.render_single(**context)
@ -280,7 +322,8 @@ class TimeSheetView(View):
context = {
'page_title': self.get_title_full(),
'form': forms.FormRenderer(form) if form else None,
'form': form,
'dform': form.make_deform_form() if form else None,
'employees': employees,
'stores': stores,
'store_options': store_options,
@ -326,7 +369,8 @@ class TimeSheetView(View):
context = {
'single': True,
'page_title': "Employee {}".format(self.get_title()),
'form': forms.FormRenderer(form) if form else None,
'form': form,
'dform': form.make_deform_form() if form else None,
'employee': employee,
'employees': [employee],
'week_of': week_of,

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -31,10 +31,8 @@ import datetime
from rattail.db import model
from rattail.time import localtime, make_utc, get_sunday
from pyramid_simpleform import Form
from tailbone.db import Session
from tailbone.views.shifts.lib import TimeSheetView, ShiftFilter
from tailbone.views.shifts.lib import TimeSheetView
class ScheduleView(TimeSheetView):
@ -61,10 +59,9 @@ class ScheduleView(TimeSheetView):
return self.redirect(self.request.route_url('schedule.edit'))
# okay then, process filters; redirect if any were received
form = Form(self.request, schema=ShiftFilter)
self.process_filter_form(form)
context = self.get_timesheet_context()
form = self.make_full_filter_form(context)
self.process_filter_form(form)
# okay then, maybe process saved shift data
if self.request.method == 'POST':
@ -199,12 +196,20 @@ class ScheduleView(TimeSheetView):
permission='schedule.edit')
config.add_tailbone_permission('schedule', 'schedule.edit', "Edit full schedule")
# print schedule
# printing "any" schedule requires this permission
config.add_tailbone_permission('schedule', 'schedule.print', "Print schedule")
# print full schedule
config.add_route('schedule.print', '/schedule/print')
config.add_view(cls, attr='full', route_name='schedule.print',
renderer='/shifts/schedule_print.mako',
permission='schedule.print')
config.add_tailbone_permission('schedule', 'schedule.print', "Print schedule")
# print employee schedule
config.add_route('schedule.employee.print', '/schedule/employee/print')
config.add_view(cls, attr='employee', route_name='schedule.employee.print',
renderer='/shifts/schedule_print_employee.mako',
permission='schedule.print')
def includeme(config):

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -31,10 +31,8 @@ import datetime
from rattail.db import model
from rattail.time import make_utc, localtime
from pyramid_simpleform import Form
from tailbone.db import Session
from tailbone.views.shifts.lib import TimeSheetView as BaseTimeSheetView, EmployeeShiftFilter
from tailbone.views.shifts.lib import TimeSheetView as BaseTimeSheetView
class TimeSheetView(BaseTimeSheetView):
@ -50,10 +48,9 @@ class TimeSheetView(BaseTimeSheetView):
View for editing single employee's timesheet
"""
# process filters; redirect if any were received
form = Form(self.request, schema=EmployeeShiftFilter)
self.process_employee_filter_form(form)
context = self.get_employee_context()
form = self.make_employee_filter_form(context)
self.process_employee_filter_form(form)
# okay then, maybe process saved shift data
if self.request.method == 'POST':