Overhaul product views a little, per customization needs
This commit is contained in:
parent
f7267597ee
commit
9a6ad16e2f
|
@ -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…
Reference in a new issue