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:
parent
d30e5e2b02
commit
4191e50456
|
@ -502,6 +502,8 @@ class Form(object):
|
||||||
if key in schema:
|
if key in schema:
|
||||||
schema[key].widget = widget
|
schema[key].widget = widget
|
||||||
|
|
||||||
|
# TODO: we are now doing this when making deform.Form, in which
|
||||||
|
# case, do we still need to do it here?
|
||||||
# apply any default values
|
# apply any default values
|
||||||
for key, default in self.defaults.items():
|
for key, default in self.defaults.items():
|
||||||
if key in schema:
|
if key in schema:
|
||||||
|
@ -656,6 +658,13 @@ class Form(object):
|
||||||
|
|
||||||
schema = self.make_schema()
|
schema = self.make_schema()
|
||||||
|
|
||||||
|
# TODO: we are still also doing this when making the schema, but
|
||||||
|
# seems like this should be the right place instead?
|
||||||
|
# apply any default values
|
||||||
|
for key, default in self.defaults.items():
|
||||||
|
if key in schema:
|
||||||
|
schema[key].default = default
|
||||||
|
|
||||||
# get initial form values from model instance
|
# get initial form values from model instance
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if self.model_instance:
|
if self.model_instance:
|
||||||
|
|
|
@ -26,6 +26,8 @@ Form Schema Types
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
|
@ -58,20 +60,28 @@ class JQueryTime(colander.Time):
|
||||||
return colander.timeparse(cstruct, formats[0])
|
return colander.timeparse(cstruct, formats[0])
|
||||||
|
|
||||||
|
|
||||||
class ObjectType(colander.SchemaType):
|
class ModelType(colander.SchemaType):
|
||||||
"""
|
"""
|
||||||
Custom schema type for scalar ORM relationship fields.
|
Custom schema type for scalar ORM relationship fields.
|
||||||
"""
|
"""
|
||||||
model_class = None
|
model_class = None
|
||||||
|
session = None
|
||||||
|
|
||||||
|
def __init__(self, model_class=None, session=None):
|
||||||
|
if model_class:
|
||||||
|
self.model_class = model_class
|
||||||
|
if session:
|
||||||
|
self.session = session
|
||||||
|
else:
|
||||||
|
self.session = self.make_session()
|
||||||
|
|
||||||
|
def make_session(self):
|
||||||
|
return Session()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def model_title(self):
|
def model_title(self):
|
||||||
self.model_class.get_model_title()
|
self.model_class.get_model_title()
|
||||||
|
|
||||||
@property
|
|
||||||
def session(self):
|
|
||||||
return Session()
|
|
||||||
|
|
||||||
def serialize(self, node, appstruct):
|
def serialize(self, node, appstruct):
|
||||||
if appstruct is colander.null:
|
if appstruct is colander.null:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
@ -86,28 +96,46 @@ class ObjectType(colander.SchemaType):
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
class StoreType(ObjectType):
|
# TODO: deprecate / remove this
|
||||||
|
ObjectType = ModelType
|
||||||
|
|
||||||
|
|
||||||
|
class StoreType(ModelType):
|
||||||
"""
|
"""
|
||||||
Custom schema type for store field.
|
Custom schema type for store field.
|
||||||
"""
|
"""
|
||||||
model_class = model.Store
|
model_class = model.Store
|
||||||
|
|
||||||
|
|
||||||
class CustomerType(ObjectType):
|
class CustomerType(ModelType):
|
||||||
"""
|
"""
|
||||||
Custom schema type for customer field.
|
Custom schema type for customer field.
|
||||||
"""
|
"""
|
||||||
model_class = model.Customer
|
model_class = model.Customer
|
||||||
|
|
||||||
|
|
||||||
class ProductType(ObjectType):
|
class DepartmentType(ModelType):
|
||||||
|
"""
|
||||||
|
Custom schema type for department field.
|
||||||
|
"""
|
||||||
|
model_class = model.Department
|
||||||
|
|
||||||
|
|
||||||
|
class EmployeeType(ModelType):
|
||||||
|
"""
|
||||||
|
Custom schema type for employee field.
|
||||||
|
"""
|
||||||
|
model_class = model.Employee
|
||||||
|
|
||||||
|
|
||||||
|
class ProductType(ModelType):
|
||||||
"""
|
"""
|
||||||
Custom schema type for product relationship field.
|
Custom schema type for product relationship field.
|
||||||
"""
|
"""
|
||||||
model_class = model.Product
|
model_class = model.Product
|
||||||
|
|
||||||
|
|
||||||
class UserType(ObjectType):
|
class UserType(ModelType):
|
||||||
"""
|
"""
|
||||||
Custom schema type for user field.
|
Custom schema type for user field.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -44,6 +44,8 @@ class ReadonlyWidget(dfwidget.HiddenWidget):
|
||||||
if cstruct in (colander.null, None):
|
if cstruct in (colander.null, None):
|
||||||
cstruct = ''
|
cstruct = ''
|
||||||
# TODO: is this hacky?
|
# TODO: is this hacky?
|
||||||
|
text = kw.get('text')
|
||||||
|
if not text:
|
||||||
text = field.parent.tailbone_form.render_field_value(field.name)
|
text = field.parent.tailbone_form.render_field_value(field.name)
|
||||||
return HTML.tag('span', text) + tags.hidden(field.name, value=cstruct, id=field.oid)
|
return HTML.tag('span', text) + tags.hidden(field.name, value=cstruct, id=field.oid)
|
||||||
|
|
||||||
|
@ -66,7 +68,9 @@ class JQueryDateWidget(dfwidget.DateInputWidget):
|
||||||
requirements = None
|
requirements = None
|
||||||
|
|
||||||
default_options = (
|
default_options = (
|
||||||
|
('changeMonth', True),
|
||||||
('changeYear', True),
|
('changeYear', True),
|
||||||
|
('dateFormat', 'yy-mm-dd'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def serialize(self, field, cstruct, **kw):
|
def serialize(self, field, cstruct, **kw):
|
||||||
|
@ -77,7 +81,7 @@ class JQueryDateWidget(dfwidget.DateInputWidget):
|
||||||
options = dict(
|
options = dict(
|
||||||
kw.get('options') or self.options or self.default_options
|
kw.get('options') or self.options or self.default_options
|
||||||
)
|
)
|
||||||
options['dateFormat'] = 'yy-mm-dd'
|
options.update(kw.get('extra_options', {}))
|
||||||
kw.setdefault('options_json', json.dumps(options))
|
kw.setdefault('options_json', json.dumps(options))
|
||||||
values = self.get_template_values(field, cstruct, kw)
|
values = self.get_template_values(field, cstruct, kw)
|
||||||
return field.renderer(template, **values)
|
return field.renderer(template, **values)
|
||||||
|
@ -132,7 +136,7 @@ class JQueryAutocompleteWidget(dfwidget.AutocompleteInputWidget):
|
||||||
kw['options'] = json.dumps(options)
|
kw['options'] = json.dumps(options)
|
||||||
kw['field_display'] = self.field_display
|
kw['field_display'] = self.field_display
|
||||||
kw['cleared_callback'] = self.cleared_callback
|
kw['cleared_callback'] = self.cleared_callback
|
||||||
kw['selected_callback'] = self.selected_callback
|
kw.setdefault('selected_callback', self.selected_callback)
|
||||||
tmpl_values = self.get_template_values(field, cstruct, kw)
|
tmpl_values = self.get_template_values(field, cstruct, kw)
|
||||||
template = readonly and self.readonly_template or self.template
|
template = readonly and self.readonly_template or self.template
|
||||||
return field.renderer(template, **tmpl_values)
|
return field.renderer(template, **tmpl_values)
|
||||||
|
|
|
@ -20,4 +20,14 @@
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script tal:condition="selected_callback" type="text/javascript">
|
||||||
|
deform.addCallback(
|
||||||
|
'${oid}',
|
||||||
|
function (oid) {
|
||||||
|
$('#' + oid).datepicker('option', 'onSelect', ${selected_callback});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,6 +41,15 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function date_selected(dateText, inst) {
|
||||||
|
if (confirm_leave()) {
|
||||||
|
$('#filter-form').submit();
|
||||||
|
} else {
|
||||||
|
// revert date value
|
||||||
|
$('.week-picker input[name="date"]').val($('.week-picker').data('week'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
|
|
||||||
$('#filter-form').submit(function() {
|
$('#filter-form').submit(function() {
|
||||||
|
@ -69,17 +78,7 @@
|
||||||
|
|
||||||
$('.week-picker button.nav').click(function() {
|
$('.week-picker button.nav').click(function() {
|
||||||
if (confirm_leave()) {
|
if (confirm_leave()) {
|
||||||
$('.week-picker #date').val($(this).data('date'));
|
$('.week-picker input[name="date"]').val($(this).data('date'));
|
||||||
$('#filter-form').submit();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.week-picker #date').datepicker({
|
|
||||||
dateFormat: 'mm/dd/yy',
|
|
||||||
changeYear: true,
|
|
||||||
changeMonth: true,
|
|
||||||
showButtonPanel: true,
|
|
||||||
onSelect: function(dateText, inst) {
|
|
||||||
$('#filter-form').submit();
|
$('#filter-form').submit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -134,8 +133,8 @@
|
||||||
<%def name="timesheet_wrapper(with_edit_form=False, change_employee=None)">
|
<%def name="timesheet_wrapper(with_edit_form=False, change_employee=None)">
|
||||||
<div class="timesheet-wrapper">
|
<div class="timesheet-wrapper">
|
||||||
|
|
||||||
${form.begin(id='filter-form')}
|
${h.form(request.current_route_url(_query=False), id='filter-form')}
|
||||||
${form.csrf_token()}
|
${h.csrf_token(request)}
|
||||||
|
|
||||||
<table class="timesheet-header">
|
<table class="timesheet-header">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -147,26 +146,31 @@
|
||||||
<div class="field-wrapper employee">
|
<div class="field-wrapper employee">
|
||||||
<label>Employee</label>
|
<label>Employee</label>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
% if request.has_perm('{}.viewall'.format(permission_prefix)):
|
${dform['employee'].serialize(text=six.text_type(employee), selected_callback='employee_selected')|n}
|
||||||
${autocomplete('employee', url('employees.autocomplete'),
|
|
||||||
field_value=employee.uuid if employee else None,
|
|
||||||
field_display=unicode(employee or ''),
|
|
||||||
selected='employee_selected',
|
|
||||||
change_clicked=change_employee)}
|
|
||||||
% else:
|
|
||||||
${form.hidden('employee', value=employee.uuid)}
|
|
||||||
${employee}
|
|
||||||
% endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if store_options is not Undefined:
|
% if store_options is not Undefined:
|
||||||
${form.field_div('store', h.select('store', store.uuid if store else None, store_options))}
|
<div class="field-wrapper store">
|
||||||
|
<div class="field-row">
|
||||||
|
<label for="store">Store</label>
|
||||||
|
<div class="field">
|
||||||
|
${dform['store'].serialize()|n}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
% if department_options is not Undefined:
|
% if department_options is not Undefined:
|
||||||
${form.field_div('department', h.select('department', department.uuid if department else None, department_options))}
|
<div class="field-wrapper department">
|
||||||
|
<div class="field-row">
|
||||||
|
<label for="department">Department</label>
|
||||||
|
<div class="field">
|
||||||
|
${dform['department'].serialize()|n}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
<div class="field-wrapper week">
|
<div class="field-wrapper week">
|
||||||
|
@ -190,11 +194,11 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tools">
|
<td class="tools">
|
||||||
<div class="grid-tools">
|
<div class="grid-tools">
|
||||||
<div class="week-picker">
|
<div class="week-picker" data-week="${sunday.strftime('%Y-%m-%d')}">
|
||||||
<button type="button" class="nav" data-date="${prev_sunday.strftime('%m/%d/%Y')}">« Previous</button>
|
<button type="button" class="nav" data-date="${prev_sunday.strftime('%Y-%m-%d')}">« Previous</button>
|
||||||
<button type="button" class="nav" data-date="${next_sunday.strftime('%m/%d/%Y')}">Next »</button>
|
<button type="button" class="nav" data-date="${next_sunday.strftime('%Y-%m-%d')}">Next »</button>
|
||||||
<label>Jump to week:</label>
|
<label>Jump to week:</label>
|
||||||
${form.text('date', value=sunday.strftime('%m/%d/%Y'))}
|
${dform['date'].serialize(extra_options={'showButtonPanel': True}, selected_callback='date_selected')|n}
|
||||||
</div>
|
</div>
|
||||||
</div><!-- grid-tools -->
|
</div><!-- grid-tools -->
|
||||||
</td><!-- tools -->
|
</td><!-- tools -->
|
||||||
|
@ -203,7 +207,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table><!-- timesheet-header -->
|
</table><!-- timesheet-header -->
|
||||||
|
|
||||||
${form.end()}
|
${h.end_form()}
|
||||||
|
|
||||||
% if with_edit_form:
|
% if with_edit_form:
|
||||||
${self.edit_form()}
|
${self.edit_form()}
|
||||||
|
|
|
@ -6,7 +6,11 @@
|
||||||
<li>${h.link_to("Edit Schedule", url('schedule.edit'))}</li>
|
<li>${h.link_to("Edit Schedule", url('schedule.edit'))}</li>
|
||||||
% endif
|
% endif
|
||||||
% if request.has_perm('schedule.print'):
|
% if request.has_perm('schedule.print'):
|
||||||
|
% if employee is Undefined:
|
||||||
<li>${h.link_to("Print Schedule", url('schedule.print'), target='_blank')}</li>
|
<li>${h.link_to("Print Schedule", url('schedule.print'), target='_blank')}</li>
|
||||||
|
% else:
|
||||||
|
<li>${h.link_to("Print this Schedule", url('schedule.employee.print'), target='_blank')}</li>
|
||||||
|
% endif
|
||||||
% endif
|
% endif
|
||||||
% if request.has_perm('timesheet.view'):
|
% if request.has_perm('timesheet.view'):
|
||||||
<li>${h.link_to("View this Time Sheet", url('schedule.goto.timesheet'), class_='goto')}</li>
|
<li>${h.link_to("View this Time Sheet", url('schedule.goto.timesheet'), class_='goto')}</li>
|
||||||
|
|
20
tailbone/templates/shifts/schedule_print_employee.mako
Normal file
20
tailbone/templates/shifts/schedule_print_employee.mako
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
<%namespace file="/shifts/base.mako" import="timesheet" />
|
||||||
|
<%namespace file="/shifts/schedule.mako" import="render_day" />
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
## TODO: this seems a little hacky..?
|
||||||
|
${h.stylesheet_link(request.static_url('tailbone:static/css/normalize.css'), media='all')}
|
||||||
|
${h.stylesheet_link(request.static_url('tailbone:static/css/base.css'), media='all')}
|
||||||
|
${h.stylesheet_link(request.static_url('tailbone:static/css/grids.css'), media='all')}
|
||||||
|
${h.stylesheet_link(request.static_url('tailbone:static/css/timesheet.css'), media='all')}
|
||||||
|
${h.stylesheet_link(request.static_url('tailbone:static/css/schedule_print.css'), media='print')}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>
|
||||||
|
${employee} -
|
||||||
|
${week_of}
|
||||||
|
</h1>
|
||||||
|
${timesheet(render_day=render_day)}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# 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.time import localtime, make_utc, get_sunday
|
||||||
from rattail.util import pretty_hours, hours_as_decimal
|
from rattail.util import pretty_hours, hours_as_decimal
|
||||||
|
|
||||||
import formencode as fe
|
import colander
|
||||||
from pyramid_simpleform import Form
|
from deform import widget as dfwidget
|
||||||
from webhelpers2.html import tags, HTML
|
from webhelpers2.html import tags, HTML
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms2 as forms
|
||||||
from tailbone.db import Session
|
from tailbone.db import Session
|
||||||
from tailbone.views import View
|
from tailbone.views import View
|
||||||
|
|
||||||
|
|
||||||
class ShiftFilter(fe.Schema):
|
class ShiftFilter(colander.Schema):
|
||||||
allow_extra_fields = True
|
|
||||||
filter_extra_fields = True
|
store = colander.SchemaNode(forms.types.StoreType())
|
||||||
store = forms.validators.ValidStore()
|
|
||||||
department = forms.validators.ValidDepartment()
|
department = colander.SchemaNode(forms.types.DepartmentType())
|
||||||
date = fe.validators.DateConverter()
|
|
||||||
|
date = colander.SchemaNode(colander.Date())
|
||||||
|
|
||||||
|
|
||||||
class EmployeeShiftFilter(fe.Schema):
|
class EmployeeShiftFilter(colander.Schema):
|
||||||
allow_extra_fields = True
|
|
||||||
filter_extra_fields = True
|
employee = colander.SchemaNode(forms.types.EmployeeType())
|
||||||
employee = forms.validators.ValidEmployee()
|
|
||||||
date = fe.validators.DateConverter()
|
date = colander.SchemaNode(colander.Date())
|
||||||
|
|
||||||
|
|
||||||
class TimeSheetView(View):
|
class TimeSheetView(View):
|
||||||
|
@ -166,13 +167,12 @@ class TimeSheetView(View):
|
||||||
Process a "shift filter" form if one was in fact POST'ed. If it was
|
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.
|
then we store new context in session and redirect to display as normal.
|
||||||
"""
|
"""
|
||||||
if self.request.method == 'POST':
|
if form.validate(newstyle=True):
|
||||||
if form.validate():
|
store = form.validated['store']
|
||||||
store = form.data['store']
|
|
||||||
self.request.session['timesheet.{}.store'.format(self.key)] = store.uuid if store else None
|
self.request.session['timesheet.{}.store'.format(self.key)] = store.uuid if store else None
|
||||||
department = form.data['department']
|
department = form.validated['department']
|
||||||
self.request.session['timesheet.{}.department'.format(self.key)] = department.uuid if department else None
|
self.request.session['timesheet.{}.department'.format(self.key)] = department.uuid if department else None
|
||||||
date = form.data['date']
|
date = form.validated['date']
|
||||||
self.request.session['timesheet.{}.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
|
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())
|
raise self.redirect(self.request.current_route_url())
|
||||||
|
|
||||||
|
@ -181,32 +181,74 @@ class TimeSheetView(View):
|
||||||
Process an "employee shift filter" form if one was in fact POST'ed. If it
|
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.
|
was then we store new context in session and redirect to display as normal.
|
||||||
"""
|
"""
|
||||||
if self.request.method == 'POST':
|
if form.validate(newstyle=True):
|
||||||
if form.validate():
|
employee = form.validated['employee']
|
||||||
employee = form.data['employee']
|
|
||||||
self.request.session['timesheet.{}.employee'.format(self.key)] = employee.uuid if employee else None
|
self.request.session['timesheet.{}.employee'.format(self.key)] = employee.uuid if employee else None
|
||||||
date = form.data['date']
|
date = form.validated['date']
|
||||||
self.request.session['timesheet.{}.employee.date'.format(self.key)] = date.strftime('%m/%d/%Y') if date else None
|
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())
|
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):
|
def full(self):
|
||||||
"""
|
"""
|
||||||
View a "full" timesheet/schedule, i.e. all employees but filterable by
|
View a "full" timesheet/schedule, i.e. all employees but filterable by
|
||||||
store and/or department.
|
store and/or department.
|
||||||
"""
|
"""
|
||||||
form = Form(self.request, schema=ShiftFilter)
|
|
||||||
self.process_filter_form(form)
|
|
||||||
context = self.get_timesheet_context()
|
context = self.get_timesheet_context()
|
||||||
|
form = self.make_full_filter_form(context)
|
||||||
|
self.process_filter_form(form)
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
return self.render_full(**context)
|
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):
|
def employee(self):
|
||||||
"""
|
"""
|
||||||
View time sheet for single employee.
|
View time sheet for single employee.
|
||||||
"""
|
"""
|
||||||
form = Form(self.request, schema=EmployeeShiftFilter)
|
|
||||||
self.process_employee_filter_form(form)
|
|
||||||
context = self.get_employee_context()
|
context = self.get_employee_context()
|
||||||
|
form = self.make_employee_filter_form(context)
|
||||||
|
self.process_employee_filter_form(form)
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
return self.render_single(**context)
|
return self.render_single(**context)
|
||||||
|
|
||||||
|
@ -280,7 +322,8 @@ class TimeSheetView(View):
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'page_title': self.get_title_full(),
|
'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,
|
'employees': employees,
|
||||||
'stores': stores,
|
'stores': stores,
|
||||||
'store_options': store_options,
|
'store_options': store_options,
|
||||||
|
@ -326,7 +369,8 @@ class TimeSheetView(View):
|
||||||
context = {
|
context = {
|
||||||
'single': True,
|
'single': True,
|
||||||
'page_title': "Employee {}".format(self.get_title()),
|
'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,
|
'employee': employee,
|
||||||
'employees': [employee],
|
'employees': [employee],
|
||||||
'week_of': week_of,
|
'week_of': week_of,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8; -*-
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -31,10 +31,8 @@ import datetime
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.time import localtime, make_utc, get_sunday
|
from rattail.time import localtime, make_utc, get_sunday
|
||||||
|
|
||||||
from pyramid_simpleform import Form
|
|
||||||
|
|
||||||
from tailbone.db import Session
|
from tailbone.db import Session
|
||||||
from tailbone.views.shifts.lib import TimeSheetView, ShiftFilter
|
from tailbone.views.shifts.lib import TimeSheetView
|
||||||
|
|
||||||
|
|
||||||
class ScheduleView(TimeSheetView):
|
class ScheduleView(TimeSheetView):
|
||||||
|
@ -61,10 +59,9 @@ class ScheduleView(TimeSheetView):
|
||||||
return self.redirect(self.request.route_url('schedule.edit'))
|
return self.redirect(self.request.route_url('schedule.edit'))
|
||||||
|
|
||||||
# okay then, process filters; redirect if any were received
|
# 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()
|
context = self.get_timesheet_context()
|
||||||
|
form = self.make_full_filter_form(context)
|
||||||
|
self.process_filter_form(form)
|
||||||
|
|
||||||
# okay then, maybe process saved shift data
|
# okay then, maybe process saved shift data
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
|
@ -199,12 +196,20 @@ class ScheduleView(TimeSheetView):
|
||||||
permission='schedule.edit')
|
permission='schedule.edit')
|
||||||
config.add_tailbone_permission('schedule', 'schedule.edit', "Edit full schedule")
|
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_route('schedule.print', '/schedule/print')
|
||||||
config.add_view(cls, attr='full', route_name='schedule.print',
|
config.add_view(cls, attr='full', route_name='schedule.print',
|
||||||
renderer='/shifts/schedule_print.mako',
|
renderer='/shifts/schedule_print.mako',
|
||||||
permission='schedule.print')
|
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):
|
def includeme(config):
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8; -*-
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -31,10 +31,8 @@ import datetime
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.time import make_utc, localtime
|
from rattail.time import make_utc, localtime
|
||||||
|
|
||||||
from pyramid_simpleform import Form
|
|
||||||
|
|
||||||
from tailbone.db import Session
|
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):
|
class TimeSheetView(BaseTimeSheetView):
|
||||||
|
@ -50,10 +48,9 @@ class TimeSheetView(BaseTimeSheetView):
|
||||||
View for editing single employee's timesheet
|
View for editing single employee's timesheet
|
||||||
"""
|
"""
|
||||||
# process filters; redirect if any were received
|
# process filters; redirect if any were received
|
||||||
form = Form(self.request, schema=EmployeeShiftFilter)
|
|
||||||
self.process_employee_filter_form(form)
|
|
||||||
|
|
||||||
context = self.get_employee_context()
|
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
|
# okay then, maybe process saved shift data
|
||||||
if self.request.method == 'POST':
|
if self.request.method == 'POST':
|
||||||
|
|
Loading…
Reference in a new issue