Add support for "new customer" when creating new custorder
This commit is contained in:
		
							parent
							
								
									25a019cc12
								
							
						
					
					
						commit
						d933dd2723
					
				
					 4 changed files with 283 additions and 35 deletions
				
			
		| 
						 | 
				
			
			@ -333,19 +333,84 @@
 | 
			
		|||
 | 
			
		||||
            <br />
 | 
			
		||||
            <div class="field">
 | 
			
		||||
              <b-radio v-model="contactIsKnown" disabled
 | 
			
		||||
              <b-radio v-model="contactIsKnown"
 | 
			
		||||
                       :native-value="false">
 | 
			
		||||
                Customer is not yet in the system.
 | 
			
		||||
              </b-radio>
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
            <div v-if="!contactIsKnown">
 | 
			
		||||
              <b-field label="Customer Name" horizontal>
 | 
			
		||||
                <b-input v-model="customerName"></b-input>
 | 
			
		||||
            <div v-if="!contactIsKnown"
 | 
			
		||||
                 style="padding-left: 10rem; display: flex;">
 | 
			
		||||
              <div>
 | 
			
		||||
                <b-field label="Customer Name">
 | 
			
		||||
                  <span>{{ newCustomerName }}</span>
 | 
			
		||||
                </b-field>
 | 
			
		||||
              <b-field label="Phone Number" horizontal>
 | 
			
		||||
                <b-input v-model="phoneNumber"></b-input>
 | 
			
		||||
                <b-field grouped>
 | 
			
		||||
                  <b-field label="Phone Number">
 | 
			
		||||
                    <span>{{ newCustomerPhone }}</span>
 | 
			
		||||
                  </b-field>
 | 
			
		||||
                  <b-field label="Email Address">
 | 
			
		||||
                    <span>{{ newCustomerEmail }}</span>
 | 
			
		||||
                  </b-field>
 | 
			
		||||
                </b-field>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div>
 | 
			
		||||
                <b-button type="is-primary"
 | 
			
		||||
                          @click="editNewCustomerInit()"
 | 
			
		||||
                          icon-pack="fas"
 | 
			
		||||
                          icon-left="edit">
 | 
			
		||||
                  Edit New Customer
 | 
			
		||||
                </b-button>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <div style="margin-left: 1rem;">
 | 
			
		||||
                <b-notification type="is-warning"
 | 
			
		||||
                                :closable="false">
 | 
			
		||||
                  <p>Duplicate records can be difficult to clean up!</p>
 | 
			
		||||
                  <p>Please be sure the customer is not already in the system.</p>
 | 
			
		||||
                </b-notification>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
              <b-modal has-modal-card
 | 
			
		||||
                       :active.sync="editNewCustomerShowDialog">
 | 
			
		||||
                <div class="modal-card">
 | 
			
		||||
 | 
			
		||||
                  <header class="modal-card-head">
 | 
			
		||||
                    <p class="modal-card-title">Edit New Customer</p>
 | 
			
		||||
                  </header>
 | 
			
		||||
 | 
			
		||||
                  <section class="modal-card-body">
 | 
			
		||||
                    <b-field label="Customer Name">
 | 
			
		||||
                      <b-input v-model="editNewCustomerName"
 | 
			
		||||
                               ref="editNewCustomerInput">
 | 
			
		||||
                      </b-input>
 | 
			
		||||
                    </b-field>
 | 
			
		||||
                    <b-field grouped>
 | 
			
		||||
                      <b-field label="Phone Number">
 | 
			
		||||
                        <b-input v-model="editNewCustomerPhone"></b-input>
 | 
			
		||||
                      </b-field>
 | 
			
		||||
                      <b-field label="Email Address">
 | 
			
		||||
                        <b-input v-model="editNewCustomerEmail"></b-input>
 | 
			
		||||
                      </b-field>
 | 
			
		||||
                    </b-field>
 | 
			
		||||
                  </section>
 | 
			
		||||
 | 
			
		||||
                  <footer class="modal-card-foot">
 | 
			
		||||
                    <b-button type="is-primary"
 | 
			
		||||
                              icon-pack="fas"
 | 
			
		||||
                              icon-left="save"
 | 
			
		||||
                              :disabled="editNewCustomerSaveDisabled"
 | 
			
		||||
                              @click="editNewCustomerSave()">
 | 
			
		||||
                      {{ editNewCustomerSaveText }}
 | 
			
		||||
                    </b-button>
 | 
			
		||||
                    <b-button @click="editNewCustomerShowDialog = false">
 | 
			
		||||
                      Cancel
 | 
			
		||||
                    </b-button>
 | 
			
		||||
                  </footer>
 | 
			
		||||
                </div>
 | 
			
		||||
              </b-modal>
 | 
			
		||||
 | 
			
		||||
            </div>
 | 
			
		||||
 | 
			
		||||
          </div>
 | 
			
		||||
| 
						 | 
				
			
			@ -577,7 +642,7 @@
 | 
			
		|||
                batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n},
 | 
			
		||||
 | 
			
		||||
                customerPanelOpen: false,
 | 
			
		||||
                contactIsKnown: true,
 | 
			
		||||
                contactIsKnown: ${json.dumps(contact_is_known)|n},
 | 
			
		||||
                % if new_order_requires_customer:
 | 
			
		||||
                contactUUID: ${json.dumps(batch.customer_uuid)|n},
 | 
			
		||||
                % else:
 | 
			
		||||
| 
						 | 
				
			
			@ -609,10 +674,17 @@
 | 
			
		|||
 | 
			
		||||
                % endif
 | 
			
		||||
 | 
			
		||||
                customerName: null,
 | 
			
		||||
                phoneNumber: null,
 | 
			
		||||
                newCustomerName: ${json.dumps(new_customer_name)|n},
 | 
			
		||||
                newCustomerPhone: ${json.dumps(new_customer_phone)|n},
 | 
			
		||||
                newCustomerEmail: ${json.dumps(new_customer_email)|n},
 | 
			
		||||
                contactNotes: ${json.dumps(contact_notes)|n},
 | 
			
		||||
 | 
			
		||||
                editNewCustomerShowDialog: false,
 | 
			
		||||
                editNewCustomerName: null,
 | 
			
		||||
                editNewCustomerPhone: null,
 | 
			
		||||
                editNewCustomerEmail: null,
 | 
			
		||||
                editNewCustomerSaving: false,
 | 
			
		||||
 | 
			
		||||
                items: ${json.dumps(order_items)|n},
 | 
			
		||||
                editingItem: null,
 | 
			
		||||
                showingItemDialog: false,
 | 
			
		||||
| 
						 | 
				
			
			@ -646,8 +718,8 @@
 | 
			
		|||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (this.customerName) {
 | 
			
		||||
                        text = "Customer: " + this.customerName
 | 
			
		||||
                    if (this.newCustomerName) {
 | 
			
		||||
                        text = "Customer: " + this.newCustomerName
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -700,19 +772,19 @@
 | 
			
		|||
                    }
 | 
			
		||||
                    phoneNumber = this.orderPhoneNumber
 | 
			
		||||
                } else { // customer is not known
 | 
			
		||||
                    if (!this.customerName) {
 | 
			
		||||
                    if (!this.newCustomerName) {
 | 
			
		||||
                        return {
 | 
			
		||||
                            type: 'is-danger',
 | 
			
		||||
                            text: "Please identify the customer.",
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (!this.phoneNumber) {
 | 
			
		||||
                    if (!this.newCustomerPhone) {
 | 
			
		||||
                        return {
 | 
			
		||||
                            type: 'is-warning',
 | 
			
		||||
                            text: "Please provide a phone number for the customer.",
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    phoneNumber = this.phoneNumber
 | 
			
		||||
                    phoneNumber = this.newCustomerPhone
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                let phoneDigits = phoneNumber.replace(/\D/g, '')
 | 
			
		||||
| 
						 | 
				
			
			@ -774,6 +846,26 @@
 | 
			
		|||
 | 
			
		||||
            % endif
 | 
			
		||||
 | 
			
		||||
            editNewCustomerSaveDisabled() {
 | 
			
		||||
                if (this.editNewCustomerSaving) {
 | 
			
		||||
                    return true
 | 
			
		||||
                }
 | 
			
		||||
                if (!this.editNewCustomerName) {
 | 
			
		||||
                    return true
 | 
			
		||||
                }
 | 
			
		||||
                if (!(this.editNewCustomerPhone || this.editNewCustomerEmail)) {
 | 
			
		||||
                    return true
 | 
			
		||||
                }
 | 
			
		||||
                return false
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            editNewCustomerSaveText() {
 | 
			
		||||
                if (this.editNewCustomerSaving) {
 | 
			
		||||
                    return "Working, please wait..."
 | 
			
		||||
                }
 | 
			
		||||
                return "Save"
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            itemsPanelHeader() {
 | 
			
		||||
                let text = "Items"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1044,6 +1136,46 @@
 | 
			
		|||
 | 
			
		||||
            % endif
 | 
			
		||||
 | 
			
		||||
            editNewCustomerInit() {
 | 
			
		||||
                this.editNewCustomerName = this.newCustomerName
 | 
			
		||||
                this.editNewCustomerPhone = this.newCustomerPhone
 | 
			
		||||
                this.editNewCustomerEmail = this.newCustomerEmail
 | 
			
		||||
                this.editNewCustomerShowDialog = true
 | 
			
		||||
                this.$nextTick(() => {
 | 
			
		||||
                    this.$refs.editNewCustomerInput.focus()
 | 
			
		||||
                })
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            editNewCustomerSave() {
 | 
			
		||||
                this.editNewCustomerSaving = true
 | 
			
		||||
 | 
			
		||||
                let params = {
 | 
			
		||||
                    action: 'update_pending_customer',
 | 
			
		||||
                    display_name: this.editNewCustomerName,
 | 
			
		||||
                    phone_number: this.editNewCustomerPhone,
 | 
			
		||||
                    email_address: this.editNewCustomerEmail,
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                this.submitBatchData(params, response => {
 | 
			
		||||
                    if (response.data.success) {
 | 
			
		||||
                        this.newCustomerName = response.data.new_customer_name
 | 
			
		||||
                        this.newCustomerPhone = response.data.phone_number
 | 
			
		||||
                        this.orderPhoneNumber = response.data.phone_number
 | 
			
		||||
                        this.newCustomerEmail = response.data.email_address
 | 
			
		||||
                        this.orderEmailAddress = response.data.email_address
 | 
			
		||||
                        this.editNewCustomerShowDialog = false
 | 
			
		||||
                    } else {
 | 
			
		||||
                        this.$buefy.toast.open({
 | 
			
		||||
                            message: "Save failed: " + (response.data.error || "(unknown error)"),
 | 
			
		||||
                            type: 'is-danger',
 | 
			
		||||
                            duration: 2000, // 2 seconds
 | 
			
		||||
                        })
 | 
			
		||||
                    }
 | 
			
		||||
                    this.editNewCustomerSaving = false
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            showAddItemDialog() {
 | 
			
		||||
                this.customerPanelOpen = false
 | 
			
		||||
                this.editingItem = null
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -450,6 +450,63 @@ class CustomerView(MasterView):
 | 
			
		|||
                            permission='{}.detach_person'.format(permission_prefix))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PendingCustomerView(MasterView):
 | 
			
		||||
    """
 | 
			
		||||
    Master view for the Pending Customer class.
 | 
			
		||||
    """
 | 
			
		||||
    model_class = model.PendingCustomer
 | 
			
		||||
    route_prefix = 'pending_customers'
 | 
			
		||||
    url_prefix = '/customers/pending'
 | 
			
		||||
 | 
			
		||||
    labels = {
 | 
			
		||||
        'id': "ID",
 | 
			
		||||
        'status_code': "Status",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    grid_columns = [
 | 
			
		||||
        'id',
 | 
			
		||||
        'display_name',
 | 
			
		||||
        'first_name',
 | 
			
		||||
        'last_name',
 | 
			
		||||
        'phone_number',
 | 
			
		||||
        'email_address',
 | 
			
		||||
        'status_code',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    form_fields = [
 | 
			
		||||
        'id',
 | 
			
		||||
        'display_name',
 | 
			
		||||
        'first_name',
 | 
			
		||||
        'middle_name',
 | 
			
		||||
        'last_name',
 | 
			
		||||
        'phone_number',
 | 
			
		||||
        'phone_type',
 | 
			
		||||
        'email_address',
 | 
			
		||||
        'email_type',
 | 
			
		||||
        'address_street',
 | 
			
		||||
        'address_street2',
 | 
			
		||||
        'address_city',
 | 
			
		||||
        'address_state',
 | 
			
		||||
        'address_zipcode',
 | 
			
		||||
        'address_type',
 | 
			
		||||
        'status_code',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    def configure_grid(self, g):
 | 
			
		||||
        super(PendingCustomerView, self).configure_grid(g)
 | 
			
		||||
 | 
			
		||||
        g.set_enum('status_code', self.enum.PENDING_CUSTOMER_STATUS)
 | 
			
		||||
 | 
			
		||||
        g.set_sort_defaults('display_name')
 | 
			
		||||
        g.set_link('id')
 | 
			
		||||
        g.set_link('display_name')
 | 
			
		||||
 | 
			
		||||
    def configure_form(self, f):
 | 
			
		||||
        super(PendingCustomerView, self).configure_form(f)
 | 
			
		||||
 | 
			
		||||
        f.set_enum('status_code', self.enum.PENDING_CUSTOMER_STATUS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# # TODO: this is referenced by some custom apps, but should be moved??
 | 
			
		||||
# def unique_id(value, field):
 | 
			
		||||
#     customer = field.parent.model
 | 
			
		||||
| 
						 | 
				
			
			@ -491,3 +548,4 @@ def includeme(config):
 | 
			
		|||
                    renderer='json', permission='customers.view')
 | 
			
		||||
 | 
			
		||||
    CustomerView.defaults(config)
 | 
			
		||||
    PendingCustomerView.defaults(config)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ import six
 | 
			
		|||
from rattail.db import model
 | 
			
		||||
 | 
			
		||||
import colander
 | 
			
		||||
from webhelpers2.html import tags
 | 
			
		||||
 | 
			
		||||
from tailbone import forms
 | 
			
		||||
from tailbone.views.batch import BatchMasterView
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +62,7 @@ class CustomerOrderBatchView(BatchMasterView):
 | 
			
		|||
        'store',
 | 
			
		||||
        'customer',
 | 
			
		||||
        'person',
 | 
			
		||||
        'pending_customer',
 | 
			
		||||
        'phone_number',
 | 
			
		||||
        'email_address',
 | 
			
		||||
        'params',
 | 
			
		||||
| 
						 | 
				
			
			@ -151,8 +153,19 @@ class CustomerOrderBatchView(BatchMasterView):
 | 
			
		|||
        else:
 | 
			
		||||
            f.set_renderer('person', self.render_person)
 | 
			
		||||
 | 
			
		||||
        # pending_customer
 | 
			
		||||
        f.set_renderer('pending_customer', self.render_pending_customer)
 | 
			
		||||
 | 
			
		||||
        f.set_type('total_price', 'currency')
 | 
			
		||||
 | 
			
		||||
    def render_pending_customer(self, batch, field):
 | 
			
		||||
        pending = batch.pending_customer
 | 
			
		||||
        if not pending:
 | 
			
		||||
            return
 | 
			
		||||
        text = six.text_type(pending)
 | 
			
		||||
        url = self.request.route_url('pending_customers.view', uuid=pending.uuid)
 | 
			
		||||
        return tags.link_to(text, url)
 | 
			
		||||
 | 
			
		||||
    def row_grid_extra_class(self, row, i):
 | 
			
		||||
        if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
 | 
			
		||||
            return 'warning'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -230,6 +230,7 @@ class CustomerOrderView(MasterView):
 | 
			
		|||
                'unassign_contact',
 | 
			
		||||
                'update_phone_number',
 | 
			
		||||
                'update_email_address',
 | 
			
		||||
                'update_pending_customer',
 | 
			
		||||
                'get_customer_info',
 | 
			
		||||
                # 'set_customer_data',
 | 
			
		||||
                'find_product_by_upc',
 | 
			
		||||
| 
						 | 
				
			
			@ -252,26 +253,17 @@ class CustomerOrderView(MasterView):
 | 
			
		|||
        else:
 | 
			
		||||
            product_autocomplete = 'products.autocomplete'
 | 
			
		||||
 | 
			
		||||
        context = {'batch': batch,
 | 
			
		||||
        context = self.get_context_contact(batch)
 | 
			
		||||
 | 
			
		||||
        context.update({
 | 
			
		||||
            'batch': batch,
 | 
			
		||||
            'normalized_batch': self.normalize_batch(batch),
 | 
			
		||||
            'new_order_requires_customer': self.handler.new_order_requires_customer(),
 | 
			
		||||
            'allow_contact_info_choice': self.handler.allow_contact_info_choice(),
 | 
			
		||||
            'restrict_contact_info': self.handler.should_restrict_contact_info(),
 | 
			
		||||
                   'contact_display': self.handler.get_contact_display(batch),
 | 
			
		||||
                   'contact_phones': self.handler.get_contact_phones(batch),
 | 
			
		||||
                   'contact_emails': self.handler.get_contact_emails(batch),
 | 
			
		||||
                   'add_phone_number': bool(batch.get_param('add_phone_number')),
 | 
			
		||||
                   'add_email_address': bool(batch.get_param('add_email_address')),
 | 
			
		||||
                   'contact_profile_url': None,
 | 
			
		||||
                   'contact_notes': self.handler.get_contact_notes(batch),
 | 
			
		||||
            'order_items': items,
 | 
			
		||||
                   'product_autocomplete_url': self.request.route_url(product_autocomplete)}
 | 
			
		||||
 | 
			
		||||
        # maybe add profile URL
 | 
			
		||||
        if batch.person_uuid:
 | 
			
		||||
            if self.request.has_perm('people.view_profile'):
 | 
			
		||||
                context['contact_profile_url'] = self.request.route_url(
 | 
			
		||||
                    'people.view_profile', uuid=batch.person_uuid)
 | 
			
		||||
            'product_autocomplete_url': self.request.route_url(product_autocomplete),
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        return self.render_to_response(template, context)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -403,14 +395,38 @@ class CustomerOrderView(MasterView):
 | 
			
		|||
            'customer_uuid': batch.customer_uuid,
 | 
			
		||||
            'person_uuid': batch.person_uuid,
 | 
			
		||||
            'phone_number': batch.phone_number,
 | 
			
		||||
            'contact_display': self.handler.get_contact_display(batch),
 | 
			
		||||
            'email_address': batch.email_address,
 | 
			
		||||
            'contact_phones': self.handler.get_contact_phones(batch),
 | 
			
		||||
            'contact_emails': self.handler.get_contact_emails(batch),
 | 
			
		||||
            'contact_notes': self.handler.get_contact_notes(batch),
 | 
			
		||||
            'add_phone_number': bool(batch.get_param('add_phone_number')),
 | 
			
		||||
            'add_email_address': bool(batch.get_param('add_email_address')),
 | 
			
		||||
            'contact_profile_url': None,
 | 
			
		||||
            'new_customer_name': None,
 | 
			
		||||
            'new_customer_phone': None,
 | 
			
		||||
            'new_customer_email': None,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pending = batch.pending_customer
 | 
			
		||||
        if pending:
 | 
			
		||||
            context.update({
 | 
			
		||||
                'new_customer_name': pending.display_name,
 | 
			
		||||
                'new_customer_phone': pending.phone_number,
 | 
			
		||||
                'new_customer_email': pending.email_address,
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        # figure out if "contact is known" from user's perspective.
 | 
			
		||||
        # if we have a uuid then it's definitely known, otherwise if
 | 
			
		||||
        # we have a pending customer then it's definitely *not* known,
 | 
			
		||||
        # but if no pending customer yet then we can still "assume" it
 | 
			
		||||
        # is known, by default, until user specifies otherwise.
 | 
			
		||||
        contact = self.handler.get_contact(batch)
 | 
			
		||||
        if contact:
 | 
			
		||||
            context['contact_is_known'] = True
 | 
			
		||||
        else:
 | 
			
		||||
            context['contact_is_known'] = not bool(pending)
 | 
			
		||||
 | 
			
		||||
        # maybe add profile URL
 | 
			
		||||
        if batch.person_uuid:
 | 
			
		||||
            if self.request.has_perm('people.view_profile'):
 | 
			
		||||
| 
						 | 
				
			
			@ -457,6 +473,35 @@ class CustomerOrderView(MasterView):
 | 
			
		|||
            'email_address': batch.email_address,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def update_pending_customer(self, batch, data):
 | 
			
		||||
        model = self.model
 | 
			
		||||
        app = self.get_rattail_app()
 | 
			
		||||
 | 
			
		||||
        # clear out any contact it may have
 | 
			
		||||
        self.handler.unassign_contact(batch)
 | 
			
		||||
 | 
			
		||||
        # create pending customer if needed
 | 
			
		||||
        pending = batch.pending_customer
 | 
			
		||||
        if not pending:
 | 
			
		||||
            pending = model.PendingCustomer()
 | 
			
		||||
            pending.user = self.request.user
 | 
			
		||||
            pending.status_code = self.enum.PENDING_CUSTOMER_STATUS_PENDING
 | 
			
		||||
            batch.pending_customer = pending
 | 
			
		||||
 | 
			
		||||
        # update pending customer info
 | 
			
		||||
        pending.display_name = data['display_name']
 | 
			
		||||
        pending.phone_number = app.format_phone_number(data['phone_number'])
 | 
			
		||||
        pending.email_address = data['email_address']
 | 
			
		||||
 | 
			
		||||
        # also update the batch w/ contact info
 | 
			
		||||
        batch.phone_number = pending.phone_number
 | 
			
		||||
        batch.email_address = pending.email_address
 | 
			
		||||
 | 
			
		||||
        self.Session.flush()
 | 
			
		||||
        context = self.get_context_contact(batch)
 | 
			
		||||
        context['success'] = True
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
    def product_autocomplete(self):
 | 
			
		||||
        """
 | 
			
		||||
        Custom product autocomplete logic, which invokes the handler.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue