Refactor forms logic when making batch from product query

use colander/deform instead of wtforms.  also make sure param names are unique
per batch type, within form controls
This commit is contained in:
Lance Edgar 2018-01-04 15:08:03 -06:00
parent 2cc0bb1995
commit 80903bde38
2 changed files with 87 additions and 64 deletions

View file

@ -1,4 +1,4 @@
## -*- coding: utf-8 -*-
## -*- coding: utf-8; -*-
<%inherit file="/base.mako" />
<%def name="title()">Products: Create Batch</%def>
@ -12,19 +12,12 @@
<script type="text/javascript">
$(function() {
$('#batch_type').selectmenu({
change: function(event, ui) {
$('select[name="batch_type"]').on('selectmenuchange', function(event, ui) {
$('.params-wrapper').hide();
$('.params-wrapper.' + ui.item.value).show();
}
});
$('.params-wrapper.' + $('#batch_type').val()).show();
$('#make-batch').click(function() {
$(this).button('disable').button('option', 'label', "Working, please wait...");
$(this).parents('form:first').submit();
});
$('.params-wrapper.' + $('select[name="batch_type"]').val()).show();
});
</script>
@ -39,26 +32,41 @@
</style>
</%def>
<%def name="render_deform_field(field)">
<div class="field-wrapper ${field.name}">
<div class="field-row">
<label for="${field.oid}">${field.title}</label>
<div class="field">
${field.serialize()|n}
</div>
</div>
</div>
</%def>
<ul id="context-menu">
${self.context_menu_items()}
</ul>
<div class="form">
${h.form(request.current_route_url())}
${h.form(request.current_route_url(), class_='autodisable')}
${h.csrf_token(request)}
${self.wtfield(form, 'batch_type')}
${render_deform_field(dform['batch_type'])}
${render_deform_field(dform['description'])}
${render_deform_field(dform['notes'])}
% for key, pform in params_forms.items():
<div class="params-wrapper ${key}">
% for name in pform._fields:
${self.wtfield(pform, name)}
## TODO: hacky to use deform? at least is explicit..
% for field in pform.make_deform_form():
${render_deform_field(field)}
% endfor
</div>
% endfor
<div class="buttons">
<button type="button" id="make-batch">Create Batch</button>
${h.submit('make-batch', "Create Batch")}
${h.link_to("Cancel", url('products'), class_='button')}
</div>

View file

@ -40,13 +40,14 @@ from rattail.exceptions import LabelPrintingError
from rattail.util import load_object, pretty_quantity
from rattail.batch import get_batch_handler
import wtforms
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, grids
from tailbone import forms, forms2, grids
from tailbone.db import Session
from tailbone.views import MasterView2 as MasterView, AutocompleteView
from tailbone.progress import SessionProgress
@ -483,23 +484,47 @@ class ProductsView(MasterView):
supported[key] = handler
batch_options.append((key, handler.get_model_title()))
class MakeBatchForm(wtforms.Form):
batch_type = wtforms.SelectField(choices=batch_options)
schema = colander.SchemaNode(
colander.Mapping(),
colander.SchemaNode(colander.String(), name='batch_type', widget=dfwidget.SelectWidget(values=batch_options)),
colander.SchemaNode(colander.String(), name='description', missing=colander.null),
colander.SchemaNode(colander.String(), name='notes', missing=colander.null),
)
form = forms2.Form(schema=schema, request=self.request,
cancel_url=self.get_index_url())
form.set_type('notes', 'text')
form = MakeBatchForm(self.request.POST)
params_forms = {}
for key, handler in supported.items():
make_form = getattr(self, 'make_batch_params_form_{}'.format(key), None)
if make_form:
params_forms[key] = make_form()
make_schema = getattr(self, 'make_batch_params_schema_{}'.format(key), None)
if make_schema:
schema = make_schema()
# must prefix node names with batch key, to guarantee unique
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)
if self.request.method == 'POST' and form.validate():
batch_key = form.batch_type.data
params = {}
if self.request.method == 'POST':
controls = self.request.POST.items()
data = form.validate(controls)
batch_key = data['batch_type']
params = {
'description': data['description'],
'notes': data['notes']}
pform = params_forms.get(batch_key)
if not pform or pform.validate(): # params form must validate if present
if pform:
params = dict((name, pform[name].data) for name in pform._fields)
pdata = pform.validate(controls)
for field in pform:
param_name = pform.schema[field.name].param_name
params[param_name] = pdata[field.name]
# TODO: should this be done elsewhere?
for name in params:
if params[name] is colander.null:
params[name] = None
handler = get_batch_handler(self.rattail_config, batch_key,
default=supported[batch_key].spec)
products = self.get_effective_data()
@ -512,31 +537,21 @@ class ProductsView(MasterView):
'cancel_msg': "Batch creation was canceled.",
})
return {'form': form, 'params_forms': params_forms}
return {
'form': form,
'dform': form.make_deform_form(), # TODO: hacky? at least is explicit..
'params_forms': params_forms,
}
def make_batch_params_form_labels(self):
def make_batch_params_schema_pricing(self):
"""
Returns a wtforms.Form object with param fields for making a new
Labels Batch.
Return params schema for making a pricing batch.
"""
class LabelsParamsForm(wtforms.Form):
description = wtforms.StringField()
notes = wtforms.TextAreaField()
return LabelsParamsForm(self.request.POST)
def make_batch_params_form_pricing(self):
"""
Returns a wtforms.Form object with param fields for making a new
Pricing Batch.
"""
class PricingParamsForm(wtforms.Form):
description = wtforms.StringField()
min_diff_threshold = wtforms.DecimalField(places=2, validators=[wtforms.validators.optional()])
calculate_for_manual = wtforms.BooleanField()
notes = wtforms.TextAreaField()
return PricingParamsForm(self.request.POST)
return colander.SchemaNode(
colander.Mapping(),
colander.SchemaNode(colander.Decimal(), name='min_diff_threshold', quant='1.00', missing=colander.null),
colander.SchemaNode(colander.Boolean(), name='calculate_for_manual'),
)
def make_batch_thread(self, handler, user_uuid, products, params, progress):
"""