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
					
				
					 10 changed files with 225 additions and 100 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar