Overhaul product views a little, per customization needs
This commit is contained in:
		
							parent
							
								
									f7267597ee
								
							
						
					
					
						commit
						9a6ad16e2f
					
				
					 5 changed files with 94 additions and 54 deletions
				
			
		|  | @ -41,7 +41,9 @@ from .employees import EmployeeFieldRenderer | |||
| 
 | ||||
| from .stores import StoreFieldRenderer | ||||
| from .vendors import VendorFieldRenderer, PurchaseFieldRenderer | ||||
| from .products import (GPCFieldRenderer, DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer, | ||||
|                        BrandFieldRenderer, ProductFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer) | ||||
| from .products import (GPCFieldRenderer, ScancodeFieldRenderer, | ||||
|                        DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer, | ||||
|                        BrandFieldRenderer, ProductFieldRenderer, | ||||
|                        PriceFieldRenderer, PriceWithExpirationFieldRenderer) | ||||
| 
 | ||||
| from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer | ||||
|  |  | |||
|  | @ -27,6 +27,8 @@ Product Field Renderers | |||
| from __future__ import unicode_literals, absolute_import | ||||
| 
 | ||||
| from rattail.gpc import GPC | ||||
| from rattail.db import model | ||||
| from rattail.db.util import maxlen | ||||
| 
 | ||||
| from formalchemy import TextFieldRenderer | ||||
| from formalchemy.fields import SelectFieldRenderer | ||||
|  | @ -57,7 +59,26 @@ class ProductFieldRenderer(AutocompleteFieldRenderer): | |||
|         return tags.link_to(product, self.request.route_url('products.view', uuid=product.uuid)) | ||||
| 
 | ||||
| 
 | ||||
| class GPCFieldRenderer(TextFieldRenderer): | ||||
| class ProductKeyFieldRenderer(TextFieldRenderer): | ||||
|     """ | ||||
|     Base class for product key field renderers. | ||||
|     """ | ||||
| 
 | ||||
|     def render_readonly(self, **kwargs): | ||||
|         value = self.raw_value | ||||
|         if value is None: | ||||
|             return '' | ||||
|         value = self.render_value(value) | ||||
|         if kwargs.get('link'): | ||||
|             product = self.field.parent.model | ||||
|             value = tags.link_to(value, kwargs['link'](product)) | ||||
|         return value | ||||
| 
 | ||||
|     def render_value(self, value): | ||||
|         return unicode(value) | ||||
| 
 | ||||
| 
 | ||||
| class GPCFieldRenderer(ProductKeyFieldRenderer): | ||||
|     """ | ||||
|     Renderer for :class:`rattail.gpc.GPC` fields. | ||||
|     """ | ||||
|  | @ -67,16 +88,18 @@ class GPCFieldRenderer(TextFieldRenderer): | |||
|         # Hm, should maybe consider hard-coding this...? | ||||
|         return len(unicode(GPC(0))) | ||||
| 
 | ||||
|     def render_readonly(self, **kwargs): | ||||
|         gpc = self.raw_value | ||||
|         if gpc is None: | ||||
|             return '' | ||||
|         gpc = unicode(gpc) | ||||
|         gpc = '{}-{}'.format(gpc[:-1], gpc[-1]) | ||||
|         if kwargs.get('link'): | ||||
|             product = self.field.parent.model | ||||
|             gpc = tags.link_to(gpc, kwargs['link'](product)) | ||||
|         return gpc | ||||
|     def render_value(self, gpc): | ||||
|         return gpc.pretty() | ||||
| 
 | ||||
| 
 | ||||
| class ScancodeFieldRenderer(ProductKeyFieldRenderer): | ||||
|     """ | ||||
|     Renderer for :class:`rattail.db.model.Product.scancode` field | ||||
|     """ | ||||
| 
 | ||||
|     @property | ||||
|     def length(self): | ||||
|         return maxlen(model.Product.scancode) | ||||
| 
 | ||||
| 
 | ||||
| class DepartmentFieldRenderer(SelectFieldRenderer): | ||||
|  |  | |||
|  | @ -63,10 +63,42 @@ | |||
|     ${render_field_readonly(form.fieldset.deleted)} | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="movement_panel()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Movement</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${self.render_movement_fields(form)} | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="render_movement_fields(form)"> | ||||
|     ${render_field_readonly(form.fieldset.last_sold)} | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="lookup_codes_panel()"> | ||||
|   <div class="panel-grid" id="product-codes"> | ||||
|     <h2>Additional Lookup Codes</h2> | ||||
|     <div class="grid full hoverable no-border"> | ||||
|       <table> | ||||
|         <thead> | ||||
|           <th>Seq</th> | ||||
|           <th>Code</th> | ||||
|         </thead> | ||||
|         <tbody> | ||||
|           % for i, code in enumerate(product._codes, 1): | ||||
|               <tr class="${'odd' if i % 2 else 'even'}"> | ||||
|                 <td>${code.ordinal}</td> | ||||
|                 <td>${code.code}</td> | ||||
|               </tr> | ||||
|           % endfor | ||||
|         </tbody> | ||||
|       </table> | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| 
 | ||||
| <div class="form-wrapper"> | ||||
|   <ul class="context-menu"> | ||||
|     ${self.context_menu_items()} | ||||
|  | @ -113,12 +145,7 @@ | |||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="panel"> | ||||
|       <h2>Movement</h2> | ||||
|       <div class="panel-body"> | ||||
|         ${self.render_movement_fields(form)} | ||||
|       </div> | ||||
|     </div> | ||||
|     ${self.movement_panel()} | ||||
| 
 | ||||
