Add way to "ignore" a pending product

and some related tweaks for sake of grid
This commit is contained in:
Lance Edgar 2023-10-26 20:43:12 -05:00
parent 1fc17658ff
commit fe4a178d43
3 changed files with 143 additions and 56 deletions

View file

@ -612,10 +612,11 @@ class AlchemyNumericFilter(AlchemyGridFilter):
""" """
value_renderer_factory = NumericValueRenderer value_renderer_factory = NumericValueRenderer
# expose greater-than / less-than verbs in addition to core def default_verbs(self):
default_verbs = ['equal', 'not_equal', 'greater_than', 'greater_equal', # expose greater-than / less-than verbs in addition to core
'less_than', 'less_equal', 'between', return ['equal', 'not_equal', 'greater_than', 'greater_equal',
'is_null', 'is_not_null', 'is_any'] 'less_than', 'less_equal', 'between',
'is_null', 'is_not_null', 'is_any']
# TODO: what follows "works" in that it prevents an error...but from the # TODO: what follows "works" in that it prevents an error...but from the
# user's perspective it still fails silently...need to improve on front-end # user's perspective it still fails silently...need to improve on front-end
@ -670,6 +671,14 @@ class AlchemyIntegerFilter(AlchemyNumericFilter):
""" """
bigint = False bigint = False
def default_verbs(self):
# limited verbs if choices are defined
if self.choices:
return ['equal', 'not_equal', 'is_null', 'is_not_null', 'is_any']
return super().default_verbs()
def value_invalid(self, value): def value_invalid(self, value):
if value: if value:
if isinstance(value, int): if isinstance(value, int):

View file

@ -7,25 +7,16 @@
${product_lookup.tailbone_product_lookup_template()} ${product_lookup.tailbone_product_lookup_template()}
</%def> </%def>
<%def name="object_helpers()"> <%def name="page_content()">
${parent.object_helpers()} ${parent.page_content()}
% if instance.status_code == enum.PENDING_PRODUCT_STATUS_PENDING and master.has_perm('resolve_product'):
<nav class="panel">
<p class="panel-heading">Tools</p>
<div class="panel-block">
<div style="display: flex; flex-direction: column;">
<div class="buttons">
<b-button type="is-primary"
@click="resolveProductInit()"
icon-pack="fas"
icon-left="object-ungroup">
Resolve Product
</b-button>
</div>
</div>
</div>
</nav>
% if master.has_perm('ignore_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY):
${h.form(master.get_action_url('ignore_product', instance), ref='ignoreProductForm')}
${h.csrf_token(request)}
${h.end_form()}
% endif
% if master.has_perm('resolve_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY, enum.PENDING_PRODUCT_STATUS_IGNORED):
<b-modal has-modal-card <b-modal has-modal-card
:active.sync="resolveProductShowDialog"> :active.sync="resolveProductShowDialog">
<div class="modal-card"> <div class="modal-card">
@ -80,39 +71,55 @@
${parent.modify_this_page_vars()} ${parent.modify_this_page_vars()}
<script type="text/javascript"> <script type="text/javascript">
ThisPageData.resolveProductShowDialog = false % if master.has_perm('ignore_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY):
ThisPageData.resolveProductUUID = null
ThisPageData.resolveProductSubmitting = false
ThisPage.computed.resolveProductSubmitDisabled = function() { ThisPage.methods.ignoreProductInit = function() {
if (this.resolveProductSubmitting) { if (!confirm("Really ignore this product?\n\n"
return true + "This will leave it unresolved, but hidden via default filters.")) {
return
}
this.$refs.ignoreProductForm.submit()
} }
if (!this.resolveProductUUID) {
return true % endif
% if master.has_perm('resolve_product') and instance.status_code in (enum.PENDING_PRODUCT_STATUS_PENDING, enum.PENDING_PRODUCT_STATUS_READY, enum.PENDING_PRODUCT_STATUS_IGNORED):
ThisPageData.resolveProductShowDialog = false
ThisPageData.resolveProductUUID = null
ThisPageData.resolveProductSubmitting = false
ThisPage.computed.resolveProductSubmitDisabled = function() {
if (this.resolveProductSubmitting) {
return true
}
if (!this.resolveProductUUID) {
return true
}
return false
} }
return false
}
ThisPage.methods.resolveProductInit = function() { ThisPage.methods.resolveProductInit = function() {
this.resolveProductUUID = null this.resolveProductUUID = null
this.resolveProductShowDialog = true this.resolveProductShowDialog = true
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.productLookup.focus() this.$refs.productLookup.focus()
}) })
} }
ThisPage.methods.resolveProductSubmit = function() { ThisPage.methods.resolveProductSubmit = function() {
this.resolveProductSubmitting = true this.resolveProductSubmitting = true
this.$refs.resolveProductForm.submit() this.$refs.resolveProductForm.submit()
} }
ThisPageData.actualProduct = null ThisPageData.actualProduct = null
ThisPage.methods.productSelected = function(product) { ThisPage.methods.productSelected = function(product) {
this.actualProduct = product this.actualProduct = product
this.resolveProductUUID = product ? product.uuid : null this.resolveProductUUID = product ? product.uuid : null
} }
% endif
</script> </script>
</%def> </%def>

View file

@ -2297,16 +2297,22 @@ class PendingProductView(MasterView):
def configure_grid(self, g): def configure_grid(self, g):
super().configure_grid(g) super().configure_grid(g)
model = self.model
g.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS) # description
g.filters['status_code'].default_active = True
g.filters['status_code'].default_verb = 'not_equal'
g.filters['status_code'].default_value = str(self.enum.PENDING_PRODUCT_STATUS_RESOLVED)
g.set_sort_defaults('created', 'desc')
g.set_link('description') g.set_link('description')
# status_code
g.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
g.set_filter('status_code', model.PendingProduct.status_code,
value_enum=self.enum.PENDING_PRODUCT_STATUS,
default_active=True,
default_verb='equal',
default_value=str(self.enum.PENDING_PRODUCT_STATUS_PENDING))
# created
g.set_sort_defaults('created', 'desc')
def configure_form(self, f): def configure_form(self, f):
super().configure_form(f) super().configure_form(f)
model = self.model model = self.model
@ -2398,8 +2404,20 @@ class PendingProductView(MasterView):
if self.creating: if self.creating:
f.remove('status_code') f.remove('status_code')
else: else:
# f.set_readonly('status_code')
f.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS) f.set_enum('status_code', self.enum.PENDING_PRODUCT_STATUS)
if self.viewing:
f.set_renderer('status_code', self.render_status_code)
if (self.has_perm('ignore_product')
and pending.status_code in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY)):
f.set_vuejs_component_kwargs(**{'@ignore-product': 'ignoreProductInit'})
if (self.has_perm('resolve_product')
and pending.status_code in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY,
self.enum.PENDING_PRODUCT_STATUS_IGNORED)):
f.set_vuejs_component_kwargs(**{'@resolve-product': 'resolveProductInit'})
# user # user
if self.creating: if self.creating:
@ -2415,6 +2433,42 @@ class PendingProductView(MasterView):
if not pending.resolved: if not pending.resolved:
f.remove('resolved', 'resolved_by') f.remove('resolved', 'resolved_by')
def render_status_code(self, pending, field):
status = pending.status_code
if not status:
return
# will just show status text by default
text = self.enum.PENDING_PRODUCT_STATUS.get(status, str(status))
html = text
# but maybe also show buttons to change status
buttons = []
if (self.has_perm('ignore_product')
and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY)):
buttons.append(self.make_buefy_button("Ignore Product",
type='is-warning',
icon_left='ban',
**{'@click': "$emit('ignore-product')"}))
if (self.has_perm('resolve_product')
and status in (self.enum.PENDING_PRODUCT_STATUS_PENDING,
self.enum.PENDING_PRODUCT_STATUS_READY,
self.enum.PENDING_PRODUCT_STATUS_IGNORED)):
buttons.append(self.make_buefy_button("Resolve Product",
is_primary=True,
icon_left='object-ungroup',
**{'@click': "$emit('resolve-product')"}))
if buttons:
text = HTML.tag('span', class_='control', c=[text])
buttons = HTML.tag('div', class_='buttons', c=buttons)
html = HTML.tag('b-field', grouped='grouped', c=[text, buttons])
return html
def editable_instance(self, pending): def editable_instance(self, pending):
if self.request.is_root: if self.request.is_root:
return True return True
@ -2487,6 +2541,12 @@ class PendingProductView(MasterView):
def get_resolve_product_kwargs(self, **kwargs): def get_resolve_product_kwargs(self, **kwargs):
return kwargs return kwargs
def ignore_product(self):
model = self.model
pending = self.get_instance()
pending.status_code = self.enum.PENDING_PRODUCT_STATUS_IGNORED
return self.redirect(self.get_action_url('view', pending))
def get_row_data(self, pending): def get_row_data(self, pending):
model = self.model model = self.model
return self.Session.query(model.CustomerOrderItem)\ return self.Session.query(model.CustomerOrderItem)\
@ -2554,6 +2614,17 @@ class PendingProductView(MasterView):
route_name='{}.resolve_product'.format(route_prefix), route_name='{}.resolve_product'.format(route_prefix),
permission='{}.resolve_product'.format(permission_prefix)) permission='{}.resolve_product'.format(permission_prefix))
# ignore product
config.add_tailbone_permission(permission_prefix,
f'{permission_prefix}.ignore_product',
f"Mark {model_title} as ignored")
config.add_route(f'{route_prefix}.ignore_product',
f'{instance_url_prefix}/ignore-product',
request_method='POST')
config.add_view(cls, attr='ignore_product',
route_name=f'{route_prefix}.ignore_product',
permission=f'{permission_prefix}.ignore_product')
def defaults(config, **kwargs): def defaults(config, **kwargs):
base = globals() base = globals()