Refactor products view to use master3
This commit is contained in:
parent
d9a5b4a0f5
commit
3097f46aa1
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2018 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -482,6 +482,8 @@ class Form(object):
|
||||||
self.set_renderer(key, self.render_currency)
|
self.set_renderer(key, self.render_currency)
|
||||||
elif type_ == 'quantity':
|
elif type_ == 'quantity':
|
||||||
self.set_renderer(key, self.render_quantity)
|
self.set_renderer(key, self.render_quantity)
|
||||||
|
elif type_ == 'gpc':
|
||||||
|
self.set_renderer(key, self.render_gpc)
|
||||||
elif type_ == 'enum':
|
elif type_ == 'enum':
|
||||||
self.set_renderer(key, self.render_enum)
|
self.set_renderer(key, self.render_enum)
|
||||||
elif type_ == 'codeblock':
|
elif type_ == 'codeblock':
|
||||||
|
@ -638,9 +640,12 @@ class Form(object):
|
||||||
value = self.obtain_value(record, field_name)
|
value = self.obtain_value(record, field_name)
|
||||||
if value is None:
|
if value is None:
|
||||||
return ""
|
return ""
|
||||||
|
try:
|
||||||
if value < 0:
|
if value < 0:
|
||||||
return "(${:0,.2f})".format(0 - value)
|
return "(${:0,.2f})".format(0 - value)
|
||||||
return "${:0,.2f}".format(value)
|
return "${:0,.2f}".format(value)
|
||||||
|
except ValueError:
|
||||||
|
return six.text_type(value)
|
||||||
|
|
||||||
def render_quantity(self, obj, field):
|
def render_quantity(self, obj, field):
|
||||||
value = self.obtain_value(obj, field)
|
value = self.obtain_value(obj, field)
|
||||||
|
@ -648,6 +653,12 @@ class Form(object):
|
||||||
return ""
|
return ""
|
||||||
return pretty_quantity(value)
|
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):
|
def render_enum(self, record, field_name):
|
||||||
value = self.obtain_value(record, field_name)
|
value = self.obtain_value(record, field_name)
|
||||||
if value is None:
|
if value is None:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
## -*- 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" />
|
|
||||||
|
|
||||||
<%def name="extra_styles()">
|
<%def name="extra_styles()">
|
||||||
${parent.extra_styles()}
|
${parent.extra_styles()}
|
||||||
|
@ -63,15 +62,15 @@
|
||||||
##############################
|
##############################
|
||||||
|
|
||||||
<%def name="render_main_fields(form)">
|
<%def name="render_main_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.upc)}
|
${form.render_field_readonly('upc')}
|
||||||
${render_field_readonly(form.fieldset.brand)}
|
${form.render_field_readonly('brand')}
|
||||||
${render_field_readonly(form.fieldset.description)}
|
${form.render_field_readonly('description')}
|
||||||
${render_field_readonly(form.fieldset.size)}
|
${form.render_field_readonly('size')}
|
||||||
${render_field_readonly(form.fieldset.unit_size)}
|
${form.render_field_readonly('unit_size')}
|
||||||
${render_field_readonly(form.fieldset.unit_of_measure)}
|
${form.render_field_readonly('unit_of_measure')}
|
||||||
${render_field_readonly(form.fieldset.unit)}
|
${form.render_field_readonly('unit')}
|
||||||
${render_field_readonly(form.fieldset.pack_size)}
|
${form.render_field_readonly('pack_size')}
|
||||||
${render_field_readonly(form.fieldset.case_size)}
|
${form.render_field_readonly('case_size')}
|
||||||
${self.extra_main_fields(form)}
|
${self.extra_main_fields(form)}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -113,30 +112,30 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_organization_fields(form)">
|
<%def name="render_organization_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.department)}
|
${form.render_field_readonly('department')}
|
||||||
${render_field_readonly(form.fieldset.subdepartment)}
|
${form.render_field_readonly('subdepartment')}
|
||||||
${render_field_readonly(form.fieldset.category)}
|
${form.render_field_readonly('category')}
|
||||||
${render_field_readonly(form.fieldset.family)}
|
${form.render_field_readonly('family')}
|
||||||
${render_field_readonly(form.fieldset.report_code)}
|
${form.render_field_readonly('report_code')}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_price_fields(form)">
|
<%def name="render_price_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.price_required)}
|
${form.render_field_readonly('price_required')}
|
||||||
${render_field_readonly(form.fieldset.regular_price)}
|
${form.render_field_readonly('regular_price')}
|
||||||
${render_field_readonly(form.fieldset.current_price)}
|
${form.render_field_readonly('current_price')}
|
||||||
${render_field_readonly(form.fieldset.current_price_ends)}
|
${form.render_field_readonly('current_price_ends')}
|
||||||
${render_field_readonly(form.fieldset.deposit_link)}
|
${form.render_field_readonly('deposit_link')}
|
||||||
${render_field_readonly(form.fieldset.tax)}
|
${form.render_field_readonly('tax')}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_flag_fields(form)">
|
<%def name="render_flag_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.weighed)}
|
${form.render_field_readonly('weighed')}
|
||||||
${render_field_readonly(form.fieldset.discountable)}
|
${form.render_field_readonly('discountable')}
|
||||||
${render_field_readonly(form.fieldset.special_order)}
|
${form.render_field_readonly('special_order')}
|
||||||
${render_field_readonly(form.fieldset.organic)}
|
${form.render_field_readonly('organic')}
|
||||||
${render_field_readonly(form.fieldset.not_for_sale)}
|
${form.render_field_readonly('not_for_sale')}
|
||||||
${render_field_readonly(form.fieldset.discontinued)}
|
${form.render_field_readonly('discontinued')}
|
||||||
${render_field_readonly(form.fieldset.deleted)}
|
${form.render_field_readonly('deleted')}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="movement_panel()">
|
<%def name="movement_panel()">
|
||||||
|
@ -149,7 +148,7 @@
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="render_movement_fields(form)">
|
<%def name="render_movement_fields(form)">
|
||||||
${render_field_readonly(form.fieldset.last_sold)}
|
${form.render_field_readonly('last_sold')}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
<%def name="lookup_codes_panel()">
|
<%def name="lookup_codes_panel()">
|
||||||
|
@ -210,7 +209,7 @@
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2>Notes</h2>
|
<h2>Notes</h2>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<div class="field">${form.fieldset.notes.render_readonly()}</div>
|
<div class="field">${form.render_field_readonly('notes')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
@ -219,7 +218,7 @@
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<h2>Ingredients</h2>
|
<h2>Ingredients</h2>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
${render_field_readonly(form.fieldset.ingredients)}
|
${form.render_field_readonly('ingredients')}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
|
@ -381,6 +381,8 @@ class MasterView(View):
|
||||||
'instance_deletable': self.deletable_instance(instance),
|
'instance_deletable': self.deletable_instance(instance),
|
||||||
'form': form,
|
'form': form,
|
||||||
}
|
}
|
||||||
|
if hasattr(form, 'make_deform_form'):
|
||||||
|
context['dform'] = form.make_deform_form()
|
||||||
if self.has_rows:
|
if self.has_rows:
|
||||||
context['rows_grid'] = grid.render_complete(allow_save_defaults=False,
|
context['rows_grid'] = grid.render_complete(allow_save_defaults=False,
|
||||||
tools=self.make_row_grid_tools(instance))
|
tools=self.make_row_grid_tools(instance))
|
||||||
|
|
|
@ -41,16 +41,15 @@ from rattail.util import load_object, pretty_quantity
|
||||||
from rattail.batch import get_batch_handler
|
from rattail.batch import get_batch_handler
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
import formalchemy as fa
|
|
||||||
from deform import widget as dfwidget
|
from deform import widget as dfwidget
|
||||||
from pyramid import httpexceptions
|
from pyramid import httpexceptions
|
||||||
from pyramid.renderers import render_to_response
|
|
||||||
from webhelpers2.html import tags, HTML
|
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.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.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
|
# 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',
|
'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 = {
|
labels = {
|
||||||
'status_code': "Status",
|
'status_code': "Status",
|
||||||
}
|
}
|
||||||
|
@ -312,71 +351,136 @@ class ProductsView(MasterView):
|
||||||
return price.product
|
return price.product
|
||||||
raise httpexceptions.HTTPNotFound()
|
raise httpexceptions.HTTPNotFound()
|
||||||
|
|
||||||
def _preconfigure_fieldset(self, fs):
|
def configure_form(self, f):
|
||||||
fs.upc.set(label="UPC", renderer=forms.renderers.GPCFieldRenderer)
|
super(ProductsView, self).configure_form(f)
|
||||||
fs.brand.set(renderer=forms.renderers.BrandFieldRenderer, options=[])
|
|
||||||
fs.department.set(renderer=forms.renderers.DepartmentFieldRenderer)
|
# upc
|
||||||
fs.subdepartment.set(renderer=forms.renderers.SubdepartmentFieldRenderer)
|
f.set_type('upc', 'gpc')
|
||||||
fs.category.set(renderer=forms.renderers.CategoryFieldRenderer)
|
f.set_label('upc', "UPC")
|
||||||
fs.unit_size.set(renderer=forms.renderers.QuantityFieldRenderer)
|
|
||||||
fs.unit_of_measure.set(label="Unit of Measure",
|
# department
|
||||||
renderer=forms.renderers.EnumFieldRenderer(self.enum.UNIT_OF_MEASURE))
|
f.set_renderer('department', self.render_department)
|
||||||
fs.unit.set(renderer=forms.renderers.ProductFieldRenderer, label="Unit Item")
|
|
||||||
fs.pack_size.set(renderer=forms.renderers.QuantityFieldRenderer)
|
# subdepartment
|
||||||
fs.regular_price.set(renderer=forms.renderers.PriceFieldRenderer, readonly=True)
|
f.set_renderer('subdepartment', self.render_subdepartment)
|
||||||
fs.current_price.set(renderer=forms.renderers.PriceFieldRenderer, readonly=True)
|
|
||||||
fs.last_sold.set(readonly=True)
|
# category
|
||||||
fs.status_code.set(label="Status")
|
f.set_renderer('category', self.render_category)
|
||||||
fs.notes.set(renderer=fa.TextAreaFieldRenderer, size=(80, 10))
|
|
||||||
fs.append(fa.Field('current_price_ends', type=fa.types.DateTime, readonly=True,
|
# unit_size
|
||||||
value=lambda p: p.current_price.ends if p.current_price else None))
|
f.set_type('unit_size', 'quantity')
|
||||||
fs.append(fa.Field('inventory_on_hand', readonly=True, label="On Hand",
|
|
||||||
value=lambda p: p.inventory.on_hand if p.inventory else None))
|
# unit_of_measure
|
||||||
fs.append(fa.Field('inventory_on_order', readonly=True, label="On Order",
|
f.set_enum('unit_of_measure', self.enum.UNIT_OF_MEASURE)
|
||||||
value=lambda p: p.inventory.on_order if p.inventory else None))
|
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'):
|
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):
|
def template_kwargs_view(self, **kwargs):
|
||||||
kwargs['image'] = False
|
kwargs['image'] = False
|
||||||
|
@ -491,7 +595,7 @@ class ProductsView(MasterView):
|
||||||
colander.SchemaNode(colander.String(), name='notes', missing=colander.null),
|
colander.SchemaNode(colander.String(), name='notes', missing=colander.null),
|
||||||
)
|
)
|
||||||
|
|
||||||
form = forms2.Form(schema=schema, request=self.request,
|
form = forms.Form(schema=schema, request=self.request,
|
||||||
cancel_url=self.get_index_url())
|
cancel_url=self.get_index_url())
|
||||||
form.set_type('notes', 'text')
|
form.set_type('notes', 'text')
|
||||||
|
|
||||||
|
@ -504,7 +608,7 @@ class ProductsView(MasterView):
|
||||||
for node in schema:
|
for node in schema:
|
||||||
node.param_name = node.name
|
node.param_name = node.name
|
||||||
node.name = '{}_{}'.format(key, 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':
|
if self.request.method == 'POST':
|
||||||
controls = self.request.POST.items()
|
controls = self.request.POST.items()
|
||||||
|
|
Loading…
Reference in a new issue