|     <div class="panel-grid" id="product-costs"> | ||||
|       <h2>Vendor Sources</h2> | ||||
|  | @ -148,25 +175,7 @@ | |||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="panel-grid" id="product-codes"> | ||||
|       <h2>Additional Lookup Codes</h2> | ||||
|       <div class="grid full hoverable no-border"> | ||||
|         <table> | ||||
|           <thead> | ||||
|             <th>Seq</th> | ||||
|             <th>Code</th> | ||||
|           </thead> | ||||
|           <tbody> | ||||
|             % for i, code in enumerate(product._codes, 1): | ||||
|                 <tr class="${'odd' if i % 2 else 'even'}"> | ||||
|                   <td>${code.ordinal}</td> | ||||
|                   <td>${code.code}</td> | ||||
|                 </tr> | ||||
|             % endfor | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     </div> | ||||
|     ${self.lookup_codes_panel()} | ||||
| 
 | ||||
|     ${self.extra_right_panels()} | ||||
| 
 | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ class MasterView(View): | |||
|     viewing = False | ||||
|     editing = False | ||||
|     deleting = False | ||||
|     has_pk_fields = False | ||||
| 
 | ||||
|     row_attrs = {} | ||||
|     cell_attrs = {} | ||||
|  | @ -1292,6 +1293,9 @@ class MasterView(View): | |||
|         if cls.viewable: | ||||
|             config.add_tailbone_permission(permission_prefix, '{}.view'.format(permission_prefix), | ||||
|                                            "View details for {}".format(model_title)) | ||||
|             if cls.has_pk_fields: | ||||
|                 config.add_tailbone_permission(permission_prefix, '{}.view_pk_fields'.format(permission_prefix), | ||||
|                                                "View all PK-type fields for {}".format(model_title_plural)) | ||||
| 
 | ||||
|             # view by grid index | ||||
|             config.add_route('{}.view_index'.format(route_prefix), '{}/view'.format(url_prefix)) | ||||
|  |  | |||
|  | @ -132,8 +132,7 @@ class ProductsView(MasterView): | |||
| 
 | ||||
|         return query | ||||
| 
 | ||||
|     def configure_grid(self, g): | ||||
| 
 | ||||
|     def _preconfigure_grid(self, g): | ||||
|         def join_vendor(q): | ||||
|             return q.outerjoin(model.ProductCost, | ||||
|                                sa.and_( | ||||
|  | @ -202,35 +201,38 @@ class ProductsView(MasterView): | |||
|         g.filters['vendor_code'] = g.make_filter('vendor_code', ProductCostCode.code) | ||||
|         g.filters['vendor_code_any'] = g.make_filter('vendor_code_any', ProductCostCodeAny.code) | ||||
| 
 | ||||
|         g.default_sortkey = 'upc' | ||||
| 
 | ||||
|         product_link = lambda p: self.get_action_url('view', p) | ||||
| 
 | ||||
|         g.upc.set(renderer=forms.renderers.GPCFieldRenderer) | ||||
|         g.upc.set(label="UPC", renderer=forms.renderers.GPCFieldRenderer) | ||||
|         g.upc.attrs(link=product_link) | ||||
| 
 | ||||
|         g.description.set(renderer=DescriptionFieldRenderer) | ||||
|         g.description.attrs(link=product_link) | ||||
| 
 | ||||
|         g.regular_price.set(renderer=forms.renderers.PriceFieldRenderer) | ||||
|         g.current_price.set(renderer=forms.renderers.PriceFieldRenderer) | ||||
|         g.regular_price.set(label="Reg. Price", renderer=forms.renderers.PriceFieldRenderer) | ||||
|         g.current_price.set(label="Cur. Price", renderer=forms.renderers.PriceFieldRenderer) | ||||
|          | ||||
|         g.vendor.set(label="Pref. Vendor") | ||||
| 
 | ||||
|         g.default_sortkey = 'upc' | ||||
| 
 | ||||
|         if self.print_labels and self.request.has_perm('products.print_labels'): | ||||
|             g.more_actions.append(grids.GridAction('print_label', icon='print')) | ||||
| 
 | ||||
|     def configure_grid(self, g): | ||||
|         g.configure( | ||||
|             include=[ | ||||
|                 g.upc.label("UPC"), | ||||
|                 g.upc, | ||||
|                 g.brand, | ||||
|                 g.description, | ||||
|                 g.size, | ||||
|                 g.subdepartment, | ||||
|                 g.vendor.label("Pref. Vendor"), | ||||
|                 g.regular_price.label("Reg. Price"), | ||||
|                 g.current_price.label("Cur. Price"), | ||||
|                 ], | ||||
|                 g.vendor, | ||||
|                 g.regular_price, | ||||
|                 g.current_price, | ||||
|             ], | ||||
|             readonly=True) | ||||
| 
 | ||||
|         # TODO: need to check for 'print labels' permission here also | ||||
|         if self.print_labels: | ||||
|             g.more_actions.append(grids.GridAction('print_label', icon='print')) | ||||
| 
 | ||||
|     def template_kwargs_index(self, **kwargs): | ||||
|         if self.print_labels: | ||||
|             kwargs['label_profiles'] = Session.query(model.LabelProfile)\ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar