From 9a6ad16e2f8089761cfcd2f2039ffaf64a5eb025 Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Thu, 15 Dec 2016 15:11:15 -0600 Subject: [PATCH] Overhaul product views a little, per customization needs --- tailbone/forms/renderers/__init__.py | 6 ++- tailbone/forms/renderers/products.py | 45 +++++++++++++++----- tailbone/templates/products/view.mako | 59 +++++++++++++++------------ tailbone/views/master.py | 4 ++ tailbone/views/products.py | 34 +++++++-------- 5 files changed, 94 insertions(+), 54 deletions(-) diff --git a/tailbone/forms/renderers/__init__.py b/tailbone/forms/renderers/__init__.py index 78e2131f..bf9a58c5 100644 --- a/tailbone/forms/renderers/__init__.py +++ b/tailbone/forms/renderers/__init__.py @@ -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 diff --git a/tailbone/forms/renderers/products.py b/tailbone/forms/renderers/products.py index e3a71c43..f19b4abc 100644 --- a/tailbone/forms/renderers/products.py +++ b/tailbone/forms/renderers/products.py @@ -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): diff --git a/tailbone/templates/products/view.mako b/tailbone/templates/products/view.mako index a7a1612d..c9b7dc69 100644 --- a/tailbone/templates/products/view.mako +++ b/tailbone/templates/products/view.mako @@ -63,10 +63,42 @@ ${render_field_readonly(form.fieldset.deleted)} +<%def name="movement_panel()"> +
+

Movement

+
+ ${self.render_movement_fields(form)} +
+
+ + <%def name="render_movement_fields(form)"> ${render_field_readonly(form.fieldset.last_sold)} +<%def name="lookup_codes_panel()"> +
+

Additional Lookup Codes

+
+ + + + + + + % for i, code in enumerate(product._codes, 1): + + + + + % endfor + +
SeqCode
${code.ordinal}${code.code}
+
+
+ + +
-
-

Movement

-
- ${self.render_movement_fields(form)} -
-
+ ${self.movement_panel()}

Vendor Sources

@@ -148,25 +175,7 @@
-
-

Additional Lookup Codes

-
- - - - - - - % for i, code in enumerate(product._codes, 1): - - - - - % endfor - -
SeqCode
${code.ordinal}${code.code}
-
-
+ ${self.lookup_codes_panel()} ${self.extra_right_panels()} diff --git a/tailbone/views/master.py b/tailbone/views/master.py index a3af3e29..12bf2b75 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -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)) diff --git a/tailbone/views/products.py b/tailbone/views/products.py index f8a811c7..56e9679a 100644 --- a/tailbone/views/products.py +++ b/tailbone/views/products.py @@ -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)\