Various tweaks for support of native inventory
certaianly some other things made it in here too..
This commit is contained in:
		
							parent
							
								
									eb68eec520
								
							
						
					
					
						commit
						61d504afb8
					
				
					 11 changed files with 129 additions and 16 deletions
				
			
		|  | @ -29,6 +29,7 @@ from __future__ import unicode_literals, absolute_import | ||||||
| import datetime | import datetime | ||||||
| from decimal import Decimal | from decimal import Decimal | ||||||
| 
 | 
 | ||||||
|  | from rattail.time import localtime, make_utc | ||||||
| from rattail.util import pretty_quantity | from rattail.util import pretty_quantity | ||||||
| 
 | 
 | ||||||
| from webhelpers2.html import * | from webhelpers2.html import * | ||||||
|  |  | ||||||
|  | @ -10,6 +10,12 @@ | ||||||
|     var has_execution_options = ${'true' if master.has_execution_options else 'false'}; |     var has_execution_options = ${'true' if master.has_execution_options else 'false'}; | ||||||
| 
 | 
 | ||||||
|     $(function() { |     $(function() { | ||||||
|  |         % if master.has_worksheet: | ||||||
|  |             $('.load-worksheet').click(function() { | ||||||
|  |                 $(this).button('disable').button('option', 'label', "Working, please wait..."); | ||||||
|  |                 location.href = '${url('{}.worksheet'.format(route_prefix), uuid=batch.uuid)}'; | ||||||
|  |             }); | ||||||
|  |         % endif | ||||||
|         $('#refresh-data').click(function() { |         $('#refresh-data').click(function() { | ||||||
|             $(this) |             $(this) | ||||||
|                 .button('option', 'disabled', true) |                 .button('option', 'disabled', true) | ||||||
|  | @ -50,7 +56,11 @@ | ||||||
|     </div> |     </div> | ||||||
| </%def> | </%def> | ||||||
| 
 | 
 | ||||||
| <%def name="leading_buttons()"></%def> | <%def name="leading_buttons()"> | ||||||
|  |   % if master.has_worksheet and not batch.executed and request.has_perm('{}.worksheet'.format(permission_prefix)): | ||||||
|  |       <button type="button" class="load-worksheet">Edit as Worksheet</button> | ||||||
|  |   % endif | ||||||
|  | </%def> | ||||||
| 
 | 
 | ||||||
| <%def name="refresh_button()"> | <%def name="refresh_button()"> | ||||||
|   % if master.viewing and master.batch_refreshable(batch) and request.has_perm('{}.refresh'.format(permission_prefix)): |   % if master.viewing and master.batch_refreshable(batch) and request.has_perm('{}.refresh'.format(permission_prefix)): | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								tailbone/templates/batch/worksheet.mako
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tailbone/templates/batch/worksheet.mako
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | ## -*- coding: utf-8; -*- | ||||||
|  | <%inherit file="/base.mako" /> | ||||||
|  | 
 | ||||||
|  | <%def name="extra_javascript()"> | ||||||
|  |   ${parent.extra_javascript()} | ||||||
|  |   <script type="text/javascript"> | ||||||
|  | 
 | ||||||
|  |     $(function() { | ||||||
|  | 
 | ||||||
|  |         $('.worksheet .current-entry input').focus(function(event) { | ||||||
|  |             $(this).parents('tr:first').addClass('active'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         $('.worksheet .current-entry input').blur(function(event) { | ||||||
|  |             $(this).parents('tr:first').removeClass('active'); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |     }); | ||||||
|  |   </script> | ||||||
|  | </%def> | ||||||
|  | 
 | ||||||
|  | <%def name="extra_styles()"> | ||||||
|  |   ${parent.extra_styles()} | ||||||
|  |   <style type="text/css"> | ||||||
|  | 
 | ||||||
|  |     .worksheet tr.active { | ||||||
|  |         border: 5px solid Blue; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .worksheet .current-entry { | ||||||
|  |         text-align: center; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .worksheet .current-entry input { | ||||||
|  |         text-align: center; | ||||||
|  |         width: 3em; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   </style> | ||||||
|  | </%def> | ||||||
|  | 
 | ||||||
|  | <%def name="worksheet_grid()"></%def> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ${self.worksheet_grid()} | ||||||
|  | @ -8,7 +8,7 @@ | ||||||
| ## ############################################################################## | ## ############################################################################## | ||||||
| <%inherit file="/base.mako" /> | <%inherit file="/base.mako" /> | ||||||
| 
 | 
 | ||||||
| <%def name="title()">${model_title_plural}</%def> | <%def name="title()">${index_title}</%def> | ||||||
| 
 | 
 | ||||||
| <%def name="extra_javascript()"> | <%def name="extra_javascript()"> | ||||||
|   ${parent.extra_javascript()} |   ${parent.extra_javascript()} | ||||||
|  |  | ||||||
|  | @ -3,6 +3,10 @@ | ||||||
| 
 | 
 | ||||||
| <%def name="title()">${model_title}</%def> | <%def name="title()">${model_title}</%def> | ||||||
| 
 | 
 | ||||||
|  | <%def name="content_title()"> | ||||||
|  |   <h1>Row ${instance.sequence}</h1> | ||||||
|  | </%def> | ||||||
|  | 
 | ||||||
| <%def name="context_menu_items()"> | <%def name="context_menu_items()"> | ||||||
|   <li>${h.link_to("Back to {}".format(parent_model_title), index_url)}</li> |   <li>${h.link_to("Back to {}".format(parent_model_title), index_url)}</li> | ||||||
|   % if master.rows_editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)): |   % if master.rows_editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)): | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ | ||||||
|           % if master: |           % if master: | ||||||
|               <span class="global">»</span> |               <span class="global">»</span> | ||||||
|               % if master.listing: |               % if master.listing: | ||||||
|                   <span class="global">${model_title_plural}</span> |                   <span class="global">${index_title}</span> | ||||||
|               % else: |               % else: | ||||||
|                   ${h.link_to(index_title, index_url, class_='global')} |                   ${h.link_to(index_title, index_url, class_='global')} | ||||||
|                   % if instance_url is not Undefined: |                   % if instance_url is not Undefined: | ||||||
|  |  | ||||||
|  | @ -72,6 +72,7 @@ class BatchMasterView(MasterView): | ||||||
|     supports_mobile = True |     supports_mobile = True | ||||||
|     mobile_filterable = True |     mobile_filterable = True | ||||||
|     mobile_rows_viewable = True |     mobile_rows_viewable = True | ||||||
|  |     has_worksheet = False | ||||||
| 
 | 
 | ||||||
|     def __init__(self, request): |     def __init__(self, request): | ||||||
|         super(BatchMasterView, self).__init__(request) |         super(BatchMasterView, self).__init__(request) | ||||||
|  | @ -422,6 +423,9 @@ class BatchMasterView(MasterView): | ||||||
|     def editable_instance(self, batch): |     def editable_instance(self, batch): | ||||||
|         return not bool(batch.executed) |         return not bool(batch.executed) | ||||||
| 
 | 
 | ||||||
|  |     def after_edit_row(self, row): | ||||||
|  |         self.handler.refresh_row(row) | ||||||
|  | 
 | ||||||
|     def executable(self, batch=None): |     def executable(self, batch=None): | ||||||
|         return self.handler.executable(batch) |         return self.handler.executable(batch) | ||||||
| 
 | 
 | ||||||
|  | @ -954,6 +958,17 @@ class BatchMasterView(MasterView): | ||||||
|         config.add_view(cls, attr='prefill', route_name='{}.prefill'.format(route_prefix), |         config.add_view(cls, attr='prefill', route_name='{}.prefill'.format(route_prefix), | ||||||
|                         permission='{}.create'.format(permission_prefix)) |                         permission='{}.create'.format(permission_prefix)) | ||||||
| 
 | 
 | ||||||
|  |         # worksheet | ||||||
|  |         if cls.has_worksheet: | ||||||
|  |             config.add_tailbone_permission(permission_prefix, '{}.worksheet'.format(permission_prefix), | ||||||
|  |                                            "Edit {} data as worksheet".format(model_title)) | ||||||
|  |             config.add_route('{}.worksheet'.format(route_prefix), '{}/{{{}}}/worksheet'.format(url_prefix, model_key)) | ||||||
|  |             config.add_view(cls, attr='worksheet', route_name='{}.worksheet'.format(route_prefix), | ||||||
|  |                             permission='{}.worksheet'.format(permission_prefix)) | ||||||
|  |             config.add_route('{}.worksheet_update'.format(route_prefix), '{}/{{{}}}/worksheet/update'.format(url_prefix, model_key)) | ||||||
|  |             config.add_view(cls, attr='worksheet_update', route_name='{}.worksheet_update'.format(route_prefix), | ||||||
|  |                             renderer='json', permission='{}.worksheet'.format(permission_prefix)) | ||||||
|  | 
 | ||||||
|         # refresh batch data |         # refresh batch data | ||||||
|         config.add_route('{}.refresh'.format(route_prefix), '{}/{{uuid}}/refresh'.format(url_prefix)) |         config.add_route('{}.refresh'.format(route_prefix), '{}/{{uuid}}/refresh'.format(url_prefix)) | ||||||
|         config.add_view(cls, attr='refresh', route_name='{}.refresh'.format(route_prefix), |         config.add_view(cls, attr='refresh', route_name='{}.refresh'.format(route_prefix), | ||||||
|  |  | ||||||
|  | @ -103,6 +103,7 @@ class BatchMasterView2(MasterView2, BatchMasterView): | ||||||
| 
 | 
 | ||||||
|         g.set_label('sequence', "Seq.") |         g.set_label('sequence', "Seq.") | ||||||
|         g.set_label('status_code', "Status") |         g.set_label('status_code', "Status") | ||||||
|  |         g.set_label('item_id', "Item ID") | ||||||
| 
 | 
 | ||||||
|     def render_row_status(self, row, column): |     def render_row_status(self, row, column): | ||||||
|         code = row.status_code |         code = row.status_code | ||||||
|  |  | ||||||
|  | @ -59,10 +59,11 @@ class InventoryBatchView(BatchMasterView): | ||||||
|         'id', |         'id', | ||||||
|         'created', |         'created', | ||||||
|         'created_by', |         'created_by', | ||||||
|  |         'mode', | ||||||
|         'rowcount', |         'rowcount', | ||||||
|  |         'total_cost', | ||||||
|         'executed', |         'executed', | ||||||
|         'executed_by', |         'executed_by', | ||||||
|         'mode', |  | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     model_row_class = model.InventoryBatchRow |     model_row_class = model.InventoryBatchRow | ||||||
|  | @ -71,18 +72,21 @@ class InventoryBatchView(BatchMasterView): | ||||||
|     row_grid_columns = [ |     row_grid_columns = [ | ||||||
|         'sequence', |         'sequence', | ||||||
|         'upc', |         'upc', | ||||||
|  |         'item_id', | ||||||
|         'brand_name', |         'brand_name', | ||||||
|         'description', |         'description', | ||||||
|         'size', |         'size', | ||||||
|         'cases', |         'cases', | ||||||
|         'units', |         'units', | ||||||
|         'unit_cost', |         'unit_cost', | ||||||
|  |         'total_cost', | ||||||
|         'status_code', |         'status_code', | ||||||
|     ] |     ] | ||||||
| 
 | 
 | ||||||
|     def configure_grid(self, g): |     def configure_grid(self, g): | ||||||
|         super(InventoryBatchView, self).configure_grid(g) |         super(InventoryBatchView, self).configure_grid(g) | ||||||
|         g.set_enum('mode', self.enum.INVENTORY_MODE) |         g.set_enum('mode', self.enum.INVENTORY_MODE) | ||||||
|  |         g.set_type('total_cost', 'currency') | ||||||
|         g.set_label('mode', "Count Mode") |         g.set_label('mode', "Count Mode") | ||||||
| 
 | 
 | ||||||
|     def render_mobile_listitem(self, batch, i): |     def render_mobile_listitem(self, batch, i): | ||||||
|  | @ -96,6 +100,7 @@ class InventoryBatchView(BatchMasterView): | ||||||
|         super(InventoryBatchView, self)._preconfigure_fieldset(fs) |         super(InventoryBatchView, self)._preconfigure_fieldset(fs) | ||||||
|         fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE), |         fs.mode.set(renderer=forms.renderers.EnumFieldRenderer(self.enum.INVENTORY_MODE), | ||||||
|                     label="Count Mode") |                     label="Count Mode") | ||||||
|  |         fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer) | ||||||
|         fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True, |         fs.append(fa.Field('handheld_batches', renderer=forms.renderers.HandheldBatchesFieldRenderer, readonly=True, | ||||||
|                            value=lambda b: b._handhelds)) |                            value=lambda b: b._handhelds)) | ||||||
| 
 | 
 | ||||||
|  | @ -114,6 +119,22 @@ class InventoryBatchView(BatchMasterView): | ||||||
|                 fs.executed_by, |                 fs.executed_by, | ||||||
|             ]) |             ]) | ||||||
| 
 | 
 | ||||||
|  |     def save_edit_row_form(self, form): | ||||||
|  |         row = form.fieldset.model | ||||||
|  |         batch = row.batch | ||||||
|  |         if batch.total_cost is not None and row.total_cost is not None: | ||||||
|  |             batch.total_cost -= row.total_cost | ||||||
|  |         return super(InventoryBatchView, self).save_edit_row_form(form) | ||||||
|  | 
 | ||||||
|  |     def delete_row(self): | ||||||
|  |         row = self.Session.query(model.InventoryBatchRow).get(self.request.matchdict['uuid']) | ||||||
|  |         if not row: | ||||||
|  |             raise self.notfound() | ||||||
|  |         batch = row.batch | ||||||
|  |         if batch.total_cost is not None and row.total_cost is not None: | ||||||
|  |             batch.total_cost -= row.total_cost | ||||||
|  |         return super(InventoryBatchView, self).delete_row() | ||||||
|  | 
 | ||||||
|     def configure_mobile_fieldset(self, fs): |     def configure_mobile_fieldset(self, fs): | ||||||
|         fs.configure(include=[ |         fs.configure(include=[ | ||||||
|             fs.mode, |             fs.mode, | ||||||
|  | @ -220,9 +241,15 @@ class InventoryBatchView(BatchMasterView): | ||||||
|     def configure_row_grid(self, g): |     def configure_row_grid(self, g): | ||||||
|         super(InventoryBatchView, self).configure_row_grid(g) |         super(InventoryBatchView, self).configure_row_grid(g) | ||||||
| 
 | 
 | ||||||
|         g.set_renderer('cases', 'quantity') |         g.set_type('cases', 'quantity') | ||||||
|         g.set_renderer('units', 'quantity') |         g.set_type('units', 'quantity') | ||||||
|         g.set_renderer('unit_cost', 'currency') |         g.set_type('unit_cost', 'currency') | ||||||
|  |         g.set_type('total_cost', 'currency') | ||||||
|  | 
 | ||||||
|  |         # TODO: i guess row grids don't support this properly yet? | ||||||
|  |         # g.set_link('upc') | ||||||
|  |         # g.set_link('item_id') | ||||||
|  |         # g.set_link('description') | ||||||
| 
 | 
 | ||||||
|         g.set_label('upc', "UPC") |         g.set_label('upc', "UPC") | ||||||
|         g.set_label('brand_name', "Brand") |         g.set_label('brand_name', "Brand") | ||||||
|  | @ -242,10 +269,14 @@ class InventoryBatchView(BatchMasterView): | ||||||
|         super(InventoryBatchView, self)._preconfigure_row_fieldset(fs) |         super(InventoryBatchView, self)._preconfigure_row_fieldset(fs) | ||||||
|         fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer, |         fs.upc.set(readonly=True, label="UPC", renderer=forms.renderers.GPCFieldRenderer, | ||||||
|                    attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)}) |                    attrs={'link': lambda r: self.request.route_url('products.view', uuid=r.product_uuid)}) | ||||||
|  |         fs.item_id.set(readonly=True) | ||||||
|         fs.brand_name.set(readonly=True) |         fs.brand_name.set(readonly=True) | ||||||
|         fs.description.set(readonly=True) |         fs.description.set(readonly=True) | ||||||
|         fs.size.set(readonly=True) |         fs.size.set(readonly=True) | ||||||
|         fs.unit_cost.set(renderer=forms.renderers.CurrencyFieldRenderer) |         fs.cases.set(renderer=forms.renderers.QuantityFieldRenderer) | ||||||
|  |         fs.units.set(renderer=forms.renderers.QuantityFieldRenderer) | ||||||
|  |         fs.unit_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer) | ||||||
|  |         fs.total_cost.set(readonly=True, renderer=forms.renderers.CurrencyFieldRenderer) | ||||||
| 
 | 
 | ||||||
|     def configure_row_fieldset(self, fs): |     def configure_row_fieldset(self, fs): | ||||||
|         fs.configure( |         fs.configure( | ||||||
|  | @ -259,18 +290,22 @@ class InventoryBatchView(BatchMasterView): | ||||||
|                 fs.cases, |                 fs.cases, | ||||||
|                 fs.units, |                 fs.units, | ||||||
|                 fs.unit_cost, |                 fs.unit_cost, | ||||||
|  |                 fs.total_cost, | ||||||
|             ]) |             ]) | ||||||
| 
 | 
 | ||||||
|     @classmethod |     @classmethod | ||||||
|     def defaults(cls, config): |     def defaults(cls, config): | ||||||
|  |         cls._inventory_defaults(config) | ||||||
|  |         cls._batch_defaults(config) | ||||||
|  |         cls._defaults(config) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def _inventory_defaults(cls, config): | ||||||
|         model_key = cls.get_model_key() |         model_key = cls.get_model_key() | ||||||
|         route_prefix = cls.get_route_prefix() |         route_prefix = cls.get_route_prefix() | ||||||
|         url_prefix = cls.get_url_prefix() |         url_prefix = cls.get_url_prefix() | ||||||
|         row_permission_prefix = cls.get_row_permission_prefix() |         row_permission_prefix = cls.get_row_permission_prefix() | ||||||
| 
 | 
 | ||||||
|         cls._batch_defaults(config) |  | ||||||
|         cls._defaults(config) |  | ||||||
| 
 |  | ||||||
|         # mobile - make new row from UPC |         # mobile - make new row from UPC | ||||||
|         config.add_route('mobile.{}.row_from_upc'.format(route_prefix), '/mobile{}/{{{}}}/row-from-upc'.format(url_prefix, model_key)) |         config.add_route('mobile.{}.row_from_upc'.format(route_prefix), '/mobile{}/{{{}}}/row-from-upc'.format(url_prefix, model_key)) | ||||||
|         config.add_view(cls, attr='mobile_row_from_upc', route_name='mobile.{}.row_from_upc'.format(route_prefix), |         config.add_view(cls, attr='mobile_row_from_upc', route_name='mobile.{}.row_from_upc'.format(route_prefix), | ||||||
|  |  | ||||||
|  | @ -1340,17 +1340,15 @@ class MasterView(View): | ||||||
|         parent = self.get_parent(row) |         parent = self.get_parent(row) | ||||||
|         return self.render_to_response('view_row', { |         return self.render_to_response('view_row', { | ||||||
|             'instance': row, |             'instance': row, | ||||||
|             'instance_title': self.get_row_instance_title(row), |             'batch': row.batch, | ||||||
|  |             'instance_title': self.get_instance_title(row.batch), | ||||||
|  |             'instance_url': self.get_action_url('view', parent), | ||||||
|             'instance_editable': self.row_editable(row), |             'instance_editable': self.row_editable(row), | ||||||
|             'instance_deletable': self.row_deletable(row), |             'instance_deletable': self.row_deletable(row), | ||||||
|             'rows_creatable': self.rows_creatable and self.rows_creatable_for(parent), |             'rows_creatable': self.rows_creatable and self.rows_creatable_for(parent), | ||||||
|             'model_title': self.get_row_model_title(), |             'model_title': self.get_row_model_title(), | ||||||
|             'model_title_plural': self.get_row_model_title_plural(), |             'model_title_plural': self.get_row_model_title_plural(), | ||||||
|             'parent_model_title': self.get_model_title(), |             'parent_model_title': self.get_model_title(), | ||||||
|             'index_url': self.get_action_url('view', parent), |  | ||||||
|             'index_title': '{} {}'.format( |  | ||||||
|                 self.get_model_title(), |  | ||||||
|                 self.get_instance_title(parent)), |  | ||||||
|             'action_url': self.get_row_action_url, |             'action_url': self.get_row_action_url, | ||||||
|             'form': form}) |             'form': form}) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -291,6 +291,10 @@ class ProductsView(MasterView): | ||||||
|         fs.last_sold.set(readonly=True) |         fs.last_sold.set(readonly=True) | ||||||
|         fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True, |         fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True, | ||||||
|                            value=lambda p: p.current_price.ends if p.current_price else None)) |                            value=lambda p: p.current_price.ends if p.current_price else None)) | ||||||
|  |         fs.append(fa.Field('inventory_on_hand', readonly=True, label="On Hand", | ||||||
|  |                            value=lambda p: p.inventory.on_hand if p.inventory else None)) | ||||||
|  |         fs.append(fa.Field('inventory_on_order', readonly=True, label="On Order", | ||||||
|  |                            value=lambda p: p.inventory.on_order if p.inventory else None)) | ||||||
| 
 | 
 | ||||||
|     def configure_fieldset(self, fs): |     def configure_fieldset(self, fs): | ||||||
|         fs.configure( |         fs.configure( | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar