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 .stores import StoreFieldRenderer
|
||||||
from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
|
from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
|
||||||
from .products import (GPCFieldRenderer, DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
|
from .products import (GPCFieldRenderer, ScancodeFieldRenderer,
|
||||||
BrandFieldRenderer, ProductFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer)
|
DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
|
||||||
|
BrandFieldRenderer, ProductFieldRenderer,
|
||||||
|
PriceFieldRenderer, PriceWithExpirationFieldRenderer)
|
||||||
|
|
||||||
from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer
|
from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer
|
||||||
|
|
|
@ -27,6 +27,8 @@ Product Field Renderers
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from rattail.gpc import GPC
|
from rattail.gpc import GPC
|
||||||
|
from rattail.db import model
|
||||||
|
from rattail.db.util import maxlen
|
||||||
|
|
||||||
from formalchemy import TextFieldRenderer
|
from formalchemy import TextFieldRenderer
|
||||||
from formalchemy.fields import SelectFieldRenderer
|
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))
|
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.
|
Renderer for :class:`rattail.gpc.GPC` fields.
|
||||||
"""
|
"""
|
||||||
|
@ -67,16 +88,18 @@ class GPCFieldRenderer(TextFieldRenderer):
|
||||||
# Hm, should maybe consider hard-coding this...?
|
# Hm, should maybe consider hard-coding this...?
|
||||||
return len(unicode(GPC(0)))
|
return len(unicode(GPC(0)))
|
||||||
|
|
||||||
def render_readonly(self, **kwargs):
|
def render_value(self, gpc):
|
||||||
gpc = self.raw_value
|
return gpc.pretty()
|
||||||
if gpc is None:
|
|
||||||
return ''
|
|
||||||
gpc = unicode(gpc)
|
class ScancodeFieldRenderer(ProductKeyFieldRenderer):
|
||||||
gpc = '{}-{}'.format(gpc[:-1], gpc[-1])
|
"""
|
||||||
if kwargs.get('link'):
|
Renderer for :class:`rattail.db.model.Product.scancode` field
|
||||||
product = self.field.parent.model
|
"""
|
||||||
gpc = tags.link_to(gpc, kwargs['link'](product))
|
|
||||||
return gpc
|
@property
|
||||||
|
def length(self):
|
||||||
|
return maxlen(model.Product.scancode)
|
||||||
|
|
||||||
|
|
||||||
class DepartmentFieldRenderer(SelectFieldRenderer):
|
class DepartmentFieldRenderer(SelectFieldRenderer):
|
||||||
|
|
|
@ -63,10 +63,42 @@
|
||||||
${render_field_readonly(form.fieldset.deleted)}
|
${render_field_readonly(form.fieldset.deleted)}
|
||||||
</%def>
|
</%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)">
|
<%def name="render_movement_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.last_sold)}
|
${render_field_readonly(form.fieldset.last_sold)}
|
||||||
</%def>
|
</%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">
|
<div class="form-wrapper">
|
||||||
<ul class="context-menu">
|
<ul class="context-menu">
|
||||||
${self.context_menu_items()}
|
${self.context_menu_items()}
|
||||||
|
@ -113,12 +145,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel">
|
${self.movement_panel()}
|
||||||
<h2>Movement</h2>
|
|
||||||
<div class="panel-body">
|
|
||||||
${self.render_movement_fields(form)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="panel-grid" id="product-costs">
|
<div class="panel-grid" id="product-costs">
|
||||||
<h2>Vendor Sources</h2>
|
<h2>Vendor Sources</h2>
|
||||||
|
@ -148,25 +175,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel-grid" id="product-codes">
|
${self.lookup_codes_panel()}
|
||||||
<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.extra_right_panels()}
|
${self.extra_right_panels()}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ class MasterView(View):
|
||||||
viewing = False
|
viewing = False
|
||||||
editing = False
|
editing = False
|
||||||
deleting = False
|
deleting = False
|
||||||
|
has_pk_fields = False
|
||||||
|
|
||||||
row_attrs = {}
|
row_attrs = {}
|
||||||
cell_attrs = {}
|
cell_attrs = {}
|
||||||
|
@ -1292,6 +1293,9 @@ class MasterView(View):
|
||||||
if cls.viewable:
|
if cls.viewable:
|
||||||
config.add_tailbone_permission(permission_prefix, '{}.view'.format(permission_prefix),
|
config.add_tailbone_permission(permission_prefix, '{}.view'.format(permission_prefix),
|
||||||
"View details for {}".format(model_title))
|
"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
|
# view by grid index
|
||||||
config.add_route('{}.view_index'.format(route_prefix), '{}/view'.format(url_prefix))
|
config.add_route('{}.view_index'.format(route_prefix), '{}/view'.format(url_prefix))
|
||||||
|
|
|
@ -132,8 +132,7 @@ class ProductsView(MasterView):
|
||||||
|
|
||||||
return query
|
return query
|
||||||
|
|
||||||
def configure_grid(self, g):
|
def _preconfigure_grid(self, g):
|
||||||
|
|
||||||
def join_vendor(q):
|
def join_vendor(q):
|
||||||
return q.outerjoin(model.ProductCost,
|
return q.outerjoin(model.ProductCost,
|
||||||
sa.and_(
|
sa.and_(
|
||||||
|
@ -202,35 +201,38 @@ class ProductsView(MasterView):
|
||||||
g.filters['vendor_code'] = g.make_filter('vendor_code', ProductCostCode.code)
|
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.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)
|
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.upc.attrs(link=product_link)
|
||||||
|
|
||||||
g.description.set(renderer=DescriptionFieldRenderer)
|
g.description.set(renderer=DescriptionFieldRenderer)
|
||||||
g.description.attrs(link=product_link)
|
g.description.attrs(link=product_link)
|
||||||
|
|
||||||
g.regular_price.set(renderer=forms.renderers.PriceFieldRenderer)
|
g.regular_price.set(label="Reg. Price", renderer=forms.renderers.PriceFieldRenderer)
|
||||||
g.current_price.set(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(
|
g.configure(
|
||||||
include=[
|
include=[
|
||||||
g.upc.label("UPC"),
|
g.upc,
|
||||||
g.brand,
|
g.brand,
|
||||||
g.description,
|
g.description,
|
||||||
g.size,
|
g.size,
|
||||||
g.subdepartment,
|
g.subdepartment,
|
||||||
g.vendor.label("Pref. Vendor"),
|
g.vendor,
|
||||||
g.regular_price.label("Reg. Price"),
|
g.regular_price,
|
||||||
g.current_price.label("Cur. Price"),
|
g.current_price,
|
||||||
],
|
],
|
||||||
readonly=True)
|
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):
|
def template_kwargs_index(self, **kwargs):
|
||||||
if self.print_labels:
|
if self.print_labels:
|
||||||
kwargs['label_profiles'] = Session.query(model.LabelProfile)\
|
kwargs['label_profiles'] = Session.query(model.LabelProfile)\
|
||||||
|
|
Loading…
Reference in a new issue