Add way to "ignore" a pending product
and some related tweaks for sake of grid
This commit is contained in:
		
							parent
							
								
									1fc17658ff
								
							
						
					
					
						commit
						fe4a178d43
					
				
					 3 changed files with 143 additions and 56 deletions
				
			
		|  | @ -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): | ||||||
|  |  | ||||||
|  | @ -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> | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lance Edgar
						Lance Edgar