Add CostFieldRenderer
and tweak product view template
latter being for easier customization
This commit is contained in:
parent
95b2ce25c1
commit
3dfe3dfa28
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'):
|
||||||
|
|
Loading…
Reference in a new issue