Add CostFieldRenderer and tweak product view template

latter being for easier customization
This commit is contained in:
Lance Edgar 2017-03-23 20:32:56 -05:00
parent 95b2ce25c1
commit 3dfe3dfa28
4 changed files with 135 additions and 103 deletions

View file

@ -44,7 +44,7 @@ from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
from .products import (GPCFieldRenderer, ScancodeFieldRenderer, from .products import (GPCFieldRenderer, ScancodeFieldRenderer,
DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer, DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
BrandFieldRenderer, ProductFieldRenderer, BrandFieldRenderer, ProductFieldRenderer,
PriceFieldRenderer, PriceWithExpirationFieldRenderer) CostFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer)
from .custorders import CustomerOrderFieldRenderer from .custorders import CustomerOrderFieldRenderer

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar # Copyright © 2010-2017 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -30,6 +30,7 @@ from rattail.gpc import GPC
from rattail.db import model from rattail.db import model
from rattail.db.util import maxlen from rattail.db.util import maxlen
import formalchemy as fa
from formalchemy import TextFieldRenderer from formalchemy import TextFieldRenderer
from formalchemy.fields import SelectFieldRenderer from formalchemy.fields import SelectFieldRenderer
from webhelpers.html import tags, literal from webhelpers.html import tags, literal
@ -158,6 +159,18 @@ class BrandFieldRenderer(AutocompleteFieldRenderer):
service_route = 'brands.autocomplete' 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): class PriceFieldRenderer(TextFieldRenderer):
""" """
Renderer for fields which reference a :class:`ProductPrice` instance. Renderer for fields which reference a :class:`ProductPrice` instance.

View file

@ -1,9 +1,9 @@
## -*- coding: utf-8 -*- ## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" /> <%inherit file="/master/view.mako" />
<%namespace file="/forms/lib.mako" import="render_field_readonly" /> <%namespace file="/forms/lib.mako" import="render_field_readonly" />
<%def name="head_tags()"> <%def name="extra_styles()">
${parent.head_tags()} ${parent.extra_styles()}
<style type="text/css"> <style type="text/css">
#product-main { #product-main {
margin-top: 1em; margin-top: 1em;
@ -20,6 +20,52 @@
</style> </style>
</%def> </%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()"> <%def name="context_menu_items()">
${parent.context_menu_items()} ${parent.context_menu_items()}
% if version_count is not Undefined and request.has_perm('instance.versions.view'): % if version_count is not Undefined and request.has_perm('instance.versions.view'):
@ -38,6 +84,37 @@
${self.extra_main_fields(form)} ${self.extra_main_fields(form)}
</%def> </%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="extra_main_fields(form)"></%def>
<%def name="render_organization_fields(form)"> <%def name="render_organization_fields(form)">
@ -101,6 +178,36 @@
</div> </div>
</%def> </%def>
<%def name="sources_panel()">
<div class="panel-grid" id="product-costs">
<h2>Vendor Sources</h2>
<div class="newgrid full no-border">
<table>
<thead>
<th>Pref.</th>
<th>Vendor</th>
<th>Code</th>
<th>Case Size</th>
<th>Case Cost</th>
<th>Unit Cost</th>
</thead>
<tbody>
% for cost in instance.costs:
<tr>
<td class="center">${'X' if cost.preference == 1 else ''}</td>
<td>${cost.vendor}</td>
<td class="center">${cost.code or ''}</td>
<td class="center">${h.pretty_quantity(cost.case_size)}</td>
<td class="right">${'$ %0.2f' % cost.case_cost if cost.case_cost is not None else ''}</td>
<td class="right">${'$ %0.4f' % cost.unit_cost if cost.unit_cost is not None else ''}</td>
</tr>
% endfor
</tbody>
</table>
</div>
</div>
</%def>
<%def name="notes_panel()"> <%def name="notes_panel()">
<div class="panel"> <div class="panel">
<h2>Notes</h2> <h2>Notes</h2>
@ -119,102 +226,6 @@
</div> </div>
</%def> </%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()}
<div class="panel-grid" id="product-costs">
<h2>Vendor Sources</h2>
<div class="newgrid full no-border">
<table>
<thead>
<th>Pref.</th>
<th>Vendor</th>
<th>Code</th>
<th>Case Size</th>
<th>Case Cost</th>
<th>Unit Cost</th>
</thead>
<tbody>
% for cost in instance.costs:
<tr>
<td class="center">${'X' if cost.preference == 1 else ''}</td>
<td>${cost.vendor}</td>
<td class="center">${cost.code or ''}</td>
<td class="center">${h.pretty_quantity(cost.case_size)}</td>
<td class="right">${'$ %0.2f' % cost.case_cost if cost.case_cost is not None else ''}</td>
<td class="right">${'$ %0.4f' % cost.unit_cost if cost.unit_cost is not None else ''}</td>
</tr>
% endfor
</tbody>
</table>
</div>
</div>
${self.notes_panel()}
${self.ingredients_panel()}
${self.lookup_codes_panel()}
${self.extra_right_panels()}
</div> <!-- right column -->
% if buttons:
${buttons|n}
% endif
</div>
<%def name="extra_left_panels()"></%def> <%def name="extra_left_panels()"></%def>
<%def name="extra_right_panels()"></%def> <%def name="extra_right_panels()"></%def>

View file

@ -1,4 +1,4 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8; -*-
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
@ -216,6 +216,14 @@ class ProductsView(MasterView):
g.vendor.set(label="Pref. Vendor") 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' g.default_sortkey = 'upc'
if self.print_labels and self.request.has_perm('products.print_labels'): if self.print_labels and self.request.has_perm('products.print_labels'):