Add support for "new customer" when creating new custorder
This commit is contained in:
parent
25a019cc12
commit
d933dd2723
|
@ -333,19 +333,84 @@
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<b-radio v-model="contactIsKnown" disabled
|
<b-radio v-model="contactIsKnown"
|
||||||
: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="!contactIsKnown">
|
<div v-if="!contactIsKnown"
|
||||||
<b-field label="Customer Name" horizontal>
|
style="padding-left: 10rem; display: flex;">
|
||||||
<b-input v-model="customerName"></b-input>
|
<div>
|
||||||
|
<b-field label="Customer Name">
|
||||||
|
<span>{{ newCustomerName }}</span>
|
||||||
</b-field>
|
</b-field>
|
||||||
<b-field label="Phone Number" horizontal>
|
<b-field grouped>
|
||||||
<b-input v-model="phoneNumber"></b-input>
|
<b-field label="Phone Number">
|
||||||
|
<span>{{ newCustomerPhone }}</span>
|
||||||
</b-field>
|
</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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -577,7 +642,7 @@
|
||||||
batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n},
|
batchTotalPriceDisplay: ${json.dumps(normalized_batch['total_price_display'])|n},
|
||||||
|
|
||||||
customerPanelOpen: false,
|
customerPanelOpen: false,
|
||||||
contactIsKnown: true,
|
contactIsKnown: ${json.dumps(contact_is_known)|n},
|
||||||
% if new_order_requires_customer:
|
% if new_order_requires_customer:
|
||||||
contactUUID: ${json.dumps(batch.customer_uuid)|n},
|
contactUUID: ${json.dumps(batch.customer_uuid)|n},
|
||||||
% else:
|
% else:
|
||||||
|
@ -609,10 +674,17 @@
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
customerName: null,
|
newCustomerName: ${json.dumps(new_customer_name)|n},
|
||||||
phoneNumber: null,
|
newCustomerPhone: ${json.dumps(new_customer_phone)|n},
|
||||||
|
newCustomerEmail: ${json.dumps(new_customer_email)|n},
|
||||||
contactNotes: ${json.dumps(contact_notes)|n},
|
contactNotes: ${json.dumps(contact_notes)|n},
|
||||||
|
|
||||||
|
editNewCustomerShowDialog: false,
|
||||||
|
editNewCustomerName: null,
|
||||||
|
editNewCustomerPhone: null,
|
||||||
|
editNewCustomerEmail: null,
|
||||||
|
editNewCustomerSaving: false,
|
||||||
|
|
||||||
items: ${json.dumps(order_items)|n},
|
items: ${json.dumps(order_items)|n},
|
||||||
editingItem: null,
|
editingItem: null,
|
||||||
showingItemDialog: false,
|
showingItemDialog: false,
|
||||||
|
@ -646,8 +718,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.customerName) {
|
if (this.newCustomerName) {
|
||||||
text = "Customer: " + this.customerName
|
text = "Customer: " + this.newCustomerName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,19 +772,19 @@
|
||||||
}
|
}
|
||||||
phoneNumber = this.orderPhoneNumber
|
phoneNumber = this.orderPhoneNumber
|
||||||
} else { // customer is not known
|
} else { // customer is not known
|
||||||
if (!this.customerName) {
|
if (!this.newCustomerName) {
|
||||||
return {
|
return {
|
||||||
type: 'is-danger',
|
type: 'is-danger',
|
||||||
text: "Please identify the customer.",
|
text: "Please identify the customer.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.phoneNumber) {
|
if (!this.newCustomerPhone) {
|
||||||
return {
|
return {
|
||||||
type: 'is-warning',
|
type: 'is-warning',
|
||||||
text: "Please provide a phone number for the customer.",
|
text: "Please provide a phone number for the customer.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
phoneNumber = this.phoneNumber
|
phoneNumber = this.newCustomerPhone
|
||||||
}
|
}
|
||||||
|
|
||||||
let phoneDigits = phoneNumber.replace(/\D/g, '')
|
let phoneDigits = phoneNumber.replace(/\D/g, '')
|
||||||
|
@ -774,6 +846,26 @@
|
||||||
|
|
||||||
% endif
|
% 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() {
|
itemsPanelHeader() {
|
||||||
let text = "Items"
|
let text = "Items"
|
||||||
|
|
||||||
|
@ -1044,6 +1136,46 @@
|
||||||
|
|
||||||
% endif
|
% 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() {
|
showAddItemDialog() {
|
||||||
this.customerPanelOpen = false
|
this.customerPanelOpen = false
|
||||||
this.editingItem = null
|
this.editingItem = null
|
||||||
|
|
|
@ -450,6 +450,63 @@ class CustomerView(MasterView):
|
||||||
permission='{}.detach_person'.format(permission_prefix))
|
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??
|
# # TODO: this is referenced by some custom apps, but should be moved??
|
||||||
# def unique_id(value, field):
|
# def unique_id(value, field):
|
||||||
# customer = field.parent.model
|
# customer = field.parent.model
|
||||||
|
@ -491,3 +548,4 @@ def includeme(config):
|
||||||
renderer='json', permission='customers.view')
|
renderer='json', permission='customers.view')
|
||||||
|
|
||||||
CustomerView.defaults(config)
|
CustomerView.defaults(config)
|
||||||
|
PendingCustomerView.defaults(config)
|
||||||
|
|
|
@ -31,6 +31,7 @@ import six
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
|
from webhelpers2.html import tags
|
||||||
|
|
||||||
from tailbone import forms
|
from tailbone import forms
|
||||||
from tailbone.views.batch import BatchMasterView
|
from tailbone.views.batch import BatchMasterView
|
||||||
|
@ -61,6 +62,7 @@ class CustomerOrderBatchView(BatchMasterView):
|
||||||
'store',
|
'store',
|
||||||
'customer',
|
'customer',
|
||||||
'person',
|
'person',
|
||||||
|
'pending_customer',
|
||||||
'phone_number',
|
'phone_number',
|
||||||
'email_address',
|
'email_address',
|
||||||
'params',
|
'params',
|
||||||
|
@ -151,8 +153,19 @@ class CustomerOrderBatchView(BatchMasterView):
|
||||||
else:
|
else:
|
||||||
f.set_renderer('person', self.render_person)
|
f.set_renderer('person', self.render_person)
|
||||||
|
|
||||||
|
# pending_customer
|
||||||
|
f.set_renderer('pending_customer', self.render_pending_customer)
|
||||||
|
|
||||||
f.set_type('total_price', 'currency')
|
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):
|
def row_grid_extra_class(self, row, i):
|
||||||
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
|
if row.status_code == row.STATUS_PRODUCT_NOT_FOUND:
|
||||||
return 'warning'
|
return 'warning'
|
||||||
|
|
|
@ -230,6 +230,7 @@ class CustomerOrderView(MasterView):
|
||||||
'unassign_contact',
|
'unassign_contact',
|
||||||
'update_phone_number',
|
'update_phone_number',
|
||||||
'update_email_address',
|
'update_email_address',
|
||||||
|
'update_pending_customer',
|
||||||
'get_customer_info',
|
'get_customer_info',
|
||||||
# 'set_customer_data',
|
# 'set_customer_data',
|
||||||
'find_product_by_upc',
|
'find_product_by_upc',
|
||||||
|
@ -252,26 +253,17 @@ class CustomerOrderView(MasterView):
|
||||||
else:
|
else:
|
||||||
product_autocomplete = 'products.autocomplete'
|
product_autocomplete = 'products.autocomplete'
|
||||||
|
|
||||||
context = {'batch': batch,
|
context = self.get_context_contact(batch)
|
||||||
|
|
||||||
|
context.update({
|
||||||
|
'batch': batch,
|
||||||
'normalized_batch': self.normalize_batch(batch),
|
'normalized_batch': self.normalize_batch(batch),
|
||||||
'new_order_requires_customer': self.handler.new_order_requires_customer(),
|
'new_order_requires_customer': self.handler.new_order_requires_customer(),
|
||||||
'allow_contact_info_choice': self.handler.allow_contact_info_choice(),
|
'allow_contact_info_choice': self.handler.allow_contact_info_choice(),
|
||||||
'restrict_contact_info': self.handler.should_restrict_contact_info(),
|
'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,
|
'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)
|
||||||
|
|
||||||
|
@ -403,14 +395,38 @@ class CustomerOrderView(MasterView):
|
||||||
'customer_uuid': batch.customer_uuid,
|
'customer_uuid': batch.customer_uuid,
|
||||||
'person_uuid': batch.person_uuid,
|
'person_uuid': batch.person_uuid,
|
||||||
'phone_number': batch.phone_number,
|
'phone_number': batch.phone_number,
|
||||||
|
'contact_display': self.handler.get_contact_display(batch),
|
||||||
'email_address': batch.email_address,
|
'email_address': batch.email_address,
|
||||||
'contact_phones': self.handler.get_contact_phones(batch),
|
'contact_phones': self.handler.get_contact_phones(batch),
|
||||||
'contact_emails': self.handler.get_contact_emails(batch),
|
'contact_emails': self.handler.get_contact_emails(batch),
|
||||||
'contact_notes': self.handler.get_contact_notes(batch),
|
'contact_notes': self.handler.get_contact_notes(batch),
|
||||||
'add_phone_number': bool(batch.get_param('add_phone_number')),
|
'add_phone_number': bool(batch.get_param('add_phone_number')),
|
||||||
'add_email_address': bool(batch.get_param('add_email_address')),
|
'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
|
# maybe add profile URL
|
||||||
if batch.person_uuid:
|
if batch.person_uuid:
|
||||||
if self.request.has_perm('people.view_profile'):
|
if self.request.has_perm('people.view_profile'):
|
||||||
|
@ -457,6 +473,35 @@ class CustomerOrderView(MasterView):
|
||||||
'email_address': batch.email_address,
|
'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):
|
def product_autocomplete(self):
|
||||||
"""
|
"""
|
||||||
Custom product autocomplete logic, which invokes the handler.
|
Custom product autocomplete logic, which invokes the handler.
|
||||||
|
|
Loading…
Reference in a new issue