Overhaul new custorder so contact may be either Person or Customer

also make the handler responsible for (un)assigning contact
This commit is contained in:
Lance Edgar 2021-09-27 09:22:06 -04:00
parent 12310da09e
commit a52b5ec380
2 changed files with 202 additions and 91 deletions

View file

@ -101,50 +101,63 @@
<br /> <br />
<div class="field"> <div class="field">
<b-radio v-model="customerIsKnown" <b-radio v-model="contactIsKnown"
:native-value="true"> :native-value="true">
Customer is already in the system. Customer is already in the system.
</b-radio> </b-radio>
</div> </div>
<div v-show="customerIsKnown"> <div v-show="contactIsKnown">
<b-field label="Customer" horizontal> <b-field label="Customer" horizontal>
<tailbone-autocomplete ref="customerAutocomplete" <tailbone-autocomplete ref="contactAutocomplete"
v-model="customerUUID" v-model="contactUUID"
placeholder="Enter name or phone number" placeholder="Enter name or phone number"
:initial-label="customerDisplay" :initial-label="contactDisplay"
% if new_order_requires_customer:
serviceUrl="${url('{}.customer_autocomplete'.format(route_prefix))}" serviceUrl="${url('{}.customer_autocomplete'.format(route_prefix))}"
@input="customerChanged"> % else:
serviceUrl="${url('{}.person_autocomplete'.format(route_prefix))}"
% endif
@input="contactChanged">
</tailbone-autocomplete> </tailbone-autocomplete>
<b-button v-if="contactUUID && contactProfileURL"
type="is-primary"
tag="a" target="_blank"
:href="contactProfileURL"
icon-pack="fas"
icon-left="external-link-alt">
View Profile
</b-button>
</b-field> </b-field>
<b-field label="Phone Number" horizontal <b-field label="Phone Number" horizontal
v-show="customerUUID"> v-show="contactUUID">
<b-input v-model="phoneNumberEntry" {{ phoneNumberEntry }}
@input="phoneNumberChanged" ## <b-input v-model="phoneNumberEntry"
@keydown.native="phoneNumberKeyDown"> ## @input="phoneNumberChanged"
</b-input> ## @keydown.native="phoneNumberKeyDown">
<b-button v-if="!phoneNumberSaved" ## </b-input>
type="is-primary" ## <b-button v-if="!phoneNumberSaved"
icon-pack="fas" ## type="is-primary"
icon-left="fas fa-save" ## icon-pack="fas"
@click="setCustomerData()"> ## icon-left="fas fa-save"
Please save when finished editing ## @click="setContactData()">
</b-button> ## Please save when finished editing
<!-- <tailbone-autocomplete --> ## </b-button>
<!-- serviceUrl="${url('customers.autocomplete.phone')}"> --> ## <!-- <tailbone-autocomplete -->
<!-- </tailbone-autocomplete> --> ## <!-- serviceUrl="${url('customers.autocomplete.phone')}"> -->
## <!-- </tailbone-autocomplete> -->
</b-field> </b-field>
</div> </div>
<br /> <br />
<div class="field"> <div class="field">
<b-radio v-model="customerIsKnown" disabled <b-radio v-model="contactIsKnown" disabled
:native-value="false"> :native-value="false">
Customer is not yet in the system. Customer is not yet in the system.
</b-radio> </b-radio>
</div> </div>
<div v-if="!customerIsKnown"> <div v-if="!contactIsKnown">
<b-field label="Customer Name" horizontal> <b-field label="Customer Name" horizontal>
<b-input v-model="customerName"></b-input> <b-input v-model="customerName"></b-input>
</b-field> </b-field>
@ -382,10 +395,11 @@
batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n}, batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n},
customerPanelOpen: false, customerPanelOpen: false,
customerIsKnown: true, contactIsKnown: true,
customerUUID: ${json.dumps(batch.customer_uuid)|n}, contactUUID: ${json.dumps(batch.customer_uuid)|n},
customerDisplay: ${json.dumps(six.text_type(batch.customer or ''))|n}, contactDisplay: ${json.dumps(six.text_type(batch.customer or ''))|n},
customerEntry: null, customerEntry: null,
contactProfileURL: ${json.dumps(contact_profile_url)|n},
phoneNumberEntry: ${json.dumps(batch.phone_number)|n}, phoneNumberEntry: ${json.dumps(batch.phone_number)|n},
phoneNumberSaved: true, phoneNumberSaved: true,
customerName: null, customerName: null,
@ -415,12 +429,12 @@
customerPanelHeader() { customerPanelHeader() {
let text = "Customer" let text = "Customer"
if (this.customerIsKnown) { if (this.contactIsKnown) {
if (this.customerUUID) { if (this.contactUUID) {
if (this.$refs.customerAutocomplete) { if (this.$refs.contactAutocomplete) {
text = "Customer: " + this.$refs.customerAutocomplete.getDisplayText() text = "Customer: " + this.$refs.contactAutocomplete.getDisplayText()
} else { } else {
text = "Customer: " + this.customerDisplay text = "Customer: " + this.contactDisplay
} }
} }
} else { } else {
@ -457,8 +471,8 @@
}, },
customerStatusTypeAndText() { customerStatusTypeAndText() {
let phoneNumber = null let phoneNumber = null
if (this.customerIsKnown) { if (this.contactIsKnown) {
if (!this.customerUUID) { if (!this.contactUUID) {
return { return {
type: 'is-danger', type: 'is-danger',
text: "Please identify the customer.", text: "Please identify the customer.",
@ -495,7 +509,7 @@
} }
} }
if (!this.customerIsKnown) { if (!this.contactIsKnown) {
return { return {
type: 'is-warning', type: 'is-warning',
text: "Will create a new customer record.", text: "Will create a new customer record.",
@ -555,8 +569,8 @@
// return // return
// } // }
// } // }
// this.customerIsKnown = true // this.contactIsKnown = true
// this.customerUUID = null // this.contactUUID = null
// // this.customerEntry = null // // this.customerEntry = null
// this.phoneNumberEntry = null // this.phoneNumberEntry = null
// this.customerName = null // this.customerName = null
@ -607,17 +621,17 @@
}) })
}, },
setCustomerData() { // setContactData() {
let params = { // let params = {
action: 'set_customer_data', // action: 'set_customer_data',
customer_uuid: this.customerUUID, // customer_uuid: this.contactUUID,
phone_number: this.phoneNumberEntry, // phone_number: this.phoneNumberEntry,
} // }
let that = this // let that = this
this.submitBatchData(params, function(response) { // this.submitBatchData(params, function(response) {
that.phoneNumberSaved = true // that.phoneNumberSaved = true
}) // })
}, // },
submitOrder() { submitOrder() {
this.submittingOrder = true this.submittingOrder = true
@ -644,32 +658,40 @@
}) })
}, },
customerChanged(uuid) { contactChanged(uuid) {
let params
if (!uuid) { if (!uuid) {
this.phoneNumberEntry = null params = {
this.setCustomerData() action: 'unassign_contact',
} else { }
let params = { } else {
action: 'get_customer_info', params = {
uuid: this.customerUUID, action: 'assign_contact',
uuid: this.contactUUID,
} }
let that = this
this.submitBatchData(params, function(response) {
that.phoneNumberEntry = response.data.phone_number
that.setCustomerData()
})
} }
let that = this
this.submitBatchData(params, function(response) {
console.log(response.data)
% if new_order_requires_customer:
that.contactUUID = response.data.customer_uuid
% else:
that.contactUUID = response.data.person_uuid
% endif
that.phoneNumberEntry = response.data.phone_number
that.contactProfileURL = response.data.contact_profile_url
})
}, },
phoneNumberChanged(value) { // phoneNumberChanged(value) {
this.phoneNumberSaved = false // this.phoneNumberSaved = false
}, // },
phoneNumberKeyDown(event) { // phoneNumberKeyDown(event) {
if (event.which == 13) { // Enter // if (event.which == 13) { // Enter
this.setCustomerData() // this.setContactData()
} // }
}, // },
showAddItemDialog() { showAddItemDialog() {
this.editingItem = null this.editingItem = null

View file

@ -227,8 +227,10 @@ class CustomerOrderView(MasterView):
data = dict(self.request.json_body) data = dict(self.request.json_body)
action = data.get('action') action = data.get('action')
json_actions = [ json_actions = [
'assign_contact',
'unassign_contact',
'get_customer_info', 'get_customer_info',
'set_customer_data', # 'set_customer_data',
'find_product_by_upc', 'find_product_by_upc',
'get_product_info', 'get_product_info',
'add_item', 'add_item',
@ -251,8 +253,17 @@ class CustomerOrderView(MasterView):
context = {'batch': batch, context = {'batch': batch,
'normalized_batch': self.normalize_batch(batch), 'normalized_batch': self.normalize_batch(batch),
'new_order_requires_customer': self.handler.new_order_requires_customer(),
'contact_profile_url': None,
'order_items': items, 'order_items': items,
'product_autocomplete_url': self.request.route_url(product_autocomplete)} '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)
return self.render_to_response(template, context) return self.render_to_response(template, context)
def get_current_batch(self): def get_current_batch(self):
@ -307,13 +318,22 @@ class CustomerOrderView(MasterView):
def customer_autocomplete(self): def customer_autocomplete(self):
""" """
Custom customer autocomplete logic, which invokes the handler. Customer autocomplete logic, which invokes the handler.
""" """
self.handler = self.get_batch_handler() self.handler = self.get_batch_handler()
term = self.request.GET['term'] term = self.request.GET['term']
return self.handler.customer_autocomplete(self.Session(), term, return self.handler.customer_autocomplete(self.Session(), term,
user=self.request.user) user=self.request.user)
def person_autocomplete(self):
"""
Person autocomplete logic, which invokes the handler.
"""
self.handler = self.get_batch_handler()
term = self.request.GET['term']
return self.handler.person_autocomplete(self.Session(), term,
user=self.request.user)
def get_customer_info(self, batch, data): def get_customer_info(self, batch, data):
uuid = data.get('uuid') uuid = data.get('uuid')
if not uuid: if not uuid:
@ -326,30 +346,90 @@ class CustomerOrderView(MasterView):
return self.info_for_customer(batch, data, customer) return self.info_for_customer(batch, data, customer)
def info_for_customer(self, batch, data, customer): def info_for_customer(self, batch, data, customer):
phone = customer.first_phone()
email = customer.first_email() # most info comes from handler
return { info = self.handler.get_customer_info(batch)
'uuid': customer.uuid,
'phone_number': phone.number if phone else None, # maybe add profile URL
'email_address': email.address if email else None, if info['person_uuid']:
if self.request.has_perm('people.view_profile'):
info['contact_profile_url'] = self.request.route_url(
'people.view_profile', uuid=info['person_uuid']),
return info
def assign_contact(self, batch, data):
kwargs = {}
# this will either be a Person or Customer UUID
uuid = data['uuid']
if self.handler.new_order_requires_customer():
customer = self.Session.query(model.Customer).get(uuid)
if not customer:
return {'error': "Customer not found"}
kwargs['customer'] = customer
else:
person = self.Session.query(model.Person).get(uuid)
if not person:
return {'error': "Person not found"}
kwargs['person'] = person
# invoke handler to assign contact
try:
self.handler.assign_contact(batch, **kwargs)
except ValueError as error:
return {'error': six.text_type(error)}
context = {
'success': True,
'customer_uuid': batch.customer_uuid,
'person_uuid': batch.person_uuid,
'phone_number': batch.phone_number,
'email_address': batch.email_address,
} }
def set_customer_data(self, batch, data): # maybe add profile URL
if 'customer_uuid' in data: if batch.person_uuid:
batch.customer_uuid = data['customer_uuid'] if self.request.has_perm('people.view_profile'):
if 'person_uuid' in data: context['contact_profile_url'] = self.request.route_url(
batch.person_uuid = data['person_uuid'] 'people.view_profile', uuid=batch.person_uuid)
elif batch.customer_uuid:
self.Session.flush() return context
batch.person = batch.customer.first_person()
else: # no customer set def unassign_contact(self, batch, data):
batch.person_uuid = None self.handler.unassign_contact(batch)
if 'phone_number' in data:
batch.phone_number = data['phone_number'] context = {
if 'email_address' in data: 'success': True,
batch.email_address = data['email_address'] 'customer_uuid': batch.customer_uuid,
self.Session.flush() 'person_uuid': batch.person_uuid,
return {'success': True} 'phone_number': batch.phone_number,
'email_address': batch.email_address,
'contact_profile_url': None,
}
return context
# def set_customer_data(self, batch, data):
# if 'customer_uuid' in data:
# batch.customer_uuid = data['customer_uuid']
# if 'person_uuid' in data:
# batch.person_uuid = data['person_uuid']
# elif batch.customer_uuid:
# self.Session.flush()
# batch.person = batch.customer.first_person()
# else: # no customer set
# batch.person_uuid = None
# if 'phone_number' in data:
# batch.phone_number = data['phone_number']
# if 'email_address' in data:
# batch.email_address = data['email_address']
# self.Session.flush()
# return {'success': True}
def product_autocomplete(self): def product_autocomplete(self):
""" """
@ -601,6 +681,15 @@ class CustomerOrderView(MasterView):
route_prefix = cls.get_route_prefix() route_prefix = cls.get_route_prefix()
url_prefix = cls.get_url_prefix() url_prefix = cls.get_url_prefix()
# person autocomplete
config.add_route('{}.person_autocomplete'.format(route_prefix),
'{}/person-autocomplete'.format(url_prefix),
request_method='GET')
config.add_view(cls, attr='person_autocomplete',
route_name='{}.person_autocomplete'.format(route_prefix),
renderer='json',
permission='people.list')
# customer autocomplete # customer autocomplete
config.add_route('{}.customer_autocomplete'.format(route_prefix), config.add_route('{}.customer_autocomplete'.format(route_prefix),
'{}/customer-autocomplete'.format(url_prefix), '{}/customer-autocomplete'.format(url_prefix),