Add basic ability to edit employee schedule
This commit is contained in:
parent
788f3ad386
commit
048951153d
6 changed files with 419 additions and 57 deletions
|
@ -59,6 +59,11 @@ class ScheduledShiftsView(MasterView):
|
|||
url_prefix = '/shifts/scheduled'
|
||||
|
||||
def configure_grid(self, g):
|
||||
g.joiners['employee'] = lambda q: q.join(model.Employee).join(model.Person)
|
||||
g.filters['employee'] = g.make_filter('employee', model.Person.display_name,
|
||||
default_active=True, default_verb='contains',
|
||||
label="Employee Name")
|
||||
|
||||
g.default_sortkey = 'start_time'
|
||||
g.default_sortdir = 'desc'
|
||||
g.append(ShiftLengthField('length'))
|
||||
|
|
|
@ -34,6 +34,7 @@ from rattail.time import localtime, make_utc, get_sunday
|
|||
|
||||
import formencode as fe
|
||||
from pyramid_simpleform import Form
|
||||
from webhelpers.html import HTML
|
||||
|
||||
from tailbone import forms
|
||||
from tailbone.db import Session
|
||||
|
@ -71,14 +72,60 @@ class TimeSheetView(View):
|
|||
def get_title(cls):
|
||||
return cls.title or cls.key.capitalize()
|
||||
|
||||
def full(self):
|
||||
def get_timesheet_context(self):
|
||||
"""
|
||||
Determine date/store/dept context from user's session and/or defaults.
|
||||
"""
|
||||
date = None
|
||||
date_key = 'timesheet.{}.date'.format(self.key)
|
||||
if date_key in self.request.session:
|
||||
date_value = self.request.session.get(date_key)
|
||||
if date_value:
|
||||
try:
|
||||
date = datetime.datetime.strptime(date_value, '%m/%d/%Y').date()
|
||||
except ValueError:
|
||||
pass
|
||||
if not date:
|
||||
date = localtime(self.rattail_config).date()
|
||||
|
||||
store = None
|
||||
department = None
|
||||
store_key = 'timesheet.{}.store'.format(self.key)
|
||||
department_key = 'timesheet.{}.department'.format(self.key)
|
||||
if store_key in self.request.session or department_key in self.request.session:
|
||||
store_uuid = self.request.session.get(store_key)
|
||||
if store_uuid:
|
||||
store = Session.query(model.Store).get(store_uuid) if store_uuid else None
|
||||
department_uuid = self.request.session.get(department_key)
|
||||
if department_uuid:
|
||||
department = Session.query(model.Department).get(department_uuid)
|
||||
else: # no store/department in session
|
||||
if self.default_filter_store:
|
||||
store = self.rattail_config.get('rattail', 'store')
|
||||
if store:
|
||||
store = api.get_store(Session(), store)
|
||||
|
||||
employees = Session.query(model.Employee)\
|
||||
.filter(model.Employee.status == enum.EMPLOYEE_STATUS_CURRENT)
|
||||
if store:
|
||||
employees = employees.join(model.EmployeeStore)\
|
||||
.filter(model.EmployeeStore.store == store)
|
||||
if department:
|
||||
employees = employees.join(model.EmployeeDepartment)\
|
||||
.filter(model.EmployeeDepartment.department == department)
|
||||
|
||||
form = Form(self.request, schema=ShiftFilter)
|
||||
return {
|
||||
'date': date,
|
||||
'store': store,
|
||||
'department': department,
|
||||
'employees': employees.all(),
|
||||
}
|
||||
|
||||
def process_filter_form(self, form):
|
||||
"""
|
||||
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']
|
||||
|
@ -87,45 +134,18 @@ class TimeSheetView(View):
|
|||
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
|
||||
return self.redirect(self.request.current_route_url())
|
||||
raise self.redirect(self.request.current_route_url())
|
||||
|
||||
else:
|
||||
store_key = 'timesheet.{}.store'.format(self.key)
|
||||
department_key = 'timesheet.{}.department'.format(self.key)
|
||||
if store_key in self.request.session or department_key in self.request.session:
|
||||
store_uuid = self.request.session.get(store_key)
|
||||
if store_uuid:
|
||||
store = Session.query(model.Store).get(store_uuid) if store_uuid else None
|
||||
department_uuid = self.request.session.get(department_key)
|
||||
if department_uuid:
|
||||
department = Session.query(model.Department).get(department_uuid)
|
||||
else: # no store/department in session
|
||||
if self.default_filter_store:
|
||||
store = self.rattail_config.get('rattail', 'store')
|
||||
if store:
|
||||
store = api.get_store(Session(), store)
|
||||
|
||||
date_key = 'timesheet.{}.date'.format(self.key)
|
||||
if date_key in self.request.session:
|
||||
date_value = self.request.session.get(date_key)
|
||||
if date_value:
|
||||
try:
|
||||
date = datetime.datetime.strptime(date_value, '%m/%d/%Y').date()
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if store:
|
||||
employees = employees.join(model.EmployeeStore)\
|
||||
.filter(model.EmployeeStore.store == store)
|
||||
|
||||
if department:
|
||||
employees = employees.join(model.EmployeeDepartment)\
|
||||
.filter(model.EmployeeDepartment.department == department)
|
||||
|
||||
if not date:
|
||||
date = localtime(self.rattail_config).date()
|
||||
|
||||
return self.render_full(date, employees.all(), store=store, department=department, form=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()
|
||||
context['form'] = form
|
||||
return self.render_full(**context)
|
||||
|
||||
def employee(self):
|
||||
"""
|
||||
|
@ -233,7 +253,7 @@ class TimeSheetView(View):
|
|||
options.insert(0, ('', "(all)"))
|
||||
return options
|
||||
|
||||
def render_full(self, date, employees, store=None, department=None, form=None):
|
||||
def render_full(self, date=None, employees=None, store=None, department=None, form=None, **kwargs):
|
||||
"""
|
||||
Render a time sheet for one or more employees, for the week which
|
||||
includes the specified date.
|
||||
|
@ -257,7 +277,7 @@ class TimeSheetView(View):
|
|||
departments = self.get_departments()
|
||||
department_options = self.get_department_options(departments)
|
||||
|
||||
return {
|
||||
context = {
|
||||
'page_title': "Full {}".format(self.get_title()),
|
||||
'form': forms.FormRenderer(form) if form else None,
|
||||
'employees': employees,
|
||||
|
@ -275,9 +295,11 @@ class TimeSheetView(View):
|
|||
'permission_prefix': self.key,
|
||||
'render_shift': self.render_shift,
|
||||
}
|
||||
context.update(kwargs)
|
||||
return context
|
||||
|
||||
def render_shift(self, shift):
|
||||
return shift.get_display(self.rattail_config)
|
||||
return HTML.tag('span', c=shift.get_display(self.rattail_config))
|
||||
|
||||
def render_single(self, date, employee, form=None):
|
||||
"""
|
||||
|
@ -371,7 +393,7 @@ class TimeSheetView(View):
|
|||
"""
|
||||
title = cls.get_title()
|
||||
config.add_tailbone_permission_group(cls.key, title)
|
||||
config.add_tailbone_permission(cls.key, '{}.view'.format(cls.key), "View employee {}".format(title))
|
||||
config.add_tailbone_permission(cls.key, '{}.view'.format(cls.key), "View single employee {}".format(title))
|
||||
config.add_tailbone_permission(cls.key, '{}.viewall'.format(cls.key), "View full {}".format(title))
|
||||
|
||||
# full time sheet
|
||||
|
|
|
@ -26,9 +26,15 @@ Views for employee schedules
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail.db import model
|
||||
import datetime
|
||||
|
||||
from tailbone.views.shifts.lib import TimeSheetView
|
||||
from rattail.db import model
|
||||
from rattail.time import localtime, make_utc
|
||||
|
||||
from pyramid_simpleform import Form
|
||||
|
||||
from tailbone.db import Session
|
||||
from tailbone.views.shifts.lib import TimeSheetView, ShiftFilter
|
||||
|
||||
|
||||
class ScheduleView(TimeSheetView):
|
||||
|
@ -38,6 +44,76 @@ class ScheduleView(TimeSheetView):
|
|||
key = 'schedule'
|
||||
model_class = model.ScheduledShift
|
||||
|
||||
def edit(self):
|
||||
"""
|
||||
View for editing (full) schedule.
|
||||
"""
|
||||
if self.request.method == 'POST':
|
||||
|
||||
# organize form data by uuid / field
|
||||
fields = ['employee_uuid', 'store_uuid', 'start_time', 'end_time', 'delete']
|
||||
data = dict([(f, {}) for f in fields])
|
||||
for key in self.request.POST:
|
||||
for field in fields:
|
||||
if key.startswith('{}-'.format(field)):
|
||||
uuid = key[len('{}-'.format(field)):]
|
||||
if uuid:
|
||||
data[field][uuid] = self.request.POST[key]
|
||||
|
||||
# apply delete operations
|
||||
deleted = []
|
||||
for uuid, value in data['delete'].iteritems():
|
||||
assert value == 'delete'
|
||||
shift = Session.query(model.ScheduledShift).get(uuid)
|
||||
assert shift
|
||||
Session.delete(shift)
|
||||
deleted.append(uuid)
|
||||
|
||||
# apply create / update operations
|
||||
created = {}
|
||||
updated = {}
|
||||
time_format = '%a %d %b %Y %I:%M %p'
|
||||
for uuid, employee_uuid in data['start_time'].iteritems():
|
||||
if uuid in deleted:
|
||||
continue
|
||||
if uuid.startswith('new-'):
|
||||
shift = model.ScheduledShift()
|
||||
shift.employee_uuid = data['employee_uuid'][uuid]
|
||||
shift.store_uuid = data['store_uuid'][uuid]
|
||||
Session.add(shift)
|
||||
created[uuid] = shift
|
||||
else:
|
||||
shift = Session.query(model.ScheduledShift).get(uuid)
|
||||
assert shift
|
||||
updated[uuid] = shift
|
||||
start_time = datetime.datetime.strptime(data['start_time'][uuid], time_format)
|
||||
shift.start_time = make_utc(localtime(self.rattail_config, start_time))
|
||||
end_time = datetime.datetime.strptime(data['end_time'][uuid], time_format)
|
||||
shift.end_time = make_utc(localtime(self.rattail_config, end_time))
|
||||
|
||||
self.request.session.flash("Changes were applied: created {}, updated {}, "
|
||||
"deleted {} Scheduled Shifts".format(
|
||||
len(created), len(updated), len(deleted)))
|
||||
return self.redirect(self.request.route_url('schedule.edit'))
|
||||
|
||||
form = Form(self.request, schema=ShiftFilter)
|
||||
self.process_filter_form(form)
|
||||
context = self.get_timesheet_context()
|
||||
context['form'] = form
|
||||
context['page_title'] = "Edit Schedule"
|
||||
return self.render_full(**context)
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._defaults(config)
|
||||
|
||||
# edit schedule
|
||||
config.add_route('schedule.edit', '/schedule/edit')
|
||||
config.add_view(cls, attr='edit', route_name='schedule.edit',
|
||||
renderer='/shifts/schedule_edit.mako',
|
||||
permission='schedule.edit')
|
||||
config.add_tailbone_permission('schedule', 'schedule.edit', "Edit full schedule")
|
||||
|
||||
|
||||
def includeme(config):
|
||||
ScheduleView.defaults(config)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue