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

View file

@ -40,13 +40,14 @@ from rattail.exceptions import LabelPrintingError
from rattail.util import load_object, pretty_quantity from rattail.util import load_object, pretty_quantity
from rattail.batch import get_batch_handler from rattail.batch import get_batch_handler
import wtforms import colander
import formalchemy as fa import formalchemy as fa
from deform import widget as dfwidget
from pyramid import httpexceptions from pyramid import httpexceptions
from pyramid.renderers import render_to_response from pyramid.renderers import render_to_response
from webhelpers2.html import tags, HTML from webhelpers2.html import tags, HTML
from tailbone import forms, grids from tailbone import forms, forms2, grids
from tailbone.db import Session from tailbone.db import Session
from tailbone.views import MasterView2 as MasterView, AutocompleteView from tailbone.views import MasterView2 as MasterView, AutocompleteView
from tailbone.progress import SessionProgress from tailbone.progress import SessionProgress
@ -483,60 +484,74 @@ class ProductsView(MasterView):
supported[key] = handler supported[key] = handler
batch_options.append((key, handler.get_model_title())) batch_options.append((key, handler.get_model_title()))
class MakeBatchForm(wtforms.Form): schema = colander.SchemaNode(
batch_type = wtforms.SelectField(choices=batch_options) 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 = {} params_forms = {}
for key, handler in supported.items(): for key, handler in supported.items():
make_form = getattr(self, 'make_batch_params_form_{}'.format(key), None) make_schema = getattr(self, 'make_batch_params_schema_{}'.format(key), None)
if make_form: if make_schema:
params_forms[key] = make_form() 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(): if self.request.method == 'POST':
batch_key = form.batch_type.data controls = self.request.POST.items()
params = {} data = form.validate(controls)
batch_key = data['batch_type']
params = {
'description': data['description'],
'notes': data['notes']}
pform = params_forms.get(batch_key) pform = params_forms.get(batch_key)
if not pform or pform.validate(): # params form must validate if present if pform:
if pform: pdata = pform.validate(controls)
params = dict((name, pform[name].data) for name in pform._fields) for field in pform:
handler = get_batch_handler(self.rattail_config, batch_key, param_name = pform.schema[field.name].param_name
default=supported[batch_key].spec) params[param_name] = pdata[field.name]
products = self.get_effective_data()
progress = SessionProgress(self.request, 'products.batch')
thread = Thread(target=self.make_batch_thread,
args=(handler, self.request.user.uuid, products, params, progress))
thread.start()
return self.render_progress(progress, {
'cancel_url': self.get_index_url(),
'cancel_msg': "Batch creation was canceled.",
})
return {'form': form, 'params_forms': params_forms}
def make_batch_params_form_labels(self): # TODO: should this be done elsewhere?
""" for name in params:
Returns a wtforms.Form object with param fields for making a new if params[name] is colander.null:
Labels Batch. params[name] = None
"""
class LabelsParamsForm(wtforms.Form):
description = wtforms.StringField()
notes = wtforms.TextAreaField()
return LabelsParamsForm(self.request.POST) handler = get_batch_handler(self.rattail_config, batch_key,
default=supported[batch_key].spec)
products = self.get_effective_data()
progress = SessionProgress(self.request, 'products.batch')
thread = Thread(target=self.make_batch_thread,
args=(handler, self.request.user.uuid, products, params, progress))
thread.start()
return self.render_progress(progress, {
'cancel_url': self.get_index_url(),
'cancel_msg': "Batch creation was canceled.",
})
def make_batch_params_form_pricing(self): return {
""" 'form': form,
Returns a wtforms.Form object with param fields for making a new 'dform': form.make_deform_form(), # TODO: hacky? at least is explicit..
Pricing Batch. 'params_forms': params_forms,
""" }
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) def make_batch_params_schema_pricing(self):
"""
Return params schema for making a pricing batch.
"""
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): def make_batch_thread(self, handler, user_uuid, products, params, progress):
""" """