Add support for "full" schedule and time sheet views
Temporarily removes support for viewing current user's time sheet; that will be added back in soon.
This commit is contained in:
		
							parent
							
								
									181123dfaa
								
							
						
					
					
						commit
						123f5ce0c6
					
				
					 11 changed files with 327 additions and 165 deletions
				
			
		| 
						 | 
					@ -48,7 +48,18 @@ class ModelValidator(fe.validators.FancyValidator):
 | 
				
			||||||
            obj = Session.query(self.model_class).get(value)
 | 
					            obj = Session.query(self.model_class).get(value)
 | 
				
			||||||
            if obj:
 | 
					            if obj:
 | 
				
			||||||
                return obj
 | 
					                return obj
 | 
				
			||||||
            raise formencode.Invalid("{} not found".format(self.model_name), value, state)
 | 
					            raise fe.Invalid("{} not found".format(self.model_name), value, state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _from_python(self, value, state):
 | 
				
			||||||
 | 
					        obj = value
 | 
				
			||||||
 | 
					        if not obj:
 | 
				
			||||||
 | 
					            return ''
 | 
				
			||||||
 | 
					        return obj.uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def validate_python(self, value, state):
 | 
				
			||||||
 | 
					        obj = value
 | 
				
			||||||
 | 
					        if obj is not None and not isinstance(obj, self.model_class):
 | 
				
			||||||
 | 
					            raise fe.Invalid("Value must be a valid {} object".format(self.model_name), value, state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ValidStore(ModelValidator):
 | 
					class ValidStore(ModelValidator):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,24 +3,50 @@
 | 
				
			||||||
 * styles for time sheets / schedules
 | 
					 * styles for time sheets / schedules
 | 
				
			||||||
 **********************************************************************/
 | 
					 **********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/******************************
 | 
				
			||||||
 | 
					 * header table
 | 
				
			||||||
 | 
					 ******************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.timesheet-header {
 | 
					.timesheet-header {
 | 
				
			||||||
    overflow: auto;
 | 
					    width: 100%;
 | 
				
			||||||
    position: relative;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.timesheet-header .week-picker {
 | 
					.timesheet-header td.filters {
 | 
				
			||||||
    bottom: 0.5em;
 | 
					    vertical-align: bottom;
 | 
				
			||||||
    position: absolute;
 | 
					    width: 100%;
 | 
				
			||||||
    right: 0;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.timesheet-header td.filters .field {
 | 
				
			||||||
 | 
					    width: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.timesheet-header td.menu {
 | 
				
			||||||
 | 
					    padding: 0.5em;
 | 
				
			||||||
 | 
					    vertical-align: top;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.timesheet-header td.tools {
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					    padding: 0;
 | 
				
			||||||
 | 
					    text-align: right;
 | 
				
			||||||
 | 
					    vertical-align: bottom;
 | 
				
			||||||
 | 
					    white-space: nowrap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.timesheet-header .week-picker label {
 | 
					.timesheet-header .week-picker label {
 | 
				
			||||||
    margin-left: 1em;
 | 
					    margin-left: 1em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/******************************
 | 
				
			||||||
 | 
					 * timesheet table
 | 
				
			||||||
 | 
					 ******************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.timesheet {
 | 
					.timesheet {
 | 
				
			||||||
    border-bottom: 1px solid black;
 | 
					    border-bottom: 1px solid black;
 | 
				
			||||||
    border-right: 1px solid black;
 | 
					    border-right: 1px solid black;
 | 
				
			||||||
 | 
					    clear: both;
 | 
				
			||||||
 | 
					    margin-top: 0.3em;
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,124 +8,148 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      $(function() {
 | 
					      $(function() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          $('.timesheet-header select').selectmenu();
 | 
					          $('.timesheet-wrapper form').submit(function() {
 | 
				
			||||||
 | 
					              $('.timesheet-header').mask("Fetching data");
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          $('.timesheet-header select').selectmenu({
 | 
				
			||||||
 | 
					              change: function(event, ui) {
 | 
				
			||||||
 | 
					                  $(ui.item.element).parents('form').submit();
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          $('.timesheet-header a.goto').click(function() {
 | 
				
			||||||
 | 
					              $('.timesheet-header').mask("Fetching data");
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          $('.week-picker button.nav').click(function() {
 | 
				
			||||||
 | 
					              $('.week-picker #date').val($(this).data('date'));
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          $('.week-picker #date').datepicker({
 | 
					          $('.week-picker #date').datepicker({
 | 
				
			||||||
              dateFormat: 'yy-mm-dd',
 | 
					              dateFormat: 'mm/dd/yy',
 | 
				
			||||||
              changeYear: true,
 | 
					              changeYear: true,
 | 
				
			||||||
              changeMonth: true,
 | 
					              changeMonth: true,
 | 
				
			||||||
              showButtonPanel: true,
 | 
					              showButtonPanel: true,
 | 
				
			||||||
              onSelect: function(dateText, inst) {
 | 
					              onSelect: function(dateText, inst) {
 | 
				
			||||||
                  $(this).focus().select();
 | 
					                  $(this).parents('form').submit();
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          $('.week-picker form').submit(function() {
 | 
					 | 
				
			||||||
              location.href = '?date=' + $('.week-picker #date').val();
 | 
					 | 
				
			||||||
              return false;
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
</%def>
 | 
					</%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<%def name="timesheet(employees, employee_column=True)">
 | 
					<%def name="context_menu()"></%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<%def name="timesheet(employee_column=True)">
 | 
				
			||||||
    <style type="text/css">
 | 
					    <style type="text/css">
 | 
				
			||||||
      .timesheet thead th {
 | 
					      .timesheet thead th {
 | 
				
			||||||
          width: ${'{:0.2f}'.format(100.0 / float(9 if employee_column else 8))}%;
 | 
					          width: ${'{:0.2f}'.format(100.0 / float(9 if employee_column else 8))}%;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
    <div class="timesheet-header">
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
##       <div class="field-wrapper employee">
 | 
					    <div class="timesheet-wrapper">
 | 
				
			||||||
##         <label>Employee</label>
 | 
					 | 
				
			||||||
##         <div class="field">
 | 
					 | 
				
			||||||
##           ${employee}
 | 
					 | 
				
			||||||
##         </div>
 | 
					 | 
				
			||||||
##       </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="fieldset">
 | 
					      ${form.begin()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="field-wrapper week">
 | 
					      <table class="timesheet-header">
 | 
				
			||||||
          <label>Store</label>
 | 
					        <tbody>
 | 
				
			||||||
          <div class="field">
 | 
					          <tr>
 | 
				
			||||||
            ${form.select('store', store_options, selected_value=store.uuid if store else None)}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="field-wrapper week">
 | 
					            <td class="filters" rowspan="2">
 | 
				
			||||||
          <label>Department</label>
 | 
					 | 
				
			||||||
          <div class="field">
 | 
					 | 
				
			||||||
            ${form.select('department', department_options, selected_value=department.uuid if department else None)}
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div class="field-wrapper week">
 | 
					  ##                 <div class="field-wrapper employee">
 | 
				
			||||||
          <label>Week of</label>
 | 
					  ##                   <label>Employee</label>
 | 
				
			||||||
          <div class="field">
 | 
					  ##                   <div class="field">
 | 
				
			||||||
            ${week_of}
 | 
					  ##                     ${employee}
 | 
				
			||||||
          </div>
 | 
					  ##                   </div>
 | 
				
			||||||
        </div>
 | 
					  ##                 </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      </div>
 | 
					              ${form.field_div('store', h.select('store', store.uuid if store else None, store_options))}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div class="week-picker">
 | 
					              ${form.field_div('department', h.select('department', department.uuid if department else None,  department_options))}
 | 
				
			||||||
        ${h.form(request.current_route_url())}
 | 
					 | 
				
			||||||
        ${h.link_to(u"« Previous", '?date=' + prev_sunday.strftime('%Y-%m-%d'), class_='button')}
 | 
					 | 
				
			||||||
        ${h.link_to(u"Next »", '?date=' + next_sunday.strftime('%Y-%m-%d'), class_='button')}
 | 
					 | 
				
			||||||
        <label>Jump to week:</label>
 | 
					 | 
				
			||||||
        ${h.text('date', value=sunday.strftime('%Y-%m-%d'))}
 | 
					 | 
				
			||||||
        ${h.submit('go', "Go")}
 | 
					 | 
				
			||||||
        ${h.end_form()}
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </div><!-- timesheet-header -->
 | 
					              <div class="field-wrapper week">
 | 
				
			||||||
 | 
					                <label>Week of</label>
 | 
				
			||||||
 | 
					                <div class="field">
 | 
				
			||||||
 | 
					                  ${week_of}
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <table class="timesheet">
 | 
					            </td><!-- filters -->
 | 
				
			||||||
      <thead>
 | 
					
 | 
				
			||||||
        <tr>
 | 
					            <td class="menu">
 | 
				
			||||||
          % if employee_column:
 | 
					              <ul id="context-menu">
 | 
				
			||||||
              <th>Employee</th>
 | 
					                ${self.context_menu()}
 | 
				
			||||||
          % endif
 | 
					              </ul>
 | 
				
			||||||
          % for day in weekdays:
 | 
					            </td><!-- menu -->
 | 
				
			||||||
              <th>${day.strftime('%A')}<br />${day.strftime('%b %d')}</th>
 | 
					          </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <tr>
 | 
				
			||||||
 | 
					            <td class="tools">
 | 
				
			||||||
 | 
					              <div class="grid-tools">
 | 
				
			||||||
 | 
					                <div class="week-picker">
 | 
				
			||||||
 | 
					                  <button class="nav" data-date="${prev_sunday.strftime('%m/%d/%Y')}">« Previous</button>
 | 
				
			||||||
 | 
					                  <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()}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <table class="timesheet">
 | 
				
			||||||
 | 
					        <thead>
 | 
				
			||||||
 | 
					          <tr>
 | 
				
			||||||
 | 
					            % if employee_column:
 | 
				
			||||||
 | 
					                <th>Employee</th>
 | 
				
			||||||
 | 
					            % endif
 | 
				
			||||||
 | 
					            % for day in weekdays:
 | 
				
			||||||
 | 
					                <th>${day.strftime('%A')}<br />${day.strftime('%b %d')}</th>
 | 
				
			||||||
 | 
					            % endfor
 | 
				
			||||||
 | 
					            <th>Total<br />Hours</th>
 | 
				
			||||||
 | 
					          </tr>
 | 
				
			||||||
 | 
					        </thead>
 | 
				
			||||||
 | 
					        <tbody>
 | 
				
			||||||
 | 
					          % for employee in sorted(employees, key=unicode):
 | 
				
			||||||
 | 
					              <tr>
 | 
				
			||||||
 | 
					                % if employee_column:
 | 
				
			||||||
 | 
					                    <td class="employee">${employee}</td>
 | 
				
			||||||
 | 
					                % endif
 | 
				
			||||||
 | 
					                % for day in employee.weekdays:
 | 
				
			||||||
 | 
					                    <td>
 | 
				
			||||||
 | 
					                      % for shift in day['shifts']:
 | 
				
			||||||
 | 
					                          <p class="shift">${shift.get_display(request.rattail_config)}</p>
 | 
				
			||||||
 | 
					                      % endfor
 | 
				
			||||||
 | 
					                    </td>
 | 
				
			||||||
 | 
					                % endfor
 | 
				
			||||||
 | 
					                <td>${employee.hours_display}</td>
 | 
				
			||||||
 | 
					              </tr>
 | 
				
			||||||
          % endfor
 | 
					          % endfor
 | 
				
			||||||
          <th>Total<br />Hours</th>
 | 
					          % if employee_column:
 | 
				
			||||||
        </tr>
 | 
					              <tr class="total">
 | 
				
			||||||
      </thead>
 | 
					                <td class="employee">${len(employees)} employees</td>
 | 
				
			||||||
      <tbody>
 | 
					                % for day in weekdays:
 | 
				
			||||||
        % for employee in sorted(employees, key=unicode):
 | 
					                    <td></td>
 | 
				
			||||||
            <tr>
 | 
					                % endfor
 | 
				
			||||||
              % if employee_column:
 | 
					                <td></td>
 | 
				
			||||||
                  <td class="employee">${employee}</td>
 | 
					              </tr>
 | 
				
			||||||
              % endif
 | 
					          % else:
 | 
				
			||||||
              % for day in employee.weekdays:
 | 
					              <tr>
 | 
				
			||||||
                  <td>
 | 
					                % for day in employee.weekdays:
 | 
				
			||||||
                    % for shift in day['shifts']:
 | 
					                    <td>${day['hours_display']}</td>
 | 
				
			||||||
                        <p class="shift">${shift.get_display(request.rattail_config)}</p>
 | 
					                % endfor
 | 
				
			||||||
                    % endfor
 | 
					                <td>${employee.hours_display}</td>
 | 
				
			||||||
                  </td>
 | 
					              </tr>
 | 
				
			||||||
              % endfor
 | 
					          % endif
 | 
				
			||||||
              <td>${employee.hours_display}</td>
 | 
					        </tbody>
 | 
				
			||||||
            </tr>
 | 
					      </table>
 | 
				
			||||||
        % endfor
 | 
					    </div><!-- timesheet-wrapper -->
 | 
				
			||||||
        % if employee_column:
 | 
					 | 
				
			||||||
            <tr class="total">
 | 
					 | 
				
			||||||
              <td class="employee">${len(employees)} employees</td>
 | 
					 | 
				
			||||||
              % for day in weekdays:
 | 
					 | 
				
			||||||
                  <td></td>
 | 
					 | 
				
			||||||
              % endfor
 | 
					 | 
				
			||||||
              <td></td>
 | 
					 | 
				
			||||||
            </tr>
 | 
					 | 
				
			||||||
        % else:
 | 
					 | 
				
			||||||
            <tr>
 | 
					 | 
				
			||||||
              % for day in employee.weekdays:
 | 
					 | 
				
			||||||
                  <td>${day['hours_display']}</td>
 | 
					 | 
				
			||||||
              % endfor
 | 
					 | 
				
			||||||
              <td>${employee.hours_display}</td>
 | 
					 | 
				
			||||||
            </tr>
 | 
					 | 
				
			||||||
        % endif
 | 
					 | 
				
			||||||
      </tbody>
 | 
					 | 
				
			||||||
    </table>
 | 
					 | 
				
			||||||
</%def>
 | 
					</%def>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,14 @@
 | 
				
			||||||
## -*- coding: utf-8 -*-
 | 
					## -*- coding: utf-8 -*-
 | 
				
			||||||
<%inherit file="/shifts/base.mako" />
 | 
					<%inherit file="/shifts/base.mako" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<%def name="title()">Schedule: ${sunday}</%def>
 | 
					<%def name="title()">Full Schedule</%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<ul id="context-menu">
 | 
					<%def name="context_menu()">
 | 
				
			||||||
  <li>${h.link_to("Print this Schedule", '#')}</li>
 | 
					    % if request.has_perm('timesheet.view'):
 | 
				
			||||||
  <li>${h.link_to("Edit this Schedule", '#')}</li>
 | 
					        <li>${h.link_to("View this Time Sheet", url('schedule.goto.timesheet'), class_='goto')}</li>
 | 
				
			||||||
</ul>
 | 
					    % endif
 | 
				
			||||||
 | 
					##     <li>${h.link_to("Print this Schedule", '#')}</li>
 | 
				
			||||||
 | 
					##     <li>${h.link_to("Edit this Schedule", '#')}</li>
 | 
				
			||||||
 | 
					</%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${self.timesheet(employees)}
 | 
					${self.timesheet()}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,12 @@
 | 
				
			||||||
## -*- coding: utf-8 -*-
 | 
					## -*- coding: utf-8 -*-
 | 
				
			||||||
<%inherit file="/shifts/base.mako" />
 | 
					<%inherit file="/shifts/base.mako" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<%def name="title()">Time Sheet: ${sunday}</%def>
 | 
					<%def name="title()">Full Time Sheet</%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
${self.timesheet(employees, employee_column=False)}
 | 
					<%def name="context_menu()">
 | 
				
			||||||
 | 
					    % if request.has_perm('schedule.view'):
 | 
				
			||||||
 | 
					        <li>${h.link_to("View this Schedule", url('timesheet.goto.schedule'), class_='goto')}</li>
 | 
				
			||||||
 | 
					    % endif
 | 
				
			||||||
 | 
					</%def>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					${self.timesheet()}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,8 @@ from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rattail.db import model
 | 
					from rattail.db import model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from pyramid import httpexceptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tailbone.db import Session
 | 
					from tailbone.db import Session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,6 +58,12 @@ class View(object):
 | 
				
			||||||
            if uuid:
 | 
					            if uuid:
 | 
				
			||||||
                return Session.query(model.User).get(uuid)
 | 
					                return Session.query(model.User).get(uuid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def redirect(self, url):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Convenience method to return a HTTP 302 response.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return httpexceptions.HTTPFound(location=url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def fake_error(request):
 | 
					def fake_error(request):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -396,12 +396,6 @@ class MasterView(View):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return kwargs
 | 
					        return kwargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def redirect(self, url):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Convenience method to return a HTTP 302 response.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        return httpexceptions.HTTPFound(location=url)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    ##############################
 | 
					    ##############################
 | 
				
			||||||
    # Grid Stuff
 | 
					    # Grid Stuff
 | 
				
			||||||
    ##############################
 | 
					    ##############################
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,11 +44,11 @@ class ShiftLengthField(formalchemy.Field):
 | 
				
			||||||
        super(ShiftLengthField, self).__init__(name, **kwargs)
 | 
					        super(ShiftLengthField, self).__init__(name, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def shift_length(self, shift):
 | 
					    def shift_length(self, shift):
 | 
				
			||||||
        if not shift.punch_in or not shift.punch_out:
 | 
					        if not shift.start_time or not shift.end_time:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        if shift.punch_out < shift.punch_in:
 | 
					        if shift.end_time < shift.start_time:
 | 
				
			||||||
            return "??"
 | 
					            return "??"
 | 
				
			||||||
        return humanize.naturaldelta(shift.punch_out - shift.punch_in)
 | 
					        return humanize.naturaldelta(shift.end_time - shift.start_time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ScheduledShiftsView(MasterView):
 | 
					class ScheduledShiftsView(MasterView):
 | 
				
			||||||
| 
						 | 
					@ -61,22 +61,26 @@ class ScheduledShiftsView(MasterView):
 | 
				
			||||||
    def configure_grid(self, g):
 | 
					    def configure_grid(self, g):
 | 
				
			||||||
        g.default_sortkey = 'start_time'
 | 
					        g.default_sortkey = 'start_time'
 | 
				
			||||||
        g.default_sortdir = 'desc'
 | 
					        g.default_sortdir = 'desc'
 | 
				
			||||||
 | 
					        g.append(ShiftLengthField('length'))
 | 
				
			||||||
        g.configure(
 | 
					        g.configure(
 | 
				
			||||||
            include=[
 | 
					            include=[
 | 
				
			||||||
                g.employee,
 | 
					                g.employee,
 | 
				
			||||||
                g.store,
 | 
					                g.store,
 | 
				
			||||||
                g.start_time,
 | 
					                g.start_time,
 | 
				
			||||||
                g.end_time,
 | 
					                g.end_time,
 | 
				
			||||||
 | 
					                g.length,
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            readonly=True)
 | 
					            readonly=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def configure_fieldset(self, fs):
 | 
					    def configure_fieldset(self, fs):
 | 
				
			||||||
 | 
					        fs.append(ShiftLengthField('length'))
 | 
				
			||||||
        fs.configure(
 | 
					        fs.configure(
 | 
				
			||||||
            include=[
 | 
					            include=[
 | 
				
			||||||
                fs.employee,
 | 
					                fs.employee,
 | 
				
			||||||
                fs.store,
 | 
					                fs.store,
 | 
				
			||||||
                fs.start_time,
 | 
					                fs.start_time,
 | 
				
			||||||
                fs.end_time,
 | 
					                fs.end_time,
 | 
				
			||||||
 | 
					                fs.length,
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,15 +92,18 @@ class WorkedShiftsView(MasterView):
 | 
				
			||||||
    url_prefix = '/shifts/worked'
 | 
					    url_prefix = '/shifts/worked'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def configure_grid(self, g):
 | 
					    def configure_grid(self, g):
 | 
				
			||||||
        g.default_sortkey = 'punch_in'
 | 
					        # TODO: these sorters should be automatic once we fix the schema
 | 
				
			||||||
 | 
					        g.sorters['start_time'] = g.make_sorter(model.WorkedShift.punch_in)
 | 
				
			||||||
 | 
					        g.sorters['end_time'] = g.make_sorter(model.WorkedShift.punch_out)
 | 
				
			||||||
 | 
					        g.default_sortkey = 'start_time'
 | 
				
			||||||
        g.default_sortdir = 'desc'
 | 
					        g.default_sortdir = 'desc'
 | 
				
			||||||
        g.append(ShiftLengthField('length'))
 | 
					        g.append(ShiftLengthField('length'))
 | 
				
			||||||
        g.configure(
 | 
					        g.configure(
 | 
				
			||||||
            include=[
 | 
					            include=[
 | 
				
			||||||
                g.employee,
 | 
					                g.employee,
 | 
				
			||||||
                g.store,
 | 
					                g.store,
 | 
				
			||||||
                g.punch_in,
 | 
					                g.start_time,
 | 
				
			||||||
                g.punch_out,
 | 
					                g.end_time,
 | 
				
			||||||
                g.length,
 | 
					                g.length,
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
            readonly=True)
 | 
					            readonly=True)
 | 
				
			||||||
| 
						 | 
					@ -107,8 +114,8 @@ class WorkedShiftsView(MasterView):
 | 
				
			||||||
            include=[
 | 
					            include=[
 | 
				
			||||||
                fs.employee,
 | 
					                fs.employee,
 | 
				
			||||||
                fs.store,
 | 
					                fs.store,
 | 
				
			||||||
                fs.punch_in,
 | 
					                fs.start_time,
 | 
				
			||||||
                fs.punch_out,
 | 
					                fs.end_time,
 | 
				
			||||||
                fs.length,
 | 
					                fs.length,
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,38 +45,66 @@ class ShiftFilter(fe.Schema):
 | 
				
			||||||
    filter_extra_fields = True
 | 
					    filter_extra_fields = True
 | 
				
			||||||
    store = forms.validators.ValidStore()
 | 
					    store = forms.validators.ValidStore()
 | 
				
			||||||
    department = forms.validators.ValidDepartment()
 | 
					    department = forms.validators.ValidDepartment()
 | 
				
			||||||
 | 
					    date = fe.validators.DateConverter()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TimeSheetView(View):
 | 
					class TimeSheetView(View):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Base view for time sheets.
 | 
					    Base view for time sheets.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    key = None
 | 
				
			||||||
 | 
					    title = None
 | 
				
			||||||
    model_class = None
 | 
					    model_class = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # 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.
 | 
				
			||||||
    default_filter_store = True
 | 
					    default_filter_store = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __call__(self):
 | 
					    @classmethod
 | 
				
			||||||
        date = self.get_date()
 | 
					    def get_title(cls):
 | 
				
			||||||
 | 
					        return cls.title or cls.key.capitalize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def full(self):
 | 
				
			||||||
 | 
					        date = None
 | 
				
			||||||
        store = None
 | 
					        store = None
 | 
				
			||||||
        department = None
 | 
					        department = None
 | 
				
			||||||
        employees = Session.query(model.Employee)\
 | 
					        employees = Session.query(model.Employee)\
 | 
				
			||||||
                           .filter(model.Employee.status == enum.EMPLOYEE_STATUS_CURRENT)
 | 
					                           .filter(model.Employee.status == enum.EMPLOYEE_STATUS_CURRENT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        form = Form(self.request, schema=ShiftFilter)
 | 
					        form = Form(self.request, schema=ShiftFilter)
 | 
				
			||||||
        if form.validate():
 | 
					        if self.request.method == 'POST':
 | 
				
			||||||
            store = form.data['store']
 | 
					            if form.validate():
 | 
				
			||||||
            department = form.data['department']
 | 
					                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
 | 
				
			||||||
 | 
					                return self.redirect(self.request.current_route_url())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif self.request.method != 'POST' and self.default_filter_store:
 | 
					        else:
 | 
				
			||||||
            store = self.rattail_config.get('rattail', 'store')
 | 
					            store_key = 'timesheet.{}.store'.format(self.key)
 | 
				
			||||||
            if store:
 | 
					            department_key = 'timesheet.{}.department'.format(self.key)
 | 
				
			||||||
                store = api.get_store(Session(), store)
 | 
					            date_key = 'timesheet.{}.date'.format(self.key)
 | 
				
			||||||
 | 
					            if store_key in self.request.session or department_key in self.request.session or date_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)
 | 
				
			||||||
 | 
					                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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # TODO:
 | 
					            else: # nothing stored in session
 | 
				
			||||||
        # store = Session.query(model.Store).filter_by(id='003').one()
 | 
					                if self.default_filter_store:
 | 
				
			||||||
        # department = Session.query(model.Department).filter_by(number=6).one()
 | 
					                    store = self.rattail_config.get('rattail', 'store')
 | 
				
			||||||
 | 
					                    if store:
 | 
				
			||||||
 | 
					                        store = api.get_store(Session(), store)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if store:
 | 
					        if store:
 | 
				
			||||||
            employees = employees.join(model.EmployeeStore)\
 | 
					            employees = employees.join(model.EmployeeStore)\
 | 
				
			||||||
| 
						 | 
					@ -86,25 +114,49 @@ class TimeSheetView(View):
 | 
				
			||||||
            employees = employees.join(model.EmployeeDepartment)\
 | 
					            employees = employees.join(model.EmployeeDepartment)\
 | 
				
			||||||
                                 .filter(model.EmployeeDepartment.department == department)
 | 
					                                 .filter(model.EmployeeDepartment.department == department)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.render(date, employees.all(), store=store, department=department, form=form)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_date(self):
 | 
					 | 
				
			||||||
        date = None
 | 
					 | 
				
			||||||
        if 'date' in self.request.params:
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                date = datetime.datetime.strptime(self.request.params['date'], '%Y-%m-%d').date()
 | 
					 | 
				
			||||||
            except ValueError:
 | 
					 | 
				
			||||||
                self.request.session.flash("The specified date is not valid: {}".format(self.request.params['date']), 'error')
 | 
					 | 
				
			||||||
        if not date:
 | 
					        if not date:
 | 
				
			||||||
            date = localtime(self.rattail_config).date()
 | 
					            date = localtime(self.rattail_config).date()
 | 
				
			||||||
        return date
 | 
					
 | 
				
			||||||
 | 
					        return self.render(date, employees.all(), store=store, department=department, form=form)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def crossview(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Update session storage to so 'other' view reflects current view
 | 
				
			||||||
 | 
					        filters, then redirect to other view.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        other_key = 'timesheet' if self.key == 'schedule' else 'schedule'
 | 
				
			||||||
 | 
					        self.session_put('store', self.session_get('store'), mainkey=other_key)
 | 
				
			||||||
 | 
					        self.session_put('department', self.session_get('department'), mainkey=other_key)
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					        return self.request.session.get('timesheet.{}.{}'.format(mainkey, key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def session_put(self, key, value, mainkey=None):
 | 
				
			||||||
 | 
					        if mainkey is None:
 | 
				
			||||||
 | 
					            mainkey = self.key
 | 
				
			||||||
 | 
					        self.request.session['timesheet.{}.{}'.format(mainkey, key)] = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_stores(self):
 | 
					    def get_stores(self):
 | 
				
			||||||
        return Session.query(model.Store).order_by(model.Store.id).all()
 | 
					        return Session.query(model.Store).order_by(model.Store.id).all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_store_options(self, stores):
 | 
					    def get_store_options(self, stores):
 | 
				
			||||||
        options = [(s.uuid, "{} - {}".format(s.id, s.name)) for s in stores]
 | 
					        options = [(s.uuid, "{} - {}".format(s.id, s.name)) for s in stores]
 | 
				
			||||||
        options.insert(0, (None, "(all)"))
 | 
					        options.insert(0, ('', "(all)"))
 | 
				
			||||||
        return options
 | 
					        return options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_departments(self):
 | 
					    def get_departments(self):
 | 
				
			||||||
| 
						 | 
					@ -112,7 +164,7 @@ class TimeSheetView(View):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_department_options(self, departments):
 | 
					    def get_department_options(self, departments):
 | 
				
			||||||
        options = [(d.uuid, d.name) for d in departments]
 | 
					        options = [(d.uuid, d.name) for d in departments]
 | 
				
			||||||
        options.insert(0, (None, "(all)"))
 | 
					        options.insert(0, ('', "(all)"))
 | 
				
			||||||
        return options
 | 
					        return options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def render(self, date, employees, store=None, department=None, form=None):
 | 
					    def render(self, date, employees, store=None, department=None, form=None):
 | 
				
			||||||
| 
						 | 
					@ -184,8 +236,10 @@ class TimeSheetView(View):
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
                    elif shift.get_date(self.rattail_config) == day:
 | 
					                    elif shift.get_date(self.rattail_config) == day:
 | 
				
			||||||
                        empday['shifts'].append(shift)
 | 
					                        empday['shifts'].append(shift)
 | 
				
			||||||
                        empday['hours'] += shift.length
 | 
					                        length = shift.length
 | 
				
			||||||
                        employee.hours += shift.length
 | 
					                        if length is not None:
 | 
				
			||||||
 | 
					                            empday['hours'] += shift.length
 | 
				
			||||||
 | 
					                            employee.hours += shift.length
 | 
				
			||||||
                        del employee_shifts[0]
 | 
					                        del employee_shifts[0]
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        break
 | 
					                        break
 | 
				
			||||||
| 
						 | 
					@ -198,3 +252,38 @@ class TimeSheetView(View):
 | 
				
			||||||
            if employee.hours:
 | 
					            if employee.hours:
 | 
				
			||||||
                minutes = (employee.hours.days * 1440) + (employee.hours.seconds / 60)
 | 
					                minutes = (employee.hours.days * 1440) + (employee.hours.seconds / 60)
 | 
				
			||||||
                employee.hours_display = '{}:{:02d}'.format(minutes // 60, minutes % 60)
 | 
					                employee.hours_display = '{}:{:02d}'.format(minutes // 60, minutes % 60)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def defaults(cls, config):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Provide default configuration for a time sheet view.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        cls._defaults(config)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def _defaults(cls, config):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Provide default configuration for a time sheet view.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        title = cls.get_title()
 | 
				
			||||||
 | 
					        config.add_tailbone_permission_group(cls.key, title)
 | 
				
			||||||
 | 
					        # config.add_tailbone_permission(cls.key, '{}.view'.format(cls.key), "View personal {}".format(title))
 | 
				
			||||||
 | 
					        config.add_tailbone_permission(cls.key, '{}.viewall'.format(cls.key), "View full {}".format(title))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # full time sheet
 | 
				
			||||||
 | 
					        config.add_route(cls.key, '/{}/'.format(cls.key))
 | 
				
			||||||
 | 
					        config.add_view(cls, attr='full', route_name=cls.key,
 | 
				
			||||||
 | 
					                        renderer='/shifts/{}.mako'.format(cls.key),
 | 
				
			||||||
 | 
					                        permission='{}.viewall'.format(cls.key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # # single employee time sheet
 | 
				
			||||||
 | 
					        # config.add_route('{}.employee'.format(cls.key), '/{}/employee/'.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)
 | 
				
			||||||
 | 
					        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_view(cls, attr='crossview', route_name='{}.goto.{}'.format(cls.key, other_key),
 | 
				
			||||||
 | 
					                        permission='{}.view'.format(other_key))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,10 +26,8 @@ Views for employee schedules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from __future__ import unicode_literals, absolute_import
 | 
					from __future__ import unicode_literals, absolute_import
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rattail import enum
 | 
					 | 
				
			||||||
from rattail.db import model
 | 
					from rattail.db import model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tailbone.db import Session
 | 
					 | 
				
			||||||
from tailbone.views.shifts.lib import TimeSheetView
 | 
					from tailbone.views.shifts.lib import TimeSheetView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,14 +35,9 @@ class ScheduleView(TimeSheetView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Simple view for current user's schedule.
 | 
					    Simple view for current user's schedule.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    key = 'schedule'
 | 
				
			||||||
    model_class = model.ScheduledShift
 | 
					    model_class = model.ScheduledShift
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def includeme(config):
 | 
					def includeme(config):
 | 
				
			||||||
 | 
					    ScheduleView.defaults(config)
 | 
				
			||||||
    config.add_tailbone_permission('schedule', 'schedule.view', "View Schedule")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    # current user's schedule
 | 
					 | 
				
			||||||
    config.add_route('schedule', '/schedule/')
 | 
					 | 
				
			||||||
    config.add_view(ScheduleView, route_name='schedule',
 | 
					 | 
				
			||||||
                    renderer='/shifts/schedule.mako', permission='schedule.view')
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,20 +35,21 @@ class TimeSheetView(TimeSheetView):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Simple view for current user's time sheet.
 | 
					    Simple view for current user's time sheet.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    key = 'timesheet'
 | 
				
			||||||
 | 
					    title = "Time Sheet"
 | 
				
			||||||
    model_class = model.WorkedShift
 | 
					    model_class = model.WorkedShift
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __call__(self):
 | 
					    # def __call__(self):
 | 
				
			||||||
        date = self.get_date()
 | 
					    #     date = self.get_date()
 | 
				
			||||||
        employee = self.request.user.employee
 | 
					    #     employee = self.request.user.employee
 | 
				
			||||||
        assert employee
 | 
					    #     assert employee
 | 
				
			||||||
        return self.render(date, [employee])
 | 
					    #     return self.render(date, [employee])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def includeme(config):
 | 
					def includeme(config):
 | 
				
			||||||
 | 
					    TimeSheetView.defaults(config)
 | 
				
			||||||
    config.add_tailbone_permission('timesheet', 'timesheet.view', "View Time Sheet")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # current user's time sheet
 | 
					    # current user's time sheet
 | 
				
			||||||
    config.add_route('timesheet', '/timesheet/')
 | 
					    # config.add_route('timesheet', '/timesheet/')
 | 
				
			||||||
    config.add_view(TimeSheetView, route_name='timesheet',
 | 
					    # config.add_view(TimeSheetView, route_name='timesheet',
 | 
				
			||||||
                    renderer='/shifts/timesheet.mako', permission='timesheet.view')
 | 
					    #                 renderer='/shifts/timesheet.mako', permission='timesheet.view')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue