Add CostFieldRenderer and tweak product view template
				
					
				
			latter being for easier customization
This commit is contained in:
		
							parent
							
								
									95b2ce25c1
								
							
						
					
					
						commit
						3dfe3dfa28
					
				
					 4 changed files with 135 additions and 103 deletions
				
			
		|  | @ -44,7 +44,7 @@ from .vendors import VendorFieldRenderer, PurchaseFieldRenderer | |||
| from .products import (GPCFieldRenderer, ScancodeFieldRenderer, | ||||
|                        DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer, | ||||
|                        BrandFieldRenderer, ProductFieldRenderer, | ||||
|                        PriceFieldRenderer, PriceWithExpirationFieldRenderer) | ||||
|                        CostFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer) | ||||
| 
 | ||||
| from .custorders import CustomerOrderFieldRenderer | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # -*- coding: utf-8; -*- | ||||
| ################################################################################ | ||||
| # | ||||
| #  Rattail -- Retail Software Framework | ||||
| #  Copyright © 2010-2016 Lance Edgar | ||||
| #  Copyright © 2010-2017 Lance Edgar | ||||
| # | ||||
| #  This file is part of Rattail. | ||||
| # | ||||
|  | @ -30,6 +30,7 @@ from rattail.gpc import GPC | |||
| from rattail.db import model | ||||
| from rattail.db.util import maxlen | ||||
| 
 | ||||
| import formalchemy as fa | ||||
| from formalchemy import TextFieldRenderer | ||||
| from formalchemy.fields import SelectFieldRenderer | ||||
| from webhelpers.html import tags, literal | ||||
|  | @ -158,6 +159,18 @@ class BrandFieldRenderer(AutocompleteFieldRenderer): | |||
|     service_route = 'brands.autocomplete' | ||||
| 
 | ||||
| 
 | ||||
| class CostFieldRenderer(fa.FieldRenderer): | ||||
|     """ | ||||
|     Renders fields which reference a ProductCost object | ||||
|     """ | ||||
| 
 | ||||
|     def render_readonly(self, **kwargs): | ||||
|         cost = self.raw_value | ||||
|         if not cost: | ||||
|             return '' | ||||
|         return '${:0.2f}'.format(cost.unit_cost) | ||||
| 
 | ||||
| 
 | ||||
| class PriceFieldRenderer(TextFieldRenderer): | ||||
|     """ | ||||
|     Renderer for fields which reference a :class:`ProductPrice` instance. | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| ## -*- coding: utf-8 -*- | ||||
| ## -*- coding: utf-8; -*- | ||||
| <%inherit file="/master/view.mako" /> | ||||
| <%namespace file="/forms/lib.mako" import="render_field_readonly" /> | ||||
| 
 | ||||
| <%def name="head_tags()"> | ||||
|   ${parent.head_tags()} | ||||
| <%def name="extra_styles()"> | ||||
|   ${parent.extra_styles()} | ||||
|   <style type="text/css"> | ||||
|     #product-main { | ||||
|         margin-top: 1em; | ||||
|  | @ -20,6 +20,52 @@ | |||
|   </style> | ||||
| </%def> | ||||
| 
 | ||||
| ############################## | ||||
| ## page body | ||||
| ############################## | ||||
| 
 | ||||
| <div class="form-wrapper"> | ||||
|   <ul class="context-menu"> | ||||
|     ${self.context_menu_items()} | ||||
|   </ul> | ||||
| 
 | ||||
|   <div class="panel" id="product-main"> | ||||
|     <h2>Product</h2> | ||||
|     <div class="panel-body"> | ||||
|       <div style="clear: none; float: left;"> | ||||
|         ${self.render_main_fields(form)} | ||||
|       </div> | ||||
|       % if image_url: | ||||
|           % if image_path is not Undefined: | ||||
|               ${h.image(image_url, "Product Image", id='product-image', path=image_path, use_pil=False)} | ||||
|           % else: | ||||
|               ${h.image(image_url, "Product Image", id='product-image', width=150, height=150)} | ||||
|           % endif | ||||
|       % endif | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="panel-wrapper"> <!-- left column --> | ||||
| 
 | ||||
|     ${self.left_column()} | ||||
| 
 | ||||
|   </div> <!-- left column --> | ||||
| 
 | ||||
|   <div class="panel-wrapper"> <!-- right column --> | ||||
| 
 | ||||
|     ${self.right_column()} | ||||
| 
 | ||||
|   </div> <!-- right column --> | ||||
| 
 | ||||
|   % if buttons: | ||||
|       ${buttons|n} | ||||
|   % endif | ||||
| </div> | ||||
| 
 | ||||
| ############################## | ||||
| ## rendering methods | ||||
| ############################## | ||||
| 
 | ||||
| <%def name="context_menu_items()"> | ||||
|   ${parent.context_menu_items()} | ||||
|   % if version_count is not Undefined and request.has_perm('instance.versions.view'): | ||||
|  | @ -38,6 +84,37 @@ | |||
|   ${self.extra_main_fields(form)} | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="left_column()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Pricing</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${self.render_price_fields(form)} | ||||
|     </div> | ||||
|   </div> | ||||
|   <div class="panel"> | ||||
|     <h2>Flags</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${self.render_flag_fields(form)} | ||||
|     </div> | ||||
|   </div> | ||||
|   ${self.extra_left_panels()} | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="right_column()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Organization</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${self.render_organization_fields(form)} | ||||
|     </div> | ||||
|   </div> | ||||
|   ${self.movement_panel()} | ||||
|   ${self.sources_panel()} | ||||
|   ${self.notes_panel()} | ||||
|   ${self.ingredients_panel()} | ||||
|   ${self.lookup_codes_panel()} | ||||
|   ${self.extra_right_panels()} | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="extra_main_fields(form)"></%def> | ||||
| 
 | ||||
| <%def name="render_organization_fields(form)"> | ||||
|  | @ -101,77 +178,7 @@ | |||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="notes_panel()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Notes</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${render_field_readonly(form.fieldset.notes)} | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="ingredients_panel()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Ingredients</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${render_field_readonly(form.fieldset.ingredients)} | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| 
 | ||||
| <div class="form-wrapper"> | ||||
|   <ul class="context-menu"> | ||||
|     ${self.context_menu_items()} | ||||
|   </ul> | ||||
| 
 | ||||
|   <div class="panel" id="product-main"> | ||||
|     <h2>Product</h2> | ||||
|     <div class="panel-body"> | ||||
|       <div style="clear: none; float: left;"> | ||||
|         ${self.render_main_fields(form)} | ||||
|       </div> | ||||
|       % if image_url: | ||||
|           % if image_path is not Undefined: | ||||
|               ${h.image(image_url, "Product Image", id='product-image', path=image_path, use_pil=False)} | ||||
|           % else: | ||||
|               ${h.image(image_url, "Product Image", id='product-image', width=150, height=150)} | ||||
|           % endif | ||||
|       % endif | ||||
|     </div> | ||||
|   </div> | ||||
| 
 | ||||
|   <div class="panel-wrapper"> <!-- left column --> | ||||
| 
 | ||||
|     <div class="panel"> | ||||
|       <h2>Pricing</h2> | ||||
|       <div class="panel-body"> | ||||
|         ${self.render_price_fields(form)} | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="panel"> | ||||
|       <h2>Flags</h2> | ||||
|       <div class="panel-body"> | ||||
|         ${self.render_flag_fields(form)} | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     ${self.extra_left_panels()} | ||||
| 
 | ||||
|   </div> <!-- left column --> | ||||
| 
 | ||||
|   <div class="panel-wrapper"> <!-- right column --> | ||||
| 
 | ||||
|     <div class="panel"> | ||||
|       <h2>Organization</h2> | ||||
|       <div class="panel-body"> | ||||
|         ${self.render_organization_fields(form)} | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     ${self.movement_panel()} | ||||
| 
 | ||||
| <%def name="sources_panel()"> | ||||
|   <div class="panel-grid" id="product-costs"> | ||||
|     <h2>Vendor Sources</h2> | ||||
|     <div class="newgrid full no-border"> | ||||
|  | @ -199,21 +206,25 @@ | |||
|       </table> | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
|     ${self.notes_panel()} | ||||
| 
 | ||||
|     ${self.ingredients_panel()} | ||||
| 
 | ||||
|     ${self.lookup_codes_panel()} | ||||
| 
 | ||||
|     ${self.extra_right_panels()} | ||||
| 
 | ||||
|   </div> <!-- right column --> | ||||
| 
 | ||||
|   % if buttons: | ||||
|       ${buttons|n} | ||||
|   % endif | ||||
| <%def name="notes_panel()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Notes</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${render_field_readonly(form.fieldset.notes)} | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="ingredients_panel()"> | ||||
|   <div class="panel"> | ||||
|     <h2>Ingredients</h2> | ||||
|     <div class="panel-body"> | ||||
|       ${render_field_readonly(form.fieldset.ingredients)} | ||||
|     </div> | ||||
|   </div> | ||||
| </%def> | ||||
| 
 | ||||
| <%def name="extra_left_panels()"></%def> | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| # -*- coding: utf-8; -*- | ||||
| ################################################################################ | ||||
| # | ||||
| #  Rattail -- Retail Software Framework | ||||
|  | @ -216,6 +216,14 @@ class ProductsView(MasterView): | |||
|          | ||||
|         g.vendor.set(label="Pref. Vendor") | ||||
| 
 | ||||
|         g.joiners['cost'] = lambda q: q.outerjoin(model.ProductCost, | ||||
|                                                   sa.and_( | ||||
|                                                       model.ProductCost.product_uuid == model.Product.uuid, | ||||
|                                                       model.ProductCost.preference == 1)) | ||||
|         g.sorters['cost'] = g.make_sorter(model.ProductCost.unit_cost) | ||||
|         g.filters['cost'] = g.make_filter('cost', model.ProductCost.unit_cost) | ||||
|         g.cost.set(renderer=forms.renderers.CostFieldRenderer) | ||||
| 
 | ||||
|         g.default_sortkey = 'upc' | ||||
| 
 | ||||
|         if self.print_labels and self.request.has_perm('products.print_labels'): | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar