Several disparate changes needed for vendor catalog improvements

- invoke vendor handler where appropriate, e.g. for parsers
- reverse "polarity" of dropdown chooser setting; rename it
- tweak autocomplete behavior yet again, for dynamic values
- auto-select vendor upon parser selection, when possible
This commit is contained in:
Lance Edgar 2022-01-07 19:27:10 -06:00
parent ab61778d35
commit 88b3279e63
12 changed files with 164 additions and 93 deletions

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar # Copyright © 2010-2022 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -247,6 +247,7 @@ class JQueryAutocompleteWidget(dfwidget.AutocompleteInputWidget):
template = 'autocomplete_jquery' template = 'autocomplete_jquery'
requirements = None requirements = None
field_display = "" field_display = ""
assigned_label = None
service_url = None service_url = None
cleared_callback = None cleared_callback = None
selected_callback = None selected_callback = None
@ -275,6 +276,7 @@ class JQueryAutocompleteWidget(dfwidget.AutocompleteInputWidget):
kw['options'] = json.dumps(options) kw['options'] = json.dumps(options)
kw['field_display'] = self.field_display kw['field_display'] = self.field_display
kw['cleared_callback'] = self.cleared_callback kw['cleared_callback'] = self.cleared_callback
kw['assigned_label'] = self.assigned_label
kw.setdefault('selected_callback', self.selected_callback) kw.setdefault('selected_callback', self.selected_callback)
tmpl_values = self.get_template_values(field, cstruct, kw) tmpl_values = self.get_template_values(field, cstruct, kw)
template = readonly and self.readonly_template or self.template template = readonly and self.readonly_template or self.template

View file

@ -95,6 +95,22 @@ const TailboneAutocomplete = {
} }
}, },
watch: {
// TODO: yikes this feels hacky. what happens is, when the
// caller explicitly assigns a new UUID value to the tailbone
// autocomplate component, the underlying buefy autocomplete
// component was not getting the new value. so here we are
// explicitly making sure it is in sync. this issue was
// discovered on the "new vendor catalog batch" page
value(val) {
this.$nextTick(() => {
if (this.buefyValue != val) {
this.buefyValue = val
}
})
},
},
methods: { methods: {
// fetch new search results from the server. this is invoked // fetch new search results from the server. this is invoked

View file

@ -64,7 +64,7 @@
<b-autocomplete ref="autocomplete" <b-autocomplete ref="autocomplete"
:name="name" :name="name"
v-show="!assignedValue && !selected" v-show="!value && !selected"
v-model="buefyValue" v-model="buefyValue"
:placeholder="placeholder" :placeholder="placeholder"
:data="data" :data="data"
@ -76,7 +76,7 @@
</template> </template>
</b-autocomplete> </b-autocomplete>
<b-button v-if="assignedValue || selected" <b-button v-if="value || selected"
style="width: 100%; justify-content: left;" style="width: 100%; justify-content: left;"
@click="clearSelection(true)"> @click="clearSelection(true)">
{{ getDisplayText() }} (click to change) {{ getDisplayText() }} (click to change)

View file

@ -3,6 +3,7 @@
<%def name="extra_javascript()"> <%def name="extra_javascript()">
${parent.extra_javascript()} ${parent.extra_javascript()}
% if not use_buefy:
<script type="text/javascript"> <script type="text/javascript">
var vendormap = { var vendormap = {
@ -50,6 +51,29 @@
}); });
</script> </script>
% endif
</%def> </%def>
<%def name="modify_this_page_vars()">
${parent.modify_this_page_vars()}
<script type="text/javascript">
${form.component_studly}Data.parsers = ${json.dumps(parsers_data)|n}
${form.component_studly}Data.vendorName = null
${form.component_studly}.watch.field_model_parser_key = function(val) {
let parser = this.parsers[val]
if (parser.vendor_uuid) {
if (this.field_model_vendor_uuid != parser.vendor_uuid) {
this.field_model_vendor_uuid = parser.vendor_uuid
this.vendorName = parser.vendor_name
}
}
}
</script>
</%def>
${parent.body()} ${parent.body()}

View file

@ -110,7 +110,8 @@
<tailbone-autocomplete name="${name}" <tailbone-autocomplete name="${name}"
service-url="${url}" service-url="${url}"
v-model="${vmodel}" v-model="${vmodel}"
initial-label="${field_display}"> initial-label="${field_display}"
tal:attributes=":assigned-label assigned_label or 'null';">
</tailbone-autocomplete> </tailbone-autocomplete>
</div> </div>

View file

@ -76,6 +76,8 @@
template: '#${form.component}-template', template: '#${form.component}-template',
components: {}, components: {},
props: {}, props: {},
watch: {},
computed: {},
methods: { methods: {
## TODO: deprecate / remove the latter option here ## TODO: deprecate / remove the latter option here

View file

@ -6,11 +6,11 @@
<h3 class="block is-size-3">Display</h3> <h3 class="block is-size-3">Display</h3>
<div class="block" style="padding-left: 2rem;"> <div class="block" style="padding-left: 2rem;">
<b-field message="If not set, vendor chooser is a dropdown field."> <b-field message="If not set, vendor chooser is an autocomplete field.">
<b-checkbox name="rattail.vendor.use_autocomplete" <b-checkbox name="rattail.vendors.choice_uses_dropdown"
v-model="simpleSettings['rattail.vendor.use_autocomplete']" v-model="simpleSettings['rattail.vendors.choice_uses_dropdown']"
@input="settingsNeedSaved = true"> @input="settingsNeedSaved = true">
Show vendor chooser as autocomplete field Show vendor chooser as dropdown (select) element
</b-checkbox> </b-checkbox>
</b-field> </b-field>

View file

@ -30,15 +30,13 @@ import logging
import six import six
from rattail.db import model, api from rattail.db import model
from rattail.vendors.catalogs import iter_catalog_parsers
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
from webhelpers2.html import tags from webhelpers2.html import tags
from tailbone import forms from tailbone import forms
from tailbone.db import Session
from tailbone.views.batch import FileBatchMasterView from tailbone.views.batch import FileBatchMasterView
from tailbone.diffs import Diff from tailbone.diffs import Diff
@ -139,13 +137,9 @@ class VendorCatalogView(FileBatchMasterView):
def get_parsers(self): def get_parsers(self):
if not hasattr(self, 'parsers'): if not hasattr(self, 'parsers'):
parsers = sorted(iter_catalog_parsers(), key=lambda p: p.display) app = self.get_rattail_app()
supported = self.rattail_config.getlist( vendor_handler = app.get_vendor_handler()
'tailbone', 'batch.vendorcatalog.supported_parsers') self.parsers = vendor_handler.get_supported_catalog_parsers()
if supported:
parsers = [parser for parser in parsers
if parser.key in supported]
self.parsers = parsers
return self.parsers return self.parsers
def configure_grid(self, g): def configure_grid(self, g):
@ -160,24 +154,8 @@ class VendorCatalogView(FileBatchMasterView):
def configure_form(self, f): def configure_form(self, f):
super(VendorCatalogView, self).configure_form(f) super(VendorCatalogView, self).configure_form(f)
app = self.get_rattail_app()
# vendor vendor_handler = app.get_vendor_handler()
f.set_renderer('vendor', self.render_vendor)
if self.creating and 'vendor' in f:
f.replace('vendor', 'vendor_uuid')
f.set_node('vendor_uuid', colander.String())
vendor_display = ""
if self.request.method == 'POST':
if self.request.POST.get('vendor_uuid'):
vendor = self.Session.query(model.Vendor).get(self.request.POST['vendor_uuid'])
if vendor:
vendor_display = six.text_type(vendor)
vendors_url = self.request.route_url('vendors.autocomplete')
f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url))
f.set_label('vendor_uuid', "Vendor")
else:
f.set_readonly('vendor')
# filename # filename
f.set_label('filename', "Catalog File") f.set_label('filename', "Catalog File")
@ -196,12 +174,75 @@ class VendorCatalogView(FileBatchMasterView):
f.set_widget('parser_key', dfwidget.SelectWidget(values=values)) f.set_widget('parser_key', dfwidget.SelectWidget(values=values))
f.set_label('parser_key', "File Type") f.set_label('parser_key', "File Type")
# vendor
f.set_renderer('vendor', self.render_vendor)
if self.creating and 'vendor' in f:
f.replace('vendor', 'vendor_uuid')
f.set_label('vendor_uuid', "Vendor")
use_dropdown = vendor_handler.choice_uses_dropdown()
if use_dropdown:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id,
vendor.name))
for vendor in vendors]
f.set_widget('vendor_uuid',
dfwidget.SelectWidget(values=vendor_values))
else:
vendor_display = ""
if self.request.method == 'POST':
if self.request.POST.get('vendor_uuid'):
vendor = self.Session.query(model.Vendor).get(
self.request.POST['vendor_uuid'])
if vendor:
vendor_display = six.text_type(vendor)
vendors_url = self.request.route_url('vendors.autocomplete')
f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url,
assigned_label='vendorName'))
else:
f.set_readonly('vendor')
# effective # effective
if self.creating: if self.creating:
f.remove('effective') f.remove('effective')
else: else:
f.set_readonly('effective') f.set_readonly('effective')
def template_kwargs_create(self, **kwargs):
use_buefy = self.get_use_buefy()
app = self.get_rattail_app()
vendor_handler = app.get_vendor_handler()
parsers = self.get_parsers()
parsers_data = {}
for parser in parsers:
if use_buefy:
pdata = {'key': parser.key,
'vendor_key': parser.vendor_key}
if parser.vendor_key:
vendor = vendor_handler.get_vendor(self.Session(),
parser.vendor_key)
if vendor:
pdata['vendor_uuid'] = vendor.uuid
pdata['vendor_name'] = vendor.name
parsers_data[parser.key] = pdata
else:
if parser.vendor_key:
vendor = vendor_handler.get_vendor(self.Session(),
parser.vendor_key)
if vendor:
parser.vendormap_value = "{{uuid: '{}', name: '{}'}}".format(
vendor.uuid, vendor.name.replace("'", "\\'"))
else:
log.warning("vendor '{}' not found for parser: {}".format(
parser.vendor_key, parser.key))
parser.vendormap_value = 'null'
else:
parser.vendormap_value = 'null'
kwargs['parsers'] = parsers
kwargs['parsers_data'] = parsers_data
return kwargs
def get_batch_kwargs(self, batch): def get_batch_kwargs(self, batch):
kwargs = super(VendorCatalogView, self).get_batch_kwargs(batch) kwargs = super(VendorCatalogView, self).get_batch_kwargs(batch)
kwargs['parser_key'] = batch.parser_key kwargs['parser_key'] = batch.parser_key
@ -275,23 +316,6 @@ class VendorCatalogView(FileBatchMasterView):
return kwargs return kwargs
def template_kwargs_create(self, **kwargs):
parsers = self.get_parsers()
for parser in parsers:
if parser.vendor_key:
vendor = api.get_vendor(Session(), parser.vendor_key)
if vendor:
parser.vendormap_value = "{{uuid: '{}', name: '{}'}}".format(
vendor.uuid, vendor.name.replace("'", "\\'"))
else:
log.warning("vendor '{}' not found for parser: {}".format(
parser.vendor_key, parser.key))
parser.vendormap_value = 'null'
else:
parser.vendormap_value = 'null'
kwargs['parsers'] = parsers
return kwargs
# TODO: deprecate / remove this # TODO: deprecate / remove this
VendorCatalogsView = VendorCatalogView VendorCatalogsView = VendorCatalogView

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar # Copyright © 2010-2022 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -29,7 +29,6 @@ from __future__ import unicode_literals, absolute_import
import six import six
from rattail.db import model, api from rattail.db import model, api
from rattail.time import localtime
import colander import colander
from deform import widget as dfwidget from deform import widget as dfwidget
@ -230,7 +229,8 @@ class PurchasingBatchView(BatchMasterView):
super(PurchasingBatchView, self).configure_form(f) super(PurchasingBatchView, self).configure_form(f)
model = self.model model = self.model
batch = f.model_instance batch = f.model_instance
today = localtime(self.rattail_config).date() app = self.get_rattail_app()
today = app.localtime().date()
use_buefy = self.get_use_buefy() use_buefy = self.get_use_buefy()
# mode # mode
@ -265,9 +265,15 @@ class PurchasingBatchView(BatchMasterView):
if self.creating: if self.creating:
f.replace('vendor', 'vendor_uuid') f.replace('vendor', 'vendor_uuid')
f.set_label('vendor_uuid', "Vendor") f.set_label('vendor_uuid', "Vendor")
use_autocomplete = self.rattail_config.getbool( vendor_handler = app.get_vendor_handler()
'rattail', 'vendor.use_autocomplete', default=True) use_dropdown = vendor_handler.choice_uses_dropdown()
if use_autocomplete: if use_dropdown:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
f.set_widget('vendor_uuid', dfwidget.SelectWidget(values=vendor_values))
else:
vendor_display = "" vendor_display = ""
if self.request.method == 'POST': if self.request.method == 'POST':
if self.request.POST.get('vendor_uuid'): if self.request.POST.get('vendor_uuid'):
@ -277,12 +283,6 @@ class PurchasingBatchView(BatchMasterView):
vendors_url = self.request.route_url('vendors.autocomplete') vendors_url = self.request.route_url('vendors.autocomplete')
f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget( f.set_widget('vendor_uuid', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url)) field_display=vendor_display, service_url=vendors_url))
else:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
f.set_widget('vendor_uuid', dfwidget.SelectWidget(values=vendor_values))
elif self.editing: elif self.editing:
f.set_readonly('vendor') f.set_readonly('vendor')

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar # Copyright © 2010-2022 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -200,9 +200,19 @@ class CostingBatchView(PurchasingBatchView):
form.set_default('workflow', valid_workflows[0]) form.set_default('workflow', valid_workflows[0])
# configure vendor field # configure vendor field
use_autocomplete = self.rattail_config.getbool( app = self.get_rattail_app()
'rattail', 'vendor.use_autocomplete', default=True) vendor_handler = app.get_vendor_handler()
if use_autocomplete: use_dropdown = vendor_handler.choice_uses_dropdown()
if use_dropdown:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
if use_buefy:
form.set_widget('vendor', dfwidget.SelectWidget(values=vendor_values))
else:
form.set_widget('vendor', forms.widgets.JQuerySelectWidget(values=vendor_values))
else:
vendor_display = "" vendor_display = ""
if self.request.method == 'POST': if self.request.method == 'POST':
if self.request.POST.get('vendor'): if self.request.POST.get('vendor'):
@ -212,15 +222,6 @@ class CostingBatchView(PurchasingBatchView):
vendors_url = self.request.route_url('vendors.autocomplete') vendors_url = self.request.route_url('vendors.autocomplete')
form.set_widget('vendor', forms.widgets.JQueryAutocompleteWidget( form.set_widget('vendor', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url)) field_display=vendor_display, service_url=vendors_url))
else:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
if use_buefy:
form.set_widget('vendor', dfwidget.SelectWidget(values=vendor_values))
else:
form.set_widget('vendor', forms.widgets.JQuerySelectWidget(values=vendor_values))
# configure workflow field # configure workflow field
values = [(workflow['workflow_key'], workflow['display']) values = [(workflow['workflow_key'], workflow['display'])

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar # Copyright © 2010-2022 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -294,9 +294,19 @@ class ReceivingBatchView(PurchasingBatchView):
use_buefy=use_buefy) use_buefy=use_buefy)
# configure vendor field # configure vendor field
use_autocomplete = self.rattail_config.getbool( app = self.get_rattail_app()
'rattail', 'vendor.use_autocomplete', default=True) vendor_handler = app.get_vendor_handler()
if use_autocomplete: use_dropdown = vendor_handler.choice_uses_dropdown()
if use_dropdown:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
if use_buefy:
form.set_widget('vendor', dfwidget.SelectWidget(values=vendor_values))
else:
form.set_widget('vendor', forms.widgets.JQuerySelectWidget(values=vendor_values))
else:
vendor_display = "" vendor_display = ""
if self.request.method == 'POST': if self.request.method == 'POST':
if self.request.POST.get('vendor'): if self.request.POST.get('vendor'):
@ -306,15 +316,6 @@ class ReceivingBatchView(PurchasingBatchView):
vendors_url = self.request.route_url('vendors.autocomplete') vendors_url = self.request.route_url('vendors.autocomplete')
form.set_widget('vendor', forms.widgets.JQueryAutocompleteWidget( form.set_widget('vendor', forms.widgets.JQueryAutocompleteWidget(
field_display=vendor_display, service_url=vendors_url)) field_display=vendor_display, service_url=vendors_url))
else:
vendors = self.Session.query(model.Vendor)\
.order_by(model.Vendor.id)
vendor_values = [(vendor.uuid, "({}) {}".format(vendor.id, vendor.name))
for vendor in vendors]
if use_buefy:
form.set_widget('vendor', dfwidget.SelectWidget(values=vendor_values))
else:
form.set_widget('vendor', forms.widgets.JQuerySelectWidget(values=vendor_values))
# configure workflow field # configure workflow field
values = [(workflow['workflow_key'], workflow['display']) values = [(workflow['workflow_key'], workflow['display'])

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2021 Lance Edgar # Copyright © 2010-2022 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -175,7 +175,7 @@ class VendorView(MasterView):
# display # display
{'section': 'rattail', {'section': 'rattail',
'option': 'vendor.use_autocomplete', 'option': 'vendors.choice_uses_dropdown',
'type': bool}, 'type': bool},
] ]