Refactor schedule / timesheet views for better separation of concerns
This was needed to support a "late clock-ins" view which included both scheduled *and* worked shift data..
This commit is contained in:
parent
bd6d2d2e11
commit
1e4612bcbe
|
@ -1,5 +1,6 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/base.mako" />
|
<%inherit file="/base.mako" />
|
||||||
|
<%namespace file="/autocomplete.mako" import="autocomplete" />
|
||||||
|
|
||||||
<%def name="title()">${page_title}</%def>
|
<%def name="title()">${page_title}</%def>
|
||||||
|
|
||||||
|
@ -132,8 +133,165 @@
|
||||||
|
|
||||||
<%def name="context_menu()"></%def>
|
<%def name="context_menu()"></%def>
|
||||||
|
|
||||||
<%def name="render_day(day)">
|
<%def name="timesheet_wrapper(with_edit_form=False, change_employee=None)">
|
||||||
% for shift in day['shifts']:
|
<div class="timesheet-wrapper">
|
||||||
<p class="shift">${render_shift(shift)}</p>
|
|
||||||
% endfor
|
${form.begin(id='filter-form')}
|
||||||
|
${form.csrf_token()}
|
||||||
|
|
||||||
|
<table class="timesheet-header">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td class="filters" rowspan="2">
|
||||||
|
|
||||||
|
% if employee is not Undefined:
|
||||||
|
<div class="field-wrapper employee">
|
||||||
|
<label>Employee</label>
|
||||||
|
<div class="field">
|
||||||
|
% if request.has_perm('{}.viewall'.format(permission_prefix)):
|
||||||
|
${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>
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if store_options is not Undefined:
|
||||||
|
${form.field_div('store', h.select('store', store.uuid if store else None, store_options))}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if department_options is not Undefined:
|
||||||
|
${form.field_div('department', h.select('department', department.uuid if department else None, department_options))}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
<div class="field-wrapper week">
|
||||||
|
<label>Week of</label>
|
||||||
|
<div class="field">
|
||||||
|
${week_of}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${self.edit_tools()}
|
||||||
|
|
||||||
|
</td><!-- filters -->
|
||||||
|
|
||||||
|
<td class="menu">
|
||||||
|
<ul id="context-menu">
|
||||||
|
${self.context_menu()}
|
||||||
|
</ul>
|
||||||
|
</td><!-- menu -->
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="tools">
|
||||||
|
<div class="grid-tools">
|
||||||
|
<div class="week-picker">
|
||||||
|
<button type="button" class="nav" data-date="${prev_sunday.strftime('%m/%d/%Y')}">« Previous</button>
|
||||||
|
<button type="button" class="nav" data-date="${next_sunday.strftime('%m/%d/%Y')}">Next »</button>
|
||||||
|
<label>Jump to week:</label>
|
||||||
|
${form.text('date', value=sunday.strftime('%m/%d/%Y'))}
|
||||||
|
</div>
|
||||||
|
</div><!-- grid-tools -->
|
||||||
|
</td><!-- tools -->
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table><!-- timesheet-header -->
|
||||||
|
|
||||||
|
${form.end()}
|
||||||
|
|
||||||
|
% if with_edit_form:
|
||||||
|
${self.edit_form()}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
${self.timesheet()}
|
||||||
|
|
||||||
|
% if with_edit_form:
|
||||||
|
${h.end_form()}
|
||||||
|
% endif
|
||||||
|
|
||||||
|
</div><!-- timesheet-wrapper -->
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="timesheet(render_day=None)">
|
||||||
|
<style type="text/css">
|
||||||
|
.timesheet thead th {
|
||||||
|
width: ${'{:0.2f}'.format(100.0 / 9)}%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<table class="timesheet">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Employee</th>
|
||||||
|
% for day in weekdays:
|
||||||
|
<th>${day.strftime('%A')}<br />${day.strftime('%b %d')}</th>
|
||||||
|
% endfor
|
||||||
|
<th>Total<br />Hours</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
% for emp in sorted(employees, key=unicode):
|
||||||
|
<tr data-employee-uuid="${emp.uuid}">
|
||||||
|
<td class="employee">
|
||||||
|
## TODO: add link to single employee schedule / timesheet here...
|
||||||
|
${emp}
|
||||||
|
</td>
|
||||||
|
% for day in emp.weekdays:
|
||||||
|
<td class="day">
|
||||||
|
% if render_day:
|
||||||
|
${render_day(day)}
|
||||||
|
% else:
|
||||||
|
${self.render_day(day)}
|
||||||
|
% endif
|
||||||
|
</td>
|
||||||
|
% endfor
|
||||||
|
<td class="total">
|
||||||
|
${self.render_employee_total(emp)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
% endfor
|
||||||
|
% if employee is Undefined:
|
||||||
|
<tr class="total">
|
||||||
|
<td class="employee">${len(employees)} employees</td>
|
||||||
|
% for day in weekdays:
|
||||||
|
<td></td>
|
||||||
|
% endfor
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
% else:
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
% for day in employee.weekdays:
|
||||||
|
<td>
|
||||||
|
${self.render_employee_day_total(day)}
|
||||||
|
</td>
|
||||||
|
% endfor
|
||||||
|
<td>
|
||||||
|
${self.render_employee_total(employee)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
% endif
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="edit_form()"></%def>
|
||||||
|
|
||||||
|
<%def name="edit_tools()"></%def>
|
||||||
|
|
||||||
|
<%def name="render_day(day)"></%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_total(employee)"></%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_day_total(day)"></%def>
|
||||||
|
|
||||||
|
|
||||||
|
${self.timesheet_wrapper()}
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
## -*- coding: utf-8 -*-
|
|
||||||
<%namespace file="/autocomplete.mako" import="autocomplete" />
|
|
||||||
|
|
||||||
<%def name="timesheet_wrapper(edit_form=None, edit_tools=None, context_menu=None, render_day=None, change_employee=None)">
|
|
||||||
<div class="timesheet-wrapper">
|
|
||||||
|
|
||||||
${form.begin(id='filter-form')}
|
|
||||||
${form.csrf_token()}
|
|
||||||
|
|
||||||
<table class="timesheet-header">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
|
|
||||||
<td class="filters" rowspan="2">
|
|
||||||
|
|
||||||
% if employee is not UNDEFINED:
|
|
||||||
<div class="field-wrapper employee">
|
|
||||||
<label>Employee</label>
|
|
||||||
<div class="field">
|
|
||||||
% if request.has_perm('{}.viewall'.format(permission_prefix)):
|
|
||||||
${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>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if store_options is not UNDEFINED:
|
|
||||||
${form.field_div('store', h.select('store', store.uuid if store else None, store_options))}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% if department_options is not UNDEFINED:
|
|
||||||
${form.field_div('department', h.select('department', department.uuid if department else None, department_options))}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
<div class="field-wrapper week">
|
|
||||||
<label>Week of</label>
|
|
||||||
<div class="field">
|
|
||||||
${week_of}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
% if edit_tools:
|
|
||||||
${edit_tools()}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</td><!-- filters -->
|
|
||||||
|
|
||||||
<td class="menu">
|
|
||||||
<ul id="context-menu">
|
|
||||||
% if context_menu:
|
|
||||||
${context_menu()}
|
|
||||||
% endif
|
|
||||||
</ul>
|
|
||||||
</td><!-- menu -->
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td class="tools">
|
|
||||||
<div class="grid-tools">
|
|
||||||
<div class="week-picker">
|
|
||||||
<button type="button" class="nav" data-date="${prev_sunday.strftime('%m/%d/%Y')}">« Previous</button>
|
|
||||||
<button type="button" class="nav" data-date="${next_sunday.strftime('%m/%d/%Y')}">Next »</button>
|
|
||||||
<label>Jump to week:</label>
|
|
||||||
${form.text('date', value=sunday.strftime('%m/%d/%Y'))}
|
|
||||||
</div>
|
|
||||||
</div><!-- grid-tools -->
|
|
||||||
</td><!-- tools -->
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table><!-- timesheet-header -->
|
|
||||||
|
|
||||||
${form.end()}
|
|
||||||
|
|
||||||
% if edit_form:
|
|
||||||
${edit_form()}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
${timesheet(render_day=render_day)}
|
|
||||||
|
|
||||||
% if edit_form:
|
|
||||||
${h.end_form()}
|
|
||||||
% endif
|
|
||||||
|
|
||||||
</div><!-- timesheet-wrapper -->
|
|
||||||
</%def>
|
|
||||||
|
|
||||||
<%def name="timesheet(render_day=None)">
|
|
||||||
<style type="text/css">
|
|
||||||
.timesheet thead th {
|
|
||||||
width: ${'{:0.2f}'.format(100.0 / 9)}%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<table class="timesheet">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Employee</th>
|
|
||||||
% for day in weekdays:
|
|
||||||
<th>${day.strftime('%A')}<br />${day.strftime('%b %d')}</th>
|
|
||||||
% endfor
|
|
||||||
<th>Total<br />Hours</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
% for emp in sorted(employees, key=unicode):
|
|
||||||
<tr data-employee-uuid="${emp.uuid}">
|
|
||||||
<td class="employee">
|
|
||||||
## TODO: add link to single employee schedule / timesheet here...
|
|
||||||
${emp}
|
|
||||||
</td>
|
|
||||||
% for day in emp.weekdays:
|
|
||||||
<td class="day">
|
|
||||||
% if render_day:
|
|
||||||
${render_day(day)}
|
|
||||||
% endif
|
|
||||||
</td>
|
|
||||||
% endfor
|
|
||||||
<td class="total">${emp.hours_display}</td>
|
|
||||||
</tr>
|
|
||||||
% endfor
|
|
||||||
% if employee is UNDEFINED:
|
|
||||||
<tr class="total">
|
|
||||||
<td class="employee">${len(employees)} employees</td>
|
|
||||||
% for day in weekdays:
|
|
||||||
<td></td>
|
|
||||||
% endfor
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
% else:
|
|
||||||
<tr>
|
|
||||||
<td> </td>
|
|
||||||
% for day in employee.weekdays:
|
|
||||||
<td>${day['hours_display']}</td>
|
|
||||||
% endfor
|
|
||||||
<td>${employee.hours_display}</td>
|
|
||||||
</tr>
|
|
||||||
% endif
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</%def>
|
|
|
@ -1,6 +1,5 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
|
||||||
|
|
||||||
<%def name="context_menu()">
|
<%def name="context_menu()">
|
||||||
% if request.has_perm('schedule.edit'):
|
% if request.has_perm('schedule.edit'):
|
||||||
|
@ -14,4 +13,19 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${timesheet_wrapper(context_menu=context_menu, render_day=self.render_day)}
|
<%def name="render_day(day)">
|
||||||
|
% for shift in day['scheduled_shifts']:
|
||||||
|
<p class="shift">${render_shift(shift)}</p>
|
||||||
|
% endfor
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_total(employee)">
|
||||||
|
${employee.scheduled_hours_display}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_day_total(day)">
|
||||||
|
${day['scheduled_hours_display']}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${parent.body()}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
<%def name="extra_javascript()">
|
||||||
${parent.extra_javascript()}
|
${parent.extra_javascript()}
|
||||||
|
@ -69,13 +68,17 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_day(day)">
|
<%def name="render_day(day)">
|
||||||
% for shift in day['shifts']:
|
% for shift in day['scheduled_shifts']:
|
||||||
<p class="shift" data-uuid="${shift.uuid}">
|
<p class="shift" data-uuid="${shift.uuid}">
|
||||||
${render_shift(shift)}
|
${render_shift(shift)}
|
||||||
</p>
|
</p>
|
||||||
% endfor
|
% endfor
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_total(employee)">
|
||||||
|
${employee.scheduled_hours_display}
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="edit_form()">
|
<%def name="edit_form()">
|
||||||
${h.form(url('schedule.edit'), id='timetable-form')}
|
${h.form(url('schedule.edit'), id='timetable-form')}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
|
@ -90,7 +93,8 @@
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${timesheet_wrapper(edit_form=edit_form, edit_tools=edit_tools, context_menu=context_menu, render_day=render_day)}
|
|
||||||
|
${self.timesheet_wrapper(with_edit_form=True)}
|
||||||
|
|
||||||
${edit_tools()}
|
${edit_tools()}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%namespace file="/shifts/lib.mako" import="timesheet" />
|
<%namespace file="/shifts/base.mako" import="timesheet" />
|
||||||
<%namespace file="/shifts/base.mako" import="render_day" />
|
<%namespace file="/shifts/schedule.mako" import="render_day" />
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
## TODO: this seems a little hacky..?
|
## TODO: this seems a little hacky..?
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
|
||||||
|
|
||||||
<%def name="context_menu()">
|
<%def name="context_menu()">
|
||||||
% if employee is not Undefined and request.has_perm('timesheet.edit'):
|
% if employee is not Undefined and request.has_perm('timesheet.edit'):
|
||||||
|
@ -11,4 +10,15 @@
|
||||||
% endif
|
% endif
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${timesheet_wrapper(context_menu=context_menu, render_day=self.render_day)}
|
<%def name="render_day(day)">
|
||||||
|
% for shift in day['worked_shifts']:
|
||||||
|
<p class="shift">${render_shift(shift)}</p>
|
||||||
|
% endfor
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_total(employee)">
|
||||||
|
${employee.worked_hours_display}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
${self.timesheet_wrapper()}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/shifts/base.mako" />
|
<%inherit file="/shifts/base.mako" />
|
||||||
<%namespace file="/shifts/lib.mako" import="timesheet_wrapper" />
|
|
||||||
|
|
||||||
<%def name="extra_javascript()">
|
<%def name="extra_javascript()">
|
||||||
${parent.extra_javascript()}
|
${parent.extra_javascript()}
|
||||||
|
@ -22,13 +21,17 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_day(day)">
|
<%def name="render_day(day)">
|
||||||
% for shift in day['shifts']:
|
% for shift in day['worked_shifts']:
|
||||||
<p class="shift" data-uuid="${shift.uuid}">
|
<p class="shift" data-uuid="${shift.uuid}">
|
||||||
${render_shift(shift)}
|
${render_shift(shift)}
|
||||||
</p>
|
</p>
|
||||||
% endfor
|
% endfor
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_employee_total(employee)">
|
||||||
|
${employee.worked_hours_display}
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="edit_form()">
|
<%def name="edit_form()">
|
||||||
${h.form(url('timesheet.employee.edit'), id='timetable-form')}
|
${h.form(url('timesheet.employee.edit'), id='timetable-form')}
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
|
@ -41,7 +44,8 @@
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
${timesheet_wrapper(edit_form=edit_form, edit_tools=edit_tools, context_menu=context_menu, render_day=render_day, change_employee='confirm_leave')}
|
|
||||||
|
${self.timesheet_wrapper(with_edit_form=True, change_employee='confirm_leave')}
|
||||||
|
|
||||||
${edit_tools()}
|
${edit_tools()}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ class TimeSheetView(View):
|
||||||
key = None
|
key = None
|
||||||
title = None
|
title = None
|
||||||
model_class = None
|
model_class = None
|
||||||
|
expose_employee_views = True
|
||||||
|
|
||||||
# Set this to False to avoid the default behavior of auto-filtering by
|
# Set this to False to avoid the default behavior of auto-filtering by
|
||||||
# current store.
|
# current store.
|
||||||
|
@ -72,6 +73,10 @@ class TimeSheetView(View):
|
||||||
def get_title(cls):
|
def get_title(cls):
|
||||||
return cls.title or cls.key.capitalize()
|
return cls.title or cls.key.capitalize()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_url_prefix(cls):
|
||||||
|
return getattr(cls, 'url_prefix', cls.key).rstrip('/')
|
||||||
|
|
||||||
def get_timesheet_context(self):
|
def get_timesheet_context(self):
|
||||||
"""
|
"""
|
||||||
Determine date/store/dept context from user's session and/or defaults.
|
Determine date/store/dept context from user's session and/or defaults.
|
||||||
|
@ -272,7 +277,7 @@ class TimeSheetView(View):
|
||||||
department_options = self.get_department_options(departments)
|
department_options = self.get_department_options(departments)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'page_title': "Full {}".format(self.get_title()),
|
'page_title': self.get_title_full(),
|
||||||
'form': forms.FormRenderer(form) if form else None,
|
'form': forms.FormRenderer(form) if form else None,
|
||||||
'employees': employees,
|
'employees': employees,
|
||||||
'stores': stores,
|
'stores': stores,
|
||||||
|
@ -292,6 +297,9 @@ class TimeSheetView(View):
|
||||||
context.update(kwargs)
|
context.update(kwargs)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_title_full(self):
|
||||||
|
return "Full {}".format(self.get_title())
|
||||||
|
|
||||||
def render_shift(self, shift):
|
def render_shift(self, shift):
|
||||||
return HTML.tag('span', c=shift.get_display(self.rattail_config))
|
return HTML.tag('span', c=shift.get_display(self.rattail_config))
|
||||||
|
|
||||||
|
@ -330,26 +338,35 @@ class TimeSheetView(View):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def modify_employees(self, employees, weekdays):
|
def modify_employees(self, employees, weekdays):
|
||||||
|
self.fetch_shift_data(self.model_class, employees, weekdays)
|
||||||
|
|
||||||
|
def fetch_shift_data(self, cls, employees, weekdays):
|
||||||
|
"""
|
||||||
|
Fetch all shift data of the given model class (``cls``), according to
|
||||||
|
the given params. The cached shift data is attached to each employee.
|
||||||
|
"""
|
||||||
|
shift_type = 'scheduled' if cls is model.ScheduledShift else 'worked'
|
||||||
min_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[0], datetime.time(0)))
|
min_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[0], datetime.time(0)))
|
||||||
max_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[-1] + datetime.timedelta(days=1), datetime.time(0)))
|
max_time = localtime(self.rattail_config, datetime.datetime.combine(weekdays[-1] + datetime.timedelta(days=1), datetime.time(0)))
|
||||||
shifts = Session.query(self.model_class)\
|
shifts = Session.query(cls)\
|
||||||
.filter(self.model_class.employee_uuid.in_([e.uuid for e in employees]))\
|
.filter(cls.employee_uuid.in_([e.uuid for e in employees]))\
|
||||||
.filter(self.model_class.start_time >= make_utc(min_time))\
|
.filter(cls.start_time >= make_utc(min_time))\
|
||||||
.filter(self.model_class.start_time < make_utc(max_time))\
|
.filter(cls.start_time < make_utc(max_time))\
|
||||||
.all()
|
.all()
|
||||||
|
|
||||||
for employee in employees:
|
for employee in employees:
|
||||||
employee_shifts = sorted([s for s in shifts if s.employee_uuid == employee.uuid],
|
employee_shifts = sorted([s for s in shifts if s.employee_uuid == employee.uuid],
|
||||||
key=lambda s: (s.start_time, s.end_time))
|
key=lambda s: (s.start_time, s.end_time))
|
||||||
employee.weekdays = []
|
if not hasattr(employee, 'weekdays'):
|
||||||
employee.hours = datetime.timedelta(0)
|
employee.weekdays = [{} for day in weekdays]
|
||||||
employee.hours_display = '0'
|
setattr(employee, '{}_hours'.format(shift_type), datetime.timedelta(0))
|
||||||
|
setattr(employee, '{}_hours_display'.format(shift_type), '0')
|
||||||
|
|
||||||
for day in weekdays:
|
for i, day in enumerate(weekdays):
|
||||||
empday = {
|
empday = {
|
||||||
'shifts': [],
|
'{}_shifts'.format(shift_type): [],
|
||||||
'hours': datetime.timedelta(0),
|
'{}_hours'.format(shift_type): datetime.timedelta(0),
|
||||||
'hours_display': '',
|
'{}_hours_display'.format(shift_type): '',
|
||||||
}
|
}
|
||||||
|
|
||||||
while employee_shifts:
|
while employee_shifts:
|
||||||
|
@ -357,23 +374,27 @@ class TimeSheetView(View):
|
||||||
if shift.employee_uuid != employee.uuid:
|
if shift.employee_uuid != employee.uuid:
|
||||||
break
|
break
|
||||||
elif shift.get_date(self.rattail_config) == day:
|
elif shift.get_date(self.rattail_config) == day:
|
||||||
empday['shifts'].append(shift)
|
empday['{}_shifts'.format(shift_type)].append(shift)
|
||||||
length = shift.length
|
length = shift.length
|
||||||
if length is not None:
|
if length is not None:
|
||||||
empday['hours'] += shift.length
|
empday['{}_hours'.format(shift_type)] += shift.length
|
||||||
employee.hours += shift.length
|
setattr(employee, '{}_hours'.format(shift_type),
|
||||||
|
getattr(employee, '{}_hours'.format(shift_type)) + shift.length)
|
||||||
del employee_shifts[0]
|
del employee_shifts[0]
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
if empday['hours']:
|
hours = empday['{}_hours'.format(shift_type)]
|
||||||
minutes = (empday['hours'].days * 1440) + (empday['hours'].seconds / 60)
|
if hours:
|
||||||
empday['hours_display'] = '{}:{:02d}'.format(minutes // 60, minutes % 60)
|
minutes = (hours.days * 1440) + (hours.seconds / 60)
|
||||||
employee.weekdays.append(empday)
|
empday['{}_hours_display'.format(shift_type)] = '{}:{:02d}'.format(minutes // 60, minutes % 60)
|
||||||
|
employee.weekdays[i].update(empday)
|
||||||
|
|
||||||
if employee.hours:
|
hours = getattr(employee, '{}_hours'.format(shift_type))
|
||||||
minutes = (employee.hours.days * 1440) + (employee.hours.seconds / 60)
|
if hours:
|
||||||
employee.hours_display = '{}:{:02d}'.format(minutes // 60, minutes % 60)
|
minutes = (hours.days * 1440) + (hours.seconds / 60)
|
||||||
|
setattr(employee, '{}_hours_display'.format(shift_type),
|
||||||
|
'{}:{:02d}'.format(minutes // 60, minutes % 60))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
|
@ -388,24 +409,26 @@ class TimeSheetView(View):
|
||||||
Provide default configuration for a time sheet view.
|
Provide default configuration for a time sheet view.
|
||||||
"""
|
"""
|
||||||
title = cls.get_title()
|
title = cls.get_title()
|
||||||
|
url_prefix = cls.get_url_prefix()
|
||||||
config.add_tailbone_permission_group(cls.key, title)
|
config.add_tailbone_permission_group(cls.key, 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))
|
config.add_tailbone_permission(cls.key, '{}.viewall'.format(cls.key), "View full {}".format(title))
|
||||||
|
|
||||||
# full time sheet
|
# full time sheet
|
||||||
config.add_route(cls.key, '/{}/'.format(cls.key))
|
config.add_route(cls.key, '{}/'.format(url_prefix))
|
||||||
config.add_view(cls, attr='full', route_name=cls.key,
|
config.add_view(cls, attr='full', route_name=cls.key,
|
||||||
renderer='/shifts/{}.mako'.format(cls.key),
|
renderer='/shifts/{}.mako'.format(cls.key),
|
||||||
permission='{}.viewall'.format(cls.key))
|
permission='{}.viewall'.format(cls.key))
|
||||||
|
|
||||||
# single employee time sheet
|
# single employee time sheet
|
||||||
config.add_route('{}.employee'.format(cls.key), '/{}/employee/'.format(cls.key))
|
if cls.expose_employee_views:
|
||||||
config.add_view(cls, attr='employee', route_name='{}.employee'.format(cls.key),
|
config.add_tailbone_permission(cls.key, '{}.view'.format(cls.key), "View single employee {}".format(title))
|
||||||
renderer='/shifts/{}.mako'.format(cls.key),
|
config.add_route('{}.employee'.format(cls.key), '{}/employee/'.format(url_prefix))
|
||||||
permission='{}.view'.format(cls.key))
|
config.add_view(cls, attr='employee', route_name='{}.employee'.format(cls.key),
|
||||||
|
renderer='/shifts/{}.mako'.format(cls.key),
|
||||||
|
permission='{}.view'.format(cls.key))
|
||||||
|
|
||||||
# goto cross-view (view 'timesheet' as 'schedule' or vice-versa)
|
# goto cross-view (view 'timesheet' as 'schedule' or vice-versa)
|
||||||
other_key = 'timesheet' if cls.key == 'schedule' else 'schedule'
|
other_key = 'timesheet' if cls.key == 'schedule' else 'schedule'
|
||||||
config.add_route('{}.goto.{}'.format(cls.key, other_key), '/{}/goto-{}'.format(cls.key, other_key))
|
config.add_route('{}.goto.{}'.format(cls.key, other_key), '{}/goto-{}'.format(url_prefix, other_key))
|
||||||
config.add_view(cls, attr='crossview', route_name='{}.goto.{}'.format(cls.key, other_key),
|
config.add_view(cls, attr='crossview', route_name='{}.goto.{}'.format(cls.key, other_key),
|
||||||
permission='{}.view'.format(other_key))
|
permission='{}.view'.format(other_key))
|
||||||
|
|
Loading…
Reference in a new issue