Refactor products view to use master3

This commit is contained in:
Lance Edgar 2018-01-08 18:03:51 -06:00
parent d9a5b4a0f5
commit 3097f46aa1
4 changed files with 221 additions and 105 deletions

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 Lance Edgar
# Copyright © 2010-2018 Lance Edgar
#
# This file is part of Rattail.
#
@ -482,6 +482,8 @@ class Form(object):
self.set_renderer(key, self.render_currency)
elif type_ == 'quantity':
self.set_renderer(key, self.render_quantity)
elif type_ == 'gpc':
self.set_renderer(key, self.render_gpc)
elif type_ == 'enum':
self.set_renderer(key, self.render_enum)
elif type_ == 'codeblock':
@ -638,9 +640,12 @@ class Form(object):
value = self.obtain_value(record, field_name)
if value is None:
return ""
if value < 0:
return "(${:0,.2f})".format(0 - value)
return "${:0,.2f}".format(value)
try:
if value < 0:
return "(${:0,.2f})".format(0 - value)
return "${:0,.2f}".format(value)
except ValueError:
return six.text_type(value)
def render_quantity(self, obj, field):
value = self.obtain_value(obj, field)
@ -648,6 +653,12 @@ class Form(object):
return ""
return pretty_quantity(value)
def render_gpc(self, obj, field):
value = self.obtain_value(obj, field)
if value is None:
return ""
return value.pretty()
def render_enum(self, record, field_name):
value = self.obtain_value(record, field_name)
if value is None:

View file

@ -1,6 +1,5 @@
## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" />
<%namespace file="/forms/lib.mako" import="render_field_readonly" />
<%def name="extra_styles()">
${parent.extra_styles()}
@ -63,15 +62,15 @@
##############################
<%def name="render_main_fields(form)">
${render_field_readonly(form.fieldset.upc)}
${render_field_readonly(form.fieldset.brand)}
${render_field_readonly(form.fieldset.description)}
${render_field_readonly(form.fieldset.size)}
${render_field_readonly(form.fieldset.unit_size)}
${render_field_readonly(form.fieldset.unit_of_measure)}
${render_field_readonly(form.fieldset.unit)}
${render_field_readonly(form.fieldset.pack_size)}
${render_field_readonly(form.fieldset.case_size)}
${form.render_field_readonly('upc')}
${form.render_field_readonly('brand')}
${form.render_field_readonly('description')}
${form.render_field_readonly('size')}
${form.render_field_readonly('unit_size')}
${form.render_field_readonly('unit_of_measure')}
${form.render_field_readonly('unit')}
${form.render_field_readonly('pack_size')}
${form.render_field_readonly('case_size')}
${self.extra_main_fields(form)}
</%def>
@ -113,30 +112,30 @@
</%def>
<%def name="render_organization_fields(form)">
${render_field_readonly(form.fieldset.department)}
${render_field_readonly(form.fieldset.subdepartment)}
${render_field_readonly(form.fieldset.category)}
${render_field_readonly(form.fieldset.family)}
${render_field_readonly(form.fieldset.report_code)}
${form.render_field_readonly('department')}
${form.render_field_readonly('subdepartment')}
${form.render_field_readonly('category')}
${form.render_field_readonly('family')}
${form.render_field_readonly('report_code')}
</%def>
<%def name="render_price_fields(form)">
${render_field_readonly(form.fieldset.price_required)}
${render_field_readonly(form.fieldset.regular_price)}
${render_field_readonly(form.fieldset.current_price)}
${render_field_readonly(form.fieldset.current_price_ends)}
${render_field_readonly(form.fieldset.deposit_link)}
${render_field_readonly(form.fieldset.tax)}
${form.render_field_readonly('price_required')}
${form.render_field_readonly('regular_price')}
${form.render_field_readonly('current_price')}
${form.render_field_readonly('current_price_ends')}
${form.render_field_readonly('deposit_link')}
${form.render_field_readonly('tax')}
</%def>
<%def name="render_flag_fields(form)">
${render_field_readonly(form.fieldset.weighed)}
${render_field_readonly(form.fieldset.discountable)}
${render_field_readonly(form.fieldset.special_order)}
${render_field_readonly(form.fieldset.organic)}
${render_field_readonly(form.fieldset.not_for_sale)}
${render_field_readonly(form.fieldset.discontinued)}
${render_field_readonly(form.fieldset.deleted)}
${form.render_field_readonly('weighed')}
${form.render_field_readonly('discountable')}
${form.render_field_readonly('special_order')}
${form.render_field_readonly('organic')}
${form.render_field_readonly('not_for_sale')}
${form.render_field_readonly('discontinued')}
${form.render_field_readonly('deleted')}
</%def>
<%def name="movement_panel()">
@ -149,7 +148,7 @@
</%def>
<%def name="render_movement_fields(form)">
${render_field_readonly(form.fieldset.last_sold)}
${form.render_field_readonly('last_sold')}
</%def>
<%def name="lookup_codes_panel()">
@ -210,7 +209,7 @@
<div class="panel">
<h2>Notes</h2>
<div class="panel-body">
<div class="field">${form.fieldset.notes.render_readonly()}</div>
<div class="field">${form.render_field_readonly('notes')}</div>
</div>
</div>
</%def>
@ -219,7 +218,7 @@
<div class="panel">
<h2>Ingredients</h2>
<div class="panel-body">
${render_field_readonly(form.fieldset.ingredients)}
${form.render_field_readonly('ingredients')}
</div>
</div>
</%def>

View file

@ -381,6 +381,8 @@ class MasterView(View):
'instance_deletable': self.deletable_instance(instance),
'form': form,
}
if hasattr(form, 'make_deform_form'):
context['dform'] = form.make_deform_form()
if self.has_rows:
context['rows_grid'] = grid.render_complete(allow_save_defaults=False,
tools=self.make_row_grid_tools(instance))

View file

@ -41,16 +41,15 @@ from rattail.util import load_object, pretty_quantity
from rattail.batch import get_batch_handler
import colander
import formalchemy as fa
from deform import widget as dfwidget
from pyramid import httpexceptions
from pyramid.renderers import render_to_response
from webhelpers2.html import tags, HTML
from tailbone import forms, forms2, grids
from tailbone import forms2 as forms, grids
from tailbone.db import Session
from tailbone.views import MasterView2 as MasterView, AutocompleteView
from tailbone.views import MasterView3 as MasterView, AutocompleteView
from tailbone.progress import SessionProgress
from tailbone.util import raw_datetime
# TODO: For a moment I thought this was going to be necessary, but now I think
@ -91,6 +90,46 @@ class ProductsView(MasterView):
'current_price',
]
form_fields = [
'upc',
'brand',
'description',
'unit_size',
'unit_of_measure',
'size',
'unit',
'pack_size',
'case_size',
'weighed',
'department',
'subdepartment',
'category',
'family',
'report_code',
'regular_price',
'current_price',
'current_price_ends',
'deposit_link',
'tax',
'organic',
'kosher',
'vegan',
'vegetarian',
'gluten_free',
'sugar_free',
'discountable',
'special_order',
'not_for_sale',
'ingredients',
'notes',
'status_code',
'discontinued',
'deleted',
'last_sold',
'inventory_on_hand',
'inventory_on_order',
]
labels = {
'status_code': "Status",
}
@ -312,71 +351,136 @@ class ProductsView(MasterView):
return price.product
raise httpexceptions.HTTPNotFound()
def _preconfigure_fieldset(self, fs):
fs.upc.set(label="UPC", renderer=forms.renderers.GPCFieldRenderer)
fs.brand.set(renderer=forms.renderers.BrandFieldRenderer, options=[])
fs.department.set(renderer=forms.renderers.DepartmentFieldRenderer)
fs.subdepartment.set(renderer=forms.renderers.SubdepartmentFieldRenderer)
fs.category.set(renderer=forms.renderers.CategoryFieldRenderer)
fs.unit_size.set(renderer=forms.renderers.QuantityFieldRenderer)
fs.unit_of_measure.set(label="Unit of Measure",
renderer=forms.renderers.EnumFieldRenderer(self.enum.UNIT_OF_MEASURE))
fs.unit.set(renderer=forms.renderers.ProductFieldRenderer, label="Unit Item")
fs.pack_size.set(renderer=forms.renderers.QuantityFieldRenderer)
fs.regular_price.set(renderer=forms.renderers.PriceFieldRenderer, readonly=True)
fs.current_price.set(renderer=forms.renderers.PriceFieldRenderer, readonly=True)
fs.last_sold.set(readonly=True)
fs.status_code.set(label="Status")
fs.notes.set(renderer=fa.TextAreaFieldRenderer, size=(80, 10))
fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True,
value=lambda p: p.current_price.ends if p.current_price else None))
fs.append(fa.Field('inventory_on_hand', readonly=True, label="On Hand",
value=lambda p: p.inventory.on_hand if p.inventory else None))
fs.append(fa.Field('inventory_on_order', readonly=True, label="On Order",
value=lambda p: p.inventory.on_order if p.inventory else None))
def configure_form(self, f):
super(ProductsView, self).configure_form(f)
# upc
f.set_type('upc', 'gpc')
f.set_label('upc', "UPC")
# department
f.set_renderer('department', self.render_department)
# subdepartment
f.set_renderer('subdepartment', self.render_subdepartment)
# category
f.set_renderer('category', self.render_category)
# unit_size
f.set_type('unit_size', 'quantity')
# unit_of_measure
f.set_enum('unit_of_measure', self.enum.UNIT_OF_MEASURE)
f.set_label('unit_of_measure', "Unit of Measure")
# unit
f.set_renderer('unit', self.render_unit)
f.set_label('unit', "Unit Item")
# pack_size
f.set_type('pack_size', 'quantity')
# regular_price
f.set_readonly('regular_price')
f.set_renderer('regular_price', self.render_price)
# current_price
f.set_readonly('current_price')
f.set_renderer('current_price', self.render_price)
# last_sold
f.set_readonly('last_sold')
# status_code
f.set_label('status_code', "Status")
# notes
f.set_widget('notes', dfwidget.TextAreaWidget(cols=80, rows=10))
# current_price_ends
f.set_readonly('current_price_ends')
f.set_renderer('current_price_ends', self.render_current_price_ends)
# inventory_on_hand
f.set_readonly('inventory_on_hand')
f.set_renderer('inventory_on_hand', self.render_inventory_on_hand)
f.set_label('inventory_on_hand', "On Hand")
# inventory_on_order
f.set_readonly('inventory_on_order')
f.set_renderer('inventory_on_order', self.render_inventory_on_order)
f.set_label('inventory_on_order', "On Order")
def configure_fieldset(self, fs):
fs.configure(
include=[
fs.upc,
fs.brand,
fs.description,
fs.unit_size,
fs.unit_of_measure,
fs.size,
fs.unit,
fs.pack_size,
fs.case_size,
fs.weighed,
fs.department,
fs.subdepartment,
fs.category,
fs.family,
fs.report_code,
fs.price_required,
fs.regular_price,
fs.current_price,
fs.current_price_ends,
fs.deposit_link,
fs.tax,
fs.organic,
fs.kosher,
fs.vegan,
fs.vegetarian,
fs.gluten_free,
fs.sugar_free,
fs.discountable,
fs.special_order,
fs.not_for_sale,
fs.ingredients,
fs.notes,
fs.status_code,
fs.discontinued,
fs.deleted,
fs.last_sold,
])
if not self.request.has_perm('products.view_deleted'):
del fs.deleted
f.remove('deleted')
def render_department(self, product, field):
department = product.department
if not department:
return ""
if department.number:
text = '({}) {}'.format(department.number, department.name)
else:
text = department.name
url = self.request.route_url('departments.view', uuid=department.uuid)
return tags.link_to(text, url)
def render_subdepartment(self, product, field):
subdepartment = product.subdepartment
if not subdepartment:
return ""
if subdepartment.number:
text = '({}) {}'.format(subdepartment.number, subdepartment.name)
else:
text = subdepartment.name
url = self.request.route_url('subdepartments.view', uuid=subdepartment.uuid)
return tags.link_to(text, url)
def render_category(self, product, field):
category = product.category
if not category:
return ""
if category.code:
text = '({}) {}'.format(category.code, category.name)
elif category.number:
text = '({}) {}'.format(category.number, category.name)
else:
text = category.name
url = self.request.route_url('categories.view', uuid=category.uuid)
return tags.link_to(text, url)
def render_unit(self, product, field):
product = product.unit
if not product:
return ""
text = product.full_description
url = self.request.route_url('products.view', uuid=product.uuid)
return tags.link_to(text, url)
def render_current_price_ends(self, product, field):
if not product.current_price:
return ""
value = product.current_price.ends
if not value:
return ""
return raw_datetime(self.request.rattail_config, value)
def render_inventory_on_hand(self, product, field):
if not product.inventory:
return ""
value = product.inventory.on_hand
if not value:
return ""
return pretty_quantity(value)
def render_inventory_on_order(self, product, field):
if not product.inventory:
return ""
value = product.inventory.on_order
if not value:
return ""
return pretty_quantity(value)
def template_kwargs_view(self, **kwargs):
kwargs['image'] = False
@ -491,8 +595,8 @@ class ProductsView(MasterView):
colander.SchemaNode(colander.String(), name='notes', missing=colander.null),
)
form = forms2.Form(schema=schema, request=self.request,
cancel_url=self.get_index_url())
form = forms.Form(schema=schema, request=self.request,
cancel_url=self.get_index_url())
form.set_type('notes', 'text')
params_forms = {}
@ -504,7 +608,7 @@ class ProductsView(MasterView):
for node in schema:
node.param_name = node.name
node.name = '{}_{}'.format(key, node.name)
params_forms[key] = forms2.Form(schema=schema, request=self.request)
params_forms[key] = forms.Form(schema=schema, request=self.request)
if self.request.method == 'POST':
controls = self.request.POST.items()