Add ability to edit employee time sheet
Also disable some unwanted autocomplete logic, plus add ability to prevent autocomplete "change click" event
This commit is contained in:
parent
2e88cdde88
commit
7104e275c3
11 changed files with 467 additions and 302 deletions
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2016 Lance Edgar
|
||||
# Copyright © 2010-2017 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -121,6 +121,37 @@ class TimeSheetView(View):
|
|||
'employees': employees.all(),
|
||||
}
|
||||
|
||||
def get_employee_context(self):
|
||||
"""
|
||||
Determine employee/date context from user's session and/or defaults
|
||||
"""
|
||||
date = None
|
||||
date_key = 'timesheet.{}.employee.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()
|
||||
|
||||
employee = None
|
||||
employee_key = 'timesheet.{}.employee'.format(self.key)
|
||||
if employee_key in self.request.session:
|
||||
employee_uuid = self.request.session[employee_key]
|
||||
employee = Session.query(model.Employee).get(employee_uuid) if employee_uuid else None
|
||||
if not employee:
|
||||
employee = self.request.user.employee
|
||||
|
||||
# force current user if not allowed to view all data
|
||||
if not self.request.has_perm('{}.viewall'.format(self.key)):
|
||||
employee = self.request.user.employee
|
||||
assert employee
|
||||
|
||||
return {'date': date, 'employee': employee}
|
||||
|
||||
def process_filter_form(self, form):
|
||||
"""
|
||||
Process a "shift filter" form if one was in fact POST'ed. If it was
|
||||
|
@ -136,6 +167,19 @@ class TimeSheetView(View):
|
|||
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())
|
||||
|
||||
def full(self):
|
||||
"""
|
||||
View a "full" timesheet/schedule, i.e. all employees but filterable by
|
||||
|
@ -151,50 +195,11 @@ class TimeSheetView(View):
|
|||
"""
|
||||
View time sheet for single employee.
|
||||
"""
|
||||
date = None
|
||||
employee = None
|
||||
if not self.request.has_perm('{}.viewall'.format(self.key)):
|
||||
# force current user if not allowed to view all data
|
||||
employee = self.request.user.employee
|
||||
assert employee
|
||||
form = Form(self.request, schema=EmployeeShiftFilter)
|
||||
if self.request.method == 'POST':
|
||||
if form.validate():
|
||||
if self.request.has_perm('{}.viewall'.format(self.key)):
|
||||
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
|
||||
return self.redirect(self.request.current_route_url())
|
||||
|
||||
else:
|
||||
if self.request.has_perm('{}.viewall'.format(self.key)):
|
||||
employee_key = 'timesheet.{}.employee'.format(self.key)
|
||||
if employee_key in self.request.session:
|
||||
employee_uuid = self.request.session.get(employee_key)
|
||||
if employee_uuid:
|
||||
employee = Session.query(model.Employee).get(employee_uuid)
|
||||
else: # no employee in session
|
||||
if self.request.user:
|
||||
employee = self.request.user.employee
|
||||
|
||||
date_key = 'timesheet.{}.employee.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
|
||||
|
||||
# default to current user; force unless allowed to view all data
|
||||
if not employee or not self.request.has_perm('{}.viewall'.format(self.key)):
|
||||
employee = self.request.user.employee
|
||||
assert employee
|
||||
|
||||
if not date:
|
||||
date = localtime(self.rattail_config).date()
|
||||
return self.render_single(date, employee, form=form)
|
||||
self.process_employee_filter_form(form)
|
||||
context = self.get_employee_context()
|
||||
context['form'] = form
|
||||
return self.render_single(**context)
|
||||
|
||||
def crossview(self):
|
||||
"""
|
||||
|
@ -216,17 +221,6 @@ class TimeSheetView(View):
|
|||
self.session_put('date', self.session_get('date'), mainkey=other_key)
|
||||
return self.redirect(self.request.route_url(other_key))
|
||||
|
||||
# def session_has(self, key, mainkey=None):
|
||||
# if mainkey is None:
|
||||
# mainkey = self.key
|
||||
# return 'timesheet.{}.{}'.format(mainkey, key) in self.request.session
|
||||
|
||||
# def session_has_any(self, *keys, **kwargs):
|
||||
# for key in keys:
|
||||
# if self.session_has(key, **kwargs):
|
||||
# return True
|
||||
# return False
|
||||
|
||||
def session_get(self, key, mainkey=None):
|
||||
if mainkey is None:
|
||||
mainkey = self.key
|
||||
|
@ -301,7 +295,7 @@ class TimeSheetView(View):
|
|||
def render_shift(self, shift):
|
||||
return HTML.tag('span', c=shift.get_display(self.rattail_config))
|
||||
|
||||
def render_single(self, date, employee, form=None):
|
||||
def render_single(self, date=None, employee=None, form=None, **kwargs):
|
||||
"""
|
||||
Render a time sheet for one employee, for the week which includes the
|
||||
specified date.
|
||||
|
@ -319,7 +313,7 @@ class TimeSheetView(View):
|
|||
|
||||
self.modify_employees([employee], weekdays)
|
||||
|
||||
return {
|
||||
context = {
|
||||
'page_title': "Employee {}".format(self.get_title()),
|
||||
'form': forms.FormRenderer(form) if form else None,
|
||||
'employee': employee,
|
||||
|
@ -332,6 +326,8 @@ class TimeSheetView(View):
|
|||
'permission_prefix': self.key,
|
||||
'render_shift': self.render_shift,
|
||||
}
|
||||
context.update(kwargs)
|
||||
return context
|
||||
|
||||
def modify_employees(self, employees, weekdays):
|
||||
min_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[0], datetime.time(0)))
|
||||
|
|
|
@ -64,6 +64,8 @@ class ScheduleView(TimeSheetView):
|
|||
form = Form(self.request, schema=ShiftFilter)
|
||||
self.process_filter_form(form)
|
||||
|
||||
context = self.get_timesheet_context()
|
||||
|
||||
# okay then, maybe process saved shift data
|
||||
if self.request.method == 'POST':
|
||||
|
||||
|
@ -97,7 +99,10 @@ class ScheduleView(TimeSheetView):
|
|||
if uuid.startswith('new-'):
|
||||
shift = model.ScheduledShift()
|
||||
shift.employee_uuid = data['employee_uuid'][uuid]
|
||||
shift.store_uuid = data['store_uuid'][uuid]
|
||||
if 'store_uuid' in data and uuid in data['store_uuid']:
|
||||
shift.store_uuid = data['store_uuid'][uuid]
|
||||
else:
|
||||
shift.store_uuid = context['store'].uuid if context['store'] else None
|
||||
Session.add(shift)
|
||||
created[uuid] = shift
|
||||
else:
|
||||
|
@ -114,7 +119,6 @@ class ScheduleView(TimeSheetView):
|
|||
len(created), len(updated), len(deleted)))
|
||||
return self.redirect(self.request.route_url('schedule.edit'))
|
||||
|
||||
context = self.get_timesheet_context()
|
||||
context['form'] = form
|
||||
context['page_title'] = "Edit Schedule"
|
||||
return self.render_full(**context)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2016 Lance Edgar
|
||||
# Copyright © 2010-2017 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -26,19 +26,103 @@ Views for employee time sheets
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail.db import model
|
||||
import datetime
|
||||
|
||||
from tailbone.views.shifts.lib import TimeSheetView as BaseTimeSheetView
|
||||
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
|
||||
|
||||
|
||||
class TimeSheetView(BaseTimeSheetView):
|
||||
"""
|
||||
Simple view for current user's time sheet.
|
||||
Views for employee time sheets, i.e. worked shift data
|
||||
"""
|
||||
key = 'timesheet'
|
||||
title = "Time Sheet"
|
||||
model_class = model.WorkedShift
|
||||
|
||||
def edit_employee(self):
|
||||
"""
|
||||
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()
|
||||
|
||||
# okay then, maybe process saved shift data
|
||||
if self.request.method == 'POST':
|
||||
|
||||
# TODO: most of this is copied from 'schedule.edit' view, should merge...
|
||||
|
||||
# organize form data by uuid / field
|
||||
fields = ['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]
|
||||
break
|
||||
|
||||
# apply delete operations
|
||||
deleted = []
|
||||
for uuid, value in list(data['delete'].items()):
|
||||
assert value == 'delete'
|
||||
shift = Session.query(model.WorkedShift).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, time in data['start_time'].iteritems():
|
||||
if uuid in deleted:
|
||||
continue
|
||||
if uuid.startswith('new-'):
|
||||
shift = model.WorkedShift()
|
||||
shift.employee_uuid = context['employee'].uuid
|
||||
# TODO: add support for setting store here...
|
||||
Session.add(shift)
|
||||
created[uuid] = shift
|
||||
else:
|
||||
shift = Session.query(model.WorkedShift).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 {} Worked Shifts".format(
|
||||
len(created), len(updated), len(deleted)))
|
||||
return self.redirect(self.request.route_url('timesheet.employee.edit'))
|
||||
|
||||
context['form'] = form
|
||||
context['page_title'] = "Edit Employee Time Sheet"
|
||||
return self.render_single(**context)
|
||||
|
||||
@classmethod
|
||||
def defaults(cls, config):
|
||||
cls._defaults(config)
|
||||
|
||||
# edit employee time sheet
|
||||
config.add_tailbone_permission('timesheet', 'timesheet.edit',
|
||||
"Edit time sheet (for *any* employee!)")
|
||||
config.add_route('timesheet.employee.edit', '/timesheeet/employee/edit')
|
||||
config.add_view(cls, attr='edit_employee', route_name='timesheet.employee.edit',
|
||||
renderer='/shifts/timesheet_edit.mako',
|
||||
permission='timesheet.edit')
|
||||
|
||||
|
||||
def includeme(config):
|
||||
TimeSheetView.defaults(config)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue