diff --git a/tailbone/templates/custorders/create.mako b/tailbone/templates/custorders/create.mako
index 4716404d..d921779e 100644
--- a/tailbone/templates/custorders/create.mako
+++ b/tailbone/templates/custorders/create.mako
@@ -333,19 +333,84 @@
-
Customer is not yet in the system.
-
-
-
-
-
-
-
+
+
+
+ {{ newCustomerName }}
+
+
+
+ {{ newCustomerPhone }}
+
+
+ {{ newCustomerEmail }}
+
+
+
+
+
+
+ Edit New Customer
+
+
+
+
+
+ Duplicate records can be difficult to clean up!
+ Please be sure the customer is not already in the system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -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
diff --git a/tailbone/views/customers.py b/tailbone/views/customers.py
index 27b19e94..65618c1a 100644
--- a/tailbone/views/customers.py
+++ b/tailbone/views/customers.py
@@ -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)
diff --git a/tailbone/views/custorders/batch.py b/tailbone/views/custorders/batch.py
index 1cced9de..fb47f247 100644
--- a/tailbone/views/custorders/batch.py
+++ b/tailbone/views/custorders/batch.py
@@ -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'
diff --git a/tailbone/views/custorders/orders.py b/tailbone/views/custorders/orders.py
index aa005dd1..1c6aed08 100644
--- a/tailbone/views/custorders/orders.py
+++ b/tailbone/views/custorders/orders.py
@@ -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,
- '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)}
+ context = self.get_context_contact(batch)
- # 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)
+ 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(),
+ 'order_items': items,
+ '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